diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..6516fa3 --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,3 @@ +Copyright © Facebook Technologies, LLC and its affiliates. All rights reserved. + +Your use of this SDK or tool is subject to the Oculus SDK License Agreement, available at https://developer.oculus.com/licenses/oculussdk/ \ No newline at end of file diff --git a/OPENXR_SDK_THIRD_PARTY_NOTICES.txt b/OPENXR_SDK_THIRD_PARTY_NOTICES.txt new file mode 100644 index 0000000..40a3c31 --- /dev/null +++ b/OPENXR_SDK_THIRD_PARTY_NOTICES.txt @@ -0,0 +1,289 @@ +# Third Party Notices +THE FOLLOWING SETS FORTH ATTRIBUTION NOTICES FOR THIRD PARTY SOFTWARE THAT MAY BE CONTAINED IN PORTIONS OF THIS PRODUCT. + +## Apache License 2.0 +The following components are licensed under Apache License 2.0 reproduced below: + - KTX-Software , Copyright 2013-2020 Mark Callow ,Copyright 2010-2018 The Khronos Group, Inc. + - gradle 6.1.1 , © Gradle Inc. 2023 + - OpenXR-SDK , Copyright (c) 2017-2022, The Khronos Group Inc +Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. + +## MIT License +The following component is licensed under MIT License reproduced below: +cJSON , Copyright (c) 2009-2017 Dave Gamble and cJSON contributors +MIT License Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice (including the next +paragraph) shall be included in all copies or substantial portions of the +Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS +OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF +OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +## MIT or Public Domain +The following component is licensed under MIT or Public Domain reproduced below: + - stb , Copyright (c) 2017 Sean Barrett +This software is available under 2 licenses -- choose whichever you prefer. +------------------------------------------------------------------------------ +ALTERNATIVE A - MIT License +Copyright (c) 2017 Sean Barrett +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +------------------------------------------------------------------------------ +ALTERNATIVE B - Public Domain (www.unlicense.org) +This is free and unencumbered software released into the public domain. +Anyone is free to copy, modify, publish, use, compile, sell, or distribute this +software, either in source code form or as a compiled binary, for any purpose, +commercial or non-commercial, and by any means. +In jurisdictions that recognize copyright laws, the author or authors of this +software dedicate any and all copyright interest in the software to the public +domain. We make this dedication for the benefit of the public at large and to +the detriment of our heirs and successors. We intend this dedication to be an +overt act of relinquishment in perpetuity of all present and future rights to +this software under copyright law. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +## zlib License +The following components are licensed under zlib License reproduced below: + - minizip , Copyright (c) 1998-2010 Gilles Vollant (minizip) + - tinyxml2 + - zlib License + +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. diff --git a/OpenXR/meta_openxr_preview/extx1_event_channel.h b/OpenXR/meta_openxr_preview/extx1_event_channel.h new file mode 100755 index 0000000..c11797c --- /dev/null +++ b/OpenXR/meta_openxr_preview/extx1_event_channel.h @@ -0,0 +1,104 @@ +#ifndef EXTX1_EVENT_CHANNEL_H_ +#define EXTX1_EVENT_CHANNEL_H_ 1 + +/********************** +This file is @generated from the OpenXR XML API registry. +Language : C99 +Copyright : (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. +***********************/ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +#ifndef XR_EXTX1_event_channel + +// XR_EXTX1_event_channel is a preprocessor guard. Do not pass it to API calls. +#define XR_EXTX1_event_channel 1 +XR_DEFINE_HANDLE(XrEventChannelEXTX1) +#define XR_EXTX1_event_channel_SPEC_VERSION 3 +#define XR_EXTX1_EVENT_CHANNEL_EXTENSION_NAME "XR_EXTX1_event_channel" +static const XrStructureType XR_TYPE_EVENT_CHANNEL_CREATE_INFO_EXTX1 = (XrStructureType) 1000170001; +static const XrStructureType XR_TYPE_EVENT_CHANNEL_TARGET_EXTX1 = (XrStructureType) 1000170002; +static const XrStructureType XR_TYPE_SELECT_EVENT_CHANNEL_INFO_EXTX1 = (XrStructureType) 1000170003; +// XrEventChannelEXTX1 +static const XrObjectType XR_OBJECT_TYPE_EVENT_CHANNEL_EXTX1 = (XrObjectType) 1000170000; + +typedef enum XrSelectEventChannelFlagsEXTX1 { + XR_SELECT_EVENT_CHANNEL_FLAGS_EXTX1_MAX_ENUM = 0x7FFFFFFF +} XrSelectEventChannelFlagsEXTX1; +typedef struct XrEventChannelCreateInfoEXTX1 { + XrStructureType type; + const void* XR_MAY_ALIAS next; +} XrEventChannelCreateInfoEXTX1; + +typedef struct XrEventChannelTargetEXTX1 { + XrStructureType type; + const void* XR_MAY_ALIAS next; + XrEventChannelEXTX1 channel; +} XrEventChannelTargetEXTX1; + +typedef struct XrSelectEventChannelInfoEXTX1 { + XrStructureType type; + const void* XR_MAY_ALIAS next; + XrSelectEventChannelFlagsEXTX1 flags; + XrDuration timeout; + uint32_t eventChannelCount; + const XrEventChannelEXTX1* eventChannels; +} XrSelectEventChannelInfoEXTX1; + +typedef XrResult (XRAPI_PTR *PFN_xrCreateEventChannelEXTX1)(XrInstance instance, const XrEventChannelCreateInfoEXTX1* createInfo, XrEventChannelEXTX1* channel); +typedef XrResult (XRAPI_PTR *PFN_xrDestroyEventChannelEXTX1)(XrEventChannelEXTX1 channel); +typedef XrResult (XRAPI_PTR *PFN_xrPollEventChannelEXTX1)(XrEventChannelEXTX1 channel, XrEventDataBuffer* eventData); +typedef XrResult (XRAPI_PTR *PFN_xrSelectEventChannelEXTX1)(XrInstance instance, XrSelectEventChannelInfoEXTX1* info, uint32_t* channelWithEvent); +typedef XrResult (XRAPI_PTR *PFN_xrSetDefaultEventChannelEXTX1)(XrInstance instance, XrEventChannelEXTX1 channel); + +#ifndef XR_NO_PROTOTYPES +#ifdef XR_EXTENSION_PROTOTYPES +XRAPI_ATTR XrResult XRAPI_CALL xrCreateEventChannelEXTX1( + XrInstance instance, + const XrEventChannelCreateInfoEXTX1* createInfo, + XrEventChannelEXTX1* channel); + +XRAPI_ATTR XrResult XRAPI_CALL xrDestroyEventChannelEXTX1( + XrEventChannelEXTX1 channel); + +XRAPI_ATTR XrResult XRAPI_CALL xrPollEventChannelEXTX1( + XrEventChannelEXTX1 channel, + XrEventDataBuffer* eventData); + +XRAPI_ATTR XrResult XRAPI_CALL xrSelectEventChannelEXTX1( + XrInstance instance, + XrSelectEventChannelInfoEXTX1* info, + uint32_t* channelWithEvent); + +XRAPI_ATTR XrResult XRAPI_CALL xrSetDefaultEventChannelEXTX1( + XrInstance instance, + XrEventChannelEXTX1 channel); +#endif /* XR_EXTENSION_PROTOTYPES */ +#endif /* !XR_NO_PROTOTYPES */ +#endif /* XR_EXTX1_event_channel */ + +#ifdef XR_EXTX1_EVENT_CHANNEL_TAG_ALIAS +typedef XrEventChannelCreateInfoEXTX1 XrEventChannelCreateInfoEXT; +typedef XrEventChannelEXTX1 XrEventChannelEXT; +typedef XrEventChannelTargetEXTX1 XrEventChannelTargetEXT; +typedef XrSelectEventChannelFlagsEXTX1 XrSelectEventChannelFlagsEXT; +typedef XrSelectEventChannelInfoEXTX1 XrSelectEventChannelInfoEXT; +#define XR_EXT_event_channel_SPEC_VERSION XR_EXTX1_event_channel_SPEC_VERSION +#define XR_EXT_EVENT_CHANNEL_EXTENSION_NAME XR_EXTX1_EVENT_CHANNEL_EXTENSION_NAME +#define XR_TYPE_EVENT_CHANNEL_CREATE_INFO_EXT XR_TYPE_EVENT_CHANNEL_CREATE_INFO_EXTX1 +#define XR_TYPE_EVENT_CHANNEL_TARGET_EXT XR_TYPE_EVENT_CHANNEL_TARGET_EXTX1 +#define XR_TYPE_SELECT_EVENT_CHANNEL_INFO_EXT XR_TYPE_SELECT_EVENT_CHANNEL_INFO_EXTX1 +#define XR_OBJECT_TYPE_EVENT_CHANNEL_EXT XR_OBJECT_TYPE_EVENT_CHANNEL_EXTX1 +#endif /* XR_EXTX1_EVENT_CHANNEL_TAG_ALIAS */ + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/OpenXR/meta_openxr_preview/meta_body_tracking_calibration.h b/OpenXR/meta_openxr_preview/meta_body_tracking_calibration.h new file mode 100755 index 0000000..119b649 --- /dev/null +++ b/OpenXR/meta_openxr_preview/meta_body_tracking_calibration.h @@ -0,0 +1,74 @@ +#ifndef META_BODY_TRACKING_CALIBRATION_H_ +#define META_BODY_TRACKING_CALIBRATION_H_ 1 + +/********************** +This file is @generated from the OpenXR XML API registry. +Language : C99 +Copyright : (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. +***********************/ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +#ifndef XR_META_body_tracking_calibration + +// XR_META_body_tracking_calibration is a preprocessor guard. Do not pass it to API calls. +#define XR_META_body_tracking_calibration 1 +#define XR_META_body_tracking_calibration_SPEC_VERSION 1 +#define XR_META_BODY_TRACKING_CALIBRATION_EXTENSION_NAME "XR_META_body_tracking_calibration" +static const XrStructureType XR_TYPE_BODY_TRACKING_CALIBRATION_INFO_META = (XrStructureType) 1000283002; +static const XrStructureType XR_TYPE_BODY_TRACKING_CALIBRATION_STATUS_META = (XrStructureType) 1000283003; +static const XrStructureType XR_TYPE_SYSTEM_PROPERTIES_BODY_TRACKING_CALIBRATION_META = (XrStructureType) 1000283004; + +typedef enum XrBodyTrackingCalibrationStateMETA { + // Valid calibration, pose is safe to use + XR_BODY_TRACKING_CALIBRATION_STATE_VALID_META = 1, + // Calibration is still running, pose may be incorrect + XR_BODY_TRACKING_CALIBRATION_STATE_CALIBRATING_META = 2, + // Calibration is invalid, pose is not safe to use + XR_BODY_TRACKING_CALIBRATION_STATE_INVALID_META = 3, + XR_BODY_TRACKING_CALIBRATION_STATE_MAX_ENUM_META = 0x7FFFFFFF +} XrBodyTrackingCalibrationStateMETA; +// XrBodyTrackingCalibrationStatusMETA extends XrBodyJointLocationsFB +typedef struct XrBodyTrackingCalibrationStatusMETA { + XrStructureType type; + void* XR_MAY_ALIAS next; + XrBodyTrackingCalibrationStateMETA status; +} XrBodyTrackingCalibrationStatusMETA; + +typedef struct XrBodyTrackingCalibrationInfoMETA { + XrStructureType type; + const void* XR_MAY_ALIAS next; + float bodyHeight; +} XrBodyTrackingCalibrationInfoMETA; + +typedef struct XrSystemPropertiesBodyTrackingCalibrationMETA { + XrStructureType type; + void* XR_MAY_ALIAS next; + XrBool32 supportsHeightOverride; +} XrSystemPropertiesBodyTrackingCalibrationMETA; + +typedef XrResult (XRAPI_PTR *PFN_xrSuggestBodyTrackingCalibrationOverrideMETA)(XrBodyTrackerFB bodyTracker, const XrBodyTrackingCalibrationInfoMETA* calibrationInfo); +typedef XrResult (XRAPI_PTR *PFN_xrResetBodyTrackingCalibrationMETA)(XrBodyTrackerFB bodyTracker); + +#ifndef XR_NO_PROTOTYPES +#ifdef XR_EXTENSION_PROTOTYPES +XRAPI_ATTR XrResult XRAPI_CALL xrSuggestBodyTrackingCalibrationOverrideMETA( + XrBodyTrackerFB bodyTracker, + const XrBodyTrackingCalibrationInfoMETA* calibrationInfo); + +XRAPI_ATTR XrResult XRAPI_CALL xrResetBodyTrackingCalibrationMETA( + XrBodyTrackerFB bodyTracker); +#endif /* XR_EXTENSION_PROTOTYPES */ +#endif /* !XR_NO_PROTOTYPES */ +#endif /* XR_META_body_tracking_calibration */ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/OpenXR/meta_openxr_preview/meta_body_tracking_fidelity.h b/OpenXR/meta_openxr_preview/meta_body_tracking_fidelity.h new file mode 100755 index 0000000..bbbb7a0 --- /dev/null +++ b/OpenXR/meta_openxr_preview/meta_body_tracking_fidelity.h @@ -0,0 +1,59 @@ +#ifndef META_BODY_TRACKING_FIDELITY_H_ +#define META_BODY_TRACKING_FIDELITY_H_ 1 + +/********************** +This file is @generated from the OpenXR XML API registry. +Language : C99 +Copyright : (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. +***********************/ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +#ifndef XR_META_body_tracking_fidelity + +// XR_META_body_tracking_fidelity is a preprocessor guard. Do not pass it to API calls. +#define XR_META_body_tracking_fidelity 1 +#define XR_META_body_tracking_fidelity_SPEC_VERSION 1 +#define XR_META_BODY_TRACKING_FIDELITY_EXTENSION_NAME "XR_META_body_tracking_fidelity" +static const XrStructureType XR_TYPE_BODY_TRACKING_FIDELITY_STATUS_META = (XrStructureType) 1000284000; +static const XrStructureType XR_TYPE_SYSTEM_PROPERTIES_BODY_TRACKING_FIDELITY_META = (XrStructureType) 1000284001; + +typedef enum XrBodyTrackingFidelityMETA { + XR_BODY_TRACKING_FIDELITY_LOW_META = 1, + XR_BODY_TRACKING_FIDELITY_HIGH_META = 2, + XR_BODY_TRACKING_FIDELITY_MAX_ENUM_META = 0x7FFFFFFF +} XrBodyTrackingFidelityMETA; +typedef struct XrSystemPropertiesBodyTrackingFidelityMETA { + XrStructureType type; + void* XR_MAY_ALIAS next; + XrBool32 supportsBodyTrackingFidelity; +} XrSystemPropertiesBodyTrackingFidelityMETA; + +// XrBodyTrackingFidelityStatusMETA extends XrBodyJointLocationsFB +typedef struct XrBodyTrackingFidelityStatusMETA { + XrStructureType type; + const void* XR_MAY_ALIAS next; + XrBodyTrackingFidelityMETA fidelity; +} XrBodyTrackingFidelityStatusMETA; + +typedef XrResult (XRAPI_PTR *PFN_xrRequestBodyTrackingFidelityMETA)(XrBodyTrackerFB bodyTracker, const XrBodyTrackingFidelityMETA fidelity); + +#ifndef XR_NO_PROTOTYPES +#ifdef XR_EXTENSION_PROTOTYPES +XRAPI_ATTR XrResult XRAPI_CALL xrRequestBodyTrackingFidelityMETA( + XrBodyTrackerFB bodyTracker, + const XrBodyTrackingFidelityMETA fidelity); +#endif /* XR_EXTENSION_PROTOTYPES */ +#endif /* !XR_NO_PROTOTYPES */ +#endif /* XR_META_body_tracking_fidelity */ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/OpenXR/meta_openxr_preview/meta_body_tracking_full_body.h b/OpenXR/meta_openxr_preview/meta_body_tracking_full_body.h new file mode 100755 index 0000000..b496c39 --- /dev/null +++ b/OpenXR/meta_openxr_preview/meta_body_tracking_full_body.h @@ -0,0 +1,127 @@ +#ifndef META_BODY_TRACKING_FULL_BODY_H_ +#define META_BODY_TRACKING_FULL_BODY_H_ 1 + +/********************** +This file is @generated from the OpenXR XML API registry. +Language : C99 +Copyright : (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. +***********************/ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +#ifndef XR_META_body_tracking_full_body + +// XR_META_body_tracking_full_body is a preprocessor guard. Do not pass it to API calls. +#define XR_META_body_tracking_full_body 1 +#define XR_META_body_tracking_full_body_SPEC_VERSION 1 +#define XR_META_BODY_TRACKING_FULL_BODY_EXTENSION_NAME "XR_META_body_tracking_full_body" +static const XrStructureType XR_TYPE_SYSTEM_PROPERTIES_BODY_TRACKING_FULL_BODY_META = (XrStructureType) 1000274000; +static const XrBodyJointSetFB XR_BODY_JOINT_SET_FULL_BODY_META = (XrBodyJointSetFB) 1000274000; + +typedef enum XrFullBodyJointMETA { + XR_FULL_BODY_JOINT_ROOT_META = 0, + XR_FULL_BODY_JOINT_HIPS_META = 1, + XR_FULL_BODY_JOINT_SPINE_LOWER_META = 2, + XR_FULL_BODY_JOINT_SPINE_MIDDLE_META = 3, + XR_FULL_BODY_JOINT_SPINE_UPPER_META = 4, + XR_FULL_BODY_JOINT_CHEST_META = 5, + XR_FULL_BODY_JOINT_NECK_META = 6, + XR_FULL_BODY_JOINT_HEAD_META = 7, + XR_FULL_BODY_JOINT_LEFT_SHOULDER_META = 8, + XR_FULL_BODY_JOINT_LEFT_SCAPULA_META = 9, + XR_FULL_BODY_JOINT_LEFT_ARM_UPPER_META = 10, + XR_FULL_BODY_JOINT_LEFT_ARM_LOWER_META = 11, + XR_FULL_BODY_JOINT_LEFT_HAND_WRIST_TWIST_META = 12, + XR_FULL_BODY_JOINT_RIGHT_SHOULDER_META = 13, + XR_FULL_BODY_JOINT_RIGHT_SCAPULA_META = 14, + XR_FULL_BODY_JOINT_RIGHT_ARM_UPPER_META = 15, + XR_FULL_BODY_JOINT_RIGHT_ARM_LOWER_META = 16, + XR_FULL_BODY_JOINT_RIGHT_HAND_WRIST_TWIST_META = 17, + XR_FULL_BODY_JOINT_LEFT_HAND_PALM_META = 18, + XR_FULL_BODY_JOINT_LEFT_HAND_WRIST_META = 19, + XR_FULL_BODY_JOINT_LEFT_HAND_THUMB_METACARPAL_META = 20, + XR_FULL_BODY_JOINT_LEFT_HAND_THUMB_PROXIMAL_META = 21, + XR_FULL_BODY_JOINT_LEFT_HAND_THUMB_DISTAL_META = 22, + XR_FULL_BODY_JOINT_LEFT_HAND_THUMB_TIP_META = 23, + XR_FULL_BODY_JOINT_LEFT_HAND_INDEX_METACARPAL_META = 24, + XR_FULL_BODY_JOINT_LEFT_HAND_INDEX_PROXIMAL_META = 25, + XR_FULL_BODY_JOINT_LEFT_HAND_INDEX_INTERMEDIATE_META = 26, + XR_FULL_BODY_JOINT_LEFT_HAND_INDEX_DISTAL_META = 27, + XR_FULL_BODY_JOINT_LEFT_HAND_INDEX_TIP_META = 28, + XR_FULL_BODY_JOINT_LEFT_HAND_MIDDLE_METACARPAL_META = 29, + XR_FULL_BODY_JOINT_LEFT_HAND_MIDDLE_PROXIMAL_META = 30, + XR_FULL_BODY_JOINT_LEFT_HAND_MIDDLE_INTERMEDIATE_META = 31, + XR_FULL_BODY_JOINT_LEFT_HAND_MIDDLE_DISTAL_META = 32, + XR_FULL_BODY_JOINT_LEFT_HAND_MIDDLE_TIP_META = 33, + XR_FULL_BODY_JOINT_LEFT_HAND_RING_METACARPAL_META = 34, + XR_FULL_BODY_JOINT_LEFT_HAND_RING_PROXIMAL_META = 35, + XR_FULL_BODY_JOINT_LEFT_HAND_RING_INTERMEDIATE_META = 36, + XR_FULL_BODY_JOINT_LEFT_HAND_RING_DISTAL_META = 37, + XR_FULL_BODY_JOINT_LEFT_HAND_RING_TIP_META = 38, + XR_FULL_BODY_JOINT_LEFT_HAND_LITTLE_METACARPAL_META = 39, + XR_FULL_BODY_JOINT_LEFT_HAND_LITTLE_PROXIMAL_META = 40, + XR_FULL_BODY_JOINT_LEFT_HAND_LITTLE_INTERMEDIATE_META = 41, + XR_FULL_BODY_JOINT_LEFT_HAND_LITTLE_DISTAL_META = 42, + XR_FULL_BODY_JOINT_LEFT_HAND_LITTLE_TIP_META = 43, + XR_FULL_BODY_JOINT_RIGHT_HAND_PALM_META = 44, + XR_FULL_BODY_JOINT_RIGHT_HAND_WRIST_META = 45, + XR_FULL_BODY_JOINT_RIGHT_HAND_THUMB_METACARPAL_META = 46, + XR_FULL_BODY_JOINT_RIGHT_HAND_THUMB_PROXIMAL_META = 47, + XR_FULL_BODY_JOINT_RIGHT_HAND_THUMB_DISTAL_META = 48, + XR_FULL_BODY_JOINT_RIGHT_HAND_THUMB_TIP_META = 49, + XR_FULL_BODY_JOINT_RIGHT_HAND_INDEX_METACARPAL_META = 50, + XR_FULL_BODY_JOINT_RIGHT_HAND_INDEX_PROXIMAL_META = 51, + XR_FULL_BODY_JOINT_RIGHT_HAND_INDEX_INTERMEDIATE_META = 52, + XR_FULL_BODY_JOINT_RIGHT_HAND_INDEX_DISTAL_META = 53, + XR_FULL_BODY_JOINT_RIGHT_HAND_INDEX_TIP_META = 54, + XR_FULL_BODY_JOINT_RIGHT_HAND_MIDDLE_METACARPAL_META = 55, + XR_FULL_BODY_JOINT_RIGHT_HAND_MIDDLE_PROXIMAL_META = 56, + XR_FULL_BODY_JOINT_RIGHT_HAND_MIDDLE_INTERMEDIATE_META = 57, + XR_FULL_BODY_JOINT_RIGHT_HAND_MIDDLE_DISTAL_META = 58, + XR_FULL_BODY_JOINT_RIGHT_HAND_MIDDLE_TIP_META = 59, + XR_FULL_BODY_JOINT_RIGHT_HAND_RING_METACARPAL_META = 60, + XR_FULL_BODY_JOINT_RIGHT_HAND_RING_PROXIMAL_META = 61, + XR_FULL_BODY_JOINT_RIGHT_HAND_RING_INTERMEDIATE_META = 62, + XR_FULL_BODY_JOINT_RIGHT_HAND_RING_DISTAL_META = 63, + XR_FULL_BODY_JOINT_RIGHT_HAND_RING_TIP_META = 64, + XR_FULL_BODY_JOINT_RIGHT_HAND_LITTLE_METACARPAL_META = 65, + XR_FULL_BODY_JOINT_RIGHT_HAND_LITTLE_PROXIMAL_META = 66, + XR_FULL_BODY_JOINT_RIGHT_HAND_LITTLE_INTERMEDIATE_META = 67, + XR_FULL_BODY_JOINT_RIGHT_HAND_LITTLE_DISTAL_META = 68, + XR_FULL_BODY_JOINT_RIGHT_HAND_LITTLE_TIP_META = 69, + XR_FULL_BODY_JOINT_LEFT_UPPER_LEG_META = 70, + XR_FULL_BODY_JOINT_LEFT_LOWER_LEG_META = 71, + XR_FULL_BODY_JOINT_LEFT_FOOT_ANKLE_TWIST_META = 72, + XR_FULL_BODY_JOINT_LEFT_FOOT_ANKLE_META = 73, + XR_FULL_BODY_JOINT_LEFT_FOOT_SUBTALAR_META = 74, + XR_FULL_BODY_JOINT_LEFT_FOOT_TRANSVERSE_META = 75, + XR_FULL_BODY_JOINT_LEFT_FOOT_BALL_META = 76, + XR_FULL_BODY_JOINT_RIGHT_UPPER_LEG_META = 77, + XR_FULL_BODY_JOINT_RIGHT_LOWER_LEG_META = 78, + XR_FULL_BODY_JOINT_RIGHT_FOOT_ANKLE_TWIST_META = 79, + XR_FULL_BODY_JOINT_RIGHT_FOOT_ANKLE_META = 80, + XR_FULL_BODY_JOINT_RIGHT_FOOT_SUBTALAR_META = 81, + XR_FULL_BODY_JOINT_RIGHT_FOOT_TRANSVERSE_META = 82, + XR_FULL_BODY_JOINT_RIGHT_FOOT_BALL_META = 83, + XR_FULL_BODY_JOINT_COUNT_META = 84, + XR_FULL_BODY_JOINT_NONE_META = 85, + XR_FULL_BODY_JOINT_MAX_ENUM_META = 0x7FFFFFFF +} XrFullBodyJointMETA; +typedef struct XrSystemPropertiesBodyTrackingFullBodyMETA { + XrStructureType type; + void* XR_MAY_ALIAS next; + XrBool32 supportsFullBodyTracking; +} XrSystemPropertiesBodyTrackingFullBodyMETA; + +#endif /* XR_META_body_tracking_full_body */ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/OpenXR/meta_openxr_preview/meta_boundary_visibility.h b/OpenXR/meta_openxr_preview/meta_boundary_visibility.h new file mode 100755 index 0000000..3186164 --- /dev/null +++ b/OpenXR/meta_openxr_preview/meta_boundary_visibility.h @@ -0,0 +1,61 @@ +#ifndef META_BOUNDARY_VISIBILITY_H_ +#define META_BOUNDARY_VISIBILITY_H_ 1 + +/********************** +This file is @generated from the OpenXR XML API registry. +Language : C99 +Copyright : (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. +***********************/ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +#ifndef XR_META_boundary_visibility + +// XR_META_boundary_visibility is a preprocessor guard. Do not pass it to API calls. +#define XR_META_boundary_visibility 1 +#define XR_META_boundary_visibility_SPEC_VERSION 1 +#define XR_META_BOUNDARY_VISIBILITY_EXTENSION_NAME "XR_META_boundary_visibility" +static const XrResult XR_BOUNDARY_VISIBILITY_SUPPRESSION_NOT_ALLOWED_META = (XrResult) 1000528000; +static const XrStructureType XR_TYPE_SYSTEM_BOUNDARY_VISIBILITY_PROPERTIES_META = (XrStructureType) 1000528000; +static const XrStructureType XR_TYPE_EVENT_DATA_BOUNDARY_VISIBILITY_CHANGED_META = (XrStructureType) 1000528001; + +typedef enum XrBoundaryVisibilityMETA { + // Boundary is not suppressed. + XR_BOUNDARY_VISIBILITY_NOT_SUPPRESSED_META = 1, + // Boundary is suppressed. + XR_BOUNDARY_VISIBILITY_SUPPRESSED_META = 2, + XR_BOUNDARY_VISIBILITY_MAX_ENUM_META = 0x7FFFFFFF +} XrBoundaryVisibilityMETA; +typedef struct XrSystemBoundaryVisibilityPropertiesMETA { + XrStructureType type; + const void* XR_MAY_ALIAS next; + XrBool32 supportsBoundaryVisibility; +} XrSystemBoundaryVisibilityPropertiesMETA; + +typedef struct XrEventDataBoundaryVisibilityChangedMETA { + XrStructureType type; + const void* XR_MAY_ALIAS next; + XrBoundaryVisibilityMETA boundaryVisibility; +} XrEventDataBoundaryVisibilityChangedMETA; + +typedef XrResult (XRAPI_PTR *PFN_xrRequestBoundaryVisibilityMETA)(XrSession session, XrBoundaryVisibilityMETA boundaryVisibility); + +#ifndef XR_NO_PROTOTYPES +#ifdef XR_EXTENSION_PROTOTYPES +XRAPI_ATTR XrResult XRAPI_CALL xrRequestBoundaryVisibilityMETA( + XrSession session, + XrBoundaryVisibilityMETA boundaryVisibility); +#endif /* XR_EXTENSION_PROTOTYPES */ +#endif /* !XR_NO_PROTOTYPES */ +#endif /* XR_META_boundary_visibility */ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/OpenXR/meta_openxr_preview/meta_detached_controllers.h b/OpenXR/meta_openxr_preview/meta_detached_controllers.h new file mode 100755 index 0000000..b89e1fa --- /dev/null +++ b/OpenXR/meta_openxr_preview/meta_detached_controllers.h @@ -0,0 +1,29 @@ +#ifndef META_DETACHED_CONTROLLERS_H_ +#define META_DETACHED_CONTROLLERS_H_ 1 + +/********************** +This file is @generated from the OpenXR XML API registry. +Language : C99 +Copyright : (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. +***********************/ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +#ifndef XR_META_detached_controllers + +// XR_META_detached_controllers is a preprocessor guard. Do not pass it to API calls. +#define XR_META_detached_controllers 1 +#define XR_META_detached_controllers_SPEC_VERSION 1 +#define XR_META_DETACHED_CONTROLLERS_EXTENSION_NAME "XR_META_detached_controllers" +#endif /* XR_META_detached_controllers */ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/OpenXR/meta_openxr_preview/meta_hand_tracking_wide_motion_mode.h b/OpenXR/meta_openxr_preview/meta_hand_tracking_wide_motion_mode.h new file mode 100755 index 0000000..91c53e0 --- /dev/null +++ b/OpenXR/meta_openxr_preview/meta_hand_tracking_wide_motion_mode.h @@ -0,0 +1,43 @@ +#ifndef META_HAND_TRACKING_WIDE_MOTION_MODE_H_ +#define META_HAND_TRACKING_WIDE_MOTION_MODE_H_ 1 + +/********************** +This file is @generated from the OpenXR XML API registry. +Language : C99 +Copyright : (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. +***********************/ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +#ifndef XR_META_hand_tracking_wide_motion_mode + +// XR_META_hand_tracking_wide_motion_mode is a preprocessor guard. Do not pass it to API calls. +#define XR_META_hand_tracking_wide_motion_mode 1 +#define XR_META_hand_tracking_wide_motion_mode_SPEC_VERSION 1 +#define XR_META_HAND_TRACKING_WIDE_MOTION_MODE_EXTENSION_NAME "XR_META_hand_tracking_wide_motion_mode" +static const XrStructureType XR_TYPE_HAND_TRACKING_WIDE_MOTION_MODE_INFO_META = (XrStructureType) 1000539000; + +typedef enum XrHandTrackingWideMotionModeMETA { + // The runtime will attempt to leverage high fidelity body tracking algorithms to calculate hand pose infomation outside of the range of usual tracking area. + XR_HAND_TRACKING_WIDE_MOTION_MODE_HIGH_FIDELITY_BODY_TRACKING_META = 1, + XR_HAND_TRACKING_WIDE_MOTION_MODE_MAX_ENUM_META = 0x7FFFFFFF +} XrHandTrackingWideMotionModeMETA; +// XrHandTrackingWideMotionModeInfoMETA extends XrHandTrackerCreateInfoEXT +typedef struct XrHandTrackingWideMotionModeInfoMETA { + XrStructureType type; + const void* XR_MAY_ALIAS next; + XrHandTrackingWideMotionModeMETA requestedWideMotionMode; +} XrHandTrackingWideMotionModeInfoMETA; + +#endif /* XR_META_hand_tracking_wide_motion_mode */ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/OpenXR/meta_openxr_preview/meta_simultaneous_hands_and_controllers.h b/OpenXR/meta_openxr_preview/meta_simultaneous_hands_and_controllers.h new file mode 100755 index 0000000..98c5ce9 --- /dev/null +++ b/OpenXR/meta_openxr_preview/meta_simultaneous_hands_and_controllers.h @@ -0,0 +1,62 @@ +#ifndef META_SIMULTANEOUS_HANDS_AND_CONTROLLERS_H_ +#define META_SIMULTANEOUS_HANDS_AND_CONTROLLERS_H_ 1 + +/********************** +This file is @generated from the OpenXR XML API registry. +Language : C99 +Copyright : (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. +***********************/ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +#ifndef XR_META_simultaneous_hands_and_controllers + +// XR_META_simultaneous_hands_and_controllers is a preprocessor guard. Do not pass it to API calls. +#define XR_META_simultaneous_hands_and_controllers 1 +#define XR_META_simultaneous_hands_and_controllers_SPEC_VERSION 1 +#define XR_META_SIMULTANEOUS_HANDS_AND_CONTROLLERS_EXTENSION_NAME "XR_META_simultaneous_hands_and_controllers" +static const XrStructureType XR_TYPE_SYSTEM_SIMULTANEOUS_HANDS_AND_CONTROLLERS_PROPERTIES_META = (XrStructureType) 1000532001; +static const XrStructureType XR_TYPE_SIMULTANEOUS_HANDS_AND_CONTROLLERS_TRACKING_RESUME_INFO_META = (XrStructureType) 1000532002; +static const XrStructureType XR_TYPE_SIMULTANEOUS_HANDS_AND_CONTROLLERS_TRACKING_PAUSE_INFO_META = (XrStructureType) 1000532003; +typedef struct XrSystemSimultaneousHandsAndControllersPropertiesMETA { + XrStructureType type; + void* XR_MAY_ALIAS next; + XrBool32 supportsSimultaneousHandsAndControllers; +} XrSystemSimultaneousHandsAndControllersPropertiesMETA; + +typedef struct XrSimultaneousHandsAndControllersTrackingResumeInfoMETA { + XrStructureType type; + const void* XR_MAY_ALIAS next; +} XrSimultaneousHandsAndControllersTrackingResumeInfoMETA; + +typedef struct XrSimultaneousHandsAndControllersTrackingPauseInfoMETA { + XrStructureType type; + const void* XR_MAY_ALIAS next; +} XrSimultaneousHandsAndControllersTrackingPauseInfoMETA; + +typedef XrResult (XRAPI_PTR *PFN_xrResumeSimultaneousHandsAndControllersTrackingMETA)(XrSession session, const XrSimultaneousHandsAndControllersTrackingResumeInfoMETA* resumeInfo); +typedef XrResult (XRAPI_PTR *PFN_xrPauseSimultaneousHandsAndControllersTrackingMETA)(XrSession session, const XrSimultaneousHandsAndControllersTrackingPauseInfoMETA* pauseInfo); + +#ifndef XR_NO_PROTOTYPES +#ifdef XR_EXTENSION_PROTOTYPES +XRAPI_ATTR XrResult XRAPI_CALL xrResumeSimultaneousHandsAndControllersTrackingMETA( + XrSession session, + const XrSimultaneousHandsAndControllersTrackingResumeInfoMETA* resumeInfo); + +XRAPI_ATTR XrResult XRAPI_CALL xrPauseSimultaneousHandsAndControllersTrackingMETA( + XrSession session, + const XrSimultaneousHandsAndControllersTrackingPauseInfoMETA* pauseInfo); +#endif /* XR_EXTENSION_PROTOTYPES */ +#endif /* !XR_NO_PROTOTYPES */ +#endif /* XR_META_simultaneous_hands_and_controllers */ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/OpenXR/meta_openxr_preview/meta_spatial_entity_discovery.h b/OpenXR/meta_openxr_preview/meta_spatial_entity_discovery.h new file mode 100755 index 0000000..0f81a5e --- /dev/null +++ b/OpenXR/meta_openxr_preview/meta_spatial_entity_discovery.h @@ -0,0 +1,110 @@ +#ifndef META_SPATIAL_ENTITY_DISCOVERY_H_ +#define META_SPATIAL_ENTITY_DISCOVERY_H_ 1 + +/********************** +This file is @generated from the OpenXR XML API registry. +Language : C99 +Copyright : (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. +***********************/ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +#ifndef XR_META_spatial_entity_discovery + +// XR_META_spatial_entity_discovery is a preprocessor guard. Do not pass it to API calls. +#define XR_META_spatial_entity_discovery 1 +#define XR_META_spatial_entity_discovery_SPEC_VERSION 1 +#define XR_META_SPATIAL_ENTITY_DISCOVERY_EXTENSION_NAME "XR_META_spatial_entity_discovery" +static const XrStructureType XR_TYPE_SYSTEM_SPACE_DISCOVERY_PROPERTIES_META = (XrStructureType) 1000247000; +static const XrStructureType XR_TYPE_SPACE_DISCOVERY_INFO_META = (XrStructureType) 1000247001; +static const XrStructureType XR_TYPE_SPACE_FILTER_UUID_META = (XrStructureType) 1000247003; +static const XrStructureType XR_TYPE_SPACE_FILTER_COMPONENT_META = (XrStructureType) 1000247004; +static const XrStructureType XR_TYPE_SPACE_DISCOVERY_RESULT_META = (XrStructureType) 1000247005; +static const XrStructureType XR_TYPE_SPACE_DISCOVERY_RESULTS_META = (XrStructureType) 1000247006; +static const XrStructureType XR_TYPE_EVENT_DATA_SPACE_DISCOVERY_RESULTS_AVAILABLE_META = (XrStructureType) 1000247007; +static const XrStructureType XR_TYPE_EVENT_DATA_SPACE_DISCOVERY_COMPLETE_META = (XrStructureType) 1000247008; +typedef struct XrSystemSpaceDiscoveryPropertiesMETA { + XrStructureType type; + const void* XR_MAY_ALIAS next; + XrBool32 supportsSpaceDiscovery; +} XrSystemSpaceDiscoveryPropertiesMETA; + +typedef struct XR_MAY_ALIAS XrSpaceFilterBaseHeaderMETA { + XrStructureType type; + const void* XR_MAY_ALIAS next; +} XrSpaceFilterBaseHeaderMETA; + +typedef struct XrSpaceDiscoveryInfoMETA { + XrStructureType type; + const void* XR_MAY_ALIAS next; + uint32_t filterCount; + const XrSpaceFilterBaseHeaderMETA* const * filters; +} XrSpaceDiscoveryInfoMETA; + +typedef struct XrSpaceFilterUuidMETA { + XrStructureType type; + const void* XR_MAY_ALIAS next; + uint32_t uuidCount; + const XrUuidEXT* uuids; +} XrSpaceFilterUuidMETA; + +typedef struct XrSpaceFilterComponentMETA { + XrStructureType type; + const void* XR_MAY_ALIAS next; + XrSpaceComponentTypeFB componentType; +} XrSpaceFilterComponentMETA; + +typedef struct XrSpaceDiscoveryResultMETA { + XrSpace space; + XrUuidEXT uuid; +} XrSpaceDiscoveryResultMETA; + +typedef struct XrSpaceDiscoveryResultsMETA { + XrStructureType type; + const void* XR_MAY_ALIAS next; + uint32_t resultCapacityInput; + uint32_t resultCountOutput; + XrSpaceDiscoveryResultMETA* results; +} XrSpaceDiscoveryResultsMETA; + +typedef struct XrEventDataSpaceDiscoveryResultsAvailableMETA { + XrStructureType type; + const void* XR_MAY_ALIAS next; + XrAsyncRequestIdFB requestId; +} XrEventDataSpaceDiscoveryResultsAvailableMETA; + +typedef struct XrEventDataSpaceDiscoveryCompleteMETA { + XrStructureType type; + const void* XR_MAY_ALIAS next; + XrAsyncRequestIdFB requestId; + XrResult result; +} XrEventDataSpaceDiscoveryCompleteMETA; + +typedef XrResult (XRAPI_PTR *PFN_xrDiscoverSpacesMETA)(XrSession session, const XrSpaceDiscoveryInfoMETA* info, XrAsyncRequestIdFB* requestId); +typedef XrResult (XRAPI_PTR *PFN_xrRetrieveSpaceDiscoveryResultsMETA)(XrSession session, XrAsyncRequestIdFB requestId, XrSpaceDiscoveryResultsMETA* results); + +#ifndef XR_NO_PROTOTYPES +#ifdef XR_EXTENSION_PROTOTYPES +XRAPI_ATTR XrResult XRAPI_CALL xrDiscoverSpacesMETA( + XrSession session, + const XrSpaceDiscoveryInfoMETA* info, + XrAsyncRequestIdFB* requestId); + +XRAPI_ATTR XrResult XRAPI_CALL xrRetrieveSpaceDiscoveryResultsMETA( + XrSession session, + XrAsyncRequestIdFB requestId, + XrSpaceDiscoveryResultsMETA* results); +#endif /* XR_EXTENSION_PROTOTYPES */ +#endif /* !XR_NO_PROTOTYPES */ +#endif /* XR_META_spatial_entity_discovery */ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/OpenXR/meta_openxr_preview/meta_spatial_entity_persistence.h b/OpenXR/meta_openxr_preview/meta_spatial_entity_persistence.h new file mode 100755 index 0000000..389de95 --- /dev/null +++ b/OpenXR/meta_openxr_preview/meta_spatial_entity_persistence.h @@ -0,0 +1,100 @@ +#ifndef META_SPATIAL_ENTITY_PERSISTENCE_H_ +#define META_SPATIAL_ENTITY_PERSISTENCE_H_ 1 + +/********************** +This file is @generated from the OpenXR XML API registry. +Language : C99 +Copyright : (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. +***********************/ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +#ifndef XR_META_spatial_entity_persistence + +// XR_META_spatial_entity_persistence is a preprocessor guard. Do not pass it to API calls. +#define XR_META_spatial_entity_persistence 1 +#define XR_META_spatial_entity_persistence_SPEC_VERSION 1 +#define XR_META_SPATIAL_ENTITY_PERSISTENCE_EXTENSION_NAME "XR_META_spatial_entity_persistence" +// Error: Resource limitation prevented this operation from executing. Recommend retrying, perhaps after a short delay and/or reducing memory consumption. +static const XrResult XR_ERROR_SPACE_INSUFFICIENT_RESOURCES_META = (XrResult) -1000259000; +// Error: Operation could not be completed until resources used are reduced or storage expanded. +static const XrResult XR_ERROR_SPACE_STORAGE_AT_CAPACITY_META = (XrResult) -1000259001; +// Error: Look around the environment more for space tracking to function. +static const XrResult XR_ERROR_SPACE_INSUFFICIENT_VIEW_META = (XrResult) -1000259002; +// Error: Space operation permission insufficient. Recommend confirming the status of the required permissions needed for using Space APIs. +static const XrResult XR_ERROR_SPACE_PERMISSION_INSUFFICIENT_META = (XrResult) -1000259003; +// Error: Operation cancelled due to rate limiting. Recommend retrying after a short delay. +static const XrResult XR_ERROR_SPACE_RATE_LIMITED_META = (XrResult) -1000259004; +// Error: Environment too dark for tracking to complete operation. +static const XrResult XR_ERROR_SPACE_TOO_DARK_META = (XrResult) -1000259005; +// Error: Environment too bright for tracking to complete operation. +static const XrResult XR_ERROR_SPACE_TOO_BRIGHT_META = (XrResult) -1000259006; +static const XrStructureType XR_TYPE_SYSTEM_SPACE_PERSISTENCE_PROPERTIES_META = (XrStructureType) 1000259000; +static const XrStructureType XR_TYPE_SPACES_SAVE_INFO_META = (XrStructureType) 1000259001; +static const XrStructureType XR_TYPE_EVENT_DATA_SPACES_SAVE_RESULT_META = (XrStructureType) 1000259002; +static const XrStructureType XR_TYPE_SPACES_ERASE_INFO_META = (XrStructureType) 1000259003; +static const XrStructureType XR_TYPE_EVENT_DATA_SPACES_ERASE_RESULT_META = (XrStructureType) 1000259004; +typedef struct XrSystemSpacePersistencePropertiesMETA { + XrStructureType type; + const void* XR_MAY_ALIAS next; + XrBool32 supportsSpacePersistence; +} XrSystemSpacePersistencePropertiesMETA; + +typedef struct XrSpacesSaveInfoMETA { + XrStructureType type; + const void* XR_MAY_ALIAS next; + uint32_t spaceCount; + XrSpace* spaces; +} XrSpacesSaveInfoMETA; + +typedef struct XrEventDataSpacesSaveResultMETA { + XrStructureType type; + const void* XR_MAY_ALIAS next; + XrAsyncRequestIdFB requestId; + XrResult result; +} XrEventDataSpacesSaveResultMETA; + +typedef struct XrSpacesEraseInfoMETA { + XrStructureType type; + const void* XR_MAY_ALIAS next; + uint32_t spaceCount; + XrSpace* spaces; + uint32_t uuidCount; + XrUuidEXT* uuids; +} XrSpacesEraseInfoMETA; + +typedef struct XrEventDataSpacesEraseResultMETA { + XrStructureType type; + const void* XR_MAY_ALIAS next; + XrAsyncRequestIdFB requestId; + XrResult result; +} XrEventDataSpacesEraseResultMETA; + +typedef XrResult (XRAPI_PTR *PFN_xrSaveSpacesMETA)(XrSession session, const XrSpacesSaveInfoMETA* info, XrAsyncRequestIdFB* requestId); +typedef XrResult (XRAPI_PTR *PFN_xrEraseSpacesMETA)(XrSession session, const XrSpacesEraseInfoMETA* info, XrAsyncRequestIdFB* requestId); + +#ifndef XR_NO_PROTOTYPES +#ifdef XR_EXTENSION_PROTOTYPES +XRAPI_ATTR XrResult XRAPI_CALL xrSaveSpacesMETA( + XrSession session, + const XrSpacesSaveInfoMETA* info, + XrAsyncRequestIdFB* requestId); + +XRAPI_ATTR XrResult XRAPI_CALL xrEraseSpacesMETA( + XrSession session, + const XrSpacesEraseInfoMETA* info, + XrAsyncRequestIdFB* requestId); +#endif /* XR_EXTENSION_PROTOTYPES */ +#endif /* !XR_NO_PROTOTYPES */ +#endif /* XR_META_spatial_entity_persistence */ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/OpenXR/meta_openxr_preview/openxr_extension_helpers.h b/OpenXR/meta_openxr_preview/openxr_extension_helpers.h new file mode 100755 index 0000000..1d6e630 --- /dev/null +++ b/OpenXR/meta_openxr_preview/openxr_extension_helpers.h @@ -0,0 +1,19 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/************************************************************************************ + +Filename : openxr_extension_helpers.h +Content : Helpers for private and experimental extension definition headers. +Language : C99 + +*************************************************************************************/ + +#pragma once + +#include + +#define XR_ENUM(type, enm, constant) static const type enm = (type)constant +#define XR_STRUCT_ENUM(enm, constant) XR_ENUM(XrStructureType, enm, constant) +#define XR_REFSPACE_ENUM(enm, constant) XR_ENUM(XrReferenceSpaceType, enm, constant) +#define XR_RESULT_ENUM(enm, constant) XR_ENUM(XrResult, enm, constant) +#define XR_COMPONENT_ENUM(enm, constant) XR_ENUM(XrComponentTypeFB, enm, constant) diff --git a/OpenXR/meta_openxr_preview/openxr_oculus_helpers.h b/OpenXR/meta_openxr_preview/openxr_oculus_helpers.h new file mode 100755 index 0000000..0d46964 --- /dev/null +++ b/OpenXR/meta_openxr_preview/openxr_oculus_helpers.h @@ -0,0 +1,28 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/************************************************************************************ + +Filename : openxr_oculus_helpers.h +Content : OpenXR Helper routines +Language : C99 + +*************************************************************************************/ + +#ifndef OPENXR_OCULUS_HELPERS_H +#define OPENXR_OCULUS_HELPERS_H 1 + +#include +#include + +#include "math.h" // for cosf(), sinf(), tanf() + +// OpenXR time values are expressed in nanseconds. +static inline double FromXrTime(const XrTime time) { + return (time * 1e-9); +} + +static inline XrTime ToXrTime(const double timeInSeconds) { + return (timeInSeconds * 1e9); +} + +#endif // OPENXR_OCULUS_HELPERS_H diff --git a/README.md b/README.md new file mode 100644 index 0000000..856d80d --- /dev/null +++ b/README.md @@ -0,0 +1,59 @@ +# Meta OpenXR SDK / Samples + +## Introduction +Welcome to the Meta OpenXR SDK project, a native OpenXR SDK and samples specifically designed for Meta Quest. This SDK is a comprehensive resource that includes header files and API definitions for both experimental and pre-release OpenXR APIs. + +As these API definitions reach their final stages, they will be incorporated into the [OpenXR SDK](https://github.com/KhronosGroup/OpenXR-SDK). To help you understand and utilize these features, we've included a variety of samples. These samples serve as practical demonstrations, showing you how to integrate and use the APIs effectively. + +## What are the samples? + +|Sample Name |OpenXR features / extensions shown |Target devices| Extra notes +|--|--|--|--| +|[`XrBodyFaceEyeSocial`](Samples/XrSamples/XrBodyFaceEyeSocial/) |`XR_FB_body_tracking`, `XR_FB_eye_tracking_social`, `XR_FB_face_tracking` |Meta Quest Pro +|[`XrColorSpaceFB`](Samples/XrSamples/XrColorSpaceFB/) |`XR_FB_color_space` |All Meta Quest devices +|[`XrCompositor_NativeActivity`](Samples/XrSamples/XrColorSpaceFB/) |`XR_KHR_composition_layer_cube`, `XR_KHR_composition_layer_cylinder`, `XR_KHR_composition_layer_equirect2`, `XR_FB_foveation` |All Meta Quest devices |Single file `C` sample +|[`XrControllers`](Samples/XrSamples/XrColorSpaceFB/) |`XR_FB_haptic_amplitude_envelope`, `XR_FB_haptic_pcm`|Meta Quest 2 and later devices +|[`XrHandDataSource`](Samples/XrSamples/XrColorSpaceFB/) |`XR_EXT_hand_tracking_data_source`|Meta Quest 2 and later devices +|[`XrHandsAndControllers`](Samples/XrSamples/XrColorSpaceFB/) |`XR_META_detached_controllers`,`XR_META_simultaneous_hands_and_controllers`|Meta Quest 3 and later| +|[`XrHandsFB`](Samples/XrSamples/XrColorSpaceFB/) |`XR_FB_hand_tracking_mesh`, `XR_FB_hand_tracking_capsules`,`XR_FB_hand_tracking_aim`|All Meta Quest devices| +|[`XrHandTrackingWideMotionMode`](Samples/XrSamples/XrHandTrackingWideMotionMode/) |`XR_META_hand_tracking_wide_motion_mode`|Meta Quest 3 and later| +|[`XrInput`](Samples/XrSamples/XrInput/) |OpenXR Action System|All Meta Quest devices| +|[`XrKeyboard`](Samples/XrSamples/XrKeyboard/) |`XR_FB_keyboard_tracking`,`XR_FB_passthrough_keyboard_hands`,`XR_FB_render_model`|Meta Quest 2 and later| +|[`XrPassthrough`](Samples/XrSamples/XrPassthrough/) |`XR_FB_passthrough`|All Meta Quest devices|Demonstrates the use of still and animated styles, selective and projected passthrough. +|[`XrPassthroughOcclusion`](Samples/XrSamples/XrPassthroughOcclusion/) |`XR_META_envionment_depth`|Meta Quest 3 and later| +|[`XrSceneModel`](Samples/XrSamples/XrSceneModel/) |`XR_FB_scene`|Meta Quest 2 and later|Demonstrates a scene-aware experience including floor, walls, and furniture. +|[`XrSpaceWarp`](Samples/XrSamples/XrSpaceWarp/) |`XR_FB_space_warp`|Meta Quest 2 and later| +|[`XrSpatialAnchor`](Samples/XrSamples/XrSpatialAnchor/) |`XR_FB_spatial_entity`, `XR_FB_spatial_entity_query`, `XR_FB_spatial_entity_storage`, `XR_FB_spatial_entity_storage_batch`, `XR_FB_spatial_entity_sharing`, `XR_FB_spatial_entity_user`|Meta Quest 2 and later| +|[`XrVirtualKeyboard`](Samples/XrSamples/XrVirtualKeyboard/) |`XR_META_virtual_keyboard`,`XR_FB_render_model`|Meta Quest 2 and later| + +## Build +> Enable Quest system property to use experimental features, you will need the command: `adb shell setprop debug.oculus.experimentalEnabled 1`. + +> **Note**: This value resets with each headset reboot. +### Android +#### Dependencies +* Android Studio + * The lastest Android Studio release is recommended. + * If you have agreed with the licenses previously on your development system, Android Studio will automatically install, at the start up time. Otherwise (or if the installation failed), you need to install the required CMake and NDK manually, refer to the [official instructions](https://developer.android.com/studio/projects/install-ndk) for the detailed steps. The default installed locations are: + * `$SDK-ROOT-DIR/ndk/$ndkVersion` for NDK. + * `$SDK-ROOT-DIR/cmake/$cmake-version` for CMake. +#### Build with CMake on Android Studio +1. If this is the first time you are launching Android Studio, select Open an existing Android Studio project. If you have launched Android Studio before, click File > Open instead. +2. Open build.gradle file at the top level + * You could also open individual sample app from the XrSamples folders. For example, XrSamples/XrHandsFB/Projects/Android/build.gradle +3. After the build has finished, directly run a sample with the device connected. Click Run in the toolbar. + +### Meta Quest Link +While the samples provided in this package are designed as Quest Native / Android samples, some of them can be built and run on Windows for Meta Quest Link. For building with Meta Quest Link, you'll need: +* [cmake](https://cmake.org/download/) +* A C++ development environment compatible with cmake (such as Microsoft Visual Studio 2022) +* [Meta Quest Link application](https://www.meta.com/quest/setup/) + +#### Notes for running samples with Meta Quest Link +* Ensure that Developer Runtime Features is enabled in the Meta Quest Link application. +* Make sure the headset is on, the Meta Quest Link application is running and Meta Quest Link is started; before double-click and launch the sample. + +## More details + +- [https://developer.oculus.com/downloads/package/oculus-openxr-mobile-sdk/](https://developer.oculus.com/downloads/package/oculus-openxr-mobile-sdk/) +- [https://developer.oculus.com/documentation/native/android/mobile-openxr-sample/](https://developer.oculus.com/documentation/native/android/mobile-openxr-sample/) diff --git a/Samples/1stParty/OVR/Include/JniUtils-inl.h b/Samples/1stParty/OVR/Include/JniUtils-inl.h new file mode 100755 index 0000000..34c01dc --- /dev/null +++ b/Samples/1stParty/OVR/Include/JniUtils-inl.h @@ -0,0 +1,126 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +#pragma once + +/////////////////////////////////////////////////////////////////////////////////////////////////// +// Notes on OVR_NO_SANITIZE_ADDRESS usage in this file: +// +// The reason why we need OVR_NO_SANITIZE_ADDRESS on some functions in this file is that, Android +// sometimes passes us JavaVm* with extremely huge address value, which is something like +// 0xb400007a923d7380. This address still works fine as JavaVM pointer such that we can invoke its +// method, but when ASAN instrumentation embedds the machine code to do shadow map calculation, +// because the address is in unexpectedly too big region for ASAN, the calculated address is +// pointing to non-shadowed memory, and results in SEGV. +// https://github.com/google/sanitizers/wiki/AddressSanitizerAlgorithm#mapping +// +// And the reason why we need to call JavaVM's functions via vm->functions->Xxx() instead of +// vm->Xxx(), is that the body of vm->Xxx() is written in jni.h, and we cannot add +// OVR_NO_SANITIZE_ADDRESS to jni.h. If we don't add it, the same ASAN problem happens at jni.h's +// implementation. +/////////////////////////////////////////////////////////////////////////////////////////////////// + +#if defined(OVR_OS_ANDROID) + +#include "OVR_LogUtils.h" +#include "OVR_Std.h" +#include "OVR_SanitizerMacros.h" + +#include +#include + +// Wrapper function for JavaVM->Xxx() to suppress ASAN. See the notes at the beginning of this file. +OVR_NO_SANITIZE_ADDRESS +static inline jint attachCurrentThread(JavaVM* vm, JNIEnv** pEnv, void* thrArgs) { +#if defined(OVR_USING_ADDRESS_SANITIZER) + return vm->functions->AttachCurrentThread(vm, pEnv, thrArgs); +#else + return vm->AttachCurrentThread(pEnv, thrArgs); +#endif +} + +// Wrapper function for JavaVM->Xxx() to suppress ASAN. See the notes at the beginning of this file. +OVR_NO_SANITIZE_ADDRESS +static inline jint detachCurrentThread(JavaVM* vm) { +#if defined(OVR_USING_ADDRESS_SANITIZER) + return vm->functions->DetachCurrentThread(vm); +#else + return vm->DetachCurrentThread(); +#endif +} + +// Wrapper function for JavaVM->Xxx() to suppress ASAN. See the notes at the beginning of this file. +OVR_NO_SANITIZE_ADDRESS +static inline jint getEnv(JavaVM* vm, void** env, jint version) { +#if defined(OVR_USING_ADDRESS_SANITIZER) + return vm->functions->GetEnv(vm, env, version); +#else + return vm->GetEnv(env, version); +#endif +} + +inline jint ovr_AttachCurrentThread(JavaVM* vm, JNIEnv** jni, void* args) { + // First read current thread name. + char threadName[16] = {0}; // max thread name length is 15. + char commpath[64] = {0}; + OVR::OVR_sprintf(commpath, sizeof(commpath), "/proc/%d/task/%d/comm", getpid(), gettid()); + FILE* f = fopen(commpath, "r"); + if (f != nullptr) { + fread(threadName, 1, sizeof(threadName) - 1, f); + fclose(f); + // Remove any trailing new-line characters. + for (int len = strlen(threadName) - 1; + len >= 0 && (threadName[len] == '\n' || threadName[len] == '\r'); + len--) { + threadName[len] = '\0'; + } + } + + // Attach the thread to the JVM. + const jint rtn = attachCurrentThread(vm, jni, args); + if (rtn != JNI_OK) { + OVR_FAIL("AttachCurrentThread returned %i", rtn); + } + // Restore the thread name after AttachCurrentThread() overwrites it. + if (threadName[0] != '\0') { + pthread_setname_np(pthread_self(), threadName); + } + return rtn; +} + +inline jint ovr_DetachCurrentThread(JavaVM* vm) { + const jint rtn = detachCurrentThread(vm); + if (rtn != JNI_OK) { + OVR_FAIL("DetachCurrentThread() returned %i", rtn); + } + return rtn; +} + +// This function needs to suppress ASAN because there is a pointer copy of JavaVM, for which ASAN +// will instrument the memory check. See the notes at the beginning of this file. +OVR_NO_SANITIZE_ADDRESS +inline TempJniEnv::TempJniEnv(JavaVM* vm, const char*, int) + : vm_(vm), jni_(nullptr), privateEnv_(false) { + if (!vm_) { + // The runtime supports not having Java context so just do nothing here + return; + } + + if (JNI_OK != getEnv(vm_, reinterpret_cast(&jni_), JNI_VERSION_1_6)) { + OVR_LOG( + "Creating temporary JNIEnv. This is a heavy operation and should be infrequent. To optimize, use JNI AttachCurrentThread on calling thread"); + // OVR_LOG( "Temporary JNIEnv created at %s:%d", file, line ); + ovr_AttachCurrentThread(vm_, &jni_, nullptr); + privateEnv_ = true; + } else { + // Current thread is attached to the VM. + // OVR_LOG( "Using caller's JNIEnv" ); + } +} + +inline TempJniEnv::~TempJniEnv() { + if (privateEnv_) { + ovr_DetachCurrentThread(vm_); + } +} + +#endif // defined(OVR_OS_ANDROID) diff --git a/Samples/1stParty/OVR/Include/JniUtils.h b/Samples/1stParty/OVR/Include/JniUtils.h new file mode 100755 index 0000000..b20001a --- /dev/null +++ b/Samples/1stParty/OVR/Include/JniUtils.h @@ -0,0 +1,823 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/************************************************************************************ + +Filename : JniUtils.h +Content : JNI utility functions +Created : October 21, 2014 +Authors : J.M.P. van Waveren, Jonathan E. Wright + +*************************************************************************************/ + +#ifndef OVR_JniUtils_h +#define OVR_JniUtils_h + +#include "OVR_Types.h" + +#if defined(OVR_OS_ANDROID) +#include "OVR_LogUtils.h" +#endif + +#include "OVR_Std.h" + +//============================================================== +// Various helper functions. +//============================================================== + +enum { + ANDROID_KITKAT = 19, // Build.VERSION_CODES.KITKAT + ANDROID_KITKAT_WATCH = 20, // Build.VERSION_CODES.KITKAT_WATCH + ANDROID_LOLLIPOP = 21, // Build.VERSION_CODES.LOLLIPOP + ANDROID_LOLLIPOP_MR1 = 22, // Build.VERSION_CODES.LOLLIPOP_MR1 + ANDROID_MARSHMALLOW = 23, // Build.VERSION_CODES.M + ANDROID_NOUGAT = 24, // Build.VERSION_CODES.N + ANDROID_N_MR1 = 25, // Build.VERSION_CODES.N_MR1 +}; + +// VrRuntimeClient was mainly developed for Android apps running Java, but also supports a subset of +// functionality with clients that do not have Java context. This macro is used to conditionally +// call into various parts of the runtime, and makes it easy to find where all these changes are +// made and then revisit them later on. +#define IF_JAVA_CONTEXT(_expr) (_expr) +#define IF_NO_JAVA_CONTEXT(_expr) (!(_expr)) + +#if defined(OVR_OS_ANDROID) +// JVM's AttachCurrentThread() resets the thread name if the thread name is not passed as an +// argument. Use these wrappers to attach the current thread to a JVM without resetting the current +// thread name. These will fatal error when unsuccesful. +jint ovr_AttachCurrentThread(JavaVM* vm, JNIEnv** jni, void* args); +jint ovr_DetachCurrentThread(JavaVM* vm); +#else +inline jint ovr_AttachCurrentThread(JavaVM* vm, JNIEnv** jni, void* args) { + OVR_UNUSED(vm); + OVR_UNUSED(jni); + OVR_UNUSED(args); + return 0; +} +inline jint ovr_DetachCurrentThread(JavaVM* vm) { + OVR_UNUSED(vm); + return 0; +} +#endif + +#if defined(OVR_OS_ANDROID) +//============================================================== +// TempJniEnv +// +// Gets a temporary JNIEnv for the current thread and releases the +// JNIEnv on destruction if the calling thread was not already attached. +//============================================================== +class TempJniEnv { + public: + explicit TempJniEnv(JavaVM* vm, const char* /*file*/ = "", int /*line*/ = -1); + ~TempJniEnv(); + + operator JNIEnv*() { + return jni_; + } + JNIEnv* operator->() { + return jni_; + } + + private: + JavaVM* vm_; + JNIEnv* jni_; + bool privateEnv_; +}; + +#define JNI_TMP_ENV(envVar, vm) TempJniEnv envVar(vm, __FILE__, __LINE__) + +#else // defined(OVR_OS_ANDROID) + +// Support JNI_TMP_ENV when testing on non-Android platforms. Other tests may already define +// a test mock for JNI_TMP_ENV, so only define it here if one does not already exist +#if !defined(JNI_TMP_ENV) +class TempJniEnv { + public: + explicit TempJniEnv(JavaVM* vm, const char* /*file*/ = "", int /*line*/ = -1){}; + ~TempJniEnv(){}; + + operator JNIEnv*() { + return nullptr; + } + JNIEnv* operator->() { + return nullptr; + } +}; +#define JNI_TMP_ENV(envVar, vm) TempJniEnv envVar(vm, __FILE__, __LINE__) +#endif // !defined(JNI_TMP_ENV) + +#endif // defined(OVR_OS_ANDROID) + +//============================================================== +// JavaObject +// +// Releases a java object local reference on destruction. +//============================================================== +class JavaObject { + public: + JavaObject(JNIEnv* jni_, jobject const object_) : Jni(jni_), Object(object_) { + OVR_ASSERT(Jni != NULL); + } + ~JavaObject() { +#if defined(OVR_OS_ANDROID) + if (Jni->ExceptionOccurred()) { + OVR_LOG("JNI exception before DeleteLocalRef!"); + Jni->ExceptionClear(); + } + OVR_ASSERT(Jni != NULL); + if (Object != NULL) { + Jni->DeleteLocalRef(Object); + } + if (Jni->ExceptionOccurred()) { + OVR_LOG("JNI exception occurred calling DeleteLocalRef!"); + Jni->ExceptionClear(); + } +#endif + Jni = NULL; + Object = NULL; + } + + jobject GetJObject() const { + return Object; + } + + JNIEnv* GetJNI() const { + return Jni; + } + + protected: + void SetJObject(jobject const& obj) { + Object = obj; + } + + private: + JNIEnv* Jni; + jobject Object; +}; + +//============================================================== +// JavaClass +// +// Releases a java class local reference on destruction. +//============================================================== +class JavaClass : public JavaObject { + public: + JavaClass(JNIEnv* jni_, jobject const class_) : JavaObject(jni_, class_) {} + + jclass GetJClass() const { + return static_cast(GetJObject()); + } +}; + +#if defined(OVR_OS_ANDROID) +//============================================================== +// JavaFloatArray +// +// Releases a java float array local reference on destruction. +//============================================================== +class JavaFloatArray : public JavaObject { + public: + JavaFloatArray(JNIEnv* jni_, jfloatArray const floatArray_) : JavaObject(jni_, floatArray_) {} + + jfloatArray GetJFloatArray() const { + return static_cast(GetJObject()); + } +}; + +//============================================================== +// JavaObjectArray +// +// Releases a java float array local reference on destruction. +//============================================================== +class JavaObjectArray : public JavaObject { + public: + JavaObjectArray(JNIEnv* jni_, jobjectArray const floatArray_) : JavaObject(jni_, floatArray_) {} + + jobjectArray GetJobjectArray() const { + return static_cast(GetJObject()); + } +}; +#endif + +//============================================================== +// JavaString +// +// Creates a java string on construction and releases it on destruction. +//============================================================== +class JavaString : public JavaObject { + public: + JavaString(JNIEnv* jni_, char const* string_) : JavaObject(jni_, NULL) { +#if defined(OVR_OS_ANDROID) + SetJObject(GetJNI()->NewStringUTF(string_)); + if (GetJNI()->ExceptionOccurred()) { + OVR_LOG("JNI exception occurred calling NewStringUTF!"); + } +#else + OVR_UNUSED(string_); +#endif + } + + JavaString(JNIEnv* jni_, jstring string_) : JavaObject(jni_, string_) { + OVR_ASSERT(string_ != NULL); + } + + jstring GetJString() const { + return static_cast(GetJObject()); + } +}; + +//============================================================== +// JavaUTFChars +// +// Gets a java string object as a buffer of UTF characters and +// releases the buffer on destruction. +// Use this only if you need to store a string reference and access +// the string as a char buffer of UTF8 chars. If you only need +// to store and release a reference to a string, use JavaString. +//============================================================== +class JavaUTFChars : public JavaString { + public: + JavaUTFChars(JNIEnv* jni_, jstring const string_) : JavaString(jni_, string_), UTFString(NULL) { +#if defined(OVR_OS_ANDROID) + UTFString = GetJNI()->GetStringUTFChars(GetJString(), NULL); + if (GetJNI()->ExceptionOccurred()) { + OVR_LOG("JNI exception occurred calling GetStringUTFChars!"); + } +#endif + } + + ~JavaUTFChars() { +#if defined(OVR_OS_ANDROID) + OVR_ASSERT(UTFString != NULL); + GetJNI()->ReleaseStringUTFChars(GetJString(), UTFString); + if (GetJNI()->ExceptionOccurred()) { + OVR_LOG("JNI exception occurred calling ReleaseStringUTFChars!"); + } +#endif + } + + char const* ToStr() const { + return UTFString; + } + operator char const*() const { + return UTFString; + } + + private: + char const* UTFString; +}; + +#if defined(OVR_OS_ANDROID) +#include +#include + +// Use this EVERYWHERE and you can insert your own catch here if you have string references leaking. +// Even better, use the JavaString / JavaUTFChars classes instead and they will free resources for +// you automatically. +inline jobject ovr_NewStringUTF(JNIEnv* jni, char const* str) { + return jni->NewStringUTF(str); +} +inline char const* ovr_GetStringUTFChars(JNIEnv* jni, jstring javaStr, jboolean* isCopy) { + char const* str = jni->GetStringUTFChars(javaStr, isCopy); + return str; +} + +// Every Context or its subclass (ex: Activity) has a ClassLoader +inline jclass ovr_GetClassLoader(JNIEnv* jni, jobject contextObject) { + JavaClass contextClass(jni, jni->GetObjectClass(contextObject)); + jmethodID getClassLoaderMethodId = + jni->GetMethodID(contextClass.GetJClass(), "getClassLoader", "()Ljava/lang/ClassLoader;"); + jclass localClass = + static_cast(jni->CallObjectMethod(contextObject, getClassLoaderMethodId)); + + if (localClass == 0) { + OVR_FAIL("getClassLoaderFailed failed"); + } + + return localClass; +} + +// FindClass uses the current callstack to determine which ClassLoader object to use +// to start the class search. As a result, an attempt to find an app-specific class will +// fail when FindClass is called from an arbitrary thread with the JavaVM started in +// the "system" class loader, instead of the one associated with the application. +// The following two functions can be used to find any class from any thread. These will +// fatal error if the class is not found. +inline jclass ovr_GetLocalClassReferenceWithLoader( + JNIEnv* jni, + jobject classLoader, + const char* className, + const bool fail = true, + const bool warn = true) { + if (jni == nullptr) { + if (fail) { + OVR_FAIL( + "ovr_GetLocalClassReferenceWithLoader: JNIEnv is NULL. Classname is %s", className); + } else if (warn) { + OVR_WARN( + "ovr_GetLocalClassReferenceWithLoader: JNIEnv is NULL. Classname is %s", className); + } + return nullptr; + } + // This is a lambda because if it were at file scope it would need to be inlined since + // this is a header, and that could mean the errorMsg[256] declaration could take up + // 256 bytes on the stack each time the function is called (which is currently 3x in + // this function). Whether or not that is actually the case is up to the compiler and + // optimizations, but C++ doesn't guarantee stack variables only take up space within + // the code block in which they are declared. + auto checkJNIException = + [](JNIEnv* jniLambdaScope, const bool warnLambdaScope, const char* fmt, ...) { + if (jniLambdaScope->ExceptionOccurred()) { + // something else caused a JNI exception before this and didn't clear it + if (warnLambdaScope && fmt != nullptr) { + char errorMsg[256]; + va_list argPtr; + va_start(argPtr, fmt); + vsnprintf(errorMsg, sizeof(errorMsg), fmt, argPtr); + va_end(argPtr); + OVR_LOG("JNI exception: %s", errorMsg); + } + jniLambdaScope->ExceptionClear(); + } + }; + + checkJNIException( + jni, + warn, + "UNEXPECTED before FindClass( '%s' ) in ovr_GetLocalClassReferenceWithLoader", + className); + + JavaClass classLoaderClass(jni, jni->FindClass("java/lang/ClassLoader")); + jmethodID loadClassMethodId = jni->GetMethodID( + classLoaderClass.GetJClass(), "loadClass", "(Ljava/lang/String;)Ljava/lang/Class;"); + + JavaString classNameString(jni, jni->NewStringUTF(className)); + jclass localClass = static_cast( + jni->CallObjectMethod(classLoader, loadClassMethodId, classNameString.GetJString())); + + if (localClass == 0) { + if (fail) { + OVR_FAIL("FindClass( %s ) failed", className); + } else if (warn) { + OVR_WARN("FindClass( %s ) failed", className); + } + checkJNIException(jni, warn, "class '%s' not found.", className); + } else { + // We shouldn't get an exception if the class was found. + checkJNIException( + jni, + warn, + "UNEXPECTED after loadClass( '%s' ) in ovr_GetLocalClassReferenceWithLoader!", + className); + } + + return localClass; +} +inline jclass +ovr_GetGlobalClassReferenceWithLoader(JNIEnv* jni, jobject classLoader, const char* className) { + JavaClass localClass(jni, ovr_GetLocalClassReferenceWithLoader(jni, classLoader, className)); + + // Turn it into a global reference so it can be safely used on other threads. + jclass globalClass = (jclass)jni->NewGlobalRef(localClass.GetJClass()); + return globalClass; +} + +// The following two functions can be used to find any class from any thread, but they do +// need the activity object to explicitly start the class search using the class loader +// associated with the application. These will fatal error if the class is not found. + +// This can be called from any thread but does need the activity object. +inline jclass ovr_GetLocalClassReference( + JNIEnv* jni, + jobject activityObject, + const char* className, + const bool fail = true, + const bool warn = true) { + JavaObject classLoaderObject(jni, ovr_GetClassLoader(jni, activityObject)); + + return ovr_GetLocalClassReferenceWithLoader( + jni, classLoaderObject.GetJObject(), className, fail, warn); +} +// This can be called from any thread but does need the activity object. +inline jclass ovr_GetGlobalClassReference( + JNIEnv* jni, + jobject activityObject, + const char* className, + const bool fail = true, + const bool warn = true) { + JavaClass localClass( + jni, ovr_GetLocalClassReference(jni, activityObject, className, fail, warn)); + + // Turn it into a global reference so it can be safely used on other threads. + jclass globalClass = (jclass)jni->NewGlobalRef(localClass.GetJClass()); + return globalClass; +} + +// These will fatal error if the method is not found. +inline jmethodID +ovr_GetMethodID(JNIEnv* jni, jclass jniclass, const char* name, const char* signature) { + const jmethodID methodId = jni->GetMethodID(jniclass, name, signature); + if (!methodId) { + OVR_FAIL("couldn't get %s, %s", name, signature); + } + return methodId; +} +inline jmethodID +ovr_GetStaticMethodID(JNIEnv* jni, jclass jniclass, const char* name, const char* signature) { + const jmethodID method = jni->GetStaticMethodID(jniclass, name, signature); + if (!method) { + OVR_FAIL("couldn't get %s, %s", name, signature); + } + return method; +} +inline jfieldID +ovr_GetStaticFieldID(JNIEnv* jni, jclass jniclass, const char* name, const char* signature) { + const jfieldID field = jni->GetStaticFieldID(jniclass, name, signature); + if (!field) { + OVR_FAIL("couldn't get %s, %s", name, signature); + } + return field; +} + +// Get the code path of the current package. +inline const char* ovr_GetPackageCodePath( + JNIEnv* jni, + jobject activityObject, + char* packageCodePath, + int const maxLen) { + if (packageCodePath == NULL || maxLen < 1) { + return packageCodePath; + } + + packageCodePath[0] = '\0'; + + JavaClass activityClass(jni, jni->GetObjectClass(activityObject)); + jmethodID getPackageCodePathId = + jni->GetMethodID(activityClass.GetJClass(), "getPackageCodePath", "()Ljava/lang/String;"); + if (getPackageCodePathId == 0) { + OVR_LOG( + "Failed to find getPackageCodePath on class %llu, object %llu", + (long long unsigned int)activityClass.GetJClass(), + (long long unsigned int)activityObject); + return packageCodePath; + } + + JavaUTFChars result(jni, (jstring)jni->CallObjectMethod(activityObject, getPackageCodePathId)); + if (!jni->ExceptionOccurred()) { + const char* path = result.ToStr(); + if (path != NULL) { + OVR::OVR_sprintf(packageCodePath, maxLen, "%s", path); + } + } else { + jni->ExceptionClear(); + OVR_LOG("Cleared JNI exception"); + } + + return packageCodePath; +} + +// Get the code path of the current package. +inline const char* +ovr_GetFilesDir(JNIEnv* jni, jobject activityObject, char* filesDir, int const maxLen) { + if (filesDir == NULL || maxLen < 1) { + return filesDir; + } + + filesDir[0] = '\0'; + + JavaClass activityClass(jni, jni->GetObjectClass(activityObject)); + JavaClass fileClass(jni, jni->FindClass("java/io/File")); + jmethodID getFilesDirId = + jni->GetMethodID(activityClass.GetJClass(), "getFilesDir", "()Ljava/io/File;"); + jmethodID getAbsolutePathId = + jni->GetMethodID(fileClass.GetJClass(), "getAbsolutePath", "()Ljava/lang/String;"); + if (getAbsolutePathId == 0 || getFilesDirId == 0) { + OVR_LOG( + "Failed to find getFilesDir or getAbsolutePath on class %llu, object %llu", + (long long unsigned int)activityClass.GetJClass(), + (long long unsigned int)activityObject); + return filesDir; + } + + JavaObject filesDirObject(jni, (jobject)jni->CallObjectMethod(activityObject, getFilesDirId)); + if (!jni->ExceptionOccurred()) { + JavaUTFChars result( + jni, (jstring)jni->CallObjectMethod(filesDirObject.GetJObject(), getAbsolutePathId)); + if (!jni->ExceptionOccurred()) { + const char* path = result.ToStr(); + if (path != NULL) { + OVR::OVR_sprintf(filesDir, maxLen, "%s", path); + } + } else { + jni->ExceptionClear(); + OVR_LOG("Cleared JNI exception"); + } + } + + return filesDir; +} + +// If the specified package is found, returns true and the full path for the specified package is +// copied into packagePath. If the package was not found, false is returned and packagePath will be +// an empty string. +inline bool ovr_GetInstalledPackagePath( + JNIEnv* jni, + jobject activityObject, + char const* packageName, + char* packagePath, + int const packagePathSize) { + OVR_ASSERT( + packagePath != NULL && packagePathSize > 1 && packageName != NULL && + packageName[0] != '\0'); + + packagePath[0] = '\0'; + + /// Java - for reference +#if 0 + import android.content.pm.PackageManager; + import android.content.pm.ApplicationInfo; + import android.content.Context; + + public static String getInstalledPackagePath( Context context, String packageName ) + { + final String notInstalled = ""; + Log.d( TAG, "Searching installed packages for '" + packageName + "'" ); + try { + ApplicationInfo info = context.getPackageManager().getApplicationInfo( packageName, 0 ); + return info.sourceDir != null ? info.sourceDir : notInstalled; + } catch ( NameNotFoundException e ) { + Log.w( TAG, "Package '" + packageName + "' not installed", e ); + } + return notInstalled; + } +#endif + + /// JNI equivalent + jclass activityClass = jni->GetObjectClass(activityObject); + if (activityClass == NULL) { + OVR_LOG("ovr_GetInstalledPackagePath activityClass == NULL"); + return false; + } + + jmethodID getPackageManagerMethod = jni->GetMethodID( + activityClass, "getPackageManager", "()Landroid/content/pm/PackageManager;"); + if (getPackageManagerMethod == NULL) { + OVR_LOG("ovr_GetInstalledPackagePath getPackageManagerMethod == NULL"); + return false; + } + + jobject packageManager = jni->CallObjectMethod(activityObject, getPackageManagerMethod); + if (packageManager == NULL) { + OVR_LOG("ovr_GetInstalledPackagePath packageManager == NULL"); + return false; + } + + jclass PackageManagerClass = jni->FindClass("android/content/pm/PackageManager"); + if (PackageManagerClass == NULL) { + OVR_LOG("ovr_GetInstalledPackagePath PackageManagerClass == NULL"); + return false; + } + + jmethodID getApplicationInfoMethod = jni->GetMethodID( + PackageManagerClass, + "getApplicationInfo", + "(Ljava/lang/String;I)Landroid/content/pm/ApplicationInfo;"); + if (getApplicationInfoMethod == NULL) { + OVR_LOG("ovr_GetInstalledPackagePath getApplicationInfoMethod == NULL"); + return false; + } + + jobject packageNameObj = jni->NewStringUTF(packageName); + if (packageNameObj == NULL) { + OVR_LOG("ovr_GetInstalledPackagePath packageNameObj == NULL"); + return false; + } + + jint jZero = 0; + jobject applicationInfo = + jni->CallObjectMethod(packageManager, getApplicationInfoMethod, packageNameObj, jZero); + if (applicationInfo == NULL) { + OVR_LOG( + "ovr_GetInstalledPackagePath applicationInfo == NULL packageName='%s'", packageName); + return false; + } + + if (jni->ExceptionOccurred()) { + OVR_LOG( + "ovr_GetInstalledPackagePath getApplicationInfo threw an exception: package not found."); + jni->ExceptionClear(); + return false; + } + + jclass ApplicationInfoClass = jni->GetObjectClass(applicationInfo); + if (ApplicationInfoClass == NULL) { + OVR_LOG("ovr_GetInstalledPackagePath ApplicationInfoClass == NULL"); + return false; + } + + jfieldID sourceDirField = + jni->GetFieldID(ApplicationInfoClass, "sourceDir", "Ljava/lang/String;"); + if (sourceDirField == NULL) { + OVR_LOG("ovr_GetInstalledPackagePath sourceDirField == NULL"); + return false; + } + + jstring sourceDir = (jstring)jni->GetObjectField(applicationInfo, sourceDirField); + if (sourceDir == NULL) { + OVR_LOG("ovr_GetInstalledPackagePath sourceDir == NULL"); + return false; + } + + const char* sourceDir_ch = jni->GetStringUTFChars(sourceDir, 0); + if (sourceDir_ch == NULL) { + OVR_LOG("ovr_GetInstalledPackagePath sourceDir_ch == NULL"); + return false; + } + + /// If all goes well ... :) + OVR::OVR_strcpy(packagePath, packagePathSize, sourceDir_ch); + +#if defined(OVR_BUILD_DEBUG) + OVR_LOG("ovr_GetInstalledPackagePath packagePath = '%s'", packagePath); +#endif + jni->ReleaseStringUTFChars(sourceDir, sourceDir_ch); + return true; +} + +// Get the current package name, for instance "com.oculus.systemactivities". +inline const char* ovr_GetCurrentPackageName( + JNIEnv* jni, + jobject activityObject, + char* packageName, + int const maxLen) { + packageName[0] = '\0'; + + JavaClass curActivityClass(jni, jni->GetObjectClass(activityObject)); + jmethodID getPackageNameId = + jni->GetMethodID(curActivityClass.GetJClass(), "getPackageName", "()Ljava/lang/String;"); + if (getPackageNameId != 0) { + JavaUTFChars result(jni, (jstring)jni->CallObjectMethod(activityObject, getPackageNameId)); + if (!jni->ExceptionOccurred()) { + const char* currentPackageName = result.ToStr(); + if (currentPackageName != NULL) { + OVR::OVR_sprintf(packageName, maxLen, "%s", currentPackageName); + } + } else { + jni->ExceptionClear(); + OVR_LOG("Cleared JNI exception"); + } + } + // OVR_LOG( "ovr_GetCurrentPackageName() = %s", packageName ); + + return packageName; +} + +// Get the current activity name, for instance "com.oculus.systemactivities.PlatformActivity". +inline const char* ovr_GetCurrentActivityName( + JNIEnv* jni, + jobject activityObject, + char* activityName, + int const maxLen) { + activityName[0] = '\0'; + + JavaClass curActivityClass(jni, jni->GetObjectClass(activityObject)); + jmethodID getClassMethodId = + jni->GetMethodID(curActivityClass.GetJClass(), "getClass", "()Ljava/lang/Class;"); + if (getClassMethodId != 0) { + JavaObject classObj(jni, jni->CallObjectMethod(activityObject, getClassMethodId)); + JavaClass activityClass(jni, jni->GetObjectClass(classObj.GetJObject())); + + jmethodID getNameMethodId = + jni->GetMethodID(activityClass.GetJClass(), "getName", "()Ljava/lang/String;"); + if (getNameMethodId != 0) { + JavaUTFChars utfCurrentClassName( + jni, (jstring)jni->CallObjectMethod(classObj.GetJObject(), getNameMethodId)); + const char* currentClassName = utfCurrentClassName.ToStr(); + if (currentClassName != NULL) { + OVR::OVR_sprintf(activityName, maxLen, "%s", currentClassName); + } + } + } + return activityName; +} + +// Get the current package's signing identies +inline jobject +ovr_GetPackageSignatures(JNIEnv* jni, jobject activityObject, const char* packageName) { + OVR_LOG("ovr_GetCurrentPackageSignature "); + JavaClass curActivityClass(jni, jni->GetObjectClass(activityObject)); + JavaClass packageManagerClass(jni, jni->FindClass("android/content/pm/PackageManager")); + JavaClass packageInfoClass(jni, jni->FindClass("android/content/pm/PackageInfo")); + + jmethodID getPackageManagerMethodId = jni->GetMethodID( + curActivityClass.GetJClass(), "getPackageManager", "()Landroid/content/pm/PackageManager;"); + jmethodID getPackageInfoMethodId = jni->GetMethodID( + packageManagerClass.GetJClass(), + "getPackageInfo", + "(Ljava/lang/String;I)Landroid/content/pm/PackageInfo;"); + jfieldID signaturesFieldId = jni->GetFieldID( + packageInfoClass.GetJClass(), "signatures", "[Landroid/content/pm/Signature;"); + + JavaString packageNameStringObject(jni, jni->NewStringUTF(packageName)); + + JavaObject packageManager( + jni, jni->CallObjectMethod(activityObject, getPackageManagerMethodId)); + + JavaObject packageInfo( + jni, + jni->CallObjectMethod( + packageManager.GetJObject(), + getPackageInfoMethodId, + packageNameStringObject.GetJObject(), + 64 // PackageManager.GET_SIGNATURES + )); + + if (jni->ExceptionOccurred()) { + OVR_WARN("Could not find package info for %s:", packageName); + jni->ExceptionDescribe(); + jni->ExceptionClear(); + return NULL; + } + + return jni->GetObjectField(packageInfo.GetJObject(), signaturesFieldId); +} + +// For instance packageName = "com.oculus.systemactivities". +inline bool ovr_IsCurrentPackage(JNIEnv* jni, jobject activityObject, const char* packageName) { + char currentPackageName[128]; + ovr_GetCurrentPackageName(jni, activityObject, currentPackageName, sizeof(currentPackageName)); + const bool isCurrentPackage = (strcasecmp(currentPackageName, packageName) == 0); + return isCurrentPackage; +} + +// For instance activityName = "com.oculus.systemactivities.PlatformActivity". +inline bool ovr_IsCurrentActivity(JNIEnv* jni, jobject activityObject, const char* activityName) { + char currentClassName[128]; + ovr_GetCurrentActivityName(jni, activityObject, currentClassName, sizeof(currentClassName)); + const bool isCurrentActivity = (strcasecmp(currentClassName, activityName) == 0); + return isCurrentActivity; +} + +#else +inline jclass +ovr_GetLocalClassReference(JNIEnv* jni, jobject activityObject, const char* className) { + OVR_UNUSED(jni); + OVR_UNUSED(activityObject); + OVR_UNUSED(className); + return nullptr; +} +inline jclass +ovr_GetGlobalClassReference(JNIEnv* jni, jobject activityObject, const char* className) { + OVR_UNUSED(jni); + OVR_UNUSED(activityObject); + OVR_UNUSED(className); + return nullptr; +} +inline jmethodID +ovr_GetStaticMethodID(JNIEnv* jni, jclass jniclass, const char* name, const char* signature) { + OVR_UNUSED(jni); + OVR_UNUSED(jniclass); + OVR_UNUSED(name); + OVR_UNUSED(signature); + return nullptr; +} +inline jfieldID +ovr_GetStaticFieldID(JNIEnv* jni, jclass jniclass, const char* name, const char* signature) { + OVR_UNUSED(jni); + OVR_UNUSED(jniclass); + OVR_UNUSED(name); + OVR_UNUSED(signature); + return nullptr; +} +inline bool ovr_IsCurrentActivity(JNIEnv* jni, jobject activityObject, const char* activityName) { + OVR_UNUSED(jni); + OVR_UNUSED(activityObject); + OVR_UNUSED(activityName); + return false; +} +inline bool ovr_IsCurrentPackage(JNIEnv* jni, jobject activityObject, const char* packageName) { + OVR_UNUSED(jni); + OVR_UNUSED(activityObject); + OVR_UNUSED(packageName); + return false; +} + +inline const char* ovr_GetCurrentPackageName( + JNIEnv* jni, + jobject activityObject, + char* packageName, + int const maxLen) { + packageName[0] = '\0'; + const char* currentPackageName = "test.package.name"; + OVR::OVR_sprintf(packageName, maxLen, "%s", currentPackageName); + return packageName; +} + +#endif + +#if defined(OVR_OS_ANDROID) +#include "JniUtils-inl.h" +#endif // defined(OVR_OS_ANDROID) + +#endif // OVR_JniUtils_h diff --git a/Samples/1stParty/OVR/Include/OVR_Asserts.h b/Samples/1stParty/OVR/Include/OVR_Asserts.h new file mode 100755 index 0000000..3c41661 --- /dev/null +++ b/Samples/1stParty/OVR/Include/OVR_Asserts.h @@ -0,0 +1,116 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * All rights reserved. + * + * Licensed under the Oculus SDK License Agreement (the "License"); + * you may not use the Oculus SDK except in compliance with the License, + * which is provided at the time of installation or download, or which + * otherwise accompanies this software in either electronic or hard copy form. + * + * You may obtain a copy of the License at + * + * https://developer.oculus.com/licenses/oculussdk/ + * + * Unless required by applicable law or agreed to in writing, the Oculus SDK + * 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. + */ + +/************************************************************************************ + * Filename : OVR_Asserts.h + * Content : Capture Asserts for Release and Debug builds. Send Telemetry logs in + * Release builds. + * Created : July, 2019 + * Notes : + ***********************************************************************************/ + +#ifndef OVR_Asserts_h +#define OVR_Asserts_h + +//----------------------------------------------------------------------------------- +// ***** OVR_DEBUG_BREAK, +// OVR_ASSERT, +// +// Macros have effect only in debug builds. +// +// Example OVR_DEBUG_BREAK usage (note the lack of parentheses): +// #define MY_ASSERT(expression) do { if (!(expression)) { OVR_DEBUG_BREAK; } } while(0) +// +// +// Example OVR_ASSERT usage: +// OVR_ASSERT(count < 100); +// +//-------------------------------------------------------------------------------------------------- + +#ifndef OVR_BUILD_DEBUG + +#define OVR_DEBUG_BREAK ((void)0) +#define OVR_ASSERT(p) ((void)0) + +#else // OVR_DEBUG_BUILD + +// 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 +// Android specific debugging support +#elif defined(OVR_OS_ANDROID) +#include +#define OVR_EXPAND1(s) #s +#define OVR_EXPAND(s) OVR_EXPAND1(s) +#define OVR_DEBUG_BREAK \ + do { \ + __builtin_trap(); \ + } while (0) +#define OVR_ASSERT(p) \ + do { \ + if (!(p)) { \ + __android_log_write( \ + ANDROID_LOG_WARN, "OVR", "ASSERT@ " __FILE__ "(" OVR_EXPAND(__LINE__) "): " #p); \ + OVR_DEBUG_BREAK; \ + } \ + } while (0) +// 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 { \ + *((volatile int*)0) = 1; \ + } while (0) +#endif + +#if !defined(OVR_ASSERT) // Android currently defines its own version of OVR_ASSERT() with logging +#include +#define OVR_ASSERT assert +#endif // !defined(OVR_ASSERT) + +#endif // OVR_DEBUG_BUILD + +#endif // OVR_Asserts_h diff --git a/Samples/1stParty/OVR/Include/OVR_BitFlags.h b/Samples/1stParty/OVR/Include/OVR_BitFlags.h new file mode 100755 index 0000000..fb7204e --- /dev/null +++ b/Samples/1stParty/OVR/Include/OVR_BitFlags.h @@ -0,0 +1,153 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/************************************************************************************ + +PublicHeader: OVR_Kernel.h +Filename : OVR_BitFlags.h +Content : Template for typesafe number types. +Created : June 5, 2014 +Authors : Jonathan E. wright + +*************************************************************************************/ + +#if !defined(OVR_BitFlags_h) +#define OVR_BitFlags_h + +#include + +namespace OVR { + +//============================================================== +// BitFlagsT +// +// This class provides a wrapper for C-like bit flags allowing standard syntax such as: +// +// flags |= FLAG; // set a flag +// flags &= ~FLAG; // clear a flag +// +// while providing type safety and making the flag type obvious. For instance, +// one might write an interface function such as: +// +// void DoSomething( const int flags ); +// +// Because the flags parameter is just an int, a client using the interface has no way +// of knowing what flags to pass for the second parameter without either being shown +// an example, a comment, or inspecting the code inside of DoSomething(), which may not +// be available to them! +// +// Using an enum instead of an int presents its own problems because as soon as enums +// are combined with the | operator they become an int and casting is required. +// +// Instead, this template class combines the type-safety of enums and (most of) the syntax +// of integer types and allows: +// +// void DoSomething( const SomeFlagsType flags ); +// +// The SomeFlagsType can easily be found and deciphered because it is defined as: +// +// enum eSomeFlagsType +// { +// // NOTE: do not assign actual bit values! The template handles that for simplicity. +// // The value of the enums needs to be the BIT POSITION and NOT the BIT VALUE!!! +// SOMEFLAG_ONE, +// SOMEFLAG_BIT_TWO, +// SOMEFLAG_BIT_THREE, +// SOMEFLAG_BIT_FOUR, +// // etc. +// }; +// +// typedef BitFlagsT< eFlagsType > SomeFlagsType; +// +// The flags are all exposed in one place and there is a link from the interface +// directly to the type. +// +// There are still some minor syntax differences: +// +// To maintain type safety, construction directly from integer types is not allowed. +// This is invalid: +// +// SomeFlagsType flags( SOMEFLAG_ONE | SOMEFLAG_TWO ); +// +// Instead use one of the following expression patterns: +// +// SomeFlagsType flags( SomeFlagsType( SOMEFLAG_ONE ) | SOMEFLAG_TWO ); +// SomeFlagsType flags = SomeFlagsType( SOMEFLAG_ONE ) | SOMEFLAG_TWO; +// SomeFlagsType flags( SomeFlagsType() | SOMEFLAG_ONE | SOMEFLAG_TWO ); +// SomeFlagsType flags = SomeFlagsType() | SOMEFLAG_ONE | SOMEFLAG_TWO; +// +// NOTE: do not assign actual bit values ( i.e. 1 << 0, 1 << 1, etc.) to the enums. +// The class internally will shift by the value of the enum to get the bit value. +// The value of each enum should be the BIT POSITION and not the BIT VALUE!!! + +// pass ALL_BITS to the BitFlagsT constructor to set all bit flags. +enum eAllBits { ALL_BITS }; + +template +class BitFlagsT { + public: + static _storageType_ AllBits() { + size_t numBits = sizeof(_storageType_) * 8; + _storageType_ topBit = (1ULL << (numBits - 1)); + _storageType_ allButTopBit = topBit - 1ULL; + return allButTopBit | topBit; + } + + BitFlagsT() : Value(0) {} + + inline _storageType_ ToBit(const _enumType_ e) { + return static_cast<_storageType_>(1) << e; + } + + BitFlagsT(const _enumType_ e) : Value(ToBit(e)) {} + + BitFlagsT(const BitFlagsT& other) : Value(other.Value) {} + + BitFlagsT(const eAllBits allBits) : Value(AllBits()) {} + + BitFlagsT operator|(const BitFlagsT& rhs) const { + _storageType_ v = Value | rhs.GetValue(); + return BitFlagsT(v); + } + + bool operator&(const BitFlagsT& rhs) const { + return (Value & rhs.GetValue()) != 0; + } + + BitFlagsT& operator=(const BitFlagsT& rhs) { + if (this != &rhs) { + this->Value = rhs.Value; + } + return *this; + } + + BitFlagsT& operator|=(const BitFlagsT& rhs) { + Value |= rhs.GetValue(); + return *this; + } + + BitFlagsT& operator&=(const BitFlagsT& rhs) { + Value &= rhs.GetValue(); + return *this; + } + + BitFlagsT operator~() const { + return BitFlagsT(~this->GetValue()); + } + + _storageType_ GetValue() const { + return Value; + } + + private: + _storageType_ Value; + + private: + // It's conceivable we might want to expose this as public, as long as it remains explicit + // to allow assignment of mixed flags, but that reduces the type safety of this template + // significantly. + explicit BitFlagsT(const _storageType_ value) : Value(value) {} +}; + +} // namespace OVR + +#endif // OVR_BitFlags_h diff --git a/Samples/1stParty/OVR/Include/OVR_Compiler.h b/Samples/1stParty/OVR/Include/OVR_Compiler.h new file mode 100755 index 0000000..4d8b3b1 --- /dev/null +++ b/Samples/1stParty/OVR/Include/OVR_Compiler.h @@ -0,0 +1,145 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * All rights reserved. + * + * Licensed under the Oculus SDK License Agreement (the "License"); + * you may not use the Oculus SDK except in compliance with the License, + * which is provided at the time of installation or download, or which + * otherwise accompanies this software in either electronic or hard copy form. + * + * You may obtain a copy of the License at + * + * https://developer.oculus.com/licenses/oculussdk/ + * + * Unless required by applicable law or agreed to in writing, the Oculus SDK + * 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. + */ + +/************************************************************************************ + * PublicHeader: OVR_Types.h + * Filename : OVR_Compiler.h + * Content : Compiler-specific feature identification and utilities + * Created : June 19, 2014 + * Notes : + ***********************************************************************************/ + +#ifndef OVR_Compiler_h +#define OVR_Compiler_h + +#pragma once + +// References +// https://gcc.gnu.org/projects/cxx0x.html +// https://gcc.gnu.org/projects/cxx1y.html +// http://clang.llvm.org/cxx_status.html +// http://msdn.microsoft.com/en-us/library/hh567368.aspx +// https://docs.google.com/spreadsheet/pub?key=0AoBblDsbooe4dHZuVTRoSTFBejk5eFBfVk1GWlE5UlE&output=html +// http://nadeausoftware.com/articles/2012/10/c_c_tip_how_detect_compiler_name_and_version_using_compiler_predefined_macros + +//----------------------------------------------------------------------------------- +// ***** 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 +// MSVC 11.0 (VC2012) = 1700 +// MSVC 12.0 (VC2013) = 1800 +// MSVC 14.0 (VC2015) = 1900 +#define OVR_CC_MSVC _MSC_VER + +#if _MSC_VER == 0x1600 +#if _MSC_FULL_VER < 160040219 +#error "Oculus does not support VS2010 without SP1 installed." +#endif +#endif + +#if (__cplusplus > 199711L) +#define OVR_CPP11 +#else +#undef OVR_CPP11 +#endif + +#elif defined(__GNUC__) +#define OVR_CC_GNU + +#if (__cplusplus > 199711L) +#define OVR_CPP11 +#else +#undef OVR_CPP11 +#endif + +#elif defined(__clang__) +#define OVR_CC_CLANG + +#elif defined(__CC_ARM) +#define OVR_CC_ARM + +#else +#error "Oculus does not support this Compiler" +#endif + +//----------------------------------------------------------------------------------- +// ***** OVR_CC_VERSION +// +// M = major version +// m = minor version +// p = patch release +// b = build number +// +// Compiler Format Example +// ---------------------------- +// OVR_CC_GNU Mmm 408 means GCC 4.8 +// OVR_CC_CLANG Mmm 305 means clang 3.5 +// OVR_CC_MSVC MMMM 1700 means VS2012 +// OVR_CC_ARM Mmpbbb 401677 means 4.0, patch 1, build 677 +// OVR_CC_INTEL MMmm 1210 means 12.10 +// OVR_CC_EDG Mmm 407 means EDG 4.7 +// +#if defined(OVR_CC_GNU) +#define OVR_CC_VERSION ((__GNUC__ * 100) + __GNUC_MINOR__) +#elif defined(OVR_CC_CLANG) +#define OVR_CC_VERSION ((__clang_major__ * 100) + __clang_minor__) +#elif defined(OVR_CC_MSVC) +#define OVR_CC_VERSION _MSC_VER // Question: Should we recognize _MSC_FULL_VER? +#elif defined(OVR_CC_ARM) +#define OVR_CC_VERSION __ARMCC_VERSION +#elif defined(OVR_CC_INTEL) +#if defined(__INTEL_COMPILER) +#define OVR_CC_VERSION __INTEL_COMPILER +#elif defined(__ICL) +#define OVR_CC_VERSION __ICL +#elif defined(__ICC) +#define OVR_CC_VERSION __ICC +#elif defined(__ECC) +#define OVR_CC_VERSION __ECC +#endif +#elif defined(OVR_CC_EDG) +#define OVR_CC_VERSION \ + __EDG_VERSION__ // This is a generic fallback for EDG-based compilers which aren't specified + // above (e.g. as OVR_CC_ARM) +#endif + +#endif // header include guard diff --git a/Samples/1stParty/OVR/Include/OVR_JSON.h b/Samples/1stParty/OVR/Include/OVR_JSON.h new file mode 100755 index 0000000..b62b500 --- /dev/null +++ b/Samples/1stParty/OVR/Include/OVR_JSON.h @@ -0,0 +1,1317 @@ +/* + * Portions Copyright (c) Meta Platforms, Inc. and affiliates. + * All rights reserved. + * + * Licensed under the Oculus SDK License Agreement (the "License"); + * you may not use the Oculus SDK except in compliance with the License, + * which is provided at the time of installation or download, or which + * otherwise accompanies this software in either electronic or hard copy form. + * + * You may obtain a copy of the License at + * + * https://developer.oculus.com/licenses/oculussdk/ + * + * Unless required by applicable law or agreed to in writing, the Oculus SDK + * 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. + */ + +/************************************************************************************ + +PublicHeader: None +Filename : OVR_JSON.h +Content : JSON format reader and writer +Created : April 9, 2013 +Author : Brant Lewis +Notes : + The code is a derivative of the cJSON library written by Dave Gamble and subject + to the following permissive copyright. + + Copyright (c) 2009 Dave Gamble + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + +************************************************************************************/ +#ifndef OVR_JSON_h +#define OVR_JSON_h + +#include +#include +#include +#include +#include + +#include "OVR_Types.h" +#include "OVR_Math.h" +#include "OVR_Std.h" +#include "OVR_LogUtils.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace OVR { + +// JSONItemType describes the type of JSON item, specifying the type of +// data that can be obtained from it. +enum JSONItemType { + JSON_None = 0, + JSON_Null = 1, + JSON_Bool = 2, + JSON_Number = 3, + JSON_String = 4, + JSON_Array = 5, + JSON_Object = 6 +}; + +//----------------------------------------------------------------------------- +// Create a new copy of a string +inline char* JSON_strdup(const char* str) { + size_t len = OVR_strlen(str) + 1; + char* copy = (char*)malloc(len); + if (!copy) + return 0; + memcpy(copy, str, len); + return copy; +} + +//----------------------------------------------------------------------------- +// Render the number from the given item into a string. +inline char* PrintNumber(double d) { + char* str; + int valueint = (int)d; + + if (fabs(((double)valueint) - d) <= DBL_EPSILON && d <= INT_MAX && d >= INT_MIN) { + str = (char*)malloc(21); // 2^64+1 can be represented in 21 chars. + if (str) + OVR_sprintf(str, 21, "%d", valueint); + } else { + str = (char*)malloc(64); // This is a nice tradeoff. + if (str) { + // The JSON Standard, section 7.8.3, specifies that decimals are always expressed with + // '.' and not some locale-specific decimal such as ',' or ' '. However, since we are + // using the C standard library below to write a floating point number, we need to make + // sure that it's writing a '.' and not something else. We can't change the locale (even + // temporarily) here, as it will affect the whole process by default. That are + // compiler-specific ways to change this per-thread, but below we implement the simple + // solution of simply fixing the decimal after the string was written. + + if (fabs(floor(d) - d) <= DBL_EPSILON && fabs(d) < 1.0e60) + OVR_sprintf(str, 64, "%.0f", d); + else if (fabs(d) < 1.0e-6 || fabs(d) > 1.0e9) + OVR_sprintf(str, 64, "%e", d); + else + OVR_sprintf(str, 64, "%f", d); + } + } + return str; +} + +//----------------------------------------------------------------------------- +// Parse the input text into an un-escaped cstring, and populate item. +static constexpr unsigned char firstByteMark[7] = {0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC}; + +//----------------------------------------------------------------------------- +// Helper to assign error sting and return 0. +inline const char* AssignError(const char** perror, const char* errorMessage) { + if (perror) + *perror = errorMessage; + return nullptr; +} + +//----------------------------------------------------------------------------- +// Parses a hex string up to the specified number of digits. +// Returns the first character after the string. +inline const char* ParseHex(unsigned* val, unsigned digits, const char* str) { + *val = 0; + + for (unsigned digitCount = 0; digitCount < digits; digitCount++, str++) { + unsigned v = *str; + + if ((v >= '0') && (v <= '9')) + v -= '0'; + else if ((v >= 'a') && (v <= 'f')) + v = 10 + v - 'a'; + else if ((v >= 'A') && (v <= 'F')) + v = 10 + v - 'A'; + else + break; + + *val = *val * 16 + v; + } + + return str; +} + +//----------------------------------------------------------------------------- +// Render the string provided to an escaped version that can be printed. +inline char* PrintString(const char* str) { + const char* ptr; + char *ptr2, *out; + int len = 0; + unsigned char token; + + if (!str) + return JSON_strdup(""); + ptr = str; + + token = *ptr; + while (token && ++len) { + if (strchr("\"\\\b\f\n\r\t", token)) + len++; + else if (token < 32) + len += 5; + ptr++; + token = *ptr; + } + + int buff_size = len + 3; + out = (char*)malloc(buff_size); + if (!out) + return 0; + + ptr2 = out; + ptr = str; + *ptr2++ = '\"'; + + while (*ptr) { + if ((unsigned char)*ptr > 31 && *ptr != '\"' && *ptr != '\\') + *ptr2++ = *ptr++; + else { + *ptr2++ = '\\'; + switch (token = *ptr++) { + case '\\': + *ptr2++ = '\\'; + break; + case '\"': + *ptr2++ = '\"'; + break; + case '\b': + *ptr2++ = 'b'; + break; + case '\f': + *ptr2++ = 'f'; + break; + case '\n': + *ptr2++ = 'n'; + break; + case '\r': + *ptr2++ = 'r'; + break; + case '\t': + *ptr2++ = 't'; + break; + default: + OVR_sprintf(ptr2, buff_size - (ptr2 - out), "u%04x", token); + ptr2 += 5; + break; // Escape and print. + } + } + } + *ptr2++ = '\"'; + *ptr2++ = '\0'; + return out; +} + +//----------------------------------------------------------------------------- +// Utility to jump whitespace and cr/lf +static const char* skip(const char* in) { + while (in && *in && (unsigned char)*in <= ' ') + in++; + return in; +} + +//----------------------------------------------------------------------------- +// ***** JSON + +// JSON object represents a JSON node that can be either a root of the JSON tree +// or a child item. Every node has a type that describes what it is. +// New JSON trees are typically loaded with JSON::Load or created with JSON::Parse. + +class JSON { + public: + std::list> Children; + JSONItemType Type; // Type of this JSON node. + std::string Name; // Name part of the {Name, Value} pair in a parent object. + std::string Value; + double dValue; + + public: + JSON(JSONItemType itemType = JSON_Object) : Type(itemType), dValue(0.0) {} + ~JSON() {} + + // *** Creation of NEW JSON objects + + static std::shared_ptr CreateObject() { + return std::make_shared(JSON_Object); + } + static std::shared_ptr CreateNull() { + return std::make_shared(JSON_Null); + } + static std::shared_ptr CreateArray() { + return std::make_shared(JSON_Array); + } + static std::shared_ptr CreateBool(bool b) { + return createHelper(JSON_Bool, b ? 1.0 : 0.0); + } + static std::shared_ptr CreateNumber(double num) { + return createHelper(JSON_Number, num); + } + static std::shared_ptr CreateString(const char* s) { + return createHelper(JSON_String, 0.0, s); + } + + // Creates a new JSON object from parsing the given string. + // Returns a null pointer and fills in *perror in case of parse error. + static std::shared_ptr Parse(const char* buff, const char** perror = nullptr) { + const char* end = nullptr; + std::shared_ptr json = std::make_shared(); + + if (json == nullptr) { + AssignError(perror, "Error: Failed to allocate memory"); + return nullptr; + } + + end = json->parseValue(skip(buff), perror); + if (!end) { + return nullptr; + } // parse failure. ep is set. + + return json; + } + + // Loads and parses a JSON object from a file. + // Returns a null pointer and fills in *perror in case of parse error. + static std::shared_ptr Load(const char* path, const char** perror = nullptr) { + std::ifstream is; + is.open(path, std::ifstream::in); + if (!is.is_open()) { +#if defined(OVR_OS_ANDROID) + OVR_LOG("JSON::Load failed to open %s", path); +#endif + AssignError(perror, "Failed to open file"); + return nullptr; + } + + // get size + is.seekg(0, is.end); + int len = static_cast(is.tellg()); + is.seekg(0, is.beg); + + // allocate buffer + std::vector buff; + buff.resize(len + 1); + + // read all file + is.read((char*)buff.data(), len); + if (!is) { +#if defined(OVR_OS_ANDROID) + OVR_LOG("JSON::Load failed to read %s", path); +#endif + AssignError(perror, "Failed to read file"); + return nullptr; + } + + // close + is.close(); + + // Ensure the result is null-terminated since Parse() expects null-terminated input. + buff[len] = '\0'; + + std::shared_ptr json = JSON::Parse((char*)buff.data(), perror); + +#if defined(OVR_OS_ANDROID) +#if defined(OVR_BUILD_DEBUG) + OVR_LOG( + "JSON::Load finished reading %s - length = %d ptr = %p", + path, + (int)buff.size(), + json.get()); +#endif +#endif + + return json; + } + + // Saves a JSON object to a file. + bool Save(const char* path) const { + std::ofstream os; + os.open(path, std::ios::out | std::ios::trunc); + if (!os.is_open()) { + return false; + } + + char* text = PrintValue(0, true); + if (text) { + intptr_t len = OVR_strlen(text); + OVR_ASSERT(len <= (intptr_t)(int)len); + + os.write((char*)text, (int)len); + // save stream state + bool writeComplete = !!os; + os.close(); + + free(text); + return writeComplete; + } else { + return false; + } + } + // Child item access functions + void AddItem(const char* string, std::shared_ptr item) { + if (!item) + return; + item->Name = string; + Children.push_back(item); + } + void AddBoolItem(const char* name, bool b) { + AddItem(name, CreateBool(b)); + } + void AddNumberItem(const char* name, double n) { + AddItem(name, CreateNumber(n)); + } + void AddStringItem(const char* name, const char* s) { + AddItem(name, CreateString(s)); + } + + // *** Object Member Access + + // These provide access to child items of the list. + bool HasItems() const { + return Children.empty(); + } + // Returns first/last child item, or null if child list is empty. + std::shared_ptr GetFirstItem() { + return (!Children.empty()) ? Children.front() : nullptr; + } + const std::shared_ptr GetFirstItem() const { + return (!Children.empty()) ? Children.front() : nullptr; + } + std::shared_ptr GetLastItem() { + return (!Children.empty()) ? Children.back() : nullptr; + } + const std::shared_ptr GetLastItem() const { + return (!Children.empty()) ? Children.back() : nullptr; + } + + // Counts the number of items in the object; these methods are inefficient. + unsigned GetItemCount() const { + return static_cast(Children.size()); + } + std::shared_ptr GetItemByIndex(unsigned index) { + for (const std::shared_ptr& child : Children) { + if (index-- == 0) { + return child; + } + } + return nullptr; + } + const std::shared_ptr GetItemByIndex(unsigned index) const { + for (const std::shared_ptr& child : Children) { + if (index-- == 0) { + return child; + } + } + return nullptr; + } + std::shared_ptr GetItemByName(const char* name) { + for (const std::shared_ptr& child : Children) { + if (OVR_strcmp(child->Name.c_str(), name) == 0) { + return child; + } + } + return nullptr; + } + const std::shared_ptr GetItemByName(const char* name) const { + for (const std::shared_ptr& child : Children) { + if (OVR_strcmp(child->Name.c_str(), name) == 0) { + return child; + } + } + return nullptr; + } + void ReplaceNodeWith(const char* name, const std::shared_ptr newNode) { + for (auto it = Children.begin(); it != Children.end(); ++it) { + if (OVR_strcmp((*it)->Name.c_str(), name) == 0) { + *it = newNode; + return; + } + } + } + + /* + // Returns next item in a list of children; 0 if no more items exist. + JSON* GetNextItem(JSON* item) { return (item->pNext == nullptr) ? + nullptr : item->pNext; } const JSON* GetNextItem(const JSON* item) const { return + (item->pNext == nullptr) ? nullptr : item->pNext; } JSON* GetPrevItem(JSON* item) + { return (item->pPrev == nullptr) ? nullptr : item->pPrev; } const JSON* GetPrevItem(const + JSON* item) const { return (item->pPrev == nullptr) ? nullptr : item->pPrev; } + */ + + // Value access with range checking where possible. + // Using the JsonReader class is recommended instead of using these. + bool GetBoolValue() const { + OVR_ASSERT((Type == JSON_Number) || (Type == JSON_Bool)); + OVR_ASSERT(dValue == 0.0 || dValue == 1.0); // if this hits, value is out of range + return (dValue != 0.0); + } + int32_t GetInt32Value() const { + OVR_ASSERT(Type == JSON_Number); + OVR_ASSERT(dValue >= INT_MIN && dValue <= INT_MAX); // if this hits, value is out of range + return (int32_t)dValue; + } + int64_t GetInt64Value() const { + OVR_ASSERT(Type == JSON_Number); + OVR_ASSERT( + dValue >= -9007199254740992LL && + dValue <= 9007199254740992LL); // 2^53 - if this hits, value is out of range + return (int64_t)dValue; + } + float GetFloatValue() const { + OVR_ASSERT(Type == JSON_Number); + OVR_ASSERT(dValue >= -FLT_MAX && dValue <= FLT_MAX); // too large to represent as a float + OVR_ASSERT( + dValue == 0 || dValue <= -FLT_MIN || + dValue >= FLT_MIN); // if the number is too small to be represented as a float + return (float)dValue; + } + double GetDoubleValue() const { + OVR_ASSERT(Type == JSON_Number); + return dValue; + } + const std::string& GetStringValue() const { + OVR_ASSERT( + Type == JSON_String || Type == JSON_Null); // May be JSON_Null if the value od a string + // field was actually the word "null" + return Value; + } + + // *** Array Element Access + + // Add new elements to the end of array. + void AddArrayElement(std::shared_ptr item) { + if (!item) { + return; + } + + Children.push_back(item); + } + void AddArrayBool(bool b) { + AddArrayElement(CreateBool(b)); + } + void AddArrayNumber(double n) { + AddArrayElement(CreateNumber(n)); + } + void AddArrayString(const char* s) { + AddArrayElement(CreateString(s)); + } + + // Accessed array elements; currently inefficient. + int GetArraySize() const { + if (Type == JSON_Array) { + return GetItemCount(); + } else + return 0; + } + double GetArrayNumber(int index) const { + if (Type == JSON_Array) { + const std::shared_ptr number = GetItemByIndex(index); + return number ? number->dValue : 0.0; + } else { + return 0; + } + } + const char* GetArrayString(int index) const { + if (Type == JSON_Array) { + const std::shared_ptr number = GetItemByIndex(index); + return number ? number->Value.c_str() : nullptr; + } else { + return nullptr; + } + } + + // Return text value of JSON. Use free when done with return value + char* PrintValue(int depth, bool fmt) const { + char* out = nullptr; + + switch (Type) { + case JSON_Null: + out = JSON_strdup("null"); + break; + case JSON_Bool: + if (dValue == 0) + out = JSON_strdup("false"); + else + out = JSON_strdup("true"); + break; + case JSON_Number: + out = PrintNumber(dValue); + break; + case JSON_String: + out = PrintString(Value.c_str()); + break; + case JSON_Array: + out = PrintArray(depth, fmt); + break; + case JSON_Object: + out = PrintObject(depth, fmt); + break; + case JSON_None: { + OVR_ASSERT(false); +#if defined(OVR_OS_ANDROID) + OVR_LOG("JSON::PrintValue - Bad JSON type."); +#endif + break; + } + } + return out; + } + + protected: + static std::shared_ptr + createHelper(JSONItemType itemType, double dval, const char* strVal = nullptr) { + std::shared_ptr item = std::make_shared(itemType); + if (item) { + item->dValue = dval; + if (strVal) + item->Value = strVal; + } + return item; + } + + // JSON Parsing helper functions. + const char* parseValue(const char* buff, const char** perror) { + if (perror) + *perror = 0; + + if (!buff) + return nullptr; // Fail on null. + + if (!strncmp(buff, "null", 4)) { + Type = JSON_Null; + return buff + 4; + } + if (!strncmp(buff, "false", 5)) { + Type = JSON_Bool; + Value = "false"; + dValue = 0.0; + return buff + 5; + } + if (!strncmp(buff, "true", 4)) { + Type = JSON_Bool; + Value = "true"; + dValue = 1.0; + return buff + 4; + } + if (*buff == '\"') { + return parseString(buff, perror); + } + if (*buff == '-' || (*buff >= '0' && *buff <= '9')) { + return parseNumber(buff); + } + if (*buff == '[') { + return parseArray(buff, perror); + } + if (*buff == '{') { + return parseObject(buff, perror); + } + + return AssignError(perror, (std::string("Syntax Error: Invalid syntax: ") + buff).c_str()); + } + const char* parseNumber(const char* num) { + const char* num_start = num; + double n = 0, sign = 1, scale = 0; + int subscale = 0, signsubscale = 1; + + // Could use sscanf for this? + if (*num == '-') { + sign = -1, num++; // Has sign? + } + if (*num == '0') { + num++; // is zero + } + + if (*num >= '1' && *num <= '9') { + do { + n = (n * 10.0) + (*num++ - '0'); + } while (*num >= '0' && *num <= '9'); // Number? + } + + if (*num == '.' && num[1] >= '0' && num[1] <= '9') { + num++; + do { + n = (n * 10.0) + (*num++ - '0'); + scale--; + } while (*num >= '0' && *num <= '9'); // Fractional part? + } + + if (*num == 'e' || *num == 'E') // Exponent? + { + num++; + if (*num == '+') { + num++; + } else if (*num == '-') { + signsubscale = -1; + num++; // With sign? + } + + while (*num >= '0' && *num <= '9') { + subscale = (subscale * 10) + (*num++ - '0'); // Number? + } + } + + // Number = +/- number.fraction * 10^+/- exponent + n = sign * n * pow(10.0, (scale + subscale * signsubscale)); + + // Assign parsed value. + Type = JSON_Number; + dValue = n; + Value.assign(num_start, num - num_start); + + return num; + } + const char* parseArray(const char* buff, const char** perror) { + std::shared_ptr child; + if (*buff != '[') { + return AssignError(perror, "Syntax Error: Missing opening bracket"); + } + + Type = JSON_Array; + buff = skip(buff + 1); + + if (*buff == ']') + return buff + 1; // empty array. + + child = std::make_shared(); + if (!child) + return nullptr; // memory fail + Children.push_back(child); + + buff = skip(child->parseValue(skip(buff), perror)); // skip any spacing, get the buff. + if (!buff) + return 0; + + while (*buff == ',') { + std::shared_ptr new_item = std::make_shared(); + if (!new_item) + return AssignError(perror, "Error: Failed to allocate memory"); + + Children.push_back(new_item); + + buff = skip(new_item->parseValue(skip(buff + 1), perror)); + if (!buff) + return AssignError(perror, "Error: Failed to allocate memory"); + } + + if (*buff == ']') + return buff + 1; // end of array + + return AssignError(perror, "Syntax Error: Missing ending bracket"); + } + const char* parseObject(const char* buff, const char** perror) { + if (*buff != '{') { + return AssignError(perror, "Syntax Error: Missing opening brace"); + } + + Type = JSON_Object; + buff = skip(buff + 1); + if (*buff == '}') + return buff + 1; // empty array. + + std::shared_ptr child = std::make_shared(); + Children.push_back(child); + + buff = skip(child->parseString(skip(buff), perror)); + if (!buff) + return 0; + child->Name = child->Value; + child->Value.clear(); + + if (*buff != ':') { + return AssignError(perror, "Syntax Error: Missing colon"); + } + + buff = skip(child->parseValue(skip(buff + 1), perror)); // skip any spacing, get the value. + if (!buff) + return 0; + + while (*buff == ',') { + child = std::make_shared(); + if (!child) + return 0; // memory fail + + Children.push_back(child); + + buff = skip(child->parseString(skip(buff + 1), perror)); + if (!buff) + return 0; + + child->Name = child->Value; + child->Value.clear(); + + if (*buff != ':') { + return AssignError(perror, "Syntax Error: Missing colon"); + } // fail! + + // Skip any spacing, get the value. + buff = skip(child->parseValue(skip(buff + 1), perror)); + if (!buff) + return 0; + } + + if (*buff == '}') + return buff + 1; // end of array + + return AssignError(perror, "Syntax Error: Missing closing brace"); + } + const char* parseString(const char* str, const char** perror) { + const char* ptr = str + 1; + const char* p; + char* ptr2; + char* out; + int len = 0; + unsigned uc, uc2; + + if (*str != '\"') { + return AssignError(perror, "Syntax Error: Missing quote"); + } + + while (*ptr != '\"' && *ptr && ++len) { + if (*ptr++ == '\\') { + if (*ptr) { + ptr++; // Skip escaped quotes. + } else { + // we reached the end of the file too soon, stop + } + } + } + + // This is how long we need for the string, roughly. + out = (char*)malloc(len + 1); + if (!out) + return 0; + + ptr = str + 1; + ptr2 = out; + + while (*ptr != '\"' && *ptr) { + if (*ptr != '\\') { + *ptr2++ = *ptr++; + } else { + ptr++; + switch (*ptr) { + case 'b': + *ptr2++ = '\b'; + break; + case 'f': + *ptr2++ = '\f'; + break; + case 'n': + *ptr2++ = '\n'; + break; + case 'r': + *ptr2++ = '\r'; + break; + case 't': + *ptr2++ = '\t'; + break; + + // Transcode utf16 to utf8. + case 'u': + + // Get the unicode char. + p = ParseHex(&uc, 4, ptr + 1); + if (ptr != p) + ptr = p - 1; + + if ((uc >= 0xDC00 && uc <= 0xDFFF) || uc == 0) + break; // Check for invalid. + + // UTF16 surrogate pairs. + if (uc >= 0xD800 && uc <= 0xDBFF) { + if (ptr[1] != '\\' || ptr[2] != 'u') + break; // Missing second-half of surrogate. + + p = ParseHex(&uc2, 4, ptr + 3); + if (ptr != p) + ptr = p - 1; + + if (uc2 < 0xDC00 || uc2 > 0xDFFF) + break; // Invalid second-half of surrogate. + + uc = 0x10000 + (((uc & 0x3FF) << 10) | (uc2 & 0x3FF)); + } + + len = 4; + + if (uc < 0x80) + len = 1; + else if (uc < 0x800) + len = 2; + else if (uc < 0x10000) + len = 3; + + ptr2 += len; + + switch (len) { + case 4: + *--ptr2 = static_cast((uc | 0x80) & 0xBF); + uc >>= 6; + [[fallthrough]]; + case 3: + *--ptr2 = static_cast((uc | 0x80) & 0xBF); + uc >>= 6; + [[fallthrough]]; + case 2: + *--ptr2 = static_cast((uc | 0x80) & 0xBF); + uc >>= 6; + [[fallthrough]]; + case 1: + *--ptr2 = (char)(uc | firstByteMark[len]); + // no break + } + ptr2 += len; + break; + + default: + if (*ptr) { + *ptr2++ = *ptr; + } + break; + } + if (*ptr) { + ptr++; + } + } + } + + *ptr2 = 0; + if (*ptr == '\"') + ptr++; + + // Make a copy of the string + Value = out; + free(out); + Type = JSON_String; + + return ptr; + } + + char* PrintObject(int depth, bool fmt) const { + char **entries = 0, **names = 0; + char* out = 0; + char *ptr, *ret, *str; + intptr_t len = 7, i = 0, j; + bool fail = false; + + // Count the number of entries. + int numentries = GetItemCount(); + + // Explicitly handle empty object case + if (numentries == 0) { + out = (char*)malloc(fmt ? depth + 4 : 4); + if (!out) + return 0; + ptr = out; + *ptr++ = '{'; + + if (fmt) { + *ptr++ = '\n'; + for (i = 0; i < depth - 1; i++) + *ptr++ = '\t'; + } + *ptr++ = '}'; + *ptr++ = '\0'; + return out; + } + // Allocate space for the names and the objects + entries = (char**)malloc(numentries * sizeof(char*)); + if (!entries) + return 0; + names = (char**)malloc(numentries * sizeof(char*)); + + if (!names) { + free(entries); + return 0; + } + memset(entries, 0, sizeof(char*) * numentries); + memset(names, 0, sizeof(char*) * numentries); + + // Collect all the results into our arrays: + depth++; + if (fmt) + len += depth; + + for (const auto& child : Children) { + names[i] = str = PrintString(child->Name.c_str()); + entries[i++] = ret = child->PrintValue(depth, fmt); + + if (str && ret) { + len += OVR_strlen(ret) + OVR_strlen(str) + 2 + (fmt ? 2 + depth : 0); + } else { + fail = true; + break; + } + } + + // Try to allocate the output string + if (!fail) + out = (char*)malloc(len); + if (!out) + fail = true; + + // Handle failure + if (fail) { + for (i = 0; i < numentries; i++) { + if (names[i]) + free(names[i]); + + if (entries[i]) + free(entries[i]); + } + + free(names); + free(entries); + return 0; + } + + // Compose the output: + *out = '{'; + ptr = out + 1; + if (fmt) { + *ptr++ = '\n'; + } + *ptr = 0; + + for (i = 0; i < numentries; i++) { + if (fmt) { + for (j = 0; j < depth; j++) { + *ptr++ = '\t'; + } + } + OVR_strcpy(ptr, len - (ptr - out), names[i]); + ptr += OVR_strlen(names[i]); + *ptr++ = ':'; + + if (fmt) { + *ptr++ = '\t'; + } + + OVR_strcpy(ptr, len - (ptr - out), entries[i]); + ptr += OVR_strlen(entries[i]); + + if (i != numentries - 1) { + *ptr++ = ','; + } + + if (fmt) { + *ptr++ = '\n'; + } + *ptr = 0; + + free(names[i]); + free(entries[i]); + } + + free(names); + free(entries); + + if (fmt) { + for (i = 0; i < depth - 1; i++) { + *ptr++ = '\t'; + } + } + *ptr++ = '}'; + *ptr++ = '\0'; + + return out; + } + + char* PrintArray(int depth, bool fmt) const { + char** entries; + char *out = 0, *ptr, *ret; + intptr_t len = 5; + + bool fail = false; + + // How many entries in the array? + int numentries = GetItemCount(); + if (!numentries) { + out = (char*)malloc(3); + if (out) + OVR_strcpy(out, 3, "[]"); + return out; + } + // Allocate an array to hold the values for each + entries = (char**)malloc(numentries * sizeof(char*)); + if (!entries) + return 0; + memset(entries, 0, numentries * sizeof(char*)); + + //// Retrieve all the results: + int entry = 0; + for (std::shared_ptr child : Children) { + if (entry >= numentries) + break; + + ret = child->PrintValue(depth + 1, fmt); + entries[entry] = ret; + if (ret) + len += OVR_strlen(ret) + 2 + (fmt ? 1 : 0); + else { + fail = true; + break; + } + ++entry; + } + + // If we didn't fail, try to malloc the output string + if (!fail) + out = (char*)malloc(len); + // If that fails, we fail. + if (!out) + fail = true; + + // Handle failure. + if (fail) { + for (int i = 0; i < numentries; i++) { + if (entries[i]) + free(entries[i]); + } + free(entries); + return 0; + } + + // Compose the output array. + *out = '['; + ptr = out + 1; + *ptr = '\0'; + for (int i = 0; i < numentries; i++) { + OVR_strcpy(ptr, len - (ptr - out), entries[i]); + ptr += OVR_strlen(entries[i]); + if (i != numentries - 1) { + *ptr++ = ','; + if (fmt) + *ptr++ = ' '; + *ptr = '\0'; + } + free(entries[i]); + } + free(entries); + *ptr++ = ']'; + *ptr++ = '\0'; + return out; + } + + friend class JsonReader; +}; + +//----------------------------------------------------------------------------- +// ***** JsonReader + +// Fast JSON reader. This reads one JSON node at a time. +// +// When used appropriately this class should result in easy to read, const +// correct code with maximum variable localization. Reading the JSON data with +// this class is not only fast but also safe in that variables or objects are +// either skipped or default initialized if the JSON file contains different +// data than expected. +// +// To be completely safe and to support backward / forward compatibility the +// JSON object names will have to be verified. This class will try to verify +// the object names with minimal effort. If the children of a node are read +// in the order they appear in the JSON file then using this class results in +// only one string comparison per child. Only if the children are read out +// of order, all children may have to be iterated to match a child name. +// This should, however, only happen when the code that writes out the JSON +// file has been changed without updating the code that reads back the data. +// Either way this class will do the right thing as long as the JSON tree is +// considered const and is not changed underneath this class. +// +// This is an example of how this class can be used to load a simplified indexed +// triangle model: +// +// std::shared_ptr json = JSON::Load( "filename.json" ); +// const JsonReader model( json ); +// if ( model.IsObject() ) +// { +// const JsonReader vertices( model.GetChildByName( "vertices" ) ); +// if ( vertices.IsArray() ) +// { +// while ( !vertices.IsEndOfArray() ) +// { +// const JsonReader vertex( vertices.GetNextArrayElement() ); +// if ( vertex.IsObject() ) +// { +// const float x = vertex.GetChildFloatByName( "x", 0.0f ); +// const float y = vertex.GetChildFloatByName( "y", 0.0f ); +// const float z = vertex.GetChildFloatByName( "z", 0.0f ); +// } +// } +// } +// const JsonReader indices( model.GetChildByName( "indices" ) ); +// if ( indices.IsArray() ) +// { +// while ( !indices.IsEndOfArray() ) +// { +// const int index = indices.GetNextArrayInt32( 0 ); +// } +// } +// } +// // shared_ptr will free resources when it goes out of scope +// + +class JsonReader { + public: + JsonReader(const std::shared_ptr json) : Parent(json) { + if (Parent) { + Child = Parent->Children.begin(); + } + } + + JsonReader(std::list>::iterator it) : JsonReader(*it) {} + + const std::shared_ptr AsParent() const { + return Parent; + } + + bool IsValid() const { + return Parent != nullptr; + } + bool IsObject() const { + return Parent != nullptr && Parent->Type == JSON_Object; + } + bool IsArray() const { + return Parent != nullptr && Parent->Type == JSON_Array; + } + bool IsEndOfArray() const { + OVR_ASSERT(Parent != nullptr); + return (Child == Parent->Children.end()); + } + + std::list>::iterator GetFirstChild() const { + return Parent->Children.begin(); + } + std::list>::iterator GetNextChild( + std::list>::iterator& child) const { + auto childClone = child; + ++childClone; + return childClone; + } + + const std::shared_ptr GetChildByName(const char* childName) const { + assert(IsObject()); + + // Check if the the cached child pointer is valid. + if (Child != Parent->Children.end()) { + if (OVR_strcmp((*Child)->Name.c_str(), childName) == 0) { + const std::shared_ptr c = *Child; + ++Child; // Cache the next child. + return c; + } + } + // Itereate over all children. + for (auto c = Parent->Children.begin(); c != Parent->Children.end(); ++c) { + if (OVR_strcmp((*c)->Name.c_str(), childName) == 0) { + Child = c; // Cache the next child. + return *c; + } + } + return 0; + } + bool GetChildBoolByName(const char* childName, const bool defaultValue = false) const { + const std::shared_ptr c = GetChildByName(childName); + return (c != nullptr) ? c->GetBoolValue() : defaultValue; + } + int32_t GetChildInt32ByName(const char* childName, const int32_t defaultValue = 0) const { + const std::shared_ptr c = GetChildByName(childName); + return (c != nullptr) ? c->GetInt32Value() : defaultValue; + } + int64_t GetChildInt64ByName(const char* childName, const int64_t defaultValue = 0) const { + const std::shared_ptr c = GetChildByName(childName); + return (c != nullptr) ? c->GetInt64Value() : defaultValue; + } + float GetChildFloatByName(const char* childName, const float defaultValue = 0.0f) const { + const std::shared_ptr c = GetChildByName(childName); + return (c != nullptr) ? c->GetFloatValue() : defaultValue; + } + double GetChildDoubleByName(const char* childName, const double defaultValue = 0.0) const { + const std::shared_ptr c = GetChildByName(childName); + return (c != nullptr) ? c->GetDoubleValue() : defaultValue; + } + const std::string GetChildStringByName( + const char* childName, + const std::string& defaultValue = std::string("")) const { + const std::shared_ptr c = GetChildByName(childName); + return std::string( + (c != nullptr && c->Type != JSON_Null) ? c->GetStringValue() : defaultValue); + } + + const std::shared_ptr GetNextArrayElement() const { + assert(IsArray()); + + // Check if the the cached child pointer is valid. + if (Child != Parent->Children.end()) { + const std::shared_ptr c = *Child; + ++Child; // Cache the next child. + return c; + } + return nullptr; + } + + bool GetNextArrayBool(const bool defaultValue = false) const { + const std::shared_ptr c = GetNextArrayElement(); + return (c != nullptr) ? c->GetBoolValue() : defaultValue; + } + int32_t GetNextArrayInt32(const int32_t defaultValue = 0) const { + const std::shared_ptr c = GetNextArrayElement(); + return (c != nullptr) ? c->GetInt32Value() : defaultValue; + } + int64_t GetNextArrayInt64(const int64_t defaultValue = 0) const { + const std::shared_ptr c = GetNextArrayElement(); + return (c != nullptr) ? c->GetInt64Value() : defaultValue; + } + float GetNextArrayFloat(const float defaultValue = 0.0f) const { + const std::shared_ptr c = GetNextArrayElement(); + return (c != nullptr) ? c->GetFloatValue() : defaultValue; + } + double GetNextArrayDouble(const double defaultValue = 0.0) const { + const std::shared_ptr c = GetNextArrayElement(); + return (c != nullptr) ? c->GetDoubleValue() : defaultValue; + } + const std::string GetNextArrayString(const std::string& defaultValue = std::string("")) const { + const std::shared_ptr c = GetNextArrayElement(); + return std::string((c != nullptr) ? c->GetStringValue() : defaultValue); + } + + private: + std::shared_ptr Parent; + mutable std::list>::iterator Child; // cached child pointer (iterator) +}; + +} // namespace OVR + +#endif // OVR_JSON_h diff --git a/Samples/1stParty/OVR/Include/OVR_LogUtils.h b/Samples/1stParty/OVR/Include/OVR_LogUtils.h new file mode 100755 index 0000000..7abf270 --- /dev/null +++ b/Samples/1stParty/OVR/Include/OVR_LogUtils.h @@ -0,0 +1,421 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/************************************************************************************ + +Filename : OVR_LogUtils.h (Previously Log.h) +Content : Macros and helpers for Android logging. +Created : 4/15/2014 +Authors : Jonathan E. Wright + +*************************************************************************************/ + +#if !defined(OVRLib_Log_h) +#define OVRLib_Log_h + +#include +#include +#include /* va_list, va_start, va_arg, va_end */ +#include +#include +#include +#include + +#include "OVR_Types.h" + +#if defined(WIN32) || defined(WIN64) || defined(_WIN32) || defined(_WIN64) +#if !defined(NOMINMAX) +#define NOMINMAX // stop Windows.h from redefining min and max and breaking std::min / std::max +#endif +#include // OutputDebugString +#endif + +#if defined(OVR_OS_ANDROID) +#include +#include + +void LogWithTag(const int prio, const char* tag, const char* fmt, ...) + __attribute__((__format__(printf, 3, 4))) __attribute__((__nonnull__(3))); + +void LogWithFileTag(const int prio, const char* fileTag, const char* fmt, ...) + __attribute__((__format__(printf, 3, 4))) __attribute__((__nonnull__(3))); + +#endif + +// Log with an explicit tag +inline void LogWithTag(const int prio, const char* tag, const char* fmt, ...) { +#if defined(OVR_OS_ANDROID) + va_list ap; + va_start(ap, fmt); + __android_log_vprint(prio, tag, fmt, ap); + va_end(ap); +#elif defined(OVR_OS_WIN32) + OVR_UNUSED(tag); + OVR_UNUSED(prio); + + va_list args; + va_start(args, fmt); + + char buffer[4096]; + vsnprintf_s(buffer, 4096, _TRUNCATE, fmt, args); + va_end(args); + + OutputDebugStringA(buffer); +#elif (defined(OVR_OS_LINUX) || defined(OVR_OS_MAC) || defined(OVR_OS_IPHONE)) + OVR_UNUSED(prio); + OVR_UNUSED(tag); + OVR_UNUSED(fmt); +#else +#warning "LogWithTag not implemented for this given OVR_OS_" +#endif +} + +/// The log timer is copied from XR_LOG's log timer +namespace { +struct OVR_LogTimer { + explicit constexpr OVR_LogTimer(std::chrono::steady_clock::duration duration) + : nextLogTime_(std::chrono::steady_clock::time_point::min().time_since_epoch().count()), + duration_(duration.count()) {} + + bool OVR_shouldLogNow() { + const auto now = std::chrono::steady_clock::now().time_since_epoch().count(); + auto nextLogTime = nextLogTime_.load(std::memory_order_relaxed); + // It is possible that multiple shouldLogNow() calls observe nextLogTime <= now + // simultaneously, the compare-exchange however will only succeed for one of them. + return nextLogTime <= now && + nextLogTime_.compare_exchange_strong( + nextLogTime, now + duration_, std::memory_order_relaxed); + } + + private: + std::atomic nextLogTime_; + const std::chrono::steady_clock::duration::rep duration_; +}; + +template +bool OVR_shouldLogNow(T1 nanoseconds, T2 dummyLambda) { + static OVR_LogTimer timer{std::chrono::steady_clock::duration(nanoseconds)}; + return timer.OVR_shouldLogNow(); +} +} // namespace + +// Strips the directory and extension from fileTag to give a concise log tag +inline void FilePathToTag(const char* filePath, char* strippedTag, const int strippedTagSize) { + if (strippedTag == nullptr || strippedTagSize == 0) { + return; + } + + // scan backwards from the end to the first slash + const int len = static_cast(strlen(filePath)); + int slash; + for (slash = len - 1; slash > 0 && filePath[slash] != '/' && filePath[slash] != '\\'; slash--) { + } + if (filePath[slash] == '/' || filePath[slash] == '\\') { + slash++; + } + // copy forward until a dot or 0 + int i; + for (i = 0; i < strippedTagSize - 1; i++) { + const char c = filePath[slash + i]; + if (c == '.' || c == 0) { + break; + } + strippedTag[i] = c; + } + strippedTag[i] = 0; +} + +// Strips the directory and extension from fileTag to give a concise log tag +inline void LogWithFileTag(const int prio, const char* fileTag, const char* fmt, ...) { + if (fileTag == nullptr) { + return; + } +#if defined(OVR_OS_ANDROID) + va_list ap, ap2; + + // fileTag will be something like "jni/App.cpp", which we + // want to strip down to just "App" + char strippedTag[128]; + + FilePathToTag(fileTag, strippedTag, static_cast(sizeof(strippedTag))); + + va_start(ap, fmt); + + // Calculate the length of the log message... if its too long __android_log_vprint() will clip + // it! + va_copy(ap2, ap); + const int requiredLen = vsnprintf(NULL, 0, fmt, ap2); + va_end(ap2); + if (requiredLen < 0) { + __android_log_write(prio, strippedTag, "ERROR: failed to calculate log length!"); + return; + } + const size_t loglen = static_cast(requiredLen); + if (prio == ANDROID_LOG_ERROR) { + // For FAIL messages which are longer than 512, truncate at 512. + // We do not know the max size of abort message that will be taken by SIGABRT. 512 has been + // verified to work + char* formattedMsg = (char*)malloc(512); + if (formattedMsg == nullptr) { + __android_log_write(prio, strippedTag, "ERROR: out of memory allocating log message!"); + } else { + va_copy(ap2, ap); + vsnprintf(formattedMsg, 512U, fmt, ap2); + va_end(ap2); + __android_log_assert("FAIL", strippedTag, "%s", formattedMsg); + free(formattedMsg); + } + } + if (loglen < 512) { + // For short messages just use android's default formatting path (which has a fixed size + // buffer on the stack). + __android_log_vprint(prio, strippedTag, fmt, ap); + } else { + // For long messages allocate off the heap to avoid blowing the stack... + char* formattedMsg = (char*)malloc(loglen + 1); + if (formattedMsg == nullptr) { + __android_log_write(prio, strippedTag, "ERROR: out of memory allocating log message!"); + } else { + vsnprintf(formattedMsg, loglen + 1, fmt, ap); + __android_log_write(prio, strippedTag, formattedMsg); + free(formattedMsg); + } + } + + va_end(ap); +#elif defined(OVR_OS_WIN32) + OVR_UNUSED(fileTag); + OVR_UNUSED(prio); + + va_list args; + va_start(args, fmt); + + char buffer[4096]; + vsnprintf_s(buffer, 4096, _TRUNCATE, fmt, args); + va_end(args); + + OutputDebugStringA(buffer); + OutputDebugStringA("\n"); +#elif (defined(OVR_OS_LINUX) || defined(OVR_OS_MAC) || defined(OVR_OS_IPHONE)) + OVR_UNUSED(prio); + OVR_UNUSED(fileTag); + OVR_UNUSED(fmt); +#else +#warning "LogWithFileTag not implemented for this given OVR_OS_" +#endif +} + +#if defined(OVR_OS_WIN32) // allow this file to be included in PC projects + +#define OVR_LOG(...) LogWithFileTag(0, __FILE__, __VA_ARGS__) +#define OVR_WARN(...) LogWithFileTag(0, __FILE__, __VA_ARGS__) + +// This used to be called OVR_ERROR, but it would crash on mobile devices, +// but not on Windows. This was surprising to many devs and has led to multiple serious incidents. +// Please do not use this function. Be more explicit about the intended behavior, and use FAIL or +// WARN instead. +#define OVR_ERROR_CRASH_MOBILE_USE_WARN_OR_FAIL(...) \ + { LogWithFileTag(0, __FILE__, __VA_ARGS__); } + +#define OVR_FAIL(...) \ + { \ + LogWithFileTag(0, __FILE__, __VA_ARGS__); \ + abort(); \ + } +#define OVR_LOG_WITH_TAG(__tag__, ...) LogWithTag(0, __FILE__, __VA_ARGS__) +#define OVR_ASSERT_WITH_TAG(__expr__, __tag__) + +#elif defined(OVR_OS_ANDROID) + +// Our standard logging (and far too much of our debugging) involves writing +// to the system log for viewing with logcat. Previously we defined separate +// LOG() macros in each file to give them file-specific tags for filtering; +// now we use this helper function to use a OVR_LOG_TAG define (via cflags or +// #define OVR_LOG_TAG in source file) when available. Fallback to using a massaged +// __FILE__ macro turning the file base in to a tag -- jni/App.cpp becomes the +// tag "App". +#ifdef OVR_LOG_TAG +#define OVR_LOG(...) ((void)LogWithTag(ANDROID_LOG_INFO, OVR_LOG_TAG, __VA_ARGS__)) +#define OVR_WARN(...) ((void)LogWithTag(ANDROID_LOG_WARN, OVR_LOG_TAG, __VA_ARGS__)) +#define OVR_VERBOSE(...) ((void)LogWithTag(ANDROID_LOG_VERBOSE, OVR_LOG_TAG, __VA_ARGS__)) + +// This used to be called OVR_ERROR, but it would crash on mobile devices, +// but not on Windows. This was surprising to many devs and has led to multiple serious incidents. +// Please do not use this function. Be more explicit about the intended behavior, and use FAIL or +// WARN instead. +#define OVR_ERROR_CRASH_MOBILE_USE_WARN_OR_FAIL(...) \ + { (void)LogWithTag(ANDROID_LOG_ERROR, OVR_LOG_TAG, __VA_ARGS__); } + +#define OVR_FAIL(...) \ + { \ + (void)LogWithTag(ANDROID_LOG_ERROR, OVR_LOG_TAG, __VA_ARGS__); \ + abort(); \ + } +#else +#define OVR_LOG(...) LogWithFileTag(ANDROID_LOG_INFO, __FILE__, __VA_ARGS__) +#define OVR_WARN(...) LogWithFileTag(ANDROID_LOG_WARN, __FILE__, __VA_ARGS__) +#define OVR_VERBOSE(...) LogWithFileTag(ANDROID_LOG_VERBOSE, __FILE__, __VA_ARGS__) + +// This used to be called OVR_ERROR, but it would crash on mobile devices, +// but not on Windows. This was surprising to many devs and has led to multiple serious incidents. +// Please do not use this function. Be more explicit about the intended behavior, and use FAIL or +// WARN instead. +#define OVR_ERROR_CRASH_MOBILE_USE_WARN_OR_FAIL(...) \ + { LogWithFileTag(ANDROID_LOG_ERROR, __FILE__, __VA_ARGS__); } + +#define OVR_FAIL(...) \ + { \ + LogWithFileTag(ANDROID_LOG_ERROR, __FILE__, __VA_ARGS__); \ + abort(); \ + } +#endif + +#define OVR_LOG_WITH_TAG(__tag__, ...) ((void)LogWithTag(ANDROID_LOG_INFO, __tag__, __VA_ARGS__)) +#define OVR_WARN_WITH_TAG(__tag__, ...) ((void)LogWithTag(ANDROID_LOG_WARN, __tag__, __VA_ARGS__)) +#define OVR_FAIL_WITH_TAG(__tag__, ...) \ + { \ + (void)LogWithTag(ANDROID_LOG_ERROR, __tag__, __VA_ARGS__); \ + abort(); \ + } + +// LOG (usually defined on a per-file basis to write to a specific tag) is for logging that can be +// checked in enabled and generally only prints once or infrequently. SPAM is for logging you want +// to see every frame but should never be checked in +#if defined(OVR_BUILD_DEBUG) +// you should always comment this out before checkin +// #define ALLOW_LOG_SPAM +#endif + +#if defined(ALLOW_LOG_SPAM) +#define SPAM(...) LogWithTag(ANDROID_LOG_VERBOSE, "Spam", __VA_ARGS__) +#else +#define SPAM(...) \ + {} +#endif + +// TODO: we need a define for internal builds that will compile in assertion messages but not debug +// breaks and we need a define for external builds that will do nothing when an assert is hit. +#if !defined(OVR_BUILD_DEBUG) +#define OVR_ASSERT_WITH_TAG(__expr__, __tag__) \ + { \ + if (!(__expr__)) { \ + OVR_WARN_WITH_TAG(__tag__, "ASSERTION FAILED: %s", #__expr__); \ + } \ + } +#else +#define OVR_ASSERT_WITH_TAG(__expr__, __tag__) \ + { \ + if (!(__expr__)) { \ + OVR_WARN_WITH_TAG(__tag__, "ASSERTION FAILED: %s", #__expr__); \ + OVR_DEBUG_BREAK; \ + } \ + } +#endif + +#elif (defined(OVR_OS_MAC) || defined(OVR_OS_LINUX) || defined(OVR_OS_IPHONE)) +#include +#include + +// Helper that converts a printf-style format string to an std::string. Needed +// because the folly macros don't accept printf-style strings. +static inline std::string ovrLogConvertPrintfToString(const char* format, ...) { + va_list args; + va_start(args, format); + + // Determine the required size of the formatted string + va_list argsCopy; // we can't reuse va_list, so copy + va_copy(argsCopy, args); + const int size = std::vsnprintf(nullptr, 0, format, argsCopy); + va_end(argsCopy); + + if (size <= 0) { + va_end(args); + return ""; + } + + // Allocate and format the string + std::string result(size, '\0'); + std::vsnprintf(result.data(), result.size() + 1, format, args); + + va_end(args); + + return result; +} + +#define OVR_LOG(...) XLOG(INFO, ovrLogConvertPrintfToString(__VA_ARGS__)) +#define OVR_WARN(...) XLOG(WARN, ovrLogConvertPrintfToString(__VA_ARGS__)) +#define OVR_VERBOSE(...) XLOG(DBG, ovrLogConvertPrintfToString(__VA_ARGS__)) + +// This used to be called OVR_ERROR, but it would crash on mobile devices, +// but not on Windows. This was surprising to many devs and has led to multiple serious incidents. +// Please do not use this function. Be more explicit about the intended behavior, and use FAIL or +// WARN instead. +#define OVR_ERROR_CRASH_MOBILE_USE_WARN_OR_FAIL(...) \ + XLOG(ERR, ovrLogConvertPrintfToString(__VA_ARGS__)) + +#define OVR_FAIL(...) XLOG(FATAL, ovrLogConvertPrintfToString(__VA_ARGS__)) +#define OVR_LOG_WITH_TAG(__tag__, ...) OVR_LOG(__VA_ARGS__) +#define OVR_WARN_WITH_TAG(__tag__, ...) OVR_WARN(__VA_ARGS__) +#define OVR_ASSERT_WITH_TAG(__expr__, __tag__) \ + { \ + if (!(__expr__)) { \ + OVR_FAIL("ASSERTION FAILED: %s", #__expr__); \ + } \ + } + +#else +#error "unknown OVR_OS" +#endif + +// OVR_LOG_BUILD_DEBUG is for logging that should only be compiled in for debug builds +// with OVR_BUILD_DEBUG, cleaner than putting #ifdefs around OVR_LOG calls. +// This is not defined in user or userdebug STU builds, only in local /dev BUCK builds. +#if defined(OVR_BUILD_DEBUG) +#define OVR_LOG_BUILD_DEBUG(...) OVR_LOG(__VA_ARGS__) +#else +#define OVR_LOG_BUILD_DEBUG(...) \ + {} +#endif // OVR_LOG_BUILD_DEBUG + +// logs only the first time to avoid spam +#define OVR_LOG_ONCE(...) \ + { \ + static bool alreadyLogged = false; \ + if (!alreadyLogged) { \ + OVR_LOG(__VA_ARGS__); \ + alreadyLogged = true; \ + } \ + } + +#define OVR_WARN_ONCE(...) \ + { \ + static bool alreadyWarned = false; \ + if (!alreadyWarned) { \ + OVR_WARN(__VA_ARGS__); \ + alreadyWarned = true; \ + } \ + } + +#define OVR_LOG_IF(__condition__, ...) \ + { \ + if (__condition__) { \ + OVR_LOG(__VA_ARGS__); \ + } \ + } + +#define OVR_WARN_IF(__condition__, ...) \ + { \ + if (__condition__) { \ + OVR_WARN(__VA_ARGS__); \ + } \ + } + +#define OVR_LOG_EVERY_N_SEC(n, ...) \ + OVR_LOG_IF( \ + (n) == 0 ? true : OVR_shouldLogNow(static_cast(1.0e9 * (n)), []() {}), \ + __VA_ARGS__) +#define OVR_WARN_EVERY_N_SEC(n, ...) \ + OVR_WARN_IF( \ + (n) == 0 ? true : OVR_shouldLogNow(static_cast(1.0e9 * (n)), []() {}), \ + __VA_ARGS__) + +#endif // OVRLib_Log_h diff --git a/Samples/1stParty/OVR/Include/OVR_Math.h b/Samples/1stParty/OVR/Include/OVR_Math.h new file mode 100755 index 0000000..784c018 --- /dev/null +++ b/Samples/1stParty/OVR/Include/OVR_Math.h @@ -0,0 +1,4802 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/********************************************************************************/ /** + \file OVR_Math.h + \brief Implementation of 3D primitives such as vectors, matrices. + *************************************************************************************/ + +#ifndef OVR_Math_h +#define OVR_Math_h + +#include +#include +#include +#include +#include +#include + +#include // for min, max +#include +#include + +#if defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable : 4127) // conditional expression is constant +#endif + +#if defined(_MSC_VER) +#define OVRMath_sprintf sprintf_s +#else +#define OVRMath_sprintf snprintf +#endif + +//------------------------------------------------------------------------------------- +// ***** OVR_MATH_DEBUG_BREAK +// +// Independent debug break implementation for OVR_Math.h. + +#if !defined(OVR_MATH_DEBUG_BREAK) +#if defined(_DEBUG) +#if defined(_MSC_VER) +#define OVR_MATH_DEBUG_BREAK __debugbreak() +#else +#define OVR_MATH_DEBUG_BREAK __builtin_trap() +#endif +#else +#define OVR_MATH_DEBUG_BREAK ((void)0) +#endif +#endif + +//------------------------------------------------------------------------------------- +// ***** OVR_MATH_ASSERT +// +// Independent OVR_MATH_ASSERT implementation for OVR_Math.h. + +#if !defined(OVR_MATH_ASSERT) +#if defined(OVR_GTEST) +#define OVR_MATH_ASSERT(p) EXPECT_TRUE((p)); +#elif defined(_DEBUG) +#define OVR_MATH_ASSERT(p) \ + if (!(p)) { \ + OVR_MATH_DEBUG_BREAK; \ + } +#else +#define OVR_MATH_ASSERT(p) ((void)0) +#endif +#endif + +//------------------------------------------------------------------------------------- +// ***** OVR_MATH_STATIC_ASSERT +// +// Independent OVR_MATH_ASSERT implementation for OVR_Math.h. + +#if !defined(OVR_MATH_STATIC_ASSERT) +#if defined(__cplusplus) && \ + ((defined(_MSC_VER) && (defined(_MSC_VER) >= 1600)) || defined(__GXX_EXPERIMENTAL_CXX0X__) || \ + (__cplusplus >= 201103L)) +#define OVR_MATH_STATIC_ASSERT static_assert +#else +#if !defined(OVR_SA_UNUSED) +#if defined(__GNUC__) || defined(__clang__) +#define OVR_SA_UNUSED __attribute__((unused)) +#else +#define OVR_SA_UNUSED +#endif +#define OVR_SA_PASTE(a, b) a##b +#define OVR_SA_HELP(a, b) OVR_SA_PASTE(a, b) +#endif + +#define OVR_MATH_STATIC_ASSERT(expression, msg) \ + typedef char OVR_SA_HELP( \ + compileTimeAssert, __LINE__)[((expression) != 0) ? 1 : -1] OVR_SA_UNUSED +#endif +#endif + +//------------------------------------------------------------------------------------- +// ***** OVR_MATH_UNUSED +// +// Independent OVR_MATH_UNUSED implementation for OVR_Math.h. + +#if defined(__GNUC__) || defined(__clang__) +#define OVR_MATH_UNUSED(a) \ + do { \ + __typeof__(&a) __attribute__((unused)) __tmp = &a; \ + } while (0) +#else +#define OVR_MATH_UNUSED(a) (a) +#endif + +namespace OVR { + +template +const T OVRMath_Min(const T a, const T b) { + return (a < b) ? a : b; +} + +template +const T OVRMath_Max(const T a, const T b) { + return (b < a) ? a : b; +} + +template +const T OVRMath_Clamp(const T v, const T minVal, const T maxVal) { + return OVRMath_Max(minVal, OVRMath_Min(v, maxVal)); +} + +template +void OVRMath_Swap(T& a, T& b) { + T temp(a); + a = b; + b = temp; +} + +template +T OVRMath_Lerp(T a, T b, F f) { + return (b - a) * f + a; +} + +template +F OVRMath_InvLerp(T a, T b, T c) { + return (c - a) / (b - a); +} + +//------------------------------------------------------------------------------------- +// ***** 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 }; + +// Constants for right handed and left handed coordinate systems +enum HandedSystem { Handed_R = 1, Handed_L = -1 }; + +// AxisDirection describes which way the coordinate 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_MATH_ASSERT(abs(x) != abs(y) && abs(y) != abs(z) && abs(z) != abs(x)); + } +}; + +} // namespace OVR + +//------------------------------------------------------------------------------------// +// ***** C Compatibility Types + +// These declarations are used to support conversion between C types used in +// LibOVR C interfaces and their C++ versions. As an example, they allow passing +// Vector3f into a function that expects ovrVector3f. + +typedef struct ovrQuatf_ ovrQuatf; +typedef struct ovrQuatd_ ovrQuatd; +typedef struct ovrSizei_ ovrSizei; +typedef struct ovrSizef_ ovrSizef; +typedef struct ovrSized_ ovrSized; +typedef struct ovrRecti_ ovrRecti; +typedef struct ovrRectf_ ovrRectf; +typedef struct ovrVector2i_ ovrVector2i; +typedef struct ovrVector2f_ ovrVector2f; +typedef struct ovrVector2d_ ovrVector2d; +typedef struct ovrVector3f_ ovrVector3f; +typedef struct ovrVector3d_ ovrVector3d; +typedef struct ovrVector4f_ ovrVector4f; +typedef struct ovrVector4d_ ovrVector4d; +typedef struct ovrVector4s_ ovrVector4s; +typedef struct ovrMatrix2f_ ovrMatrix2f; +typedef struct ovrMatrix2d_ ovrMatrix2d; +typedef struct ovrMatrix3f_ ovrMatrix3f; +typedef struct ovrMatrix3d_ ovrMatrix3d; +typedef struct ovrMatrix4f_ ovrMatrix4f; +typedef struct ovrMatrix4d_ ovrMatrix4d; +typedef struct ovrPosef_ ovrPosef; +typedef struct ovrPosed_ ovrPosed; +typedef struct ovrPoseStatef_ ovrPoseStatef; +typedef struct ovrPoseStated_ ovrPoseStated; + +namespace OVR { + +// Forward-declare our templates. +template +class Quat; +template +class Size; +template +class Rect; +template +class Vector2; +template +class Vector3; +template +class Vector4; +template +class Matrix2; +template +class Matrix3; +template +class Matrix4; +template +class Pose; +template +class PoseState; + +// CompatibleTypes::Type is used to lookup a compatible C-version of a C++ class. +template +struct CompatibleTypes { + // Declaration here seems necessary for MSVC; specializations are + // used instead. + typedef struct { + } Type; +}; + +// Specializations providing CompatibleTypes::Type value. +template <> +struct CompatibleTypes> { + typedef ovrQuatf Type; +}; +template <> +struct CompatibleTypes> { + typedef ovrQuatd Type; +}; +template <> +struct CompatibleTypes> { + typedef ovrMatrix2f Type; +}; +template <> +struct CompatibleTypes> { + typedef ovrMatrix2d Type; +}; +template <> +struct CompatibleTypes> { + typedef ovrMatrix3f Type; +}; +template <> +struct CompatibleTypes> { + typedef ovrMatrix3d Type; +}; +template <> +struct CompatibleTypes> { + typedef ovrMatrix4f Type; +}; +template <> +struct CompatibleTypes> { + typedef ovrMatrix4d Type; +}; +template <> +struct CompatibleTypes> { + typedef ovrSizei Type; +}; +template <> +struct CompatibleTypes> { + typedef ovrSizef Type; +}; +template <> +struct CompatibleTypes> { + typedef ovrSized Type; +}; +template <> +struct CompatibleTypes> { + typedef ovrRecti Type; +}; +template <> +struct CompatibleTypes> { + typedef ovrRectf Type; +}; +template <> +struct CompatibleTypes> { + typedef ovrVector2i Type; +}; +template <> +struct CompatibleTypes> { + typedef ovrVector2f Type; +}; +template <> +struct CompatibleTypes> { + typedef ovrVector2d Type; +}; +template <> +struct CompatibleTypes> { + typedef ovrVector3f Type; +}; +template <> +struct CompatibleTypes> { + typedef ovrVector3d Type; +}; +template <> +struct CompatibleTypes> { + typedef ovrVector4f Type; +}; +template <> +struct CompatibleTypes> { + typedef ovrVector4d Type; +}; +template <> +struct CompatibleTypes> { + typedef ovrVector4s Type; +}; +template <> +struct CompatibleTypes> { + typedef ovrPosef Type; +}; +template <> +struct CompatibleTypes> { + typedef ovrPosed Type; +}; + +//------------------------------------------------------------------------------------// +// ***** Math +// +// Math class contains constants and functions. This class is a template specialized +// per type, with Math and Math being distinct. +template +class Math { + public: + // By default, support explicit conversion to float. This allows Vector2 to + // compile, for example. + typedef float OtherFloatType; + + static int Tolerance() { + return 0; + } // Default value so integer types compile +}; + +//------------------------------------------------------------------------------------// +// ***** double constants +#define MATH_DOUBLE_PI 3.14159265358979323846 +#define MATH_DOUBLE_TWOPI (2 * MATH_DOUBLE_PI) +#define MATH_DOUBLE_PIOVER2 (0.5 * MATH_DOUBLE_PI) +#define MATH_DOUBLE_PIOVER4 (0.25 * MATH_DOUBLE_PI) +#define MATH_FLOAT_MAXVALUE (FLT_MAX) + +#define MATH_DOUBLE_RADTODEGREEFACTOR (360.0 / MATH_DOUBLE_TWOPI) +#define MATH_DOUBLE_DEGREETORADFACTOR (MATH_DOUBLE_TWOPI / 360.0) + +#define MATH_DOUBLE_E 2.71828182845904523536 +#define MATH_DOUBLE_LOG2E 1.44269504088896340736 +#define MATH_DOUBLE_LOG10E 0.434294481903251827651 +#define MATH_DOUBLE_LN2 0.693147180559945309417 +#define MATH_DOUBLE_LN10 2.30258509299404568402 + +#define MATH_DOUBLE_SQRT2 1.41421356237309504880 +#define MATH_DOUBLE_SQRT1_2 0.707106781186547524401 + +#define MATH_DOUBLE_TOLERANCE \ + 1e-12 // a default number for value equality tolerance: about 4500*Epsilon; +#define MATH_DOUBLE_SINGULARITYRADIUS \ + 1e-12 // about 1-cos(.0001 degree), for gimbal lock numerical problems + +#define MATH_DOUBLE_SMALLEST_NON_DENORMAL 2.2250738585072014e-308 // ( 1ULL << 52 ) +#define MATH_DOUBLE_HUGE_NUMBER \ + 1.3407807929942596e+154 // ( ( ( 1023ULL * 3 / 2 ) << 52 ) | ( ( 1 << 52 ) - 1 ) ) + +//------------------------------------------------------------------------------------// +// ***** float constants +#define MATH_FLOAT_PI float(MATH_DOUBLE_PI) +#define MATH_FLOAT_TWOPI float(MATH_DOUBLE_TWOPI) +#define MATH_FLOAT_PIOVER2 float(MATH_DOUBLE_PIOVER2) +#define MATH_FLOAT_PIOVER4 float(MATH_DOUBLE_PIOVER4) + +#define MATH_FLOAT_RADTODEGREEFACTOR float(MATH_DOUBLE_RADTODEGREEFACTOR) +#define MATH_FLOAT_DEGREETORADFACTOR float(MATH_DOUBLE_DEGREETORADFACTOR) + +#define MATH_FLOAT_E float(MATH_DOUBLE_E) +#define MATH_FLOAT_LOG2E float(MATH_DOUBLE_LOG2E) +#define MATH_FLOAT_LOG10E float(MATH_DOUBLE_LOG10E) +#define MATH_FLOAT_LN2 float(MATH_DOUBLE_LN2) +#define MATH_FLOAT_LN10 float(MATH_DOUBLE_LN10) + +#define MATH_FLOAT_SQRT2 float(MATH_DOUBLE_SQRT2) +#define MATH_FLOAT_SQRT1_2 float(MATH_DOUBLE_SQRT1_2) + +#define MATH_FLOAT_TOLERANCE \ + 1e-5f // a default number for value equality tolerance: 1e-5, about 84*EPSILON; +#define MATH_FLOAT_SINGULARITYRADIUS \ + 1e-7f // about 1-cos(.025 degree), for gimbal lock numerical problems + +#define MATH_FLOAT_SMALLEST_NON_DENORMAL 1.1754943508222875e-038f // ( 1U << 23 ) +#define MATH_FLOAT_HUGE_NUMBER \ + 1.8446742974197924e+019f // ( ( ( 127U * 3 / 2 ) << 23 ) | ( ( 1 << 23 ) - 1 ) ) + +// Single-precision Math constants class. +template <> +class Math { + public: + typedef double OtherFloatType; + + static inline constexpr float SmallestNonDenormal() { + return MATH_FLOAT_SMALLEST_NON_DENORMAL; + } + static inline constexpr float HugeNumber() { + return MATH_FLOAT_HUGE_NUMBER; + } + static inline constexpr float MaxValue() { + return FLT_MAX; + } + static inline constexpr float Tolerance() { + return MATH_FLOAT_TOLERANCE; + } // a default number for value equality tolerance + static inline constexpr float SingularityRadius() { + return MATH_FLOAT_SINGULARITYRADIUS; + } // for gimbal lock numerical problems +}; + +// Double-precision Math constants class +template <> +class Math { + public: + typedef float OtherFloatType; + + static inline constexpr double SmallestNonDenormal() { + return MATH_DOUBLE_SMALLEST_NON_DENORMAL; + } + static inline constexpr double HugeNumber() { + return MATH_DOUBLE_HUGE_NUMBER; + } + static inline constexpr double Tolerance() { + return MATH_DOUBLE_TOLERANCE; + } // a default number for value equality tolerance + static inline constexpr double SingularityRadius() { + return MATH_DOUBLE_SINGULARITYRADIUS; + } // for gimbal lock numerical problems +}; + +typedef Math Mathf; +typedef Math Mathd; + +// From The Art of Computer Programming: Seminumerical algorithms (Knuth) +inline bool EssentiallyEqual(float a, float b) { + return fabs(a - b) <= + ((fabs(a) > fabs(b) ? fabs(b) : fabs(a)) * std::numeric_limits::epsilon()); +} + +inline bool DefinitelyGreaterThan(float a, float b) +{ + return (a - b) > ( (fabs(a) < fabs(b) ? fabs(b) : fabs(a)) * std::numeric_limits::epsilon()); +} + +inline bool DefinitelyLessThan(float a, float b) +{ + return (b - a) > ( (fabs(a) < fabs(b) ? fabs(b) : fabs(a)) * std::numeric_limits::epsilon()); +} + +// Conversion functions between degrees and radians +// (non-templated to ensure passing int arguments causes warning) +inline float RadToDegree(float rad) { + return rad * MATH_FLOAT_RADTODEGREEFACTOR; +} +inline double RadToDegree(double rad) { + return rad * MATH_DOUBLE_RADTODEGREEFACTOR; +} + +inline float DegreeToRad(float deg) { + return deg * MATH_FLOAT_DEGREETORADFACTOR; +} +inline double DegreeToRad(double deg) { + return deg * MATH_DOUBLE_DEGREETORADFACTOR; +} + +// Square function +template +inline T Sqr(T x) { + return x * x; +} + +// MERGE_MOBILE_SDK +// Safe reciprocal square root. +template +T RcpSqrt(const T f) { + return (f >= Math::SmallestNonDenormal()) ? static_cast(1.0 / sqrt(f)) + : Math::HugeNumber(); +} +// MERGE_MOBILE_SDK + +// Sign: returns 0 if x == 0, -1 if x < 0, and 1 if x > 0 +template +inline T Sign(T x) { + return (x != T(0)) ? (x < T(0) ? T(-1) : T(1)) : T(0); +} + +// Numerically stable acos function +inline float Acos(float x) { + return (x > 1.0f) ? 0.0f : (x < -1.0f) ? MATH_FLOAT_PI : acosf(x); +} +inline double Acos(double x) { + return (x > 1.0) ? 0.0 : (x < -1.0) ? MATH_DOUBLE_PI : acos(x); +} +template +T Acos(T x) { + return static_cast(Acos(static_cast(x))); +} + +// Numerically stable asin function +inline float Asin(float x) { + return (x > 1.0f) ? MATH_FLOAT_PIOVER2 : (x < -1.0f) ? -MATH_FLOAT_PIOVER2 : asinf(x); +} +inline double Asin(double x) { + return (x > 1.0) ? MATH_DOUBLE_PIOVER2 : (x < -1.0) ? -MATH_DOUBLE_PIOVER2 : asin(x); +} +template +T Asin(T x) { + return static_cast(Asin(static_cast(x))); +} + +inline float Atan2(float o, float a) { + return atan2f(o, a); +} +inline double Atan2(double o, double a) { + return atan2(o, a); +} +template +T Atan2(T o, T a) { + return static_cast(Atan2(static_cast(o), static_cast(a))); +} + +#if defined(_MSC_VER) +inline int isnan(double x) { + return ::_isnan(x); +} +#endif + +template +class Quat; + +//------------------------------------------------------------------------------------- +// ***** Vector2<> + +// Vector2f (Vector2d) represents a 2-dimensional vector or point in space, +// consisting of coordinates x and y + +template +class Vector2 { + public: + typedef T ElementType; + static const size_t ElementCount = 2; + + 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) {} + explicit Vector2(const Vector2::OtherFloatType>& src) + : x((T)src.x), y((T)src.y) {} + + static const Vector2 ZERO; + + static Vector2 Zero() { + return Vector2(0, 0); + } + + // C-interop support. + typedef typename CompatibleTypes>::Type CompatibleType; + + Vector2(const CompatibleType& s) : x(s.x), y(s.y) {} + Vector2(const CompatibleType&& s) : x(s.x), y(s.y) {} + + operator const CompatibleType&() const { + OVR_MATH_STATIC_ASSERT( + sizeof(Vector2) == sizeof(CompatibleType), "sizeof(Vector2) failure"); + return reinterpret_cast(*this); + } + + 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; + } + + static Vector2 Min(const Vector2& a, const Vector2& b) { + return Vector2((a.x < b.x) ? a.x : b.x, (a.y < b.y) ? a.y : b.y); + } + static Vector2 Max(const Vector2& a, const Vector2& b) { + return Vector2((a.x > b.x) ? a.x : b.x, (a.y > b.y) ? a.y : b.y); + } + + Vector2 Clamped(T maxMag) const { + T magSquared = LengthSq(); + if (magSquared <= Sqr(maxMag)) + return *this; + else + return *this * (maxMag / sqrt(magSquared)); + } + + // Compare two vectors for equality with tolerance. Returns true if vectors match within + // tolerance. + bool IsEqual(const Vector2& b, T tolerance = Math::Tolerance()) const { + return (fabs(b.x - x) <= tolerance) && (fabs(b.y - y) <= tolerance); + } + bool Compare(const Vector2& b, T tolerance = Math::Tolerance()) const { + return IsEqual(b, tolerance); + } + + // Access element by index + T& operator[](int idx) { + OVR_MATH_ASSERT(0 <= idx && idx < 2); + return *(&x + idx); + } + const T& operator[](int idx) const { + OVR_MATH_ASSERT(0 <= idx && idx < 2); + return *(&x + idx); + } + + // Entry-wise product of two vectors + Vector2 EntrywiseMultiply(const Vector2& b) const { + return Vector2(x * b.x, y * b.y); + } + + // Multiply and divide operators do entry-wise math. Used Dot() for dot product. + Vector2 operator*(const Vector2& b) const { + return Vector2(x * b.x, y * b.y); + } + Vector2 operator/(const Vector2& b) const { + return Vector2(x / b.x, y / b.y); + } + + // Dot product + // Used to calculate angle q between two vectors among other things, + // as (A dot B) = |a||b|cos(q). + T Dot(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 { + T div = LengthSq() * b.LengthSq(); + OVR_MATH_ASSERT(div != T(0)); + T result = static_cast(Acos((this->Dot(b)) / sqrt(div))); + return result; + } + + // Return Length of the vector squared. + T LengthSq() const { + return (x * x + y * y); + } + + // Return vector length. + T Length() const { + return static_cast(sqrt(LengthSq())); + } + + // Returns squared distance between two points represented by vectors. + T DistanceSq(const Vector2& b) const { + return (*this - b).LengthSq(); + } + + // Returns distance between two points represented by vectors. + T Distance(const 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() { + T s = Length(); + if (s != T(0)) + s = T(1) / s; + *this *= s; + } + + // Returns normalized (unit) version of the vector without modifying itself. + Vector2 Normalized() const { + T s = Length(); + if (s != T(0)) + s = T(1) / s; + return *this * s; + } + + // 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 { + T l2 = b.LengthSq(); + OVR_MATH_ASSERT(l2 != T(0)); + return b * (Dot(b) / l2); + } + + // returns true if vector b is clockwise from this vector + bool IsClockwise(const Vector2& b) const { + return (x * b.y - y * b.x) < 0; + } + + template + Vector2 CastTo() const { + return Vector2(static_cast(x), static_cast(y)); + } +}; + +// Implicit instantiation +template +const Vector2 Vector2::ZERO; + +typedef Vector2 Vector2f; +typedef Vector2 Vector2d; +typedef Vector2 Vector2i; + +typedef Vector2 Point2f; +typedef Vector2 Point2d; +typedef Vector2 Point2i; + +//------------------------------------------------------------------------------------- +// ***** Vector3<> - 3D vector of {x, y, z} + +// +// Vector3f (Vector3d) represents a 3-dimensional vector or point in space, +// consisting of coordinates x, y and z. + +template +class Vector3 { + public: + typedef T ElementType; + static const size_t ElementCount = 3; + + T x, y, z; + + // FIXME: default initialization of a vector class can be very expensive in a full-blown + // application. A few hundred thousand vector constructions is not unlikely and can add + // up to milliseconds of time on processors like the PS3 PPU. + constexpr Vector3() : x(0), y(0), z(0) {} + constexpr Vector3(T x_, T y_, T z_ = 0) : x(x_), y(y_), z(z_) {} + constexpr explicit Vector3(T s) : x(s), y(s), z(s) {} + constexpr explicit Vector3(const Vector3::OtherFloatType>& src) + : x((T)src.x), y((T)src.y), z((T)src.z) {} + // MERGE_MOBILE_SDK + constexpr Vector3(const Vector2& xy, const T z_) : x(xy.x), y(xy.y), z(z_) {} + // MERGE_MOBILE_SDK + + static const Vector3 ZERO; + + static Vector3 Zero() { + return Vector3(0, 0, 0); + } + + // C-interop support. + typedef typename CompatibleTypes>::Type CompatibleType; + + Vector3(const CompatibleType& s) : x(s.x), y(s.y), z(s.z) {} + Vector3(const CompatibleType&& s) : x(s.x), y(s.y), z(s.z) {} + + operator const CompatibleType&() const { + OVR_MATH_STATIC_ASSERT( + sizeof(Vector3) == sizeof(CompatibleType), "sizeof(Vector3) failure"); + return reinterpret_cast(*this); + } + + 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; + } + + static Vector3 Min(const Vector3& a, const Vector3& b) { + return Vector3((a.x < b.x) ? a.x : b.x, (a.y < b.y) ? a.y : b.y, (a.z < b.z) ? a.z : b.z); + } + static Vector3 Max(const Vector3& a, const Vector3& b) { + return Vector3((a.x > b.x) ? a.x : b.x, (a.y > b.y) ? a.y : b.y, (a.z > b.z) ? a.z : b.z); + } + + Vector3 Clamped(T maxMag) const { + T magSquared = LengthSq(); + if (magSquared <= Sqr(maxMag)) + return *this; + else + return *this * (maxMag / sqrt(magSquared)); + } + + // Compare two vectors for equality with tolerance. Returns true if vectors match within + // tolerance. + bool IsEqual(const Vector3& b, T tolerance = Math::Tolerance()) const { + return (fabs(b.x - x) <= tolerance) && (fabs(b.y - y) <= tolerance) && + (fabs(b.z - z) <= tolerance); + } + bool Compare(const Vector3& b, T tolerance = Math::Tolerance()) const { + return IsEqual(b, tolerance); + } + + T& operator[](int idx) { + OVR_MATH_ASSERT(0 <= idx && idx < 3); + return *(&x + idx); + } + + const T& operator[](int idx) const { + OVR_MATH_ASSERT(0 <= idx && idx < 3); + return *(&x + idx); + } + + // Entrywise product of two vectors + Vector3 EntrywiseMultiply(const Vector3& b) const { + return Vector3(x * b.x, y * b.y, z * b.z); + } + + // Multiply and divide operators do entry-wise math + Vector3 operator*(const Vector3& b) const { + return Vector3(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); + } + + // Dot product + // Used to calculate angle q between two vectors among other things, + // as (A dot B) = |a||b|cos(q). + T Dot(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 { + T div = LengthSq() * b.LengthSq(); + OVR_MATH_ASSERT(div != T(0)); + T result = static_cast(Acos((this->Dot(b)) / sqrt(div))); + return result; + } + + // Return Length of the vector squared. + T LengthSq() const { + return (x * x + y * y + z * z); + } + + // Return vector length. + T Length() const { + return (T)sqrt(LengthSq()); + } + + // Returns squared distance between two points represented by vectors. + T DistanceSq(Vector3 const& b) const { + return (*this - b).LengthSq(); + } + + // Returns distance between two points represented by vectors. + T Distance(Vector3 const& b) const { + return (*this - b).Length(); + } + + bool IsNormalized() const { + return fabs(LengthSq() - T(1)) < Math::Tolerance(); + } + + // Normalize, convention vector length to 1. + void Normalize() { + T s = Length(); + if (s != T(0)) + s = T(1) / s; + *this *= s; + } + + // Returns normalized (unit) version of the vector without modifying itself. + Vector3 Normalized() const { + T s = Length(); + if (s != T(0)) + s = T(1) / s; + return *this * s; + } + + // 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 { + T l2 = b.LengthSq(); + OVR_MATH_ASSERT(l2 != T(0)); + return b * (Dot(b) / l2); + } + + // Projects this vector onto a plane defined by a normal vector + Vector3 ProjectToPlane(const Vector3& normal) const { + return *this - this->ProjectTo(normal); + } + + bool IsNan() const { + return isnan(x) || isnan(y) || isnan(z); + } + + // calculates distance from this point to the line segment (a0, a1) + T distanceToSegment(Vector3 a0, Vector3 a1) const noexcept { + const Vector3 lineVec = a1 - a0; + const Vector3 fromP0 = *this - a0; + const T normalizedProjection = fromP0.Dot(lineVec) / lineVec.Dot(lineVec); + const T closestT = std::clamp(normalizedProjection, 0.f, 1.f); + const Vector3 closestPoint = a0 + closestT * lineVec; + return (closestPoint - *this).Length(); + } + + template + Vector3 CastTo() const { + return Vector3(static_cast(x), static_cast(y), static_cast(z)); + } +}; + +// Implicit instantiation +template +const Vector3 Vector3::ZERO; + +// MERGE_MOBILE_SDK +// allow multiplication in order scalar * vector (member operator handles vector * scalar) +template +Vector3 operator*(T s, const Vector3& v) { + return Vector3(v.x * s, v.y * s, v.z * s); +} +// MERGE_MOBILE_SDK + +typedef Vector3 Vector3f; +typedef Vector3 Vector3d; +typedef Vector3 Vector3i; + +OVR_MATH_STATIC_ASSERT((sizeof(Vector3f) == 3 * sizeof(float)), "sizeof(Vector3f) failure"); +OVR_MATH_STATIC_ASSERT((sizeof(Vector3d) == 3 * sizeof(double)), "sizeof(Vector3d) failure"); +OVR_MATH_STATIC_ASSERT((sizeof(Vector3i) == 3 * sizeof(int32_t)), "sizeof(Vector3i) failure"); + +typedef Vector3 Point3f; +typedef Vector3 Point3d; +typedef Vector3 Point3i; + +//------------------------------------------------------------------------------------- +// ***** Vector4<> - 4D vector of {x, y, z, w} + +// +// Vector4f (Vector4d) represents a 3-dimensional vector or point in space, +// consisting of coordinates x, y, z and w. + +template +class Vector4 { + public: + typedef T ElementType; + static const size_t ElementCount = 4; + + T x, y, z, w; + + // FIXME: default initialization of a vector class can be very expensive in a full-blown + // application. A few hundred thousand vector constructions is not unlikely and can add + // up to milliseconds of time on processors like the PS3 PPU. + constexpr Vector4() : x(0), y(0), z(0), w(0) {} + constexpr Vector4(T x_, T y_, T z_, T w_) : x(x_), y(y_), z(z_), w(w_) {} + constexpr explicit Vector4(T s) : x(s), y(s), z(s), w(s) {} + constexpr explicit Vector4(const Vector3& v, const T w_ = T(1)) + : x(v.x), y(v.y), z(v.z), w(w_) {} + constexpr explicit Vector4(const Vector4::OtherFloatType>& src) + : x((T)src.x), y((T)src.y), z((T)src.z), w((T)src.w) {} + + static const Vector4 ZERO; + + // C-interop support. + typedef typename CompatibleTypes>::Type CompatibleType; + + Vector4(const CompatibleType& s) : x(s.x), y(s.y), z(s.z), w(s.w) {} + Vector4(const CompatibleType&& s) : x(s.x), y(s.y), z(s.z), w(s.w) {} + + operator const CompatibleType&() const { + OVR_MATH_STATIC_ASSERT( + sizeof(Vector4) == sizeof(CompatibleType), "sizeof(Vector4) failure"); + return reinterpret_cast(*this); + } + + Vector4& operator=(const Vector3& other) { + x = other.x; + y = other.y; + z = other.z; + w = 1; + return *this; + } + bool operator==(const Vector4& b) const { + return x == b.x && y == b.y && z == b.z && w == b.w; + } + bool operator!=(const Vector4& b) const { + return x != b.x || y != b.y || z != b.z || w != b.w; + } + + Vector4 operator+(const Vector4& b) const { + return Vector4(x + b.x, y + b.y, z + b.z, w + b.w); + } + Vector4& operator+=(const Vector4& b) { + x += b.x; + y += b.y; + z += b.z; + w += b.w; + return *this; + } + Vector4 operator-(const Vector4& b) const { + return Vector4(x - b.x, y - b.y, z - b.z, w - b.w); + } + Vector4& operator-=(const Vector4& b) { + x -= b.x; + y -= b.y; + z -= b.z; + w -= b.w; + return *this; + } + Vector4 operator-() const { + return Vector4(-x, -y, -z, -w); + } + + // Scalar multiplication/division scales vector. + Vector4 operator*(T s) const { + return Vector4(x * s, y * s, z * s, w * s); + } + Vector4& operator*=(T s) { + x *= s; + y *= s; + z *= s; + w *= s; + return *this; + } + + Vector4 operator/(T s) const { + T rcp = T(1) / s; + return Vector4(x * rcp, y * rcp, z * rcp, w * rcp); + } + Vector4& operator/=(T s) { + T rcp = T(1) / s; + x *= rcp; + y *= rcp; + z *= rcp; + w *= rcp; + return *this; + } + + static Vector4 Min(const Vector4& a, const Vector4& b) { + return Vector4( + (a.x < b.x) ? a.x : b.x, + (a.y < b.y) ? a.y : b.y, + (a.z < b.z) ? a.z : b.z, + (a.w < b.w) ? a.w : b.w); + } + static Vector4 Max(const Vector4& a, const Vector4& b) { + return Vector4( + (a.x > b.x) ? a.x : b.x, + (a.y > b.y) ? a.y : b.y, + (a.z > b.z) ? a.z : b.z, + (a.w > b.w) ? a.w : b.w); + } + + Vector4 Clamped(T maxMag) const { + T magSquared = LengthSq(); + if (magSquared <= Sqr(maxMag)) + return *this; + else + return *this * (maxMag / sqrt(magSquared)); + } + + // Compare two vectors for equality with tolerance. Returns true if vectors match within + // tolerance. + bool IsEqual(const Vector4& b, T tolerance = Math::Tolerance()) const { + return (fabs(b.x - x) <= tolerance) && (fabs(b.y - y) <= tolerance) && + (fabs(b.z - z) <= tolerance) && (fabs(b.w - w) <= tolerance); + } + bool Compare(const Vector4& b, T tolerance = Math::Tolerance()) const { + return IsEqual(b, tolerance); + } + + T& operator[](int idx) { + OVR_MATH_ASSERT(0 <= idx && idx < 4); + return *(&x + idx); + } + + const T& operator[](int idx) const { + OVR_MATH_ASSERT(0 <= idx && idx < 4); + return *(&x + idx); + } + + // Entry wise product of two vectors + Vector4 EntrywiseMultiply(const Vector4& b) const { + return Vector4(x * b.x, y * b.y, z * b.z, w * b.w); + } + + // Multiply and divide operators do entry-wise math + Vector4 operator*(const Vector4& b) const { + return Vector4(x * b.x, y * b.y, z * b.z, w * b.w); + } + + Vector4 operator/(const Vector4& b) const { + return Vector4(x / b.x, y / b.y, z / b.z, w / b.w); + } + + // Dot product + T Dot(const Vector4& b) const { + return x * b.x + y * b.y + z * b.z + w * b.w; + } + + // Return Length of the vector squared. + T LengthSq() const { + return (x * x + y * y + z * z + w * w); + } + + // Return vector length. + T Length() const { + return sqrt(LengthSq()); + } + + bool IsNormalized() const { + return fabs(LengthSq() - T(1)) < Math::Tolerance(); + } + + // Normalize, convention vector length to 1. + void Normalize() { + T s = Length(); + if (s != T(0)) + s = T(1) / s; + *this *= s; + } + + // Returns normalized (unit) version of the vector without modifying itself. + Vector4 Normalized() const { + T s = Length(); + if (s != T(0)) + s = T(1) / s; + return *this * s; + } + + // Linearly interpolates from this vector to another. + // Factor should be between 0.0 and 1.0, with 0 giving full value to this. + Vector4 Lerp(const Vector4& b, T f) const { + return *this * (T(1) - f) + b * f; + } + + template + Vector4 CastTo() const { + return Vector4( + static_cast(x), static_cast(y), static_cast(z), static_cast(w)); + } +}; + +// Implicit instantiation +template +const Vector4 Vector4::ZERO; + +typedef Vector4 Vector4f; +typedef Vector4 Vector4d; +typedef Vector4 Vector4i; +typedef Vector4 Vector4s; + +//------------------------------------------------------------------------------------- +// ***** Bounds3 + +// Bounds class used to describe a 3D axis aligned bounding box. + +template +class Bounds3 { + public: + // MERGE_MOBILE_SDK + enum InitType { Init }; + // MERGE_MOBILE_SDK + + Vector3 b[2]; + + Bounds3() {} + + // MERGE_MOBILE_SDK + Bounds3(const InitType init) { + OVR_MATH_UNUSED(init); + Clear(); + } + // MERGE_MOBILE_SDK + + Bounds3(const Vector3& mins, const Vector3& maxs) { + b[0] = mins; + b[1] = maxs; + } + + // MERGE_MOBILE_SDK + Bounds3(const T minx, const T miny, const T minz, const T maxx, const T maxy, const T maxz) { + b[0].x = minx; + b[0].y = miny; + b[0].z = minz; + + b[1].x = maxx; + b[1].y = maxy; + b[1].z = maxz; + } + + Bounds3 operator*(float const s) const { + return Bounds3(b[0].x * s, b[0].y * s, b[0].z * s, b[1].x * s, b[1].y * s, b[1].z * s); + } + + Bounds3 operator*(Vector3 const& s) const { + return Bounds3( + b[0].x * s.x, b[0].y * s.y, b[0].z * s.z, b[1].x * s.x, b[1].y * s.y, b[1].z * s.z); + } + // MERGE_MOBILE_SDK + + void Clear() { + b[0].x = b[0].y = b[0].z = Math::MaxValue(); + b[1].x = b[1].y = b[1].z = -Math::MaxValue(); + } + + void AddPoint(const Vector3& v) { + b[0].x = OVRMath_Min(b[0].x, v.x); + b[0].y = OVRMath_Min(b[0].y, v.y); + b[0].z = OVRMath_Min(b[0].z, v.z); + b[1].x = OVRMath_Max(b[1].x, v.x); + b[1].y = OVRMath_Max(b[1].y, v.y); + b[1].z = OVRMath_Max(b[1].z, v.z); + } + + // MERGE_MOBILE_SDK + // return a bounds representing the union of a and b + static Bounds3 Union(const Bounds3& a, const Bounds3& b) { + return Bounds3( + OVRMath_Min(a.b[0].x, b.b[0].x), + OVRMath_Min(a.b[0].y, b.b[0].y), + OVRMath_Min(a.b[0].z, b.b[0].z), + OVRMath_Max(a.b[1].x, b.b[1].x), + OVRMath_Max(a.b[1].y, b.b[1].y), + OVRMath_Max(a.b[1].z, b.b[1].z)); + } + // MERGE_MOBILE_SDK + + const Vector3& GetMins() const { + return b[0]; + } + const Vector3& GetMaxs() const { + return b[1]; + } + + Vector3& GetMins() { + return b[0]; + } + Vector3& GetMaxs() { + return b[1]; + } + + // MERGE_MOBILE_SDK + Vector3 GetSize() const { + return Vector3f(b[1].x - b[0].x, b[1].y - b[0].y, b[1].z - b[0].z); + } + + Vector3 GetCenter() const { + return Vector3f( + (b[0].x + b[1].x) * 0.5f, (b[0].y + b[1].y) * 0.5f, (b[0].z + b[1].z) * 0.5f); + } + + void Translate(const Vector3& t) { + b[0] += t; + b[1] += t; + } + + bool IsInverted() const { + return b[0].x > b[1].x && b[0].y > b[1].y && b[0].z > b[1].z; + } + + bool Contains(const Vector3& point, T expand = 0) const { + return point.x >= b[0].x - expand && point.y >= b[0].y - expand && + point.z >= b[0].z - expand && point.x <= b[1].x + expand && + point.y <= b[1].y + expand && point.z <= b[1].z + expand; + } + + // returns the axial aligned bounds of inputBounds after transformation by pose + static Bounds3 Transform(const Pose& pose, const Bounds3& inBounds) { + const Matrix3 rotation(pose.Rotation); + const Vector3 center = (inBounds.b[0] + inBounds.b[1]) * 0.5f; + const Vector3 extents = inBounds.b[1] - center; + const Vector3 newCenter = pose.Translation + rotation * center; + const Vector3 newExtents( + static_cast( + fabs(extents[0] * rotation.M[0][0]) + fabs(extents[1] * rotation.M[0][1]) + + fabs(extents[2] * rotation.M[0][2])), + static_cast( + fabs(extents[0] * rotation.M[1][0]) + fabs(extents[1] * rotation.M[1][1]) + + fabs(extents[2] * rotation.M[1][2])), + static_cast( + fabs(extents[0] * rotation.M[2][0]) + fabs(extents[1] * rotation.M[2][1]) + + fabs(extents[2] * rotation.M[2][2]))); + return Bounds3(newCenter - newExtents, newCenter + newExtents); + } + + static Bounds3 Transform(const Matrix4& matrix, const Bounds3& inBounds) { + const Vector3 center = (inBounds.b[0] + inBounds.b[1]) * 0.5f; + const Vector3 extents = inBounds.b[1] - center; + const Vector3 newCenter = matrix.Transform(center); + const Vector3 newExtents( + static_cast( + fabs(extents[0] * matrix.M[0][0]) + fabs(extents[1] * matrix.M[0][1]) + + fabs(extents[2] * matrix.M[0][2])), + static_cast( + fabs(extents[0] * matrix.M[1][0]) + fabs(extents[1] * matrix.M[1][1]) + + fabs(extents[2] * matrix.M[1][2])), + static_cast( + fabs(extents[0] * matrix.M[2][0]) + fabs(extents[1] * matrix.M[2][1]) + + fabs(extents[2] * matrix.M[2][2]))); + return Bounds3(newCenter - newExtents, newCenter + newExtents); + } + + static Bounds3 + Expand(const Bounds3& b, const Vector3& minExpand, const Vector3& maxExpand) { + return Bounds3(b.GetMins() + minExpand, b.GetMaxs() + maxExpand); + } + // MERGE_MOBILE_SDK +}; + +typedef Bounds3 Bounds3f; +typedef Bounds3 Bounds3d; + +//------------------------------------------------------------------------------------- +// ***** Size + +// Size class represents 2D size with Width, Height components. +// Used to describe distentions of render targets, etc. + +template +class Size { + public: + T w, h; + + Size() : w(0), h(0) {} + Size(T w_, T h_) : w(w_), h(h_) {} + explicit Size(T s) : w(s), h(s) {} + explicit Size(const Size::OtherFloatType>& src) : w((T)src.w), h((T)src.h) {} + + // C-interop support. + typedef typename CompatibleTypes>::Type CompatibleType; + + Size(const CompatibleType& s) : w(s.w), h(s.h) {} + + operator const CompatibleType&() const { + OVR_MATH_STATIC_ASSERT( + sizeof(Size) == sizeof(CompatibleType), "sizeof(Size) failure"); + return reinterpret_cast(*this); + } + + bool operator==(const Size& b) const { + return w == b.w && h == b.h; + } + bool operator!=(const Size& b) const { + return w != b.w || h != b.h; + } + + Size operator+(const Size& b) const { + return Size(w + b.w, h + b.h); + } + Size& operator+=(const Size& b) { + w += b.w; + h += b.h; + return *this; + } + Size operator-(const Size& b) const { + return Size(w - b.w, h - b.h); + } + Size& operator-=(const Size& b) { + w -= b.w; + h -= b.h; + return *this; + } + Size operator-() const { + return Size(-w, -h); + } + Size operator*(const Size& b) const { + return Size(w * b.w, h * b.h); + } + Size& operator*=(const Size& b) { + w *= b.w; + h *= b.h; + return *this; + } + Size operator/(const Size& b) const { + return Size(w / b.w, h / b.h); + } + Size& operator/=(const Size& b) { + w /= b.w; + h /= b.h; + return *this; + } + + // Scalar multiplication/division scales both components. + Size operator*(T s) const { + return Size(w * s, h * s); + } + Size& operator*=(T s) { + w *= s; + h *= s; + return *this; + } + Size operator/(T s) const { + return Size(w / s, h / s); + } + Size& operator/=(T s) { + w /= s; + h /= s; + return *this; + } + + static Size Min(const Size& a, const Size& b) { + return Size((a.w < b.w) ? a.w : b.w, (a.h < b.h) ? a.h : b.h); + } + static Size Max(const Size& a, const Size& b) { + return Size((a.w > b.w) ? a.w : b.w, (a.h > b.h) ? a.h : b.h); + } + + T Area() const { + return w * h; + } + + inline Vector2 ToVector() const { + return Vector2(w, h); + } +}; + +typedef Size Sizei; +typedef Size Sizeu; +typedef Size Sizef; +typedef Size Sized; + +//----------------------------------------------------------------------------------- +// ***** Rect + +// Rect describes a rectangular area for rendering, that includes position and size. +template +class Rect { + public: + T x, y; + T w, h; + + Rect() {} + Rect(T x1, T y1, T w1, T h1) : x(x1), y(y1), w(w1), h(h1) {} + Rect(const Vector2& pos, const Size& sz) : x(pos.x), y(pos.y), w(sz.w), h(sz.h) {} + Rect(const Size& sz) : x(0), y(0), w(sz.w), h(sz.h) {} + + // C-interop support. + typedef typename CompatibleTypes>::Type CompatibleType; + + Rect(const CompatibleType& s) : x(s.x), y(s.y), w(s.width), h(s.height) {} + + operator const CompatibleType&() const { + OVR_MATH_STATIC_ASSERT( + sizeof(Rect) == sizeof(CompatibleType), "sizeof(Rect) failure"); + return reinterpret_cast(*this); + } + + Vector2 GetPos() const { + return Vector2(x, y); + } + Size GetSize() const { + return Size(w, h); + } + void SetPos(const Vector2& pos) { + x = pos.x; + y = pos.y; + } + void SetSize(const Size& sz) { + w = sz.w; + h = sz.h; + } + + bool operator==(const Rect& vp) const { + return (x == vp.x) && (y == vp.y) && (w == vp.w) && (h == vp.h); + } + bool operator!=(const Rect& vp) const { + return !operator==(vp); + } +}; + +typedef Rect Recti; +typedef Rect Rectf; + +template +T CosineOfHalfAngleFromCosineOfAngle(const T cosineOfAngle) { + return (T)(sqrt(((T)1.0 + cosineOfAngle) * (T)0.5)); +} + +template +T SineOfHalfAngleFromCosineOfAngle(const T cosineOfAngle) { + return (T)(sqrtf(((T)1.0 - cosineOfAngle) * (T)0.5)); +} + +//-------------------------------------------------------------------------------------// +// ***** 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: + typedef T ElementType; + static const size_t ElementCount = 4; + + // x,y,z = axis*sin(angle/2), w = cos(angle/2) + 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_) {} + explicit Quat(const Quat::OtherFloatType>& src) + : x((T)src.x), y((T)src.y), z((T)src.z), w((T)src.w) { + // NOTE: Converting a normalized Quat to Quat + // will generally result in an un-normalized quaternion. + // But we don't normalize here in case the quaternion + // being converted is not a normalized rotation quaternion. + } + + typedef typename CompatibleTypes>::Type CompatibleType; + + // C-interop support. + Quat(const CompatibleType& s) : x(s.x), y(s.y), z(s.z), w(s.w) {} + Quat(const CompatibleType&& s) : x(s.x), y(s.y), z(s.z), w(s.w) {} + + operator CompatibleType() const { + CompatibleType result; + result.x = x; + result.y = y; + result.z = z; + result.w = w; + return result; + } + + // Constructs quaternion for rotation around the axis by an angle. + Quat(const Vector3& axis, T angle) { + // Make sure we don't divide by zero. + if (axis.LengthSq() == T(0)) { + // Assert if the axis is zero, but the angle isn't + OVR_MATH_ASSERT(angle == T(0)); + x = y = z = T(0); + w = T(1); + return; + } + + Vector3 unitAxis = axis.Normalized(); + T sinHalfAngle = static_cast(sin(angle * T(0.5))); + + w = static_cast(cos(angle * T(0.5))); + x = unitAxis.x * sinHalfAngle; + y = unitAxis.y * sinHalfAngle; + z = unitAxis.z * sinHalfAngle; + } + + // Constructs quaternion for rotation around one of the coordinate axis by an angle. + Quat(Axis A, T angle, RotateDirection dEnum = Rotate_CCW, HandedSystem sEnum = Handed_R) { + const T d = static_cast(dEnum); + const T s = static_cast(sEnum); + T sinHalfAngle = s * d * static_cast(sin(angle * T(0.5))); + T v[3]; + v[0] = v[1] = v[2] = T(0); + v[A] = sinHalfAngle; + + w = static_cast(cos(angle * T(0.5))); + x = v[0]; + y = v[1]; + z = v[2]; + } + + // constructs a quaternion from an axis and the cosine of half the rotation angle around that + // axis. Note that the dot product of two vectors is equal to the cosine of the angle between + // the vectors, and the magnitude of the cross product of two vectors is the cosine of the half + // angle between the vectors. + static Quat FromAxisAndCosineOfAngle( + const Vector3& axis, + const T cosineOfAngle, + const RotateDirection dir) { + OVR_MATH_ASSERT(axis.LengthSq() > 1e-8f); + OVR_MATH_ASSERT(axis.IsNormalized()); + + const T sineHalfAngle = SineOfHalfAngleFromCosineOfAngle(cosineOfAngle) * (T)(-dir); + return Quat( + axis.x * sineHalfAngle, + axis.y * sineHalfAngle, + axis.z * sineHalfAngle, + CosineOfHalfAngleFromCosineOfAngle(cosineOfAngle)); + } + + static Quat LookRotation(const Vector3& fwd, const Vector3& up) { + return Quat(Matrix4::LookAtRH(Vector3((T)0, (T)0, (T)0), fwd, up)).Inverted(); + } + // from basis vectors + static Quat + FromBasisVectors(const Vector3& fwd, const Vector3& right, const Vector3& /*up*/) { + const T dot = fwd.Dot(Vector3((T)0, (T)0, (T)-1)); + const RotateDirection dir = + right.Dot(Vector3((T)0, (T)0, (T)-1)) < (T)0 ? Rotate_CCW : Rotate_CW; + return FromAxisAndCosineOfAngle(Vector3((T)0, (T)1, (T)0), dot, dir); + } + + static Quat FromEulerAngles(T rx, T ry, T rz) { + // Calculate trigonometry of half Euler angles + T cy = cos(rz * T(0.5)); + T sy = sin(rz * T(0.5)); + T cr = cos(ry * T(0.5)); + T sr = sin(ry * T(0.5)); + T cp = cos(rx * T(0.5)); + T sp = sin(rx * T(0.5)); + + // Calculate quaternion components + T w = cy * cr * cp + sy * sr * sp; + T x = cy * cr * sp - sy * sr * cp; + T y = sy * cr * sp + cy * sr * cp; + T z = sy * cr * cp - cy * sr * sp; + return Quat(x, y, z, w); + } + + Quat operator-() { + return Quat(-x, -y, -z, -w); + } // unary minus + + static Quat Identity() { + return Quat(0, 0, 0, 1); + } + + // Compute axis and angle from quaternion + void GetAxisAngle(Vector3* axis, T* angle) const { + if (x * x + y * y + z * z > Math::Tolerance() * Math::Tolerance()) { + *axis = Vector3(x, y, z).Normalized(); + *angle = 2 * Acos(w); + if (*angle > ((T)MATH_DOUBLE_PI)) // Reduce the magnitude of the angle, if necessary + { + *angle = ((T)MATH_DOUBLE_TWOPI) - *angle; + *axis = *axis * (-1); + } + } else { + *axis = Vector3(static_cast(1), static_cast(0), static_cast(0)); + *angle = T(0); + } + } + + // Convert a quaternion to a rotation vector, also known as + // Rodrigues vector, AxisAngle vector, SORA vector, exponential map. + // A rotation vector describes a rotation about an axis: + // the axis of rotation is the vector normalized, + // the angle of rotation is the magnitude of the vector. + Vector3 ToRotationVector() const { + OVR_MATH_ASSERT(IsNormalized() || LengthSq() == 0); + T s = T(0); + T sinHalfAngle = (T)sqrt(x * x + y * y + z * z); + if (sinHalfAngle > T(0)) { + T cosHalfAngle = w; + T halfAngle = (T)atan2(sinHalfAngle, cosHalfAngle); + + // Ensure minimum rotation magnitude + if (cosHalfAngle < 0) + halfAngle -= T(MATH_DOUBLE_PI); + + s = T(2) * halfAngle / sinHalfAngle; + } + return Vector3(x * s, y * s, z * s); + } + + // Faster version of the above, optimized for use with small rotations, where rotation angle ~= + // sin(angle) + inline OVR::Vector3 FastToRotationVector() const { + OVR_MATH_ASSERT(IsNormalized()); + T s; + T sinHalfSquared = x * x + y * y + z * z; + if (sinHalfSquared < T(.0037)) // =~ sin(7/2 degrees)^2 + { + // Max rotation magnitude error is about .062% at 7 degrees rotation, or about .0043 + // degrees + s = T(2) * Sign(w); + } else { + T sinHalfAngle = sqrt(sinHalfSquared); + T cosHalfAngle = w; + T halfAngle = atan2(sinHalfAngle, cosHalfAngle); + + // Ensure minimum rotation magnitude + if (cosHalfAngle < 0) + halfAngle -= T(MATH_DOUBLE_PI); + + s = T(2) * halfAngle / sinHalfAngle; + } + return Vector3(x * s, y * s, z * s); + } + + // Given a rotation vector of form unitRotationAxis * angle, + // returns the equivalent quaternion (unitRotationAxis * sin(angle), cos(Angle)). + static Quat FromRotationVector(const Vector3& v) { + T angleSquared = v.LengthSq(); + T s = T(0); + T c = T(1); + if (angleSquared > T(0)) { + T angle = static_cast(sqrt(angleSquared)); + s = static_cast(sin(angle * T(0.5))) / angle; // normalize + c = static_cast(cos(angle * T(0.5))); + } + return Quat(s * v.x, s * v.y, s * v.z, c); + } + + // Faster version of above, optimized for use with small rotation magnitudes, where rotation + // angle =~ sin(angle). If normalize is false, small-angle quaternions are returned + // un-normalized. + inline static Quat FastFromRotationVector(const OVR::Vector3& v, bool normalize = true) { + T s, c; + T angleSquared = v.LengthSq(); + if (angleSquared < T(0.0076)) // =~ (5 degrees*pi/180)^2 + { + s = T(0.5); + c = T(1.0); + // Max rotation magnitude error (after normalization) is about .064% at 5 degrees + // rotation, or .0032 degrees + if (normalize && angleSquared > 0) { + // sin(angle/2)^2 ~= (angle/2)^2 and cos(angle/2)^2 ~= 1 + T invLen = T(1) / sqrt(angleSquared * T(0.25) + T(1)); // normalize + s = s * invLen; + c = c * invLen; + } + } else { + T angle = sqrt(angleSquared); + s = sin(angle * T(0.5)) / angle; + c = cos(angle * T(0.5)); + } + return Quat(s * v.x, s * v.y, s * v.z, c); + } + + // Constructs the quaternion from a rotation matrix + explicit Quat(const Matrix4& m) { + T trace = m.M[0][0] + m.M[1][1] + m.M[2][2]; + + // In almost all cases, the first part is executed. + // However, if the trace is not positive, the other + // cases arise. + if (trace > T(0)) { + T s = static_cast(sqrt(trace + T(1))) * T(2); // s=4*qw + w = T(0.25) * s; + x = (m.M[2][1] - m.M[1][2]) / s; + y = (m.M[0][2] - m.M[2][0]) / s; + z = (m.M[1][0] - m.M[0][1]) / s; + } else if ((m.M[0][0] > m.M[1][1]) && (m.M[0][0] > m.M[2][2])) { + T s = static_cast(sqrt(T(1) + m.M[0][0] - m.M[1][1] - m.M[2][2])) * T(2); + w = (m.M[2][1] - m.M[1][2]) / s; + x = T(0.25) * s; + y = (m.M[0][1] + m.M[1][0]) / s; + z = (m.M[2][0] + m.M[0][2]) / s; + } else if (m.M[1][1] > m.M[2][2]) { + T s = static_cast(sqrt(T(1) + m.M[1][1] - m.M[0][0] - m.M[2][2])) * T(2); // S=4*qy + w = (m.M[0][2] - m.M[2][0]) / s; + x = (m.M[0][1] + m.M[1][0]) / s; + y = T(0.25) * s; + z = (m.M[1][2] + m.M[2][1]) / s; + } else { + T s = static_cast(sqrt(T(1) + m.M[2][2] - m.M[0][0] - m.M[1][1])) * T(2); // S=4*qz + w = (m.M[1][0] - m.M[0][1]) / s; + x = (m.M[0][2] + m.M[2][0]) / s; + y = (m.M[1][2] + m.M[2][1]) / s; + z = T(0.25) * s; + } + OVR_MATH_ASSERT(IsNormalized()); // Ensure input matrix is orthogonal + } + + // Constructs the quaternion from a rotation matrix + explicit Quat(const Matrix3& m) { + T trace = m.M[0][0] + m.M[1][1] + m.M[2][2]; + + // In almost all cases, the first part is executed. + // However, if the trace is not positive, the other + // cases arise. + if (trace > T(0)) { + T s = static_cast(sqrt(trace + T(1))) * T(2); // s=4*qw + w = T(0.25) * s; + x = (m.M[2][1] - m.M[1][2]) / s; + y = (m.M[0][2] - m.M[2][0]) / s; + z = (m.M[1][0] - m.M[0][1]) / s; + } else if ((m.M[0][0] > m.M[1][1]) && (m.M[0][0] > m.M[2][2])) { + T s = static_cast(sqrt(T(1) + m.M[0][0] - m.M[1][1] - m.M[2][2])) * T(2); + w = (m.M[2][1] - m.M[1][2]) / s; + x = T(0.25) * s; + y = (m.M[0][1] + m.M[1][0]) / s; + z = (m.M[2][0] + m.M[0][2]) / s; + } else if (m.M[1][1] > m.M[2][2]) { + T s = static_cast(sqrt(T(1) + m.M[1][1] - m.M[0][0] - m.M[2][2])) * T(2); // S=4*qy + w = (m.M[0][2] - m.M[2][0]) / s; + x = (m.M[0][1] + m.M[1][0]) / s; + y = T(0.25) * s; + z = (m.M[1][2] + m.M[2][1]) / s; + } else { + T s = static_cast(sqrt(T(1) + m.M[2][2] - m.M[0][0] - m.M[1][1])) * T(2); // S=4*qz + w = (m.M[1][0] - m.M[0][1]) / s; + x = (m.M[0][2] + m.M[2][0]) / s; + y = (m.M[1][2] + m.M[2][1]) / s; + z = T(0.25) * s; + } + OVR_MATH_ASSERT(IsNormalized()); // Ensure input matrix is orthogonal + } + + // MERGE_MOBILE_SDK + // Constructs a quaternion that rotates 'from' to line up with 'to'. + explicit Quat(const Vector3& from, const Vector3& to) { + const T cx = from.y * to.z - from.z * to.y; + const T cy = from.z * to.x - from.x * to.z; + const T cz = from.x * to.y - from.y * to.x; + const T dot = from.x * to.x + from.y * to.y + from.z * to.z; + const T crossLengthSq = cx * cx + cy * cy + cz * cz; + const T magnitude = static_cast(sqrt(crossLengthSq + dot * dot)); + const T cw = dot + magnitude; + if (cw < Math::SmallestNonDenormal()) { + const T sx = to.y * to.y + to.z * to.z; + const T sz = to.x * to.x + to.y * to.y; + if (sx > sz) { + const T rcpLength = RcpSqrt(sx); + x = T(0); + y = to.z * rcpLength; + z = -to.y * rcpLength; + w = T(0); + } else { + const T rcpLength = RcpSqrt(sz); + x = to.y * rcpLength; + y = -to.x * rcpLength; + z = T(0); + w = T(0); + } + return; + } + const T rcpLength = RcpSqrt(crossLengthSq + cw * cw); + x = cx * rcpLength; + y = cy * rcpLength; + z = cz * rcpLength; + w = cw * rcpLength; + } + // MERGE_MOBILE_SDK + + 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; + } + + // Compare two quats for equality within tolerance. Returns true if quats match within + // tolerance. + bool IsEqual(const Quat& b, T tolerance = Math::Tolerance()) const { + return Abs(Dot(b)) >= T(1) - tolerance; + } + + // Compare two quats for equality within tolerance while checking matching hemispheres. Returns + // true if quats match within tolerance. + bool IsEqualMatchHemisphere(Quat b, T tolerance = Math::Tolerance()) const { + b.EnsureSameHemisphere(*this); + return Abs(Dot(b)) >= T(1) - tolerance; + } + + static T Abs(const T v) { + return (v >= 0) ? v : -v; + } + + // Get Imaginary part vector + Vector3 Imag() const { + return Vector3(x, y, z); + } + + // Get quaternion length. + T Length() const { + return static_cast(sqrt(LengthSq())); + } + + // Get quaternion length squared. + T LengthSq() const { + return (x * x + y * y + z * z + w * w); + } + + // Simple Euclidean 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(); // Antipodal point check + return (d1 < d2) ? d1 : d2; + } + + T DistanceSq(const Quat& q) const { + T d1 = (*this - q).LengthSq(); + T d2 = (*this + q).LengthSq(); // Antipodal point check + return (d1 < d2) ? d1 : d2; + } + + T Dot(const Quat& q) const { + return x * q.x + y * q.y + z * q.z + w * q.w; + } + + // Angle between two quaternions in radians + T Angle(const Quat& q) const { + return T(2) * Acos(Abs(Dot(q))); + } + + // Angle of quaternion + T Angle() const { + return T(2) * Acos(Abs(w)); + } + + // Normalize + bool IsNormalized() const { + return fabs(LengthSq() - T(1)) < Math::Tolerance(); + } + + void Normalize() { + T s = Length(); + if (s != T(0)) + s = T(1) / s; + *this *= s; + } + + Quat Normalized() const { + T s = Length(); + if (s != T(0)) + s = T(1) / s; + return *this * s; + } + + inline void ChangeHemisphere(){ + x = -x; + y = -y; + z = -z; + w = -w; + } + + inline void EnsureSameHemisphere(const Quat& o) { + if (Dot(o) < T(0)) { + ChangeHemisphere(); + } + } + + // Returns conjugate of the quaternion. Produces inverse rotation if quaternion is normalized. + Quat Conj() const { + return Quat(-x, -y, -z, w); + } + + // 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); + } + const Quat& operator*=(const Quat& b) { + *this = *this * b; + return *this; + } + + // MERGE_MOBILE_SDK + Vector3 operator*(const Vector3& v) const { + return Rotate(v); + } + // MERGE_MOBILE_SDK + + // + // 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); + } + + // Compute quaternion that rotates v into alignTo: alignTo = Quat::Align(alignTo, v).Rotate(v). + // NOTE: alignTo and v must be normalized. + static Quat Align(const Vector3& alignTo, const Vector3& v) { + OVR_MATH_ASSERT(alignTo.IsNormalized() && v.IsNormalized()); + Vector3 bisector = (v + alignTo); + bisector.Normalize(); + T cosHalfAngle = v.Dot(bisector); // 0..1 + if (cosHalfAngle > T(0)) { + Vector3 imag = v.Cross(bisector); + return Quat(imag.x, imag.y, imag.z, cosHalfAngle); + } else { + // cosHalfAngle == 0: a 180 degree rotation. + // sinHalfAngle == 1, rotation axis is any axis perpendicular + // to alignTo. Choose axis to include largest magnitude components + if (fabs(v.x) > fabs(v.y)) { + // x or z is max magnitude component + // = Cross(v, (0,1,0)).Normalized(); + T invLen = sqrt(v.x * v.x + v.z * v.z); + if (invLen > T(0)) + invLen = T(1) / invLen; + return Quat(-v.z * invLen, 0, v.x * invLen, 0); + } else { + // y or z is max magnitude component + // = Cross(v, (1,0,0)).Normalized(); + T invLen = sqrt(v.y * v.y + v.z * v.z); + if (invLen > T(0)) + invLen = T(1) / invLen; + return Quat(0, v.z * invLen, -v.y * invLen, 0); + } + } + } + + // Normalized linear interpolation of quaternions + // NOTE: This function is a bad approximation of Slerp() + // when the angle between the *this and b is large. + // Use FastSlerp() or Slerp() instead. + Quat Lerp(const Quat& b, T s) const { + return (*this * (T(1) - s) + b * (Dot(b) < 0 ? -s : s)).Normalized(); + } + + // Spherical linear interpolation between rotations + Quat Slerp(const Quat& b, T s) const { + Vector3 delta = (b * this->Inverted()).ToRotationVector(); + return FromRotationVector(delta * s) * *this; + } + + // Spherical linear interpolation: much faster for small rotations, accurate for large + // rotations. See FastTo/FromRotationVector + Quat FastSlerp(const Quat& b, T s) const { + Vector3 delta = (b * this->Inverted()).FastToRotationVector(); + return (FastFromRotationVector(delta * s, false) * *this).Normalized(); + } + + // MERGE_MOBILE_SDK + // FIXME: This is opposite of Lerp for some reason. It goes from 1 to 0 instead of 0 to 1. + // Leaving it as a gift for future generations to deal with. + Quat Nlerp(const Quat& other, T a) const { + T sign = (Dot(other) >= 0.0f) ? 1.0f : -1.0f; + return (*this * sign * a + other * (1 - a)).Normalized(); + } + // MERGE_MOBILE_SDK + + // 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, T(0))) * Inverted()).Imag(); + } + + // Rotation by inverse of *this + Vector3 InverseRotate(const Vector3& v) const { + return Inverted().Rotate(v); + } + + // Inversed quaternion rotates in the opposite direction. + Quat Inverted() const { + return Quat(-x, -y, -z, w); + } + + Quat Inverse() const { + return Quat(-x, -y, -z, w); + } + + // Sets this quaternion to the one rotates in the opposite direction. + void Invert() { + *this = Quat(-x, -y, -z, w); + } + + // Time integration of constant angular velocity over dt + Quat TimeIntegrate(const Vector3& angularVelocity, T dt) const { + // solution is: this * exp( omega*dt/2 ); FromRotationVector(v) gives exp(v*.5). + return (*this * FastFromRotationVector(angularVelocity * dt, false)).Normalized(); + } + + // Time integration of constant angular acceleration and velocity over dt + // These are the first two terms of the "Magnus expansion" of the solution + // + // o = o * exp( W=(W1 + W2 + W3+...) * 0.5 ); + // + // omega1 = (omega + omegaDot*dt) + // W1 = (omega + omega1)*dt/2 + // W2 = cross(omega, omega1)/12*dt^2 % (= -cross(omega_dot, omega)/12*dt^3) + // Terms 3 and beyond are vanishingly small: + // W3 = cross(omega_dot, cross(omega_dot, omega))/240*dt^5 + // + Quat TimeIntegrate( + const Vector3& angularVelocity, + const Vector3& angularAcceleration, + T dt) const { + const Vector3& omega = angularVelocity; + const Vector3& omegaDot = angularAcceleration; + + Vector3 omega1 = (omega + omegaDot * dt); + Vector3 W = ((omega + omega1) + omega.Cross(omega1) * (dt / T(6))) * (dt / T(2)); + + // FromRotationVector(v) is exp(v*.5) + return (*this * FastFromRotationVector(W, false)).Normalized(); + } + + // Decompose rotation into three rotations: + // roll radians about Z axis, then pitch radians about X axis, then yaw radians about Y axis. + // Call with nullptr if a return value is not needed. + void GetYawPitchRoll(T* yaw, T* pitch, T* roll) const { + return GetEulerAngles(yaw, pitch, roll); + } + + // 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 order is c, b, a: + // rotation c around axis A3 + // is followed by rotation b around axis A2 + // is followed by rotation a around axis A1 + // rotations are CCW or CW (D) in LH or RH coordinate system (S) + // + template + void GetEulerAngles(T* a, T* b, T* c) const { + OVR_MATH_ASSERT(IsNormalized()); + OVR_MATH_STATIC_ASSERT( + (A1 != A2) && (A2 != A3) && (A1 != A3), "(A1 != A2) && (A2 != A3) && (A1 != A3)"); + + const T D = static_cast(Denum); + const T S = static_cast(Senum); + + 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); + // Determine whether even permutation + if (((A1 + 1) % 3 == A2) && ((A2 + 1) % 3 == A3)) + psign = T(1); + + T s2 = psign * T(2) * (psign * w * Q[A2] + Q[A1] * Q[A3]); + + T singularityRadius = Math::SingularityRadius(); + if (s2 < T(-1) + singularityRadius) { // South pole singularity + if (a) + *a = T(0); + if (b) + *b = -S * D * ((T)MATH_DOUBLE_PIOVER2); + if (c) + *c = S * D * + static_cast( + atan2(T(2) * (psign * Q[A1] * Q[A2] + w * Q[A3]), ww + Q22 - Q11 - Q33)); + } else if (s2 > T(1) - singularityRadius) { // North pole singularity + if (a) + *a = T(0); + if (b) + *b = S * D * ((T)MATH_DOUBLE_PIOVER2); + if (c) + *c = S * D * + static_cast( + atan2(T(2) * (psign * Q[A1] * Q[A2] + w * Q[A3]), ww + Q22 - Q11 - Q33)); + } else { + if (a) + *a = -S * D * + static_cast( + atan2(T(-2) * (w * Q[A1] - psign * Q[A2] * Q[A3]), ww + Q33 - Q11 - Q22)); + if (b) + *b = S * D * static_cast(asin(s2)); + if (c) + *c = S * D * + static_cast( + atan2(T(2) * (w * Q[A3] - psign * Q[A1] * Q[A2]), ww + Q11 - Q22 - Q33)); + } + } + + template + void GetEulerAngles(T* a, T* b, T* c) const { + GetEulerAngles(a, b, c); + } + + template + void GetEulerAngles(T* a, T* b, T* c) const { + 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) const { + OVR_MATH_ASSERT(IsNormalized()); + OVR_MATH_STATIC_ASSERT(A1 != A2, "A1 != A2"); + + const T D = static_cast(Denum); + const T S = static_cast(Senum); + + 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); + if ((A1 + 1) % 3 == A2) // Determine whether even permutation + { + psign = T(1); + } + + T c2 = ww + Q11 - Q22 - Qmm; + T singularityRadius = Math::SingularityRadius(); + if (c2 < T(-1) + singularityRadius) { // South pole singularity + if (a) + *a = T(0); + if (b) + *b = S * D * ((T)MATH_DOUBLE_PI); + if (c) + *c = S * D * + static_cast( + atan2(T(2) * (w * Q[A1] - psign * Q[A2] * Q[m]), ww + Q22 - Q11 - Qmm)); + } else if (c2 > T(1) - singularityRadius) { // North pole singularity + if (a) + *a = T(0); + if (b) + *b = T(0); + if (c) + *c = S * D * + static_cast( + atan2(T(2) * (w * Q[A1] - psign * Q[A2] * Q[m]), ww + Q22 - Q11 - Qmm)); + } else { + if (a) + *a = S * D * + static_cast( + atan2(psign * w * Q[m] + Q[A1] * Q[A2], w * Q[A2] - psign * Q[A1] * Q[m])); + if (b) + *b = S * D * static_cast(acos(c2)); + if (c) + *c = S * D * + static_cast(atan2( + -psign * w * Q[m] + Q[A1] * Q[A2], w * Q[A2] + psign * Q[A1] * Q[m])); + } + } + + bool IsNan() const { + return isnan(x) || isnan(y) || isnan(z) || isnan(w); + } + + Quat GetTwistQuaternion(const Vector3& twistAxis) const{ + Vector3 rotAxis(x, y, z); + float dotProd = rotAxis.Dot(twistAxis); + auto projected = twistAxis * dotProd; + Quat twist(projected.x, projected.y, projected.z, w); + twist.Normalize(); + if(dotProd < 0){ + twist.ChangeHemisphere(); + } + return twist; + } + + float GetTwistAngle(const Vector3& twistAxis) const{ + Vector3 rotAxis(x, y, z); + float dotProd = rotAxis.Dot(twistAxis); + auto projected = twistAxis * dotProd; + Quat twist(projected.x, projected.y, projected.z, w); + twist.Normalize(); + return dotProd < 0? -twist.Angle():twist.Angle(); + } + + void GetSwingTwist(Quat* swing, Quat* twist, const Vector3& twistAxis) const{ + (*twist) = GetTwistQuaternion(twistAxis); + (*swing) = (*this) * twist->Inverse(); + } +}; + +// MERGE_MOBILE_SDK +// allow multiplication in order vector * quat (member operator handles quat * vector) +template +Vector3 operator*(const Vector3& v, const Quat& q) { + return Vector3(q * v); +} +// MERGE_MOBILE_SDK + +typedef Quat Quatf; +typedef Quat Quatd; + +OVR_MATH_STATIC_ASSERT((sizeof(Quatf) == 4 * sizeof(float)), "sizeof(Quatf) failure"); +OVR_MATH_STATIC_ASSERT((sizeof(Quatd) == 4 * sizeof(double)), "sizeof(Quatd) failure"); + +//------------------------------------------------------------------------------------- +// ***** Pose +// +// Position and orientation combined. +// +// This structure needs to be the same size and layout on 32-bit and 64-bit arch. +// Update OVR_PadCheck.cpp when updating this object. +template +class Pose { + public: + typedef typename CompatibleTypes>::Type CompatibleType; + + Pose() {} + + Pose(const Quat& orientation, const Vector3& pos) + : Rotation(orientation), Translation(pos) {} + + Pose(const Pose& s) : Rotation(s.Rotation), Translation(s.Translation) {} + + Pose(const Matrix3& R, const Vector3& t) : Rotation((Quat)R), Translation(t) {} + + Pose(const CompatibleType& s) : Rotation(s.Orientation), Translation(s.Position) {} + + Pose(const CompatibleType&& s) : Rotation(s.Orientation), Translation(s.Position) {} + + explicit Pose(const Pose::OtherFloatType>& s) + : Rotation(s.Rotation), Translation(s.Translation) { + // Ensure normalized rotation if converting from float to double + if (sizeof(T) > sizeof(typename Math::OtherFloatType)) + Rotation.Normalize(); + } + + Pose& operator=(Pose const& rhs) { + if (&rhs != this) { + this->Rotation = rhs.Rotation; + this->Translation = rhs.Translation; + } + return *this; + } + + explicit Pose(const Matrix4& m) : Rotation(m), Translation(m.GetTranslation()) {} + + static Pose Identity() { + return Pose(Quat(0, 0, 0, 1), Vector3(0, 0, 0)); + } + + void SetIdentity() { + Rotation = Quat(0, 0, 0, 1); + Translation = Vector3(0, 0, 0); + } + + // used to make things obviously broken if someone tries to use the value + void SetInvalid() { + Rotation = Quat(NAN, NAN, NAN, NAN); + Translation = Vector3(NAN, NAN, NAN); + } + + bool IsEqual(const Pose& b, T tolerance = Math::Tolerance()) const { + return Translation.IsEqual(b.Translation, tolerance) && + Rotation.IsEqual(b.Rotation, tolerance); + } + + bool IsEqualMatchHemisphere(const Pose& b, T tolerance = Math::Tolerance()) const { + return Translation.IsEqual(b.Translation, tolerance) && + Rotation.IsEqualMatchHemisphere(b.Rotation, tolerance); + } + + operator typename CompatibleTypes>::Type() const { + typename CompatibleTypes>::Type result; + result.Orientation = Rotation; + result.Position = Translation; + return result; + } + + Quat Rotation; + Vector3 Translation; + + OVR_MATH_STATIC_ASSERT( + (sizeof(T) == sizeof(double) || sizeof(T) == sizeof(float)), + "(sizeof(T) == sizeof(double) || sizeof(T) == sizeof(float))"); + + void ToArray(T* arr) const { + T temp[7] = { + Rotation.x, + Rotation.y, + Rotation.z, + Rotation.w, + Translation.x, + Translation.y, + Translation.z}; + for (int i = 0; i < 7; i++) + arr[i] = temp[i]; + } + + static Pose FromArray(const T* v) { + Quat rotation(v[0], v[1], v[2], v[3]); + Vector3 translation(v[4], v[5], v[6]); + // Ensure rotation is normalized, in case it was originally a float, stored in a .json file, + // etc. + return Pose(rotation.Normalized(), translation); + } + + Vector3 Rotate(const Vector3& v) const { + return Rotation.Rotate(v); + } + + Vector3 InverseRotate(const Vector3& v) const { + return Rotation.InverseRotate(v); + } + + Vector3 Translate(const Vector3& v) const { + return v + Translation; + } + + Vector3 Transform(const Vector3& v) const { + return Rotate(v) + Translation; + } + + Vector3 InverseTransform(const Vector3& v) const { + return InverseRotate(v - Translation); + } + + Vector3 Apply(const Vector3& v) const { + return Transform(v); + } + + Pose operator*(const Pose& other) const { + return Pose(Rotation * other.Rotation, Apply(other.Translation)); + } + + Pose Inverted() const { + Quat inv = Rotation.Inverted(); + return Pose(inv, inv.Rotate(-Translation)); + } + + // Interpolation between two poses: translation is interpolated with Lerp(), + // and rotations are interpolated with Slerp(). + Pose Lerp(const Pose& b, T s) const { + return Pose(Rotation.Slerp(b.Rotation, s), Translation.Lerp(b.Translation, s)); + } + + // Similar to Lerp above, except faster in case of small rotation differences. See + // Quat::FastSlerp. + Pose FastLerp(const Pose& b, T s) const { + return Pose(Rotation.FastSlerp(b.Rotation, s), Translation.Lerp(b.Translation, s)); + } + + Pose TimeIntegrate(const Vector3& linearVelocity, const Vector3& angularVelocity, T dt) + const { + return Pose( + (Rotation * Quat::FastFromRotationVector(angularVelocity * dt, false)).Normalized(), + Translation + linearVelocity * dt); + } + + Pose TimeIntegrate( + const Vector3& linearVelocity, + const Vector3& linearAcceleration, + const Vector3& angularVelocity, + const Vector3& angularAcceleration, + T dt) const { + return Pose( + Rotation.TimeIntegrate(angularVelocity, angularAcceleration, dt), + Translation + linearVelocity * dt + linearAcceleration * dt * dt * T(0.5)); + } + + bool IsNan() const { + return Translation.IsNan() || Rotation.IsNan(); + } +}; + +typedef Pose Posef; +typedef Pose Posed; + +OVR_MATH_STATIC_ASSERT( + (sizeof(Posed) == sizeof(Quatd) + sizeof(Vector3d)), + "sizeof(Posed) failure"); +OVR_MATH_STATIC_ASSERT( + (sizeof(Posef) == sizeof(Quatf) + sizeof(Vector3f)), + "sizeof(Posef) failure"); + +//------------------------------------------------------------------------------------- +// ***** Matrix4 +// +// Matrix4 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. + +template +class Matrix4 { + static const Matrix4 IdentityValue; + + public: + typedef T ElementType; + static const size_t Dimension = 4; + + T M[4][4]; + + enum NoInitType { NoInit }; + + // Construct with no memory initialization. + Matrix4(NoInitType) {} + + // By default, we construct identity matrix. + Matrix4() { + M[0][0] = M[1][1] = M[2][2] = M[3][3] = T(1); + M[0][1] = M[1][0] = M[2][3] = M[3][1] = T(0); + M[0][2] = M[1][2] = M[2][0] = M[3][2] = T(0); + M[0][3] = M[1][3] = M[2][1] = M[3][0] = T(0); + } + + Matrix4( + T m11, + T m12, + T m13, + T m14, + T m21, + T m22, + T m23, + T m24, + T m31, + T m32, + T m33, + T m34, + T m41, + T m42, + T m43, + T 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; + } + + Matrix4(T m11, T m12, T m13, T m21, T m22, T m23, T m31, T m32, T m33) { + M[0][0] = m11; + M[0][1] = m12; + M[0][2] = m13; + M[0][3] = T(0); + M[1][0] = m21; + M[1][1] = m22; + M[1][2] = m23; + M[1][3] = T(0); + M[2][0] = m31; + M[2][1] = m32; + M[2][2] = m33; + M[2][3] = T(0); + M[3][0] = T(0); + M[3][1] = T(0); + M[3][2] = T(0); + M[3][3] = T(1); + } + + explicit Matrix4(const Matrix3& m) { + M[0][0] = m.M[0][0]; + M[0][1] = m.M[0][1]; + M[0][2] = m.M[0][2]; + M[0][3] = T(0); + M[1][0] = m.M[1][0]; + M[1][1] = m.M[1][1]; + M[1][2] = m.M[1][2]; + M[1][3] = T(0); + M[2][0] = m.M[2][0]; + M[2][1] = m.M[2][1]; + M[2][2] = m.M[2][2]; + M[2][3] = T(0); + M[3][0] = T(0); + M[3][1] = T(0); + M[3][2] = T(0); + M[3][3] = T(1); + } + + explicit Matrix4(const Quat& q) { + OVR_MATH_ASSERT(q.IsNormalized()); + T ww = q.w * q.w; + T xx = q.x * q.x; + T yy = q.y * q.y; + T zz = q.z * q.z; + + M[0][0] = ww + xx - yy - zz; + M[0][1] = 2 * (q.x * q.y - q.w * q.z); + M[0][2] = 2 * (q.x * q.z + q.w * q.y); + M[0][3] = T(0); + M[1][0] = 2 * (q.x * q.y + q.w * q.z); + M[1][1] = ww - xx + yy - zz; + M[1][2] = 2 * (q.y * q.z - q.w * q.x); + M[1][3] = T(0); + M[2][0] = 2 * (q.x * q.z - q.w * q.y); + M[2][1] = 2 * (q.y * q.z + q.w * q.x); + M[2][2] = ww - xx - yy + zz; + M[2][3] = T(0); + M[3][0] = T(0); + M[3][1] = T(0); + M[3][2] = T(0); + M[3][3] = T(1); + } + + explicit Matrix4(const Pose& p) { + Matrix4 result(p.Rotation); + result.SetTranslation(p.Translation); + *this = result; + } + + // C-interop support + explicit Matrix4(const Matrix4::OtherFloatType>& src) { + for (int i = 0; i < 4; i++) + for (int j = 0; j < 4; j++) + M[i][j] = (T)src.M[i][j]; + } + + // C-interop support. + Matrix4(const typename CompatibleTypes>::Type& s) { + OVR_MATH_STATIC_ASSERT(sizeof(s) == sizeof(Matrix4), "sizeof(s) == sizeof(Matrix4)"); + memcpy(M, s.M, sizeof(M)); + } + + operator typename CompatibleTypes>::Type() const { + typename CompatibleTypes>::Type result; + OVR_MATH_STATIC_ASSERT( + sizeof(result) == sizeof(Matrix4), "sizeof(result) == sizeof(Matrix4)"); + memcpy(result.M, M, sizeof(M)); + return result; + } + + void ToString(char* dest, size_t destsize) const { + size_t pos = 0; + for (int r = 0; r < 4; r++) { + for (int c = 0; c < 4; c++) { + pos += OVRMath_sprintf(dest + pos, destsize - pos, "%g ", M[r][c]); + } + } + } + + static Matrix4 FromString(const char* src) { + Matrix4 result; + if (src) { + for (int r = 0; r < 4; r++) { + for (int c = 0; c < 4; c++) { + result.M[r][c] = (T)atof(src); + while (*src && *src != ' ') { + src++; + } + while (*src && *src == ' ') { + src++; + } + } + } + } + return result; + } + + static const Matrix4& Identity() { + return IdentityValue; + } + + void SetIdentity() { + M[0][0] = M[1][1] = M[2][2] = M[3][3] = T(1); + M[0][1] = M[1][0] = M[2][3] = M[3][1] = T(0); + M[0][2] = M[1][2] = M[2][0] = M[3][2] = T(0); + M[0][3] = M[1][3] = M[2][1] = M[3][0] = T(0); + } + + void SetXBasis(const Vector3& v) { + M[0][0] = v.x; + M[1][0] = v.y; + M[2][0] = v.z; + } + Vector3 GetXBasis() const { + return Vector3(M[0][0], M[1][0], M[2][0]); + } + + void SetYBasis(const Vector3& v) { + M[0][1] = v.x; + M[1][1] = v.y; + M[2][1] = v.z; + } + Vector3 GetYBasis() const { + return Vector3(M[0][1], M[1][1], M[2][1]); + } + + void SetZBasis(const Vector3& v) { + M[0][2] = v.x; + M[1][2] = v.y; + M[2][2] = v.z; + } + Vector3 GetZBasis() const { + return Vector3(M[0][2], M[1][2], M[2][2]); + } + + T operator()(int i, int j) const { + return M[i][j]; + } + T& operator()(int i, int j) { + return M[i][j]; + } + + bool operator==(const Matrix4& b) const { + bool isEqual = true; + for (int i = 0; i < 4; i++) + for (int j = 0; j < 4; j++) + isEqual &= (M[i][j] == b.M[i][j]); + + return isEqual; + } + + Matrix4 operator+(const Matrix4& b) const { + Matrix4 result(*this); + result += b; + return result; + } + + Matrix4& operator+=(const Matrix4& b) { + for (int i = 0; i < 4; i++) + for (int j = 0; j < 4; j++) + M[i][j] += b.M[i][j]; + return *this; + } + + Matrix4 operator-(const Matrix4& b) const { + Matrix4 result(*this); + result -= b; + return result; + } + + Matrix4& operator-=(const Matrix4& b) { + for (int i = 0; i < 4; i++) + for (int j = 0; j < 4; j++) + M[i][j] -= b.M[i][j]; + return *this; + } + + // Multiplies two matrices into destination with minimum copying. + static Matrix4& Multiply(Matrix4* d, const Matrix4& a, const Matrix4& b) { + OVR_MATH_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; + } + + Matrix4 operator*(const Matrix4& b) const { + Matrix4 result(Matrix4::NoInit); + Multiply(&result, *this, b); + return result; + } + + Matrix4& operator*=(const Matrix4& b) { + return Multiply(this, Matrix4(*this), b); + } + + Vector4 operator*(const Vector4& b) const { + return Transform(b); + } + + Matrix4 operator*(T s) const { + Matrix4 result(*this); + result *= s; + return result; + } + + Matrix4& operator*=(T s) { + for (int i = 0; i < 4; i++) + for (int j = 0; j < 4; j++) + M[i][j] *= s; + return *this; + } + + Matrix4 operator/(T s) const { + Matrix4 result(*this); + result /= s; + return result; + } + + Matrix4& operator/=(T s) { + for (int i = 0; i < 4; i++) + for (int j = 0; j < 4; j++) + M[i][j] /= s; + return *this; + } + + Vector3 Transform(const Vector3& v) const { + const T w = (M[3][0] * v.x + M[3][1] * v.y + M[3][2] * v.z + M[3][3]); + OVR_MATH_ASSERT(fabs(w) >= Math::SmallestNonDenormal()); + const T rcpW = T(1) / w; + return Vector3( + (M[0][0] * v.x + M[0][1] * v.y + M[0][2] * v.z + M[0][3]) * rcpW, + (M[1][0] * v.x + M[1][1] * v.y + M[1][2] * v.z + M[1][3]) * rcpW, + (M[2][0] * v.x + M[2][1] * v.y + M[2][2] * v.z + M[2][3]) * rcpW); + } + + Vector4 Transform(const Vector4& v) const { + return Vector4( + M[0][0] * v.x + M[0][1] * v.y + M[0][2] * v.z + M[0][3] * v.w, + M[1][0] * v.x + M[1][1] * v.y + M[1][2] * v.z + M[1][3] * v.w, + M[2][0] * v.x + M[2][1] * v.y + M[2][2] * v.z + M[2][3] * v.w, + M[3][0] * v.x + M[3][1] * v.y + M[3][2] * v.z + M[3][3] * v.w); + } + + Matrix4 Transposed() const { + return Matrix4( + 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(); + } + + T SubDet(const size_t* rows, const size_t* 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]]); + } + + T Cofactor(size_t I, size_t J) const { + const size_t 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]); + } + + T 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); + } + + Matrix4 Adjugated() const { + return Matrix4( + 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)); + } + + Matrix4 Inverted() const { + T det = Determinant(); + OVR_MATH_ASSERT(fabs(det) >= Math::SmallestNonDenormal()); + return Adjugated() * (T(1) / det); + } + + void Invert() { + *this = Inverted(); + } + + // This is more efficient than general inverse, but ONLY works + // correctly if it is a homogeneous transform matrix (rot + trans) + Matrix4 InvertedHomogeneousTransform() const { + // Make the inverse rotation matrix + Matrix4 rinv = this->Transposed(); + rinv.M[3][0] = rinv.M[3][1] = rinv.M[3][2] = T(0); + // Make the inverse translation matrix + Vector3 tvinv(-M[0][3], -M[1][3], -M[2][3]); + Matrix4 tinv = Matrix4::Translation(tvinv); + return rinv * tinv; // "untranslate", then "unrotate" + } + + // This is more efficient than general inverse, but ONLY works + // correctly if it is a homogeneous transform matrix (rot + trans) + void InvertHomogeneousTransform() { + *this = InvertedHomogeneousTransform(); + } + + // Matrix to Euler Angles conversion + // 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(T* a, T* b, T* c) const { + OVR_MATH_STATIC_ASSERT( + (A1 != A2) && (A2 != A3) && (A1 != A3), "(A1 != A2) && (A2 != A3) && (A1 != A3)"); + + const T D = static_cast(Denum); + const T S = static_cast(Senum); + + T psign = T(-1); + if (((A1 + 1) % 3 == A2) && ((A2 + 1) % 3 == A3)) // Determine whether even permutation + psign = T(1); + + T pm = psign * M[A1][A3]; + T singularityRadius = Math::SingularityRadius(); + if (pm < T(-1) + singularityRadius) { // South pole singularity + *a = T(0); + *b = -S * D * ((T)MATH_DOUBLE_PIOVER2); + *c = S * D * static_cast(atan2(psign * M[A2][A1], M[A2][A2])); + } else if (pm > T(1) - singularityRadius) { // North pole singularity + *a = T(0); + *b = S * D * ((T)MATH_DOUBLE_PIOVER2); + *c = S * D * static_cast(atan2(psign * M[A2][A1], M[A2][A2])); + } else { // Normal case (nonsingular) + *a = S * D * static_cast(atan2(-psign * M[A2][A3], M[A3][A3])); + *b = S * D * static_cast(asin(pm)); + *c = S * D * static_cast(atan2(-psign * M[A1][A2], M[A1][A1])); + } + } + + // Matrix to Euler Angles conversion + // 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(T* a, T* b, T* c) const { + OVR_MATH_STATIC_ASSERT(A1 != A2, "A1 != A2"); + + const T D = static_cast(Denum); + const T S = static_cast(Senum); + + // Determine the axis that was not supplied + int m = 3 - A1 - A2; + + T psign = T(-1); + if ((A1 + 1) % 3 == A2) // Determine whether even permutation + psign = T(1); + + T c2 = M[A1][A1]; + T singularityRadius = Math::SingularityRadius(); + if (c2 < T(-1) + singularityRadius) { // South pole singularity + *a = T(0); + *b = S * D * ((T)MATH_DOUBLE_PI); + *c = S * D * static_cast(atan2(-psign * M[A2][m], M[A2][A2])); + } else if (c2 > T(1) - singularityRadius) { // North pole singularity + *a = T(0); + *b = T(0); + *c = S * D * static_cast(atan2(-psign * M[A2][m], M[A2][A2])); + } else { // Normal case (nonsingular) + *a = S * D * static_cast(atan2(M[A2][A1], -psign * M[m][A1])); + *b = S * D * static_cast(acos(c2)); + *c = S * D * static_cast(atan2(M[A1][A2], psign * M[A1][m])); + } + } + + // Decouple a TRS (translation, rotation, scaling) matrix. + // The matrix should be a valid TRS matrix. + void DecoupleTRS(Vector3* translation, Quat* rotation, Vector3* scale) const { + *translation = Vector3(M[0][3], M[1][3], M[2][3]); + scale->x = Vector3(M[0][0], M[1][0], M[2][0]).Length(); + scale->y = Vector3(M[0][1], M[1][1], M[2][1]).Length(); + scale->z = Vector3(M[0][2], M[1][2], M[2][2]).Length(); + Matrix3 rotationM( + M[0][0]/scale->x, M[0][1]/scale->y, M[0][2]/scale->z, + M[1][0]/scale->x, M[1][1]/scale->y, M[1][2]/scale->z, + M[2][0]/scale->x, M[2][1]/scale->y, M[2][2]/scale->z); + // Because we may have flipY applied to modelMatrix, we may have a matrix which has both TRS + // and reflection. If we found there are odd number of reflections (i.e. determinate is -1), + // we arbitrarily flip one axis. + if (rotationM.Determinant() < 0) { + scale->x *= -1; + rotationM.M[0][0] *= -1; + rotationM.M[1][0] *= -1; + rotationM.M[2][0] *= -1; + } + *rotation = Quat(rotationM); + } + + // Interpolation between two TRS (translation, rotation, scaling) matrices: + // translation and scaling are interpolated with Lerp(), + // and rotations are interpolated with Slerp(). + // Both matrices should be valid TRS matrices. + Matrix4 LerpTRS(const Matrix4& b, T s) const { + Vector3 ta, tb, t_lerped; + Quat ra, rb, r_lerped; + Vector3 sa, sb, s_lerped; + this->DecoupleTRS(&ta, &ra, &sa); + b.DecoupleTRS(&tb, &rb, &sb); + t_lerped = ta.Lerp(tb, s); + r_lerped = ra.Slerp(rb, s); + s_lerped = sa.Lerp(sb, s); + return Matrix4(Pose(r_lerped, t_lerped)) * Matrix4::Scaling(s_lerped); + } + + + // Creates a matrix that converts the vertices from one coordinate system + // to another. + static Matrix4 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; + + Matrix4 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] = T(from.XAxis / toArray[inv[abs(from.XAxis)]]); + m.M[inv[abs(from.YAxis)]][1] = T(from.YAxis / toArray[inv[abs(from.YAxis)]]); + m.M[inv[abs(from.ZAxis)]][2] = T(from.ZAxis / toArray[inv[abs(from.ZAxis)]]); + return m; + } + + // Creates a matrix for translation by vector + static Matrix4 Translation(const Vector3& v) { + Matrix4 t; + t.M[0][3] = v.x; + t.M[1][3] = v.y; + t.M[2][3] = v.z; + return t; + } + + // Creates a matrix for translation by vector + static Matrix4 Translation(T x, T y, T z = T(0)) { + Matrix4 t; + t.M[0][3] = x; + t.M[1][3] = y; + t.M[2][3] = z; + return t; + } + + // Sets the translation part + void SetTranslation(const Vector3& v) { + M[0][3] = v.x; + M[1][3] = v.y; + M[2][3] = v.z; + } + + Vector3 GetTranslation() const { + return Vector3(M[0][3], M[1][3], M[2][3]); + } + + // Creates a matrix for scaling by vector + static Matrix4 Scaling(const Vector3& v) { + Matrix4 t; + t.M[0][0] = v.x; + t.M[1][1] = v.y; + t.M[2][2] = v.z; + return t; + } + + // Creates a matrix for scaling by vector + static Matrix4 Scaling(T x, T y, T z) { + Matrix4 t; + t.M[0][0] = x; + t.M[1][1] = y; + t.M[2][2] = z; + return t; + } + + // Creates a matrix for scaling by vector + static Matrix4 Scaling(const Vector4& v) { + Matrix4 t; + t.M[0][0] = v.x; + t.M[1][1] = v.y; + t.M[2][2] = v.z; + t.M[3][3] = v.w; + return t; + } + + // Creates a matrix for scaling by vector + static Matrix4 Scaling(T x, T y, T z, T w) { + Matrix4 t; + t.M[0][0] = x; + t.M[1][1] = y; + t.M[2][2] = z; + t.M[3][3] = w; + return t; + } + + // Creates a matrix for scaling by constant + static Matrix4 Scaling(T s) { + Matrix4 t; + t.M[0][0] = s; + t.M[1][1] = s; + t.M[2][2] = s; + return t; + } + + // Simple L1 distance in R^12 + T Distance(const Matrix4& m2) const { + T d = fabs(M[0][0] - m2.M[0][0]) + fabs(M[0][1] - m2.M[0][1]); + d += fabs(M[0][2] - m2.M[0][2]) + fabs(M[0][3] - m2.M[0][3]); + d += fabs(M[1][0] - m2.M[1][0]) + fabs(M[1][1] - m2.M[1][1]); + d += fabs(M[1][2] - m2.M[1][2]) + fabs(M[1][3] - m2.M[1][3]); + d += fabs(M[2][0] - m2.M[2][0]) + fabs(M[2][1] - m2.M[2][1]); + d += fabs(M[2][2] - m2.M[2][2]) + fabs(M[2][3] - m2.M[2][3]); + d += fabs(M[3][0] - m2.M[3][0]) + fabs(M[3][1] - m2.M[3][1]); + d += fabs(M[3][2] - m2.M[3][2]) + fabs(M[3][3] - m2.M[3][3]); + return d; + } + + // Creates a rotation matrix rotating around the X axis by 'angle' radians. + // Just for quick testing. Not for final API. Need to remove case. + static Matrix4 RotationAxis(Axis A, T angle, RotateDirection dEnum, HandedSystem sEnum) { + const T d = static_cast(dEnum); + const T s = static_cast(sEnum); + T sina = s * d * sin(angle); + T cosa = cos(angle); + + switch (A) { + case Axis_X: + return Matrix4(1, 0, 0, 0, cosa, -sina, 0, sina, cosa); + case Axis_Y: + return Matrix4(cosa, 0, sina, 0, 1, 0, -sina, 0, cosa); + case Axis_Z: + return Matrix4(cosa, -sina, 0, sina, cosa, 0, 0, 0, 1); + default: + return Matrix4(); + } + } + + // 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 Matrix4 RotationX(T angle) { + T sina = static_cast(sin(angle)); + T cosa = static_cast(cos(angle)); + return Matrix4(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 Matrix4 RotationY(T angle) { + T sina = static_cast(sin(angle)); + T cosa = static_cast(cos(angle)); + return Matrix4(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 Matrix4 RotationZ(T angle) { + T sina = static_cast(sin(angle)); + T cosa = static_cast(cos(angle)); + return Matrix4(cosa, -sina, 0, sina, cosa, 0, 0, 0, 1); + } + + // MERGE_MOBILE_SDK + static Matrix4 RotationAxisAngle(const Vector3& axis, T angle) { + T x = axis.x; + T y = axis.y; + T z = axis.z; + T c = cos(angle); + T s = sin(angle); + T t = 1 - c; + Matrix4 m( + t * x * x + c, + t * x * y - z * s, + t * x * z + y * s, + t * x * y + z * s, + t * y * y + c, + t * y * z - x * s, + t * x * z - y * s, + t * y * z + x * s, + t * z * z + c); + return m; + } + // MERGE_MOBILE_SDK + + // 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 Matrix4 LookAtRH(const Vector3& eye, const Vector3& at, const Vector3& up) { + // FIXME: this fails when looking straight up, should probably at least assert + Vector3 z = (eye - at).Normalized(); // Forward + Vector3 x = up.Cross(z).Normalized(); // Right + Vector3 y = z.Cross(x); + + Matrix4 m( + x.x, + x.y, + x.z, + -(x.Dot(eye)), + y.x, + y.y, + y.z, + -(y.Dot(eye)), + z.x, + z.y, + z.z, + -(z.Dot(eye)), + 0, + 0, + 0, + 1); + return m; + } + + // 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 Matrix4 LookAtLH(const Vector3& eye, const Vector3& at, const Vector3& up) { + // FIXME: this fails when looking straight up, should probably at least assert + Vector3 z = (at - eye).Normalized(); // Forward + Vector3 x = up.Cross(z).Normalized(); // Right + Vector3 y = z.Cross(x); + + Matrix4 m( + x.x, + x.y, + x.z, + -(x.Dot(eye)), + y.x, + y.y, + y.z, + -(y.Dot(eye)), + z.x, + z.y, + z.z, + -(z.Dot(eye)), + 0, + 0, + 0, + 1); + return m; + } + + // MERGE_MOBILE_SDK + static Matrix4 CreateFromBasisVectors(Vector3 const& zBasis, Vector3 const& up) { + OVR_MATH_ASSERT(zBasis.IsNormalized()); + OVR_MATH_ASSERT(up.IsNormalized()); + T dot = zBasis.Dot(up); + if (dot < (T)-0.9999 || dot > (T)0.9999) { + // z basis cannot be parallel to the specified up + OVR_MATH_ASSERT(dot >= (T)-0.9999 || dot <= (T)0.9999); + return Matrix4(); + } + + Vector3 xBasis = up.Cross(zBasis); + xBasis.Normalize(); + + Vector3 yBasis = zBasis.Cross(xBasis); + // no need to normalize yBasis because xBasis and zBasis must already be orthogonal + + return Matrix4( + xBasis.x, + yBasis.x, + zBasis.x, + (T)0, + xBasis.y, + yBasis.y, + zBasis.y, + (T)0, + xBasis.z, + yBasis.z, + zBasis.z, + (T)0, + (T)0, + (T)0, + (T)0, + (T)1); + } + // MERGE_MOBILE_SDK + + // 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 Matrix4 PerspectiveRH(T yfov, T aspect, T znear, T zfar) { + Matrix4 m; + T tanHalfFov = static_cast(tan(yfov * T(0.5))); + + m.M[0][0] = T(1) / (aspect * tanHalfFov); + m.M[1][1] = T(1) / tanHalfFov; + m.M[2][2] = zfar / (znear - zfar); + m.M[3][2] = T(-1); + m.M[2][3] = (zfar * znear) / (znear - zfar); + m.M[3][3] = T(0); + + // 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 coordinate input. + return m; + } + + // PerspectiveLH 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 Matrix4 PerspectiveLH(T yfov, T aspect, T znear, T zfar) { + Matrix4 m; + T tanHalfFov = static_cast(tan(yfov * T(0.5))); + + m.M[0][0] = T(1) / (aspect * tanHalfFov); + m.M[1][1] = T(1) / tanHalfFov; + // m.M[2][2] = zfar / (znear - zfar); + m.M[2][2] = zfar / (zfar - znear); + m.M[3][2] = T(-1); + m.M[2][3] = (zfar * znear) / (znear - zfar); + m.M[3][3] = T(0); + + // 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 coordinate input. + return m; + } + + static Matrix4 Ortho2D(T w, T h) { + Matrix4 m; + m.M[0][0] = T(2.0) / w; + m.M[1][1] = T(-2.0) / h; + m.M[0][3] = T(-1.0); + m.M[1][3] = T(1.0); + m.M[2][2] = T(0); + return m; + } +}; + +// Implicit instantiation +template +const Matrix4 Matrix4::IdentityValue; + +typedef Matrix4 Matrix4f; +typedef Matrix4 Matrix4d; + +//------------------------------------------------------------------------------------- +// ***** Matrix3 +// +// Matrix3 is a 3x3 matrix used for representing a rotation matrix. +// 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. + +template +class Matrix3 { + static const Matrix3 IdentityValue; + + public: + typedef T ElementType; + static const size_t Dimension = 3; + + T M[3][3]; + + enum NoInitType { NoInit }; + + // Construct with no memory initialization. + Matrix3(NoInitType) {} + + // By default, we construct identity matrix. + Matrix3() { + M[0][0] = M[1][1] = M[2][2] = T(1); + M[0][1] = M[1][0] = M[2][0] = T(0); + M[0][2] = M[1][2] = M[2][1] = T(0); + } + + Matrix3(const Matrix3& b) = default; + + Matrix3(T m11, T m12, T m13, T m21, T m22, T m23, T m31, T m32, T m33) { + M[0][0] = m11; + M[0][1] = m12; + M[0][2] = m13; + M[1][0] = m21; + M[1][1] = m22; + M[1][2] = m23; + M[2][0] = m31; + M[2][1] = m32; + M[2][2] = m33; + } + + // Construction from X, Y, Z basis vectors + Matrix3(const Vector3& xBasis, const Vector3& yBasis, const Vector3& zBasis) { + M[0][0] = xBasis.x; + M[0][1] = yBasis.x; + M[0][2] = zBasis.x; + M[1][0] = xBasis.y; + M[1][1] = yBasis.y; + M[1][2] = zBasis.y; + M[2][0] = xBasis.z; + M[2][1] = yBasis.z; + M[2][2] = zBasis.z; + } + + explicit Matrix3(const Quat& q) { + OVR_MATH_ASSERT(q.IsNormalized()); + const T tx = q.x + q.x, ty = q.y + q.y, tz = q.z + q.z; + const T twx = q.w * tx, twy = q.w * ty, twz = q.w * tz; + const T txx = q.x * tx, txy = q.x * ty, txz = q.x * tz; + const T tyy = q.y * ty, tyz = q.y * tz, tzz = q.z * tz; + M[0][0] = T(1) - (tyy + tzz); + M[0][1] = txy - twz; + M[0][2] = txz + twy; + M[1][0] = txy + twz; + M[1][1] = T(1) - (txx + tzz); + M[1][2] = tyz - twx; + M[2][0] = txz - twy; + M[2][1] = tyz + twx; + M[2][2] = T(1) - (txx + tyy); + } + + inline explicit Matrix3(T s) { + M[0][0] = M[1][1] = M[2][2] = s; + M[0][1] = M[0][2] = M[1][0] = M[1][2] = M[2][0] = M[2][1] = T(0); + } + + Matrix3(T m11, T m22, T m33) { + M[0][0] = m11; + M[0][1] = T(0); + M[0][2] = T(0); + M[1][0] = T(0); + M[1][1] = m22; + M[1][2] = T(0); + M[2][0] = T(0); + M[2][1] = T(0); + M[2][2] = m33; + } + + explicit Matrix3(const Matrix3::OtherFloatType>& src) { + for (int i = 0; i < 3; i++) + for (int j = 0; j < 3; j++) + M[i][j] = (T)src.M[i][j]; + } + + // C-interop support. + Matrix3(const typename CompatibleTypes>::Type& s) { + OVR_MATH_STATIC_ASSERT(sizeof(s) == sizeof(Matrix3), "sizeof(s) == sizeof(Matrix3)"); + memcpy(M, s.M, sizeof(M)); + } + + explicit Matrix3(const Matrix4& m) { + M[0][0] = m.M[0][0]; + M[0][1] = m.M[0][1]; + M[0][2] = m.M[0][2]; + M[1][0] = m.M[1][0]; + M[1][1] = m.M[1][1]; + M[1][2] = m.M[1][2]; + M[2][0] = m.M[2][0]; + M[2][1] = m.M[2][1]; + M[2][2] = m.M[2][2]; + } + + operator const typename CompatibleTypes>::Type() const { + typename CompatibleTypes>::Type result; + OVR_MATH_STATIC_ASSERT( + sizeof(result) == sizeof(Matrix3), "sizeof(result) == sizeof(Matrix3)"); + memcpy(result.M, M, sizeof(M)); + return result; + } + + T operator()(int i, int j) const { + return M[i][j]; + } + T& operator()(int i, int j) { + return M[i][j]; + } + + void ToString(char* dest, size_t destsize) const { + size_t pos = 0; + for (int r = 0; r < 3; r++) { + for (int c = 0; c < 3; c++) + pos += OVRMath_sprintf(dest + pos, destsize - pos, "%g ", M[r][c]); + } + } + + static Matrix3 FromString(const char* src) { + Matrix3 result; + if (src) { + for (int r = 0; r < 3; r++) { + for (int c = 0; c < 3; c++) { + result.M[r][c] = (T)atof(src); + while (*src && *src != ' ') + src++; + while (*src && *src == ' ') + src++; + } + } + } + return result; + } + + static const Matrix3& Identity() { + return IdentityValue; + } + + void SetIdentity() { + M[0][0] = M[1][1] = M[2][2] = T(1); + M[0][1] = M[1][0] = M[2][0] = T(0); + M[0][2] = M[1][2] = M[2][1] = T(0); + } + + static Matrix3 Diagonal(T m00, T m11, T m22) { + return Matrix3(m00, 0, 0, 0, m11, 0, 0, 0, m22); + } + static Matrix3 Diagonal(const Vector3& v) { + return Diagonal(v.x, v.y, v.z); + } + + T Trace() const { + return M[0][0] + M[1][1] + M[2][2]; + } + + bool operator==(const Matrix3& b) const { + bool isEqual = true; + for (int i = 0; i < 3; i++) { + for (int j = 0; j < 3; j++) + isEqual &= (M[i][j] == b.M[i][j]); + } + + return isEqual; + } + + Matrix3 operator+(const Matrix3& b) const { + Matrix3 result(*this); + result += b; + return result; + } + + Matrix3& operator+=(const Matrix3& b) { + for (int i = 0; i < 3; i++) + for (int j = 0; j < 3; j++) + M[i][j] += b.M[i][j]; + return *this; + } + + void operator=(const Matrix3& b) { + for (int i = 0; i < 3; i++) { + for (int j = 0; j < 3; j++) { + M[i][j] = b.M[i][j]; + } + } + } + + Matrix3 operator-(const Matrix3& b) const { + Matrix3 result(*this); + result -= b; + return result; + } + + Matrix3& operator-=(const Matrix3& b) { + for (int i = 0; i < 3; i++) { + for (int j = 0; j < 3; j++) + M[i][j] -= b.M[i][j]; + } + + return *this; + } + + // Multiplies two matrices into destination with minimum copying. + static Matrix3& Multiply(Matrix3* d, const Matrix3& a, const Matrix3& b) { + OVR_MATH_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]; + 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]; + 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]; + } while ((++i) < 3); + + return *d; + } + + Matrix3 operator*(const Matrix3& b) const { + Matrix3 result(Matrix3::NoInit); + Multiply(&result, *this, b); + return result; + } + + Matrix3& operator*=(const Matrix3& b) { + return Multiply(this, Matrix3(*this), b); + } + + Matrix3 operator*(T s) const { + Matrix3 result(*this); + result *= s; + return result; + } + + Matrix3& operator*=(T s) { + for (int i = 0; i < 3; i++) { + for (int j = 0; j < 3; j++) + M[i][j] *= s; + } + + return *this; + } + + Vector3 operator*(const Vector3& b) const { + Vector3 result; + result.x = M[0][0] * b.x + M[0][1] * b.y + M[0][2] * b.z; + result.y = M[1][0] * b.x + M[1][1] * b.y + M[1][2] * b.z; + result.z = M[2][0] * b.x + M[2][1] * b.y + M[2][2] * b.z; + + return result; + } + + Matrix3 operator/(T s) const { + Matrix3 result(*this); + result /= s; + return result; + } + + Matrix3& operator/=(T s) { + for (int i = 0; i < 3; i++) { + for (int j = 0; j < 3; j++) + M[i][j] /= s; + } + + return *this; + } + + Vector2 Transform(const Vector2& v) const { + const T rcpZ = T(1) / (M[2][0] * v.x + M[2][1] * v.y + M[2][2]); + return Vector2( + (M[0][0] * v.x + M[0][1] * v.y + M[0][2]) * rcpZ, + (M[1][0] * v.x + M[1][1] * v.y + M[1][2]) * rcpZ); + } + + Vector3 Transform(const Vector3& v) const { + return Vector3( + M[0][0] * v.x + M[0][1] * v.y + M[0][2] * v.z, + M[1][0] * v.x + M[1][1] * v.y + M[1][2] * v.z, + M[2][0] * v.x + M[2][1] * v.y + M[2][2] * v.z); + } + + Matrix3 Transposed() const { + return Matrix3( + M[0][0], M[1][0], M[2][0], M[0][1], M[1][1], M[2][1], M[0][2], M[1][2], M[2][2]); + } + + void Transpose() { + *this = Transposed(); + } + + T SubDet(const size_t* rows, const size_t* 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]]); + } + + // M += a*b.t() + inline void Rank1Add(const Vector3& a, const Vector3& b) { + M[0][0] += a.x * b.x; + M[0][1] += a.x * b.y; + M[0][2] += a.x * b.z; + M[1][0] += a.y * b.x; + M[1][1] += a.y * b.y; + M[1][2] += a.y * b.z; + M[2][0] += a.z * b.x; + M[2][1] += a.z * b.y; + M[2][2] += a.z * b.z; + } + + // M -= a*b.t() + inline void Rank1Sub(const Vector3& a, const Vector3& b) { + M[0][0] -= a.x * b.x; + M[0][1] -= a.x * b.y; + M[0][2] -= a.x * b.z; + M[1][0] -= a.y * b.x; + M[1][1] -= a.y * b.y; + M[1][2] -= a.y * b.z; + M[2][0] -= a.z * b.x; + M[2][1] -= a.z * b.y; + M[2][2] -= a.z * b.z; + } + + inline Vector3 Col(int c) const { + return Vector3(M[0][c], M[1][c], M[2][c]); + } + + inline Vector3 Row(int r) const { + return Vector3(M[r][0], M[r][1], M[r][2]); + } + + inline Vector3 GetColumn(int c) const { + return Vector3(M[0][c], M[1][c], M[2][c]); + } + + inline Vector3 GetRow(int r) const { + return Vector3(M[r][0], M[r][1], M[r][2]); + } + + inline void SetColumn(int c, const Vector3& v) { + M[0][c] = v.x; + M[1][c] = v.y; + M[2][c] = v.z; + } + + inline void SetRow(int r, const Vector3& v) { + M[r][0] = v.x; + M[r][1] = v.y; + M[r][2] = v.z; + } + + inline T Determinant() const { + const Matrix3& m = *this; + T d; + + d = m.M[0][0] * (m.M[1][1] * m.M[2][2] - m.M[1][2] * m.M[2][1]); + d -= m.M[0][1] * (m.M[1][0] * m.M[2][2] - m.M[1][2] * m.M[2][0]); + d += m.M[0][2] * (m.M[1][0] * m.M[2][1] - m.M[1][1] * m.M[2][0]); + + return d; + } + + inline Matrix3 Inverse() const { + Matrix3 a; + const Matrix3& m = *this; + T d = Determinant(); + + OVR_MATH_ASSERT(fabs(d) >= Math::SmallestNonDenormal()); + T s = T(1) / d; + + a.M[0][0] = s * (m.M[1][1] * m.M[2][2] - m.M[1][2] * m.M[2][1]); + a.M[1][0] = s * (m.M[1][2] * m.M[2][0] - m.M[1][0] * m.M[2][2]); + a.M[2][0] = s * (m.M[1][0] * m.M[2][1] - m.M[1][1] * m.M[2][0]); + + a.M[0][1] = s * (m.M[0][2] * m.M[2][1] - m.M[0][1] * m.M[2][2]); + a.M[1][1] = s * (m.M[0][0] * m.M[2][2] - m.M[0][2] * m.M[2][0]); + a.M[2][1] = s * (m.M[0][1] * m.M[2][0] - m.M[0][0] * m.M[2][1]); + + a.M[0][2] = s * (m.M[0][1] * m.M[1][2] - m.M[0][2] * m.M[1][1]); + a.M[1][2] = s * (m.M[0][2] * m.M[1][0] - m.M[0][0] * m.M[1][2]); + a.M[2][2] = s * (m.M[0][0] * m.M[1][1] - m.M[0][1] * m.M[1][0]); + + return a; + } + + // Outer Product of two column vectors: a * b.Transpose() + static Matrix3 OuterProduct(const Vector3& a, const Vector3& b) { + return Matrix3( + a.x * b.x, + a.x * b.y, + a.x * b.z, + a.y * b.x, + a.y * b.y, + a.y * b.z, + a.z * b.x, + a.z * b.y, + a.z * b.z); + } + + // Vector cross product as a premultiply matrix: + // L.Cross(R) = LeftCrossAsMatrix(L) * R + static Matrix3 LeftCrossAsMatrix(const Vector3& L) { + return Matrix3(T(0), -L.z, +L.y, +L.z, T(0), -L.x, -L.y, +L.x, T(0)); + } + + // Vector cross product as a premultiply matrix: + // L.Cross(R) = RightCrossAsMatrix(R) * L + static Matrix3 RightCrossAsMatrix(const Vector3& R) { + return Matrix3(T(0), +R.z, -R.y, -R.z, T(0), +R.x, +R.y, -R.x, T(0)); + } + + // Angle in radians of a rotation matrix + // Uses identity trace(a) = 2*cos(theta) + 1 + T Angle() const { + return Acos((Trace() - T(1)) * T(0.5)); + } + + // Angle in radians between two rotation matrices + T Angle(const Matrix3& b) const { + // Compute trace of (this->Transposed() * b) + // This works out to sum of products of elements. + T trace = T(0); + for (int i = 0; i < 3; i++) { + for (int j = 0; j < 3; j++) { + trace += M[i][j] * b.M[i][j]; + } + } + return Acos((trace - T(1)) * T(0.5)); + } + + // Return inverse the left-top corner 3x3 matrix from a 4x4 matrix. + static Matrix3 Invert3x3(const Matrix4& mat4) { + Matrix3 local3x3 = Matrix3( + mat4.M[0][0], + mat4.M[0][1], + mat4.M[0][2], + mat4.M[1][0], + mat4.M[1][1], + mat4.M[1][2], + mat4.M[2][0], + mat4.M[2][1], + mat4.M[2][2]); + local3x3 = local3x3.Inverse(); + return local3x3; + } +}; + +// Implicit instantiation +template +const Matrix3 Matrix3::IdentityValue; + +typedef Matrix3 Matrix3f; +typedef Matrix3 Matrix3d; + +//------------------------------------------------------------------------------------- +// ***** Matrix2 + +template +class Matrix2 { + public: + typedef T ElementType; + static const size_t Dimension = 2; + + T M[2][2]; + + enum NoInitType { NoInit }; + + // Construct with no memory initialization. + Matrix2(NoInitType) {} + + // By default, we construct identity matrix. + Matrix2() { + M[0][0] = M[1][1] = T(1); + M[0][1] = M[1][0] = T(0); + } + + Matrix2(T m11, T m12, T m21, T m22) { + M[0][0] = m11; + M[0][1] = m12; + M[1][0] = m21; + M[1][1] = m22; + } + + // Construction from X, Y basis vectors + Matrix2(const Vector2& xBasis, const Vector2& yBasis) { + M[0][0] = xBasis.x; + M[0][1] = yBasis.x; + M[1][0] = xBasis.y; + M[1][1] = yBasis.y; + } + + explicit Matrix2(T s) { + M[0][0] = M[1][1] = s; + M[0][1] = M[1][0] = T(0); + } + + Matrix2(T m11, T m22) { + M[0][0] = m11; + M[0][1] = T(0); + M[1][0] = T(0); + M[1][1] = m22; + } + + explicit Matrix2(const Matrix2::OtherFloatType>& src) { + M[0][0] = T(src.M[0][0]); + M[0][1] = T(src.M[0][1]); + M[1][0] = T(src.M[1][0]); + M[1][1] = T(src.M[1][1]); + } + + Matrix2(const Matrix2& other) = default; + + // C-interop support + Matrix2(const typename CompatibleTypes>::Type& s) { + OVR_MATH_STATIC_ASSERT(sizeof(s) == sizeof(Matrix2), "sizeof(s) == sizeof(Matrix2)"); + memcpy(M, s.M, sizeof(M)); + } + + operator const typename CompatibleTypes>::Type() const { + typename CompatibleTypes>::Type result; + OVR_MATH_STATIC_ASSERT( + sizeof(result) == sizeof(Matrix2), "sizeof(result) == sizeof(Matrix2)"); + memcpy(result.M, M, sizeof(M)); + return result; + } + + T operator()(int i, int j) const { + return M[i][j]; + } + T& operator()(int i, int j) { + return M[i][j]; + } + const T* operator[](int i) const { + return M[i]; + } + T* operator[](int i) { + return M[i]; + } + + static Matrix2 Identity() { + return Matrix2(); + } + + void SetIdentity() { + M[0][0] = M[1][1] = T(1); + M[0][1] = M[1][0] = T(0); + } + + static Matrix2 Diagonal(T m00, T m11) { + return Matrix2(m00, m11); + } + static Matrix2 Diagonal(const Vector2& v) { + return Matrix2(v.x, v.y); + } + + T Trace() const { + return M[0][0] + M[1][1]; + } + + bool operator==(const Matrix2& b) const { + return M[0][0] == b.M[0][0] && M[0][1] == b.M[0][1] && M[1][0] == b.M[1][0] && + M[1][1] == b.M[1][1]; + } + + Matrix2 operator+(const Matrix2& b) const { + return Matrix2( + M[0][0] + b.M[0][0], M[0][1] + b.M[0][1], M[1][0] + b.M[1][0], M[1][1] + b.M[1][1]); + } + + Matrix2& operator+=(const Matrix2& b) { + M[0][0] += b.M[0][0]; + M[0][1] += b.M[0][1]; + M[1][0] += b.M[1][0]; + M[1][1] += b.M[1][1]; + return *this; + } + + void operator=(const Matrix2& b) { + M[0][0] = b.M[0][0]; + M[0][1] = b.M[0][1]; + M[1][0] = b.M[1][0]; + M[1][1] = b.M[1][1]; + } + + Matrix2 operator-(const Matrix2& b) const { + return Matrix2( + M[0][0] - b.M[0][0], M[0][1] - b.M[0][1], M[1][0] - b.M[1][0], M[1][1] - b.M[1][1]); + } + + Matrix2& operator-=(const Matrix2& b) { + M[0][0] -= b.M[0][0]; + M[0][1] -= b.M[0][1]; + M[1][0] -= b.M[1][0]; + M[1][1] -= b.M[1][1]; + return *this; + } + + Matrix2 operator*(const Matrix2& b) const { + return Matrix2( + M[0][0] * b.M[0][0] + M[0][1] * b.M[1][0], + M[0][0] * b.M[0][1] + M[0][1] * b.M[1][1], + M[1][0] * b.M[0][0] + M[1][1] * b.M[1][0], + M[1][0] * b.M[0][1] + M[1][1] * b.M[1][1]); + } + + Matrix2& operator*=(const Matrix2& b) { + *this = *this * b; + return *this; + } + + Matrix2 operator*(T s) const { + return Matrix2(M[0][0] * s, M[0][1] * s, M[1][0] * s, M[1][1] * s); + } + + Matrix2& operator*=(T s) { + M[0][0] *= s; + M[0][1] *= s; + M[1][0] *= s; + M[1][1] *= s; + return *this; + } + + Matrix2 operator/(T s) const { + return *this * (T(1) / s); + } + + Matrix2& operator/=(T s) { + return *this *= (T(1) / s); + } + + Vector2 operator*(const Vector2& b) const { + return Vector2(M[0][0] * b.x + M[0][1] * b.y, M[1][0] * b.x + M[1][1] * b.y); + } + + Vector2 Transform(const Vector2& v) const { + return Vector2(M[0][0] * v.x + M[0][1] * v.y, M[1][0] * v.x + M[1][1] * v.y); + } + + Matrix2 Transposed() const { + return Matrix2(M[0][0], M[1][0], M[0][1], M[1][1]); + } + + void Transpose() { + OVRMath_Swap(M[1][0], M[0][1]); + } + + Vector2 GetColumn(int c) const { + return Vector2(M[0][c], M[1][c]); + } + + Vector2 GetRow(int r) const { + return Vector2(M[r][0], M[r][1]); + } + + void SetColumn(int c, const Vector2& v) { + M[0][c] = v.x; + M[1][c] = v.y; + } + + void SetRow(int r, const Vector2& v) { + M[r][0] = v.x; + M[r][1] = v.y; + } + + T Determinant() const { + return M[0][0] * M[1][1] - M[0][1] * M[1][0]; + } + + Matrix2 Inverse() const { + T rcpDet = T(1) / Determinant(); + return Matrix2(M[1][1] * rcpDet, -M[0][1] * rcpDet, -M[1][0] * rcpDet, M[0][0] * rcpDet); + } + + // Outer Product of two column vectors: a * b.Transpose() + static Matrix2 OuterProduct(const Vector2& a, const Vector2& b) { + return Matrix2(a.x * b.x, a.x * b.y, a.y * b.x, a.y * b.y); + } + + // Angle in radians between two rotation matrices + T Angle(const Matrix2& b) const { + const Matrix2& a = *this; + return Acos(a(0, 0) * b(0, 0) + a(1, 0) * b(1, 0)); + } +}; + +typedef Matrix2 Matrix2f; +typedef Matrix2 Matrix2d; + +//------------------------------------------------------------------------------------- + +template +class SymMat3 { + private: + typedef SymMat3 this_type; + + public: + typedef T Value_t; + // Upper symmetric + T v[6]; // _00 _01 _02 _11 _12 _22 + + inline SymMat3() {} + + inline explicit SymMat3(T s) { + v[0] = v[3] = v[5] = s; + v[1] = v[2] = v[4] = T(0); + } + + inline explicit SymMat3(T a00, T a01, T a02, T a11, T a12, T a22) { + v[0] = a00; + v[1] = a01; + v[2] = a02; + v[3] = a11; + v[4] = a12; + v[5] = a22; + } + + // Cast to symmetric Matrix3 + operator Matrix3() const { + return Matrix3(v[0], v[1], v[2], v[1], v[3], v[4], v[2], v[4], v[5]); + } + + static inline int Index(unsigned int i, unsigned int j) { + return static_cast( + (i <= j) ? (3 * i - i * (i + 1) / 2 + j) : (3 * j - j * (j + 1) / 2 + i)); + } + + inline T operator()(int i, int j) const { + return v[Index(i, j)]; + } + + inline T& operator()(int i, int j) { + return v[Index(i, j)]; + } + + inline this_type& operator+=(const this_type& b) { + v[0] += b.v[0]; + v[1] += b.v[1]; + v[2] += b.v[2]; + v[3] += b.v[3]; + v[4] += b.v[4]; + v[5] += b.v[5]; + return *this; + } + + inline this_type& operator-=(const this_type& b) { + v[0] -= b.v[0]; + v[1] -= b.v[1]; + v[2] -= b.v[2]; + v[3] -= b.v[3]; + v[4] -= b.v[4]; + v[5] -= b.v[5]; + + return *this; + } + + inline this_type& operator*=(T s) { + v[0] *= s; + v[1] *= s; + v[2] *= s; + v[3] *= s; + v[4] *= s; + v[5] *= s; + + return *this; + } + + inline SymMat3 operator*(T s) const { + SymMat3 d; + d.v[0] = v[0] * s; + d.v[1] = v[1] * s; + d.v[2] = v[2] * s; + d.v[3] = v[3] * s; + d.v[4] = v[4] * s; + d.v[5] = v[5] * s; + + return d; + } + + // Multiplies two matrices into destination with minimum copying. + static SymMat3& Multiply(SymMat3* d, const SymMat3& a, const SymMat3& b) { + // _00 _01 _02 _11 _12 _22 + + d->v[0] = a.v[0] * b.v[0]; + d->v[1] = a.v[0] * b.v[1] + a.v[1] * b.v[3]; + d->v[2] = a.v[0] * b.v[2] + a.v[1] * b.v[4]; + + d->v[3] = a.v[3] * b.v[3]; + d->v[4] = a.v[3] * b.v[4] + a.v[4] * b.v[5]; + + d->v[5] = a.v[5] * b.v[5]; + + return *d; + } + + inline T Determinant() const { + const this_type& m = *this; + T d; + + d = m(0, 0) * (m(1, 1) * m(2, 2) - m(1, 2) * m(2, 1)); + d -= m(0, 1) * (m(1, 0) * m(2, 2) - m(1, 2) * m(2, 0)); + d += m(0, 2) * (m(1, 0) * m(2, 1) - m(1, 1) * m(2, 0)); + + return d; + } + + inline this_type Inverse() const { + this_type a; + const this_type& m = *this; + T d = Determinant(); + + OVR_MATH_ASSERT(fabs(d) >= Math::SmallestNonDenormal()); + T s = T(1) / d; + + a(0, 0) = s * (m(1, 1) * m(2, 2) - m(1, 2) * m(2, 1)); + + a(0, 1) = s * (m(0, 2) * m(2, 1) - m(0, 1) * m(2, 2)); + a(1, 1) = s * (m(0, 0) * m(2, 2) - m(0, 2) * m(2, 0)); + + a(0, 2) = s * (m(0, 1) * m(1, 2) - m(0, 2) * m(1, 1)); + a(1, 2) = s * (m(0, 2) * m(1, 0) - m(0, 0) * m(1, 2)); + a(2, 2) = s * (m(0, 0) * m(1, 1) - m(0, 1) * m(1, 0)); + + return a; + } + + inline T Trace() const { + return v[0] + v[3] + v[5]; + } + + // M = a*a.t() + inline void Rank1(const Vector3& a) { + v[0] = a.x * a.x; + v[1] = a.x * a.y; + v[2] = a.x * a.z; + v[3] = a.y * a.y; + v[4] = a.y * a.z; + v[5] = a.z * a.z; + } + + // M += a*a.t() + inline void Rank1Add(const Vector3& a) { + v[0] += a.x * a.x; + v[1] += a.x * a.y; + v[2] += a.x * a.z; + v[3] += a.y * a.y; + v[4] += a.y * a.z; + v[5] += a.z * a.z; + } + + // M -= a*a.t() + inline void Rank1Sub(const Vector3& a) { + v[0] -= a.x * a.x; + v[1] -= a.x * a.y; + v[2] -= a.x * a.z; + v[3] -= a.y * a.y; + v[4] -= a.y * a.z; + v[5] -= a.z * a.z; + } +}; + +typedef SymMat3 SymMat3f; +typedef SymMat3 SymMat3d; + +template +inline Matrix3 operator*(const SymMat3& a, const SymMat3& b) { +#define AJB_ARBC(r, c) (a(r, 0) * b(0, c) + a(r, 1) * b(1, c) + a(r, 2) * b(2, c)) + return Matrix3( + AJB_ARBC(0, 0), + AJB_ARBC(0, 1), + AJB_ARBC(0, 2), + AJB_ARBC(1, 0), + AJB_ARBC(1, 1), + AJB_ARBC(1, 2), + AJB_ARBC(2, 0), + AJB_ARBC(2, 1), + AJB_ARBC(2, 2)); +#undef AJB_ARBC +} + +template +inline Matrix3 operator*(const Matrix3& a, const SymMat3& b) { +#define AJB_ARBC(r, c) (a(r, 0) * b(0, c) + a(r, 1) * b(1, c) + a(r, 2) * b(2, c)) + return Matrix3( + AJB_ARBC(0, 0), + AJB_ARBC(0, 1), + AJB_ARBC(0, 2), + AJB_ARBC(1, 0), + AJB_ARBC(1, 1), + AJB_ARBC(1, 2), + AJB_ARBC(2, 0), + AJB_ARBC(2, 1), + AJB_ARBC(2, 2)); +#undef AJB_ARBC +} + +//------------------------------------------------------------------------------------- +// ***** Angle + +// Cleanly representing the algebra of 2D rotations. +// The operations maintain the angle between -Pi and Pi, the same range as atan2. + +template +class Angle { + public: + enum AngularUnits { Radians = 0, Degrees = 1 }; + + Angle() : a(0) {} + + // Fix the range to be between -Pi and Pi + Angle(T a_, AngularUnits u = Radians) + : a((u == Radians) ? a_ : a_ * ((T)MATH_DOUBLE_DEGREETORADFACTOR)) { + FixRange(); + } + + Angle(T adjacent, T opposite) : Angle(Atan2(opposite, adjacent)) {} + + static Angle FromOpposite(T opposite) { + return Angle(Asin(opposite)); + } + static Angle FromAdjacent(T adjacent) { + return Angle(Acos(adjacent)); + } + + T Get(AngularUnits u = Radians) const { + return (u == Radians) ? a : a * ((T)MATH_DOUBLE_RADTODEGREEFACTOR); + } + void Set(const T& x, AngularUnits u = Radians) { + a = (u == Radians) ? x : x * ((T)MATH_DOUBLE_DEGREETORADFACTOR); + FixRange(); + } + int Sign() const { + if (a == 0) + return 0; + else + return (a > 0) ? 1 : -1; + } + T Abs() const { + return (a >= 0) ? a : -a; + } + Angle AbsAngle() const { + Angle res; + res.a = Abs(); + return res; + } + + bool operator==(const Angle& b) const { + return a == b.a; + } + bool operator!=(const Angle& b) const { + return a != b.a; + } + // bool operator< (const Angle& b) const { return a < a.b; } + // bool operator> (const Angle& b) const { return a > a.b; } + // bool operator<= (const Angle& b) const { return a <= a.b; } + // bool operator>= (const Angle& b) const { return a >= a.b; } + // bool operator= (const T& x) { a = x; FixRange(); } + + // These operations assume a is already between -Pi and Pi. + Angle& operator+=(const Angle& b) { + a = a + b.a; + FastFixRange(); + return *this; + } + Angle& operator+=(const T& x) { + a = a + x; + FixRange(); + return *this; + } + Angle operator+(const Angle& b) const { + Angle res = *this; + res += b; + return res; + } + Angle operator+(const T& x) const { + Angle res = *this; + res += x; + return res; + } + Angle& operator-=(const Angle& b) { + a = a - b.a; + FastFixRange(); + return *this; + } + Angle& operator-=(const T& x) { + a = a - x; + FixRange(); + return *this; + } + Angle operator-(const Angle& b) const { + Angle res = *this; + res -= b; + return res; + } + Angle operator-(const T& x) const { + Angle res = *this; + res -= x; + return res; + } + Angle& operator*=(const T& x) { + a = a * x; + FixRange(); + return *this; + } + Angle operator*(const T& x) const { + Angle res = *this; + res *= x; + return res; + } + + T Distance(const Angle& b) const { + T c = fabs(a - b.a); + return (c <= ((T)MATH_DOUBLE_PI)) ? c : ((T)MATH_DOUBLE_TWOPI) - c; + } + + Angle Lerp(const Angle& b, T f) const { + return *this + (b - *this) * f; + } + + private: + // The stored angle, which should be maintained between -Pi and Pi + T a; + + // Fixes the angle range to [-Pi,Pi], but assumes no more than 2Pi away on either side + inline void FastFixRange() { + if (a < -((T)MATH_DOUBLE_PI)) + a += ((T)MATH_DOUBLE_TWOPI); + else if (a > ((T)MATH_DOUBLE_PI)) + a -= ((T)MATH_DOUBLE_TWOPI); + } + + // Fixes the angle range to [-Pi,Pi] for any given range, but slower then the fast method + inline void FixRange() { + // do nothing if the value is already in the correct range, since fmod call is expensive + if (a >= -((T)MATH_DOUBLE_PI) && a <= ((T)MATH_DOUBLE_PI)) + return; + a = static_cast(fmod(a, ((T)MATH_DOUBLE_TWOPI))); + if (a < -((T)MATH_DOUBLE_PI)) + a += ((T)MATH_DOUBLE_TWOPI); + else if (a > ((T)MATH_DOUBLE_PI)) + a -= ((T)MATH_DOUBLE_TWOPI); + } +}; + +typedef Angle Anglef; +typedef Angle Angled; + +//------------------------------------------------------------------------------------- +// ***** Plane + +// Consists of a normal vector and distance from the origin where the plane is located. + +template +class Plane { + 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.Dot(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.Dot(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; +typedef Plane Planed; + +//----------------------------------------------------------------------------------- +// ***** ScaleAndOffset2D + +struct ScaleAndOffset2D { + Vector2f Scale; + Vector2f Offset; + + ScaleAndOffset2D(float sx = 0.0f, float sy = 0.0f, float ox = 0.0f, float oy = 0.0f) + : Scale(sx, sy), Offset(ox, oy) {} +}; + +//----------------------------------------------------------------------------------- +// ***** FovPort + +// FovPort describes Field Of View (FOV) of a viewport. +// This class has values for up, down, left and right, stored in +// tangent of the angle units to simplify calculations. +// +// As an example, for a standard 90 degree vertical FOV, we would +// have: { UpTan = tan(90 degrees / 2), DownTan = tan(90 degrees / 2) }. +// +// CreateFromRadians/Degrees helper functions can be used to +// access FOV in different units. + +// ***** FovPort + +struct FovPort { + float UpTan; + float DownTan; + float LeftTan; + float RightTan; + + FovPort(float sideTan = 0.0f) + : UpTan(sideTan), DownTan(sideTan), LeftTan(sideTan), RightTan(sideTan) {} + FovPort(float u, float d, float l, float r) : UpTan(u), DownTan(d), LeftTan(l), RightTan(r) {} + +#if 0 + // C-interop support. + typedef CompatibleTypes::Type CompatibleType; + + FovPort(const CompatibleType& s) + : UpTan(s.UpTan) + , DownTan(s.DownTan) + , LeftTan(s.LeftTan) + , RightTan(s.RightTan) + { } + + operator const CompatibleType& () const + { + OVR_MATH_STATIC_ASSERT(sizeof(FovPort) == sizeof(CompatibleType), "sizeof(FovPort) failure"); + return reinterpret_cast(*this); + } +#endif + + static FovPort CreateFromRadians(float horizontalFov, float verticalFov) { + FovPort result; + result.UpTan = tanf(verticalFov * 0.5f); + result.DownTan = tanf(verticalFov * 0.5f); + result.LeftTan = tanf(horizontalFov * 0.5f); + result.RightTan = tanf(horizontalFov * 0.5f); + return result; + } + + static FovPort CreateFromDegrees(float horizontalFovDegrees, float verticalFovDegrees) { + return CreateFromRadians( + DegreeToRad(horizontalFovDegrees), DegreeToRad(verticalFovDegrees)); + } + + // Get Horizontal/Vertical components of Fov in radians. + float GetVerticalFovRadians() const { + return atanf(UpTan) + atanf(DownTan); + } + float GetHorizontalFovRadians() const { + return atanf(LeftTan) + atanf(RightTan); + } + // Get Horizontal/Vertical components of Fov in degrees. + float GetVerticalFovDegrees() const { + return RadToDegree(GetVerticalFovRadians()); + } + float GetHorizontalFovDegrees() const { + return RadToDegree(GetHorizontalFovRadians()); + } + + // Compute maximum tangent value among all four sides. + float GetMaxSideTan() const { + return OVRMath_Max(OVRMath_Max(UpTan, DownTan), OVRMath_Max(LeftTan, RightTan)); + } + + static ScaleAndOffset2D CreateNDCScaleAndOffsetFromFov(FovPort tanHalfFov) { + float projXScale = 2.0f / (tanHalfFov.LeftTan + tanHalfFov.RightTan); + float projXOffset = (tanHalfFov.LeftTan - tanHalfFov.RightTan) * projXScale * 0.5f; + float projYScale = 2.0f / (tanHalfFov.UpTan + tanHalfFov.DownTan); + float projYOffset = (tanHalfFov.UpTan - tanHalfFov.DownTan) * projYScale * 0.5f; + + ScaleAndOffset2D result; + result.Scale = Vector2f(projXScale, projYScale); + result.Offset = Vector2f(projXOffset, projYOffset); + // Hey - why is that Y.Offset negated? + // It's because a projection matrix transforms from world coords with Y=up, + // whereas this is from NDC which is Y=down. + + return result; + } + + // Converts Fov Tan angle units to [-1,1] render target NDC space + Vector2f TanAngleToRendertargetNDC(Vector2f const& tanEyeAngle) { + ScaleAndOffset2D eyeToSourceNDC = CreateNDCScaleAndOffsetFromFov(*this); + return tanEyeAngle * eyeToSourceNDC.Scale + eyeToSourceNDC.Offset; + } + + // Compute per-channel minimum and maximum of Fov. + static FovPort Min(const FovPort& a, const FovPort& b) { + FovPort fov( + OVRMath_Min(a.UpTan, b.UpTan), + OVRMath_Min(a.DownTan, b.DownTan), + OVRMath_Min(a.LeftTan, b.LeftTan), + OVRMath_Min(a.RightTan, b.RightTan)); + return fov; + } + + static FovPort Max(const FovPort& a, const FovPort& b) { + FovPort fov( + OVRMath_Max(a.UpTan, b.UpTan), + OVRMath_Max(a.DownTan, b.DownTan), + OVRMath_Max(a.LeftTan, b.LeftTan), + OVRMath_Max(a.RightTan, b.RightTan)); + return fov; + } +}; + +} // Namespace OVR + +namespace std { + template + struct hash> { + size_t operator()(const OVR::Vector2& v) const { + return hash()(v.x) ^ hash()(v.y); + } + }; +} + +#if defined(_MSC_VER) +#pragma warning(pop) +#endif + +#endif diff --git a/Samples/1stParty/OVR/Include/OVR_SanitizerMacros.h b/Samples/1stParty/OVR/Include/OVR_SanitizerMacros.h new file mode 100755 index 0000000..66751e9 --- /dev/null +++ b/Samples/1stParty/OVR/Include/OVR_SanitizerMacros.h @@ -0,0 +1,41 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +#pragma once + +#if defined(__has_feature) + +// ASAN +#if __has_feature(address_sanitizer) +#define OVR_USING_ADDRESS_SANITIZER +#endif // __has_feature(address_sanitizer) + +// TSAN +#if __has_feature(thread_sanitizer) +#define OVR_USING_THREAD_SANITIZER +#endif // __has_feature(thread_sanitizer) + +#endif // defined(__has_feature) + +// ASAN supression macro +#if defined(OVR_USING_ADDRESS_SANITIZER) + +// Refer to https://clang.llvm.org/docs/AddressSanitizer.html#issue-suppression +#define OVR_NO_SANITIZE_ADDRESS __attribute__((no_sanitize_address)) + +#else // defined(OVR_USING_ADDRESS_SANITIZER) + +#define OVR_NO_SANITIZE_ADDRESS + +#endif // defined(OVR_USING_ADDRESS_SANITIZER) + +// TSAN supression macro +#if defined(OVR_USING_THREAD_SANITIZER) + +// Refer to https://clang.llvm.org/docs/ThreadSanitizer.html +#define OVR_NO_SANITIZE_THREAD __attribute__((no_sanitize("thread"))) + +#else // defined(OVR_USING_THREAD_SANITIZER) + +#define OVR_NO_SANITIZE_THREAD + +#endif // defined(OVR_USING_THREAD_SANITIZER) diff --git a/Samples/1stParty/OVR/Include/OVR_Std.h b/Samples/1stParty/OVR/Include/OVR_Std.h new file mode 100755 index 0000000..9edcc65 --- /dev/null +++ b/Samples/1stParty/OVR/Include/OVR_Std.h @@ -0,0 +1,613 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * All rights reserved. + * + * Licensed under the Oculus SDK License Agreement (the "License"); + * you may not use the Oculus SDK except in compliance with the License, + * which is provided at the time of installation or download, or which + * otherwise accompanies this software in either electronic or hard copy form. + * + * You may obtain a copy of the License at + * + * https://developer.oculus.com/licenses/oculussdk/ + * + * Unless required by applicable law or agreed to in writing, the Oculus SDK + * 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. + */ + +/************************************************************************************ + * Filename : OVR_Std.h + * Content : Standard C function interface + * Created : September 19, 2012 + * Notes : + ***********************************************************************************/ + +#ifndef OVR_Std_h +#define OVR_Std_h + +#include // for va_list args +#include +#include +#include + +#include "OVR_Compiler.h" + +#if !defined(OVR_OS_WINCE) && defined(OVR_CC_MSVC) && (OVR_CC_MSVC >= 1400) +#define OVR_MSVC_SAFESTRING +#include +#endif + +// Wide-char funcs +#include +// Only include wctype.h if preprocessor configuration indicates to rely upon +// standard library implementation for wide character type support. +#ifdef OVR_NO_WCTYPE +#include +#endif + +#include "OVR_Asserts.h" +#include "OVR_Types.h" + +namespace OVR { + +// Has the same behavior as itoa aside from also having a dest size argument. +// Return value: Pointer to the resulting null-terminated string, same as parameter str. +#if defined(OVR_OS_WIN32) +inline char* OVR_CDECL OVR_itoa(int val, char* dest, size_t 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, size_t len, int radix) { + if (val == 0) { + if (len > 1) { + dest[0] = '0'; + dest[1] = '\0'; + } + return dest; + } + + // FIXME: Fix the following code to avoid memory write overruns when len is in sufficient. + int cur = val; + size_t i = 0; + size_t 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 (size_t 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 size_t OVR_CDECL OVR_strlen(const char* str) { + return strlen(str); +} + +inline char* OVR_CDECL OVR_strcpy(char* dest, size_t destsize, const char* src) { +#if defined(OVR_MSVC_SAFESTRING) + strcpy_s(dest, destsize, src); + return dest; +#elif defined(OVR_OS_ANDROID) + strlcpy(dest, src, destsize); + return dest; +#else + // FIXME: This should be a safer implementation + OVR_UNUSED(destsize); + return strcpy(dest, src); +#endif +} + +inline char* OVR_CDECL OVR_strncpy(char* dest, size_t destsize, const char* src, size_t count) { + // handle an invalid destination the same way for all cases + if (destsize == 0 || dest == NULL || src == NULL) { + OVR_ASSERT(dest != NULL && destsize > 0 && src != NULL); + return dest; + } + if (dest == src) { + return dest; + } + // special case if count is 0, just set the first character to null byte + if (count == 0) { + dest[0] = '\0'; + return dest; + } + + if (count >= destsize) { + // if count is larger than or equal to the destination buffer size, completely fill the + // destination buffer and put the zero byte at the end +#if defined(OVR_MSVC_SAFESTRING) + strncpy_s(dest, destsize, src, destsize); +#else + strncpy(dest, src, destsize); +#endif + dest[destsize - 1] = '\0'; + } else { + // otherwise place it right after the count +#if defined(OVR_MSVC_SAFESTRING) + strncpy_s(dest, destsize, src, count); +#else + strncpy(dest, src, count); +#endif + dest[count] = '\0'; + } + return dest; +} + +inline char* OVR_CDECL OVR_strcat(char* dest, size_t destsize, const char* src) { +#if defined(OVR_MSVC_SAFESTRING) + strcat_s(dest, destsize, src); + return dest; +#elif defined(OVR_OS_ANDROID) + strlcat(dest, src, destsize); + return dest; +#else + // FIXME: This should be a safer implementation + 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) { + size_t len = OVR_strlen(str); + for (size_t i = len; i > 0; i--) + if (str[i] == c) + return str + i; + return 0; +} + +inline const uint8_t* OVR_CDECL OVR_memrchr(const uint8_t* str, size_t size, uint8_t c) { + for (intptr_t i = (intptr_t)size - 1; i >= 0; i--) { + if (str[i] == c) + return str + i; + } + return 0; +} + +inline char* OVR_CDECL OVR_strrchr(char* str, char c) { + size_t len = OVR_strlen(str); + for (size_t 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 unsigned 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, size_t size) { + return strncmp(ws1, ws2, size); +} + +inline uint64_t 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 int64_t 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 int64_t 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_t OVR_CDECL OVR_atouq(const char* string) { + return OVR_strtouq(string, NULL, 10); +} + +// Case insensitive compare implemented in platform-specific way. +inline 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 +} + +inline int OVR_CDECL OVR_strnicmp(const char* a, const char* b, size_t 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 +} + +// OVR_strdup +// Safe for Windows without turning off deprecation warnings. MSVC suggests _strdup +// but that function only exists in MSVC. +// Always 0-terminates. +// Returns an empty string if NULL is passed -- for normal strdup that is undefined behavior. +inline char* OVR_CDECL OVR_strdup(const char* str) { + if (str == NULL) { + // for POSIX strdup, str == NULL is undefined. + // assert, and just allocate a small buffer and make it empty-string + OVR_ASSERT(str != NULL); + char* result = static_cast(malloc(8)); + if (result == NULL) { + return NULL; + } + result[0] = '\0'; + return result; + } + + const size_t size = OVR_strlen(str) + 1; + + char* result = static_cast(malloc(size)); + if (result == NULL) { + return NULL; + } + + // copy the input string -- OVR_strcpy always 0-terminates + OVR_strcpy(result, size, str); + + return result; +} + +inline int OVR_CDECL OVR_sprintf(char* dest, size_t destsize, const char* format, ...) { + if (destsize <= 0 || dest == NULL) { + OVR_ASSERT(destsize > 0); + return -1; + } + va_list argList; + va_start(argList, format); + int ret; +#if defined(OVR_CC_MSVC) +#if defined(OVR_MSVC_SAFESTRING) + ret = _vsnprintf_s(dest, destsize, _TRUNCATE, format, argList); +#else + // FIXME: this is a security issue on Windows platforms that don't have _vsnprintf_s + OVR_UNUSED(destsize); + ret = _vsnprintf(dest, destsize - 1, format, argList); // -1 for space for the null character + dest[destsize - 1] = 0; // may leave trash in the destination... +#endif +#else + ret = vsnprintf(dest, destsize, format, argList); + // In the event of the output string being greater than the buffer size, vsnprintf should + // return the size of the string before truncation. In that case we zero-terminate the + // string to ensure that the result is the same as _vsnprintf_s would return for the + // MSVC compiler. vsnprintf is supposed to zero-terminate in this case, but to be sure + // we zero-terminate it ourselves. + if (ret >= (int)destsize) { + dest[destsize - 1] = '\0'; + } +#endif + // If an error occurs, vsnprintf should return -1, in which case we set zero byte to null + // character. + OVR_ASSERT(ret >= 0); // ensure the format string is not malformed + if (ret < 0) { + dest[0] = '\0'; + } + va_end(argList); + return ret; +} + +// Returns the number of characters that were written to the destination buffer. If the formatted +// text fits in the destination buffer, then this value is < destsize. +// The size of the destination buffer is returned if either of the following is true: +// - a formatting error occurs +// - the formatted text does not fit in the destination buffer. +// This does not mirror the Windows vsnprintf functionality OR the Linux functionality. +// Normally in an overflow case, Linux will return the number of bytes that would have been written +// if the buffer were large enough to fit the formatted text. This allows a caller to reallocate a +// buffer of the necessary size and try again. Windows functions, however, do not return the length +// that would have been written to the buffer, so we have no simple way to make that Linux behavior +// portable to Windows. +// Thus, in the event of an overflow we simply return destsize to indicate an overflow. +// An alternative would be to return destsize - 1, but that would rule out returning any overflow +// indicator without changing the function signature. +inline size_t OVR_CDECL +OVR_vsprintf(char* dest, size_t destsize, const char* format, va_list argList) { + OVR_ASSERT(dest != NULL); + + // always just 0-terminate the dest + dest[0] = '\0'; + +#if defined(OVR_CC_MSVC) +#if defined(OVR_MSVC_SAFESTRING) + int bytesWritten = vsnprintf_s(dest, destsize, _TRUNCATE, format, argList); +#else + OVR_UNUSED(destsize); + // for vsnprintf, the second parameters is the maximum number of characters to write, meaning + // NOT the size of the buffer, but the size of the buffer minus 1 for the NULL character. + int bytesWritten = _vsnprintf(dest, destsize - 1, format, argList); +#endif +#else + // specifically for Android / Linux + int bytesWritten = vsnprintf(dest, destsize, format, argList); +#endif + // Windows: for vsnprintf and vsnprintf_s with _TRUNCATE, a return of -1 means the buffer was + // overflowed Android / Linux: -1 is only returned if a format error occured, bytesWritten == + // destsize means buffer overflow + if (bytesWritten < 0 || static_cast(bytesWritten) >= destsize) { + OVR_ASSERT(bytesWritten >= 0); + // zero terminate at the very end + dest[destsize - 1] = '\0'; + // return the size of the buffer to indecate an overflow + return static_cast(destsize); + } else { + OVR_ASSERT(bytesWritten >= 0 && static_cast(bytesWritten) < destsize); + // ensure zero-terminator after the written bytes + dest[bytesWritten] = '\0'; + return static_cast(bytesWritten); + } +} + +// Returns the strlen of the resulting formatted string, or a negative value if the format is +// invalid. Note: If you are planning on printing a string then it's more efficient to just use +// OVR_vsnprintf and look at the return value and handle the uncommon case that there wasn't enough +// space. +inline int OVR_CDECL OVR_vscprintf(const char* format, va_list argList) { + int ret; +#if defined(OVR_CC_MSVC) + ret = _vscprintf(format, argList); +#else + ret = vsnprintf(NULL, 0, format, argList); +#endif + return ret; +} + +wchar_t* OVR_CDECL OVR_wcscpy(wchar_t* dest, size_t destsize, const wchar_t* src); +wchar_t* OVR_CDECL OVR_wcsncpy(wchar_t* dest, size_t destsize, const wchar_t* src, size_t count); +wchar_t* OVR_CDECL OVR_wcscat(wchar_t* dest, size_t destsize, const wchar_t* src); +size_t 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_t* 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_t UnicodeAlnumBits[]; +extern const uint16_t UnicodeAlphaBits[]; +extern const uint16_t UnicodeDigitBits[]; +extern const uint16_t UnicodeSpaceBits[]; +extern const uint16_t UnicodeXDigitBits[]; + +// Uncomment if necessary +// extern const uint16_t UnicodeCntrlBits[]; +// extern const uint16_t UnicodeGraphBits[]; +// extern const uint16_t UnicodeLowerBits[]; +// extern const uint16_t UnicodePrintBits[]; +// extern const uint16_t UnicodePunctBits[]; +// extern const uint16_t 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); +} + +// clang-format off +// 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); } +// clang-format on + +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; + size_t max = OVR_wcslen(string); + if (max > 63) + max = 63; + unsigned char c = 0; + for (size_t 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; + size_t max = OVR_wcslen(string); + if (max > 63) + max = 63; + unsigned char c = 0; + for (size_t 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 +} + +} // namespace OVR + +#endif // OVR_Std_h diff --git a/Samples/1stParty/OVR/Include/OVR_Types.h b/Samples/1stParty/OVR/Include/OVR_Types.h new file mode 100755 index 0000000..d671ca4 --- /dev/null +++ b/Samples/1stParty/OVR/Include/OVR_Types.h @@ -0,0 +1,546 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * All rights reserved. + * + * Licensed under the Oculus SDK License Agreement (the "License"); + * you may not use the Oculus SDK except in compliance with the License, + * which is provided at the time of installation or download, or which + * otherwise accompanies this software in either electronic or hard copy form. + * + * You may obtain a copy of the License at + * + * https://developer.oculus.com/licenses/oculussdk/ + * + * Unless required by applicable law or agreed to in writing, the Oculus SDK + * 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. + */ + +/************************************************************************************ + * Filename : OVR_Types.h + * Content : Standard library defines and simple types + * Created : September 19, 2012 + * Notes : + ***********************************************************************************/ + +#ifndef OVR_Types_h +#define OVR_Types_h + +#include "OVR_Compiler.h" + +// Unsupported compiler configurations +#if (defined(_MSC_VER) && (_MSC_VER == 0x1600)) +#if _MSC_FULL_VER < 160040219 +#error "Oculus does not support VS2010 without SP1 installed: It will crash in Release mode" +#endif +#endif + +//----------------------------------------------------------------------------------- +// ****** Operating system identification +// +// 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__ + +#if defined(OVR_CPP11) +#define OVR_OVERRIDE override +#else +#define OVR_OVERRIDE +#endif + +//----------------------------------------------------------------------------------- +// ***** Compiler Warnings + +// Disable MSVC warnings +#if defined(OVR_CC_MSVC) +#pragma warning(disable : 4127) // Inconsistent dll linkage +#pragma warning(disable : 4530) // Exception handling +#pragma warning(disable : 4351) // new behavior: elements of array will be default initialized +#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 + +// MSVC Based Memory Leak checking - for now +#if defined(OVR_CC_MSVC) && defined(OVR_BUILD_DEBUG) +#define _CRTDBG_MAP_ALLOC +#include +#include +#endif + +//----------------------------------------------------------------------------------- +// ***** int8_t, int16_t, etc. + +#if defined(OVR_CC_MSVC) && (OVR_CC_VERSION <= 1500) // VS2008 and earlier +typedef signed char int8_t; +typedef unsigned char uint8_t; +typedef signed short int16_t; +typedef unsigned short uint16_t; +typedef signed int int32_t; +typedef unsigned int uint32_t; +typedef signed __int64 int64_t; +typedef unsigned __int64 uint64_t; +#else +#include +#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 + +// osx PID is a signed int32 (already defined to pid_t in OSX framework) +// linux PID is a signed int32 (already defined) +// win32 PID is an unsigned int64 +#ifdef OVR_OS_WIN32 +// process ID representation +typedef unsigned long pid_t; +#endif + +} // namespace OVR + +// MERGE_MOBILE_SDK +#if defined(OVR_OS_ANDROID) +#include +#elif !defined(OVR_NO_JNI) +#if defined(__cplusplus) +class _jobject {}; +class _jclass : public _jobject {}; +class _jstring : public _jobject {}; + +typedef struct _JNIEnv JNIEnv; +typedef struct _JavaVM JavaVM; +typedef struct _jmethodID* jmethodID; +typedef struct _jfieldID* jfieldID; +typedef _jobject* jobject; +typedef _jclass* jclass; +typedef _jstring* jstring; +typedef int64_t jlong; +typedef OVR::SInt32 jint; +typedef OVR::UByte jboolean; +typedef float jfloat; +#else +typedef const struct JNINativeInterface* JNIEnv; +typedef const struct JNIInvokeInterface* JavaVM; +typedef const struct _jmethodID* jmethodID; +typedef const struct _jfieldID* jfieldID; +typedef void* jobject; +typedef jobject jclass; +typedef jobject jstring; +typedef int64_t jlong; +typedef int32_t jint; +typedef unsigned char jboolean; +typedef float jfloat; +#endif +#endif +// MERGE_MOBILE_SDK + +//----------------------------------------------------------------------------------- +// ***** 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 + +#if defined(OVR_OS_WIN32) + +// ***** Win32 + +// _DEBUG is added by default to debug projects in Visual Studio, so auto-define OVR_BUILD_DEBUG if +// it's set. +#if defined(_DEBUG) && !defined(OVR_BUILD_DEBUG) +#define OVR_BUILD_DEBUG +#endif + // 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) + +// MERGE_MOBILE_SDK -- NOTE: This is defined in OVR_CAPI.h. +//----------------------------------------------------------------------------------- +// ***** OVR_CC_HAS_FEATURE +// +// This is a portable way to use compile-time feature identification available +// with some compilers in a clean way. Direct usage of __has_feature in preprocessing +// statements of non-supporting compilers results in a preprocessing error. +// +// Example usage: +// #if OVR_CC_HAS_FEATURE(is_pod) +// if(__is_pod(T)) // If the type is plain data then we can safely memcpy it. +// memcpy(&destObject, &srcObject, sizeof(object)); +// #endif +// +#if !defined(OVR_CC_HAS_FEATURE) +#if defined(__clang__) // http://clang.llvm.org/docs/LanguageExtensions.html#id2 +#define OVR_CC_HAS_FEATURE(x) __has_feature(x) +#else +#define OVR_CC_HAS_FEATURE(x) 0 +#endif +#endif +// MERGE_MOBILE_SDK + +// ------------------------------------------------------------------------ +// ***** OVR_FORCE_INLINE +// +// Force inline substitute - goes before function declaration +// Example usage: +// OVR_FORCE_INLINE void Test(); + +#if defined(OVR_CC_MSVC) +#define OVR_FORCE_INLINE __forceinline +#elif defined(OVR_CC_GNU) +#define OVR_FORCE_INLINE inline __attribute__((always_inline)) +#else +#define OVR_FORCE_INLINE inline +#endif // OVR_CC_MSVC + +// Include the assert header here to maintain compatibility with the PC version +// of this header which inlined these definitions here. +#include "OVR_Asserts.h" + +// ------------------------------------------------------------------------ +// ***** OVR_COMPILER_ASSERT +// +// Compile-time assert; produces compiler error if condition is false. +// The expression must be a compile-time constant expression. +// This macro is deprecated in favor of static_assert. +// +// Example usage: +// OVR_COMPILER_ASSERT(sizeof(int32_t) == 4); + +#define OVR_COMPILER_ASSERT(x) static_assert(x, "Assert violated: " #x) + +// ------------------------------------------------------------------------ +// ***** OVR_VERIFY_ARRAY_SIZE +// An enumeration type must have a corresponding array of items where the +// number of items exactly matches the number of enums in the enumeration type. +// This macro causes a compile-time error if the number of elements in the array +// does not match the value of num_elements_. Use of this macro ensures that +// enum types and corresponding arrays do not get out of sync. +// Note that the array must be defined as: +// +// type Array[] = { } +// vs. +// type Array[Maxvalue] = { } +// +// Otherwise sizeof( Array ) would always return MaxValue, independent of how +// many entries were initialized in the array. +#define OVR_VERIFY_ARRAY_SIZE(array_, num_elements_) \ + static_assert( \ + sizeof(array_) / sizeof(array_[0]) == num_elements_, \ + "Array " #array_ " must have " #num_elements_ " elements!") + +//----------------------------------------------------------------------------------- +// ***** OVR_DEPRECATED / OVR_DEPRECATED_MSG +// +// Portably annotates a function or struct as deprecated. +// Note that clang supports __deprecated_enum_msg, which may be useful to support. +// +// Example usage: +// OVR_DEPRECATED void Test(); // Use on the function declaration, as opposed to +// definition. +// +// struct OVR_DEPRECATED Test{ ... }; +// +// OVR_DEPRECATED_MSG("Test is deprecated") +// void Test(); + +#if !defined(OVR_DEPRECATED) +#if defined(OVR_CC_MSVC) && (OVR_CC_VERSION > 1400) // VS2005+ +#define OVR_DEPRECATED __declspec(deprecated) +#define OVR_DEPRECATED_MSG(msg) __declspec(deprecated(msg)) +#elif defined(OVR_CC_CLANG) && OVR_CC_HAS_FEATURE(attribute_deprecated_with_message) +#define OVR_DEPRECATED __declspec(deprecated) +#define OVR_DEPRECATED_MSG(msg) __attribute__((deprecated(msg))) +#elif defined(OVR_CC_GNU) && (OVR_CC_VERSION >= 405) +#define OVR_DEPRECATED __declspec(deprecated) +#define OVR_DEPRECATED_MSG(msg) __attribute__((deprecated(msg))) +#elif !defined(OVR_CC_MSVC) +#define OVR_DEPRECATED __attribute__((deprecated)) +#define OVR_DEPRECATED_MSG(msg) __attribute__((deprecated)) +#else +#define OVR_DEPRECATED +#define OVR_DEPRECATED_MSG(msg) +#endif +#endif + +//----------------------------------------------------------------------------------- +// ***** OVR_UNUSED - Unused Argument handling +// Macro to quiet compiler warnings about unused parameters/variables. +// +// Example usage: +// void Test() { +// int x = SomeFunction(); +// OVR_UNUSED(x); +// } +// + +#if defined(OVR_CC_GNU) +#define OVR_UNUSED(a) \ + do { \ + __typeof__(&a) __attribute__((unused)) __tmp = &a; \ + } while (0) +#else +#define OVR_UNUSED(a) \ + { (void)(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 +// +// Expands to the current build type as a const char string literal. +// Acts as the following declaration: const char OVR_BUILD_STRING[]; + +#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/Samples/1stParty/OVR/Include/OVR_TypesafeNumber.h b/Samples/1stParty/OVR/Include/OVR_TypesafeNumber.h new file mode 100755 index 0000000..511f7fa --- /dev/null +++ b/Samples/1stParty/OVR/Include/OVR_TypesafeNumber.h @@ -0,0 +1,312 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/************************************************************************************ + +PublicHeader: OVR_Kernel.h +Filename : OVR_TypesafeNumber.h +Content : Template for typesafe number types. +Created : March 2, 2014 +Authors : Jonathan E. Wright + +*************************************************************************************/ + +#if !defined(OVR_TypesafeNumber_h) +#define OVR_TypesafeNumber_h + +namespace OVR { + +//---------------------------------------------- +// TypesafeNumberT +// +// This template implements typesafe numbers. An object of this templated type will +// act like a built-in integer or floating point type in all respects except that it +// cannot be mixed with other types. +// +// USAGE: +// The second template parameter is normally an enum type. The enum type is generally +// an empty type definition or has very few members (perhaps an intial value and a max +// value). This type uniques the instanced template. +// +// EXAMPLE: +// +// enum MyUniqueType +// { +// MYUNIQUE_INITIAL_VALUE = 0 +// }; +// +// typedef TypesafeNumber< unsigned short, MyUniqueType, MYUNIQUE_INIITIAL_VALUE > uniqueType_t; +// +// uniqueType_t var1; +// uniqueType_t var2( 100 ); +// unsigned short foo; +// var1 = var2; // this works +// var1 = foo; // this will cause a compile error +// uniqueType_t var3( foo ); // this will work because of the explicit constructor +// +// In general it is better to do this: +// var1 = uniqueType_t( foo ); +// +// Than it is to do this: +// var1.Set( foo ); +// +// This is because the explicit construction is less ambiguous and therefore easier to identify +// at a glance and search for in a large codebase, especially considering that Set() is a common +// function name. +// +//---------------------------------------------- +template +class TypesafeNumberT { + public: + // constructors + TypesafeNumberT(); + explicit TypesafeNumberT(T const value); + TypesafeNumberT(TypesafeNumberT const& value); + + // NOTE: the assignmnet of a Type value is implemented with "= delete" + // to demonstrate that the whole point of this template is to + // enforce type safety. Assignment without explicit conversion to the templated + // type would break type safety. + T& operator=(T const value) = delete; + + // It IS ok to assign to the UniqueType, however, since that should only contain + // values within the range of the underlying type. This allows us to write more + // natural code such as: + // if (myTypesafeNumber == UniqueType::kInvalidValue) { + TypesafeNumberT& operator=(UniqueType const& value); + + TypesafeNumberT& operator=(TypesafeNumberT const& rhs); + + // comparison operators + bool operator==(UniqueType const& rhs) const; + bool operator!=(UniqueType const& rhs) const; + + bool operator==(TypesafeNumberT const& rhs) const; + bool operator!=(TypesafeNumberT const& rhs) const; + + bool operator<(TypesafeNumberT const& rhs) const; + bool operator>(TypesafeNumberT const& rhs) const; + bool operator<=(TypesafeNumberT const& rhs) const; + bool operator>=(TypesafeNumberT const& rhs) const; + + // unary operators + TypesafeNumberT& operator++(); // prefix + TypesafeNumberT operator++(int); // postfix + + TypesafeNumberT& operator--(); // prefix + TypesafeNumberT operator--(int); // postfix + + // compound assignment operators + TypesafeNumberT& operator+=(TypesafeNumberT const& rhs); + TypesafeNumberT& operator-=(TypesafeNumberT const& rhs); + TypesafeNumberT& operator*=(TypesafeNumberT const& rhs); + TypesafeNumberT& operator/=(TypesafeNumberT const& rhs); + TypesafeNumberT& operator%=(TypesafeNumberT const& rhs); + + // binary arithmetic operators + TypesafeNumberT operator+(TypesafeNumberT const& rhs) const; + TypesafeNumberT operator-(TypesafeNumberT const& rhs) const; + TypesafeNumberT operator*(TypesafeNumberT const& rhs) const; + TypesafeNumberT operator/(TypesafeNumberT const& rhs) const; + TypesafeNumberT operator%(TypesafeNumberT const& rhs) const; + + T Get() const; + void Set(T const value); + + // for using as a handle + void Release() { + Value = InitialValue; + } + + bool IsValid() const { + return Value != InitialValue; + } + + private: + T Value; // the value itself +}; + +template +inline TypesafeNumberT::TypesafeNumberT() + : Value(static_cast(InitialValue)) {} + +template +inline TypesafeNumberT::TypesafeNumberT(T const value) + : Value(value) {} + +template +inline TypesafeNumberT::TypesafeNumberT(TypesafeNumberT const& value) { + *this = value; +} + +template +inline TypesafeNumberT& +TypesafeNumberT::operator=(UniqueType const& value) { + this->Value = value; + return *this; +} + +template +inline TypesafeNumberT& +TypesafeNumberT::operator=(TypesafeNumberT const& rhs) { + if (&rhs != this) { + this->Value = rhs.Value; + } + return *this; +} + +template +inline bool TypesafeNumberT::operator==(UniqueType const& rhs) const { + return this->Value == rhs; +} + +template +inline bool TypesafeNumberT::operator!=(UniqueType const& rhs) const { + return !operator==(rhs); +} + +template +inline bool TypesafeNumberT::operator==( + TypesafeNumberT const& rhs) const { + return this->Value == rhs.Value; +} + +template +inline bool TypesafeNumberT::operator!=( + TypesafeNumberT const& rhs) const { + return !operator==(rhs); +} + +template +inline bool TypesafeNumberT::operator<( + TypesafeNumberT const& rhs) const { + return this->Value < rhs.Value; +} + +template +inline bool TypesafeNumberT::operator>( + TypesafeNumberT const& rhs) const { + return this->Value > rhs.Value; +} + +template +inline bool TypesafeNumberT::operator<=( + TypesafeNumberT const& rhs) const { + return this->Value <= rhs.Value; +} + +template +inline bool TypesafeNumberT::operator>=( + TypesafeNumberT const& rhs) const { + return this->Value >= rhs.Value; +} + +template +inline TypesafeNumberT& +TypesafeNumberT::operator++() { + this->Value++; + return *this; +} + +template +inline TypesafeNumberT +TypesafeNumberT::operator++(int) { + // postfix + TypesafeNumberT temp(*this); + operator++(); + return temp; +} + +template +inline TypesafeNumberT& +TypesafeNumberT::operator--() { + this->Value--; + return *this; +} + +template +inline TypesafeNumberT +TypesafeNumberT::operator--(int) { + // postfix + TypesafeNumberT temp(*this); + operator--(); + return temp; +} + +template +inline TypesafeNumberT& +TypesafeNumberT::operator+=(TypesafeNumberT const& rhs) { + this->Value = this->Value + rhs.Value; + return *this; +} + +template +inline TypesafeNumberT& +TypesafeNumberT::operator-=(TypesafeNumberT const& rhs) { + this->Value = this->Value - rhs.Value; + return *this; +} + +template +inline TypesafeNumberT& +TypesafeNumberT::operator*=(TypesafeNumberT const& rhs) { + this->Value = this->Value * rhs.Value; + return *this; +} + +template +inline TypesafeNumberT& +TypesafeNumberT::operator/=(TypesafeNumberT const& rhs) { + this->Value = this->Value / rhs.Value; + return *this; +} + +template +inline TypesafeNumberT& +TypesafeNumberT::operator%=(TypesafeNumberT const& rhs) { + this->Value = this->Value % rhs.Value; + return *this; +} + +template +inline TypesafeNumberT +TypesafeNumberT::operator+(TypesafeNumberT const& rhs) const { + return TypesafeNumberT(this->Value + rhs.Value); +} + +template +inline TypesafeNumberT +TypesafeNumberT::operator-(TypesafeNumberT const& rhs) const { + return TypesafeNumberT(this->Value - rhs.Value); +} + +template +inline TypesafeNumberT +TypesafeNumberT::operator*(TypesafeNumberT const& rhs) const { + return TypesafeNumberT(this->Value * rhs.Value); +} + +template +inline TypesafeNumberT +TypesafeNumberT::operator/(TypesafeNumberT const& rhs) const { + return TypesafeNumberT(this->Value / rhs.Value); +} + +template +inline TypesafeNumberT +TypesafeNumberT::operator%(TypesafeNumberT const& rhs) const { + return TypesafeNumberT(this->Value % rhs.Value); +} + +template +inline T TypesafeNumberT::Get() const { + return this->Value; +} + +template +inline void TypesafeNumberT::Set(T const value) { + this->Value = value; +} + +} // namespace OVR + +#endif // OVR_TypesafeNumber_h diff --git a/Samples/1stParty/OVR/Include/StringUtils.h b/Samples/1stParty/OVR/Include/StringUtils.h new file mode 100755 index 0000000..3baa6d4 --- /dev/null +++ b/Samples/1stParty/OVR/Include/StringUtils.h @@ -0,0 +1,380 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/************************************************************************************ + +Filename : String_Utils.h +Content : std::string utility functions. +Created : May, 2014 +Authors : J.M.P. van Waveren + +*************************************************************************************/ + +#pragma once + +#include "OVR_Types.h" +#include "OVR_Std.h" + +#include +#include + +namespace OVR { +namespace StringUtils { +// +// Convert a string to a common type. +// + +template +inline size_t StringTo(_type_& value, const char* string) { + return 0; +} + +template +inline size_t StringTo(_type_* valueArray, const int count, const char* string) { + size_t length = 0; + length += strspn(string + length, "{ \t\n\r"); + for (int i = 0; i < count; i++) { + length += StringTo<_type_>(valueArray[i], string + length); + } + length += strspn(string + length, "} \t\n\r"); + return length; +} + +template +inline size_t StringTo(std::vector<_type_>& valueArray, const char* string) { + size_t length = 0; + length += strspn(string + length, "{ \t\n\r"); + for (;;) { + _type_ value; + size_t s = StringTo<_type_>(value, string + length); + if (s == 0) + break; + valueArray.push_back(value); + length += s; + } + length += strspn(string + length, "} \t\n\r"); + return length; +} + +// specializations + +template <> +inline size_t StringTo(short& value, const char* str) { + char* endptr; + value = (short)strtol(str, &endptr, 10); + return size_t(endptr - str); +} +template <> +inline size_t StringTo(unsigned short& value, const char* str) { + char* endptr; + value = (unsigned short)strtoul(str, &endptr, 10); + return size_t(endptr - str); +} +template <> +inline size_t StringTo(int& value, const char* str) { + char* endptr; + value = strtol(str, &endptr, 10); + return size_t(endptr - str); +} +template <> +inline size_t StringTo(unsigned int& value, const char* str) { + char* endptr; + value = strtoul(str, &endptr, 10); + return size_t(endptr - str); +} +template <> +inline size_t StringTo(float& value, const char* str) { + char* endptr; + value = strtof(str, &endptr); + return size_t(endptr - str); +} +template <> +inline size_t StringTo(double& value, const char* str) { + char* endptr; + value = strtod(str, &endptr); + return size_t(endptr - str); +} + +template <> +inline size_t StringTo(Vector2f& value, const char* string) { + return StringTo(&value.x, 2, string); +} +template <> +inline size_t StringTo(Vector2d& value, const char* string) { + return StringTo(&value.x, 2, string); +} +template <> +inline size_t StringTo(Vector2i& value, const char* string) { + return StringTo(&value.x, 2, string); +} + +template <> +inline size_t StringTo(Vector3f& value, const char* string) { + return StringTo(&value.x, 3, string); +} +template <> +inline size_t StringTo(Vector3d& value, const char* string) { + return StringTo(&value.x, 3, string); +} +template <> +inline size_t StringTo(Vector3i& value, const char* string) { + return StringTo(&value.x, 3, string); +} + +template <> +inline size_t StringTo(Vector4f& value, const char* string) { + return StringTo(&value.x, 4, string); +} +template <> +inline size_t StringTo(Vector4d& value, const char* string) { + return StringTo(&value.x, 4, string); +} +template <> +inline size_t StringTo(Vector4i& value, const char* string) { + return StringTo(&value.x, 4, string); +} + +template <> +inline size_t StringTo(Matrix4f& value, const char* string) { + return StringTo(&value.M[0][0], 16, string); +} +template <> +inline size_t StringTo(Matrix4d& value, const char* string) { + return StringTo(&value.M[0][0], 16, string); +} + +template <> +inline size_t StringTo(Quatf& value, const char* string) { + return StringTo(&value.x, 4, string); +} +template <> +inline size_t StringTo(Quatd& value, const char* string) { + return StringTo(&value.x, 4, string); +} + +template <> +inline size_t StringTo(Planef& value, const char* string) { + return StringTo(&value.N.x, 4, string); +} +template <> +inline size_t StringTo(Planed& value, const char* string) { + return StringTo(&value.N.x, 4, string); +} + +template <> +inline size_t StringTo(Bounds3f& value, const char* string) { + return StringTo(value.b, 2, string); +} +template <> +inline size_t StringTo(Bounds3d& value, const char* string) { + return StringTo(value.b, 2, string); +} + +template +inline std::string ToString(const _type_& value) { + return std::string(); +} + +inline std::string FormattedStringV(const char* format, va_list argList) { + std::string result; + + const size_t size1 = OVR::OVR_vscprintf(format, argList); + + result.reserve(size1 + 1); + + const size_t size2 = OVR::OVR_vsprintf((char*)result.data(), size1 + 1, format, argList); + + OVR_UNUSED1(size2); + OVR_ASSERT(size1 == size2); + + return result; +} + +inline std::string Va(const char* format, ...) { + va_list argList; + + va_start(argList, format); + const std::string result = FormattedStringV(format, argList); + va_end(argList); + + return result; +} + +template +inline std::string ToString(const _type_* valueArray, const int count) { + std::string string = "{"; + for (int i = 0; i < count; i++) { + string += ToString(valueArray[i]); + } + string += "}"; + return string; +} + +template +inline std::string ToString(const std::vector<_type_>& valueArray) { + std::string string = "{"; + for (int i = 0; i < valueArray.GetSizeI(); i++) { + string += ToString(valueArray[i]); + } + string += "}"; + return string; +} + +// specializations + +template <> +inline std::string ToString(const short& value) { + return std::string(Va(" %hi", value)); +} +template <> +inline std::string ToString(const unsigned short& value) { + return std::string(Va(" %uhi", value)); +} +template <> +inline std::string ToString(const int& value) { + return std::string(Va(" %li", value)); +} +template <> +inline std::string ToString(const unsigned int& value) { + return std::string(Va(" %uli", value)); +} +template <> +inline std::string ToString(const float& value) { + return std::string(Va(" %f", value)); +} +template <> +inline std::string ToString(const double& value) { + return std::string(Va(" %f", value)); +} + +template <> +inline std::string ToString(const Vector2f& value) { + return std::string(Va("{ %f %f }", value.x, value.y)); +} +template <> +inline std::string ToString(const Vector2d& value) { + return std::string(Va("{ %f %f }", value.x, value.y)); +} +template <> +inline std::string ToString(const Vector2i& value) { + return std::string(Va("{ %d %d }", value.x, value.y)); +} + +template <> +inline std::string ToString(const Vector3f& value) { + return std::string(Va("{ %f %f %f }", value.x, value.y, value.z)); +} +template <> +inline std::string ToString(const Vector3d& value) { + return std::string(Va("{ %f %f %f }", value.x, value.y, value.z)); +} +template <> +inline std::string ToString(const Vector3i& value) { + return std::string(Va("{ %d %d %d }", value.x, value.y, value.z)); +} + +template <> +inline std::string ToString(const Vector4f& value) { + return std::string(Va("{ %f %f %f %f }", value.x, value.y, value.z, value.w)); +} +template <> +inline std::string ToString(const Vector4d& value) { + return std::string(Va("{ %f %f %f %f }", value.x, value.y, value.z, value.w)); +} +template <> +inline std::string ToString(const Vector4i& value) { + return std::string(Va("{ %d %d %d %d }", value.x, value.y, value.z, value.w)); +} + +template <> +inline std::string ToString(const Matrix4f& value) { + return std::string( + Va("{ %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f }", + value.M[0][0], + value.M[0][1], + value.M[0][2], + value.M[0][3], + value.M[1][0], + value.M[1][1], + value.M[1][2], + value.M[1][3], + value.M[2][0], + value.M[2][1], + value.M[2][2], + value.M[2][3], + value.M[3][0], + value.M[3][1], + value.M[3][2], + value.M[3][3])); +} +template <> +inline std::string ToString(const Matrix4d& value) { + return std::string( + Va("{ %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f }", + value.M[0][0], + value.M[0][1], + value.M[0][2], + value.M[0][3], + value.M[1][0], + value.M[1][1], + value.M[1][2], + value.M[1][3], + value.M[2][0], + value.M[2][1], + value.M[2][2], + value.M[2][3], + value.M[3][0], + value.M[3][1], + value.M[3][2], + value.M[3][3])); +} + +template <> +inline std::string ToString(const Quatf& value) { + return std::string(Va("{ %f %f %f %f }", value.x, value.y, value.z, value.w)); +} +template <> +inline std::string ToString(const Quatd& value) { + return std::string(Va("{ %f %f %f %f }", value.x, value.y, value.z, value.w)); +} + +template <> +inline std::string ToString(const Planef& value) { + return std::string(Va("{ %f %f %f %f }", value.N.x, value.N.y, value.N.z, value.D)); +} +template <> +inline std::string ToString(const Planed& value) { + return std::string(Va("{ %f %f %f %f }", value.N.x, value.N.y, value.N.z, value.D)); +} + +template <> +inline std::string ToString(const Bounds3f& value) { + return std::string( + Va("{{ %f %f %f }{ %f %f %f }}", + value.b[0].x, + value.b[0].y, + value.b[0].z, + value.b[1].x, + value.b[1].y, + value.b[1].z)); +} +template <> +inline std::string ToString(const Bounds3d& value) { + return std::string( + Va("{{ %f %f %f }{ %f %f %f }}", + value.b[0].x, + value.b[0].y, + value.b[0].z, + value.b[1].x, + value.b[1].y, + value.b[1].z)); +} + +inline bool EndsWith(std::string const& value, std::string const& ending) { + if (ending.size() > value.size()) { + return false; + } + return std::equal(ending.rbegin(), ending.rend(), value.rbegin()); +} + +} // namespace StringUtils +} // namespace OVR diff --git a/Samples/1stParty/utilities/include/GL/gl_format.h b/Samples/1stParty/utilities/include/GL/gl_format.h new file mode 100755 index 0000000..007fccb --- /dev/null +++ b/Samples/1stParty/utilities/include/GL/gl_format.h @@ -0,0 +1,2836 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * 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. + */ + +// clang-format off +/* +================================================================================================ + +Description : OpenGL formats/types and properties. +Author : J.M.P. van Waveren +Date : 07/17/2016 +Language : C99 +Format : Real tabs with the tab size equal to 4 spaces. + + +DESCRIPTION +============== + +This header stores the OpenGL formats/types and two simple routines +to derive the format/type from an internal format. These routines +are useful to verify the data in a KTX container files. The OpenGL +constants are generally useful to convert files like KTX and glTF +to different graphics APIs. + +This header stores the OpenGL formats/types that are used as parameters +to the following OpenGL functions: + +void glTexImage2D( GLenum target, GLint level, GLint internalFormat, + GLsizei width, GLsizei height, GLint border, + GLenum format, GLenum type, const GLvoid * data ); +void glTexImage3D( GLenum target, GLint level, GLint internalFormat, + GLsizei width, GLsizei height, GLsizei depth, GLint border, + GLenum format, GLenum type, const GLvoid * data ); +void glCompressedTexImage2D( GLenum target, GLint level, GLenum internalformat, + GLsizei width, GLsizei height, GLint border, + GLsizei imageSize, const GLvoid * data ); +void glCompressedTexImage3D( GLenum target, GLint level, GLenum internalformat, + GLsizei width, GLsizei height, GLsizei depth, GLint border, + GLsizei imageSize, const GLvoid * data ); +void glTexStorage2D( GLenum target, GLsizei levels, GLenum internalformat, + GLsizei width, GLsizei height ); +void glTexStorage3D( GLenum target, GLsizei levels, GLenum internalformat, + GLsizei width, GLsizei height, GLsizei depth ); +void glVertexAttribPointer( GLuint index, GLint size, GLenum type, GLboolean normalized, + GLsizei stride, const GLvoid * pointer); + + +IMPLEMENTATION +============== + +This file does not include OpenGL / OpenGL ES headers because: + + 1. Including OpenGL / OpenGL ES headers is platform dependent and + may require a separate installation of an OpenGL SDK. + 2. The OpenGL format/type constants are the same between extensions and core. + 3. The OpenGL format/type constants are the same between OpenGL and OpenGL ES. + 4. The OpenGL constants in this header are also used to derive Vulkan formats + from the OpenGL formats/types stored in files like KTX and glTF. These file + formats may use OpenGL formats/types that are not supported by the OpenGL + implementation on the platform but are supported by the Vulkan implementation. + + +ENTRY POINTS +============== + +static inline GLenum glGetFormatFromInternalFormat( const GLenum internalFormat ); +static inline GLenum glGetTypeFromInternalFormat( const GLenum internalFormat ); +static inline void glGetFormatSize( const GLenum internalFormat, GlFormatSize * pFormatSize ); + +================================================================================================ +*/ +// clang-format on + +#if !defined(GL_FORMAT_H) +#define GL_FORMAT_H + +#include + +#if defined(_WIN32) +#if !defined(NOMINMAX) +#define NOMINMAX +#endif +#ifndef __cplusplus +#undef inline +#define inline __inline +#endif // __cplusplus +#endif + +typedef unsigned int GLenum; +typedef unsigned char GLboolean; +typedef unsigned int GLuint; + +#if !defined(GL_INVALID_VALUE) +#define GL_INVALID_VALUE 0x0501 +#endif + +/* +================================================================================================================================ + +Format to glTexImage2D and glTexImage3D. + +================================================================================================================================ +*/ + +#if !defined(GL_RED) +#define GL_RED 0x1903 // same as GL_RED_EXT +#endif +#if !defined(GL_GREEN) +#define GL_GREEN 0x1904 // deprecated +#endif +#if !defined(GL_BLUE) +#define GL_BLUE 0x1905 // deprecated +#endif +#if !defined(GL_ALPHA) +#define GL_ALPHA 0x1906 // deprecated +#endif +#if !defined(GL_LUMINANCE) +#define GL_LUMINANCE 0x1909 // deprecated +#endif +#if !defined(GL_SLUMINANCE) +#define GL_SLUMINANCE 0x8C46 // deprecated, same as GL_SLUMINANCE_EXT +#endif +#if !defined(GL_LUMINANCE_ALPHA) +#define GL_LUMINANCE_ALPHA 0x190A // deprecated +#endif +#if !defined(GL_SLUMINANCE_ALPHA) +#define GL_SLUMINANCE_ALPHA 0x8C44 // deprecated, same as GL_SLUMINANCE_ALPHA_EXT +#endif +#if !defined(GL_INTENSITY) +#define GL_INTENSITY 0x8049 // deprecated, same as GL_INTENSITY_EXT +#endif +#if !defined(GL_RG) +#define GL_RG 0x8227 // same as GL_RG_EXT +#endif +#if !defined(GL_RGB) +#define GL_RGB 0x1907 +#endif +#if !defined(GL_BGR) +#define GL_BGR 0x80E0 // same as GL_BGR_EXT +#endif +#if !defined(GL_RGBA) +#define GL_RGBA 0x1908 +#endif +#if !defined(GL_BGRA) +#define GL_BGRA 0x80E1 // same as GL_BGRA_EXT +#endif +#if !defined(GL_RED_INTEGER) +#define GL_RED_INTEGER 0x8D94 // same as GL_RED_INTEGER_EXT +#endif +#if !defined(GL_GREEN_INTEGER) +#define GL_GREEN_INTEGER 0x8D95 // deprecated, same as GL_GREEN_INTEGER_EXT +#endif +#if !defined(GL_BLUE_INTEGER) +#define GL_BLUE_INTEGER 0x8D96 // deprecated, same as GL_BLUE_INTEGER_EXT +#endif +#if !defined(GL_ALPHA_INTEGER) +#define GL_ALPHA_INTEGER 0x8D97 // deprecated, same as GL_ALPHA_INTEGER_EXT +#endif +#if !defined(GL_LUMINANCE_INTEGER) +#define GL_LUMINANCE_INTEGER 0x8D9C // deprecated, same as GL_LUMINANCE_INTEGER_EXT +#endif +#if !defined(GL_LUMINANCE_ALPHA_INTEGER) +#define GL_LUMINANCE_ALPHA_INTEGER 0x8D9D // deprecated, same as GL_LUMINANCE_ALPHA_INTEGER_EXT +#endif +#if !defined(GL_RG_INTEGER) +#define GL_RG_INTEGER 0x8228 // same as GL_RG_INTEGER_EXT +#endif +#if !defined(GL_RGB_INTEGER) +#define GL_RGB_INTEGER 0x8D98 // same as GL_RGB_INTEGER_EXT +#endif +#if !defined(GL_BGR_INTEGER) +#define GL_BGR_INTEGER 0x8D9A // same as GL_BGR_INTEGER_EXT +#endif +#if !defined(GL_RGBA_INTEGER) +#define GL_RGBA_INTEGER 0x8D99 // same as GL_RGBA_INTEGER_EXT +#endif +#if !defined(GL_BGRA_INTEGER) +#define GL_BGRA_INTEGER 0x8D9B // same as GL_BGRA_INTEGER_EXT +#endif +#if !defined(GL_COLOR_INDEX) +#define GL_COLOR_INDEX 0x1900 // deprecated +#endif +#if !defined(GL_STENCIL_INDEX) +#define GL_STENCIL_INDEX 0x1901 +#endif +#if !defined(GL_DEPTH_COMPONENT) +#define GL_DEPTH_COMPONENT 0x1902 +#endif +#if !defined(GL_DEPTH_STENCIL) +#define GL_DEPTH_STENCIL \ + 0x84F9 // same as GL_DEPTH_STENCIL_NV and GL_DEPTH_STENCIL_EXT and GL_DEPTH_STENCIL_OES +#endif + +/* +================================================================================================================================ + +Type to glTexImage2D, glTexImage3D and glVertexAttribPointer. + +================================================================================================================================ +*/ + +#if !defined(GL_BYTE) +#define GL_BYTE 0x1400 +#endif +#if !defined(GL_UNSIGNED_BYTE) +#define GL_UNSIGNED_BYTE 0x1401 +#endif +#if !defined(GL_SHORT) +#define GL_SHORT 0x1402 +#endif +#if !defined(GL_UNSIGNED_SHORT) +#define GL_UNSIGNED_SHORT 0x1403 +#endif +#if !defined(GL_INT) +#define GL_INT 0x1404 +#endif +#if !defined(GL_UNSIGNED_INT) +#define GL_UNSIGNED_INT 0x1405 +#endif +#if !defined(GL_INT64) +#define GL_INT64 0x140E // same as GL_INT64_NV and GL_INT64_ARB +#endif +#if !defined(GL_UNSIGNED_INT64) +#define GL_UNSIGNED_INT64 0x140F // same as GL_UNSIGNED_INT64_NV and GL_UNSIGNED_INT64_ARB +#endif +#if !defined(GL_HALF_FLOAT) +#define GL_HALF_FLOAT 0x140B // same as GL_HALF_FLOAT_NV and GL_HALF_FLOAT_ARB +#endif +#if !defined(GL_HALF_FLOAT_OES) +#define GL_HALF_FLOAT_OES 0x8D61 // Note that this different from GL_HALF_FLOAT. +#endif +#if !defined(GL_FLOAT) +#define GL_FLOAT 0x1406 +#endif +#if !defined(GL_DOUBLE) +#define GL_DOUBLE 0x140A // same as GL_DOUBLE_EXT +#endif +#if !defined(GL_UNSIGNED_BYTE_3_3_2) +#define GL_UNSIGNED_BYTE_3_3_2 0x8032 // same as GL_UNSIGNED_BYTE_3_3_2_EXT +#endif +#if !defined(GL_UNSIGNED_BYTE_2_3_3_REV) +#define GL_UNSIGNED_BYTE_2_3_3_REV 0x8362 // same as GL_UNSIGNED_BYTE_2_3_3_REV_EXT +#endif +#if !defined(GL_UNSIGNED_SHORT_5_6_5) +#define GL_UNSIGNED_SHORT_5_6_5 0x8363 // same as GL_UNSIGNED_SHORT_5_6_5_EXT +#endif +#if !defined(GL_UNSIGNED_SHORT_5_6_5_REV) +#define GL_UNSIGNED_SHORT_5_6_5_REV 0x8364 // same as GL_UNSIGNED_SHORT_5_6_5_REV_EXT +#endif +#if !defined(GL_UNSIGNED_SHORT_4_4_4_4) +#define GL_UNSIGNED_SHORT_4_4_4_4 0x8033 // same as GL_UNSIGNED_SHORT_4_4_4_4_EXT +#endif +#if !defined(GL_UNSIGNED_SHORT_4_4_4_4_REV) +#define GL_UNSIGNED_SHORT_4_4_4_4_REV \ + 0x8365 // same as GL_UNSIGNED_SHORT_4_4_4_4_REV_IMG and GL_UNSIGNED_SHORT_4_4_4_4_REV_EXT +#endif +#if !defined(GL_UNSIGNED_SHORT_5_5_5_1) +#define GL_UNSIGNED_SHORT_5_5_5_1 0x8034 // same as GL_UNSIGNED_SHORT_5_5_5_1_EXT +#endif +#if !defined(GL_UNSIGNED_SHORT_1_5_5_5_REV) +#define GL_UNSIGNED_SHORT_1_5_5_5_REV 0x8366 // same as GL_UNSIGNED_SHORT_1_5_5_5_REV_EXT +#endif +#if !defined(GL_UNSIGNED_INT_8_8_8_8) +#define GL_UNSIGNED_INT_8_8_8_8 0x8035 // same as GL_UNSIGNED_INT_8_8_8_8_EXT +#endif +#if !defined(GL_UNSIGNED_INT_8_8_8_8_REV) +#define GL_UNSIGNED_INT_8_8_8_8_REV 0x8367 // same as GL_UNSIGNED_INT_8_8_8_8_REV_EXT +#endif +#if !defined(GL_UNSIGNED_INT_10_10_10_2) +#define GL_UNSIGNED_INT_10_10_10_2 0x8036 // same as GL_UNSIGNED_INT_10_10_10_2_EXT +#endif +#if !defined(GL_UNSIGNED_INT_2_10_10_10_REV) +#define GL_UNSIGNED_INT_2_10_10_10_REV 0x8368 // same as GL_UNSIGNED_INT_2_10_10_10_REV_EXT +#endif +#if !defined(GL_UNSIGNED_INT_10F_11F_11F_REV) +#define GL_UNSIGNED_INT_10F_11F_11F_REV 0x8C3B // same as GL_UNSIGNED_INT_10F_11F_11F_REV_EXT +#endif +#if !defined(GL_UNSIGNED_INT_5_9_9_9_REV) +#define GL_UNSIGNED_INT_5_9_9_9_REV 0x8C3E // same as GL_UNSIGNED_INT_5_9_9_9_REV_EXT +#endif +#if !defined(GL_UNSIGNED_INT_24_8) +#define GL_UNSIGNED_INT_24_8 \ + 0x84FA // same as GL_UNSIGNED_INT_24_8_NV and GL_UNSIGNED_INT_24_8_EXT and + // GL_UNSIGNED_INT_24_8_OES +#endif +#if !defined(GL_FLOAT_32_UNSIGNED_INT_24_8_REV) +#define GL_FLOAT_32_UNSIGNED_INT_24_8_REV \ + 0x8DAD // same as GL_FLOAT_32_UNSIGNED_INT_24_8_REV_NV and GL_FLOAT_32_UNSIGNED_INT_24_8_REV_ARB +#endif + +/* +================================================================================================================================ + +Internal format to glTexImage2D, glTexImage3D, glCompressedTexImage2D, glCompressedTexImage3D, +glTexStorage2D, glTexStorage3D + +================================================================================================================================ +*/ + +// +// 8 bits per component +// + +#if !defined(GL_R8) +#define GL_R8 0x8229 // same as GL_R8_EXT +#endif +#if !defined(GL_RG8) +#define GL_RG8 0x822B // same as GL_RG8_EXT +#endif +#if !defined(GL_RGB8) +#define GL_RGB8 0x8051 // same as GL_RGB8_EXT and GL_RGB8_OES +#endif +#if !defined(GL_RGBA8) +#define GL_RGBA8 0x8058 // same as GL_RGBA8_EXT and GL_RGBA8_OES +#endif + +#if !defined(GL_R8_SNORM) +#define GL_R8_SNORM 0x8F94 +#endif +#if !defined(GL_RG8_SNORM) +#define GL_RG8_SNORM 0x8F95 +#endif +#if !defined(GL_RGB8_SNORM) +#define GL_RGB8_SNORM 0x8F96 +#endif +#if !defined(GL_RGBA8_SNORM) +#define GL_RGBA8_SNORM 0x8F97 +#endif + +#if !defined(GL_R8UI) +#define GL_R8UI 0x8232 +#endif +#if !defined(GL_RG8UI) +#define GL_RG8UI 0x8238 +#endif +#if !defined(GL_RGB8UI) +#define GL_RGB8UI 0x8D7D // same as GL_RGB8UI_EXT +#endif +#if !defined(GL_RGBA8UI) +#define GL_RGBA8UI 0x8D7C // same as GL_RGBA8UI_EXT +#endif + +#if !defined(GL_R8I) +#define GL_R8I 0x8231 +#endif +#if !defined(GL_RG8I) +#define GL_RG8I 0x8237 +#endif +#if !defined(GL_RGB8I) +#define GL_RGB8I 0x8D8F // same as GL_RGB8I_EXT +#endif +#if !defined(GL_RGBA8I) +#define GL_RGBA8I 0x8D8E // same as GL_RGBA8I_EXT +#endif + +#if !defined(GL_SR8) +#define GL_SR8 0x8FBD // same as GL_SR8_EXT +#endif +#if !defined(GL_SRG8) +#define GL_SRG8 0x8FBE // same as GL_SRG8_EXT +#endif +#if !defined(GL_SRGB8) +#define GL_SRGB8 0x8C41 // same as GL_SRGB8_EXT +#endif +#if !defined(GL_SRGB8_ALPHA8) +#define GL_SRGB8_ALPHA8 0x8C43 // same as GL_SRGB8_ALPHA8_EXT +#endif + +// +// 16 bits per component +// + +#if !defined(GL_R16) +#define GL_R16 0x822A // same as GL_R16_EXT +#endif +#if !defined(GL_RG16) +#define GL_RG16 0x822C // same as GL_RG16_EXT +#endif +#if !defined(GL_RGB16) +#define GL_RGB16 0x8054 // same as GL_RGB16_EXT +#endif +#if !defined(GL_RGBA16) +#define GL_RGBA16 0x805B // same as GL_RGBA16_EXT +#endif + +#if !defined(GL_R16_SNORM) +#define GL_R16_SNORM 0x8F98 // same as GL_R16_SNORM_EXT +#endif +#if !defined(GL_RG16_SNORM) +#define GL_RG16_SNORM 0x8F99 // same as GL_RG16_SNORM_EXT +#endif +#if !defined(GL_RGB16_SNORM) +#define GL_RGB16_SNORM 0x8F9A // same as GL_RGB16_SNORM_EXT +#endif +#if !defined(GL_RGBA16_SNORM) +#define GL_RGBA16_SNORM 0x8F9B // same as GL_RGBA16_SNORM_EXT +#endif + +#if !defined(GL_R16UI) +#define GL_R16UI 0x8234 +#endif +#if !defined(GL_RG16UI) +#define GL_RG16UI 0x823A +#endif +#if !defined(GL_RGB16UI) +#define GL_RGB16UI 0x8D77 // same as GL_RGB16UI_EXT +#endif +#if !defined(GL_RGBA16UI) +#define GL_RGBA16UI 0x8D76 // same as GL_RGBA16UI_EXT +#endif + +#if !defined(GL_R16I) +#define GL_R16I 0x8233 +#endif +#if !defined(GL_RG16I) +#define GL_RG16I 0x8239 +#endif +#if !defined(GL_RGB16I) +#define GL_RGB16I 0x8D89 // same as GL_RGB16I_EXT +#endif +#if !defined(GL_RGBA16I) +#define GL_RGBA16I 0x8D88 // same as GL_RGBA16I_EXT +#endif + +#if !defined(GL_R16F) +#define GL_R16F 0x822D // same as GL_R16F_EXT +#endif +#if !defined(GL_RG16F) +#define GL_RG16F 0x822F // same as GL_RG16F_EXT +#endif +#if !defined(GL_RGB16F) +#define GL_RGB16F 0x881B // same as GL_RGB16F_EXT and GL_RGB16F_ARB +#endif +#if !defined(GL_RGBA16F) +#define GL_RGBA16F 0x881A // sama as GL_RGBA16F_EXT and GL_RGBA16F_ARB +#endif + +// +// 32 bits per component +// + +#if !defined(GL_R32UI) +#define GL_R32UI 0x8236 +#endif +#if !defined(GL_RG32UI) +#define GL_RG32UI 0x823C +#endif +#if !defined(GL_RGB32UI) +#define GL_RGB32UI 0x8D71 // same as GL_RGB32UI_EXT +#endif +#if !defined(GL_RGBA32UI) +#define GL_RGBA32UI 0x8D70 // same as GL_RGBA32UI_EXT +#endif + +#if !defined(GL_R32I) +#define GL_R32I 0x8235 +#endif +#if !defined(GL_RG32I) +#define GL_RG32I 0x823B +#endif +#if !defined(GL_RGB32I) +#define GL_RGB32I 0x8D83 // same as GL_RGB32I_EXT +#endif +#if !defined(GL_RGBA32I) +#define GL_RGBA32I 0x8D82 // same as GL_RGBA32I_EXT +#endif + +#if !defined(GL_R32F) +#define GL_R32F 0x822E // same as GL_R32F_EXT +#endif +#if !defined(GL_RG32F) +#define GL_RG32F 0x8230 // same as GL_RG32F_EXT +#endif +#if !defined(GL_RGB32F) +#define GL_RGB32F 0x8815 // same as GL_RGB32F_EXT and GL_RGB32F_ARB +#endif +#if !defined(GL_RGBA32F) +#define GL_RGBA32F 0x8814 // same as GL_RGBA32F_EXT and GL_RGBA32F_ARB +#endif + +// +// Packed +// + +#if !defined(GL_R3_G3_B2) +#define GL_R3_G3_B2 0x2A10 +#endif +#if !defined(GL_RGB4) +#define GL_RGB4 0x804F // same as GL_RGB4_EXT +#endif +#if !defined(GL_RGB5) +#define GL_RGB5 0x8050 // same as GL_RGB5_EXT +#endif +#if !defined(GL_RGB565) +#define GL_RGB565 0x8D62 // same as GL_RGB565_EXT and GL_RGB565_OES +#endif +#if !defined(GL_RGB10) +#define GL_RGB10 0x8052 // same as GL_RGB10_EXT +#endif +#if !defined(GL_RGB12) +#define GL_RGB12 0x8053 // same as GL_RGB12_EXT +#endif +#if !defined(GL_RGBA2) +#define GL_RGBA2 0x8055 // same as GL_RGBA2_EXT +#endif +#if !defined(GL_RGBA4) +#define GL_RGBA4 0x8056 // same as GL_RGBA4_EXT and GL_RGBA4_OES +#endif +#if !defined(GL_RGBA12) +#define GL_RGBA12 0x805A // same as GL_RGBA12_EXT +#endif +#if !defined(GL_RGB5_A1) +#define GL_RGB5_A1 0x8057 // same as GL_RGB5_A1_EXT and GL_RGB5_A1_OES +#endif +#if !defined(GL_RGB10_A2) +#define GL_RGB10_A2 0x8059 // same as GL_RGB10_A2_EXT +#endif +#if !defined(GL_RGB10_A2UI) +#define GL_RGB10_A2UI 0x906F +#endif +#if !defined(GL_R11F_G11F_B10F) +#define GL_R11F_G11F_B10F 0x8C3A // same as GL_R11F_G11F_B10F_APPLE and GL_R11F_G11F_B10F_EXT +#endif +#if !defined(GL_RGB9_E5) +#define GL_RGB9_E5 0x8C3D // same as GL_RGB9_E5_APPLE and GL_RGB9_E5_EXT +#endif + +// +// Alpha +// + +#if !defined(GL_ALPHA4) +#define GL_ALPHA4 0x803B // deprecated, same as GL_ALPHA4_EXT +#endif +#if !defined(GL_ALPHA8) +#define GL_ALPHA8 0x803C // deprecated, same as GL_ALPHA8_EXT +#endif +#if !defined(GL_ALPHA8_SNORM) +#define GL_ALPHA8_SNORM 0x9014 // deprecated +#endif +#if !defined(GL_ALPHA8UI_EXT) +#define GL_ALPHA8UI_EXT 0x8D7E // deprecated +#endif +#if !defined(GL_ALPHA8I_EXT) +#define GL_ALPHA8I_EXT 0x8D90 // deprecated +#endif +#if !defined(GL_ALPHA12) +#define GL_ALPHA12 0x803D // deprecated, same as GL_ALPHA12_EXT +#endif +#if !defined(GL_ALPHA16) +#define GL_ALPHA16 0x803E // deprecated, same as GL_ALPHA16_EXT +#endif +#if !defined(GL_ALPHA16_SNORM) +#define GL_ALPHA16_SNORM 0x9018 // deprecated +#endif +#if !defined(GL_ALPHA16UI_EXT) +#define GL_ALPHA16UI_EXT 0x8D78 // deprecated +#endif +#if !defined(GL_ALPHA16I_EXT) +#define GL_ALPHA16I_EXT 0x8D8A // deprecated +#endif +#if !defined(GL_ALPHA16F_ARB) +#define GL_ALPHA16F_ARB \ + 0x881C // deprecated, same as GL_ALPHA_FLOAT16_APPLE and GL_ALPHA_FLOAT16_ATI +#endif +#if !defined(GL_ALPHA32UI_EXT) +#define GL_ALPHA32UI_EXT 0x8D72 // deprecated +#endif +#if !defined(GL_ALPHA32I_EXT) +#define GL_ALPHA32I_EXT 0x8D84 // deprecated +#endif +#if !defined(GL_ALPHA32F_ARB) +#define GL_ALPHA32F_ARB \ + 0x8816 // deprecated, same as GL_ALPHA_FLOAT32_APPLE and GL_ALPHA_FLOAT32_ATI +#endif + +// +// Luminance +// + +#if !defined(GL_LUMINANCE4) +#define GL_LUMINANCE4 0x803F // deprecated, same as GL_LUMINANCE4_EXT +#endif +#if !defined(GL_LUMINANCE8) +#define GL_LUMINANCE8 0x8040 // deprecated, same as GL_LUMINANCE8_EXT +#endif +#if !defined(GL_LUMINANCE8_SNORM) +#define GL_LUMINANCE8_SNORM 0x9015 // deprecated +#endif +#if !defined(GL_SLUMINANCE8) +#define GL_SLUMINANCE8 0x8C47 // deprecated, same as GL_SLUMINANCE8_EXT +#endif +#if !defined(GL_LUMINANCE8UI_EXT) +#define GL_LUMINANCE8UI_EXT 0x8D80 // deprecated +#endif +#if !defined(GL_LUMINANCE8I_EXT) +#define GL_LUMINANCE8I_EXT 0x8D92 // deprecated +#endif +#if !defined(GL_LUMINANCE12) +#define GL_LUMINANCE12 0x8041 // deprecated, same as GL_LUMINANCE12_EXT +#endif +#if !defined(GL_LUMINANCE16) +#define GL_LUMINANCE16 0x8042 // deprecated, same as GL_LUMINANCE16_EXT +#endif +#if !defined(GL_LUMINANCE16_SNORM) +#define GL_LUMINANCE16_SNORM 0x9019 // deprecated +#endif +#if !defined(GL_LUMINANCE16UI_EXT) +#define GL_LUMINANCE16UI_EXT 0x8D7A // deprecated +#endif +#if !defined(GL_LUMINANCE16I_EXT) +#define GL_LUMINANCE16I_EXT 0x8D8C // deprecated +#endif +#if !defined(GL_LUMINANCE16F_ARB) +#define GL_LUMINANCE16F_ARB \ + 0x881E // deprecated, same as GL_LUMINANCE_FLOAT16_APPLE and GL_LUMINANCE_FLOAT16_ATI +#endif +#if !defined(GL_LUMINANCE32UI_EXT) +#define GL_LUMINANCE32UI_EXT 0x8D74 // deprecated +#endif +#if !defined(GL_LUMINANCE32I_EXT) +#define GL_LUMINANCE32I_EXT 0x8D86 // deprecated +#endif +#if !defined(GL_LUMINANCE32F_ARB) +#define GL_LUMINANCE32F_ARB \ + 0x8818 // deprecated, same as GL_LUMINANCE_FLOAT32_APPLE and GL_LUMINANCE_FLOAT32_ATI +#endif + +// +// Luminance/Alpha +// + +#if !defined(GL_LUMINANCE4_ALPHA4) +#define GL_LUMINANCE4_ALPHA4 0x8043 // deprecated, same as GL_LUMINANCE4_ALPHA4_EXT +#endif +#if !defined(GL_LUMINANCE6_ALPHA2) +#define GL_LUMINANCE6_ALPHA2 0x8044 // deprecated, same as GL_LUMINANCE6_ALPHA2_EXT +#endif +#if !defined(GL_LUMINANCE8_ALPHA8) +#define GL_LUMINANCE8_ALPHA8 0x8045 // deprecated, same as GL_LUMINANCE8_ALPHA8_EXT +#endif +#if !defined(GL_LUMINANCE8_ALPHA8_SNORM) +#define GL_LUMINANCE8_ALPHA8_SNORM 0x9016 // deprecated +#endif +#if !defined(GL_SLUMINANCE8_ALPHA8) +#define GL_SLUMINANCE8_ALPHA8 0x8C45 // deprecated, same as GL_SLUMINANCE8_ALPHA8_EXT +#endif +#if !defined(GL_LUMINANCE_ALPHA8UI_EXT) +#define GL_LUMINANCE_ALPHA8UI_EXT 0x8D81 // deprecated +#endif +#if !defined(GL_LUMINANCE_ALPHA8I_EXT) +#define GL_LUMINANCE_ALPHA8I_EXT 0x8D93 // deprecated +#endif +#if !defined(GL_LUMINANCE12_ALPHA4) +#define GL_LUMINANCE12_ALPHA4 0x8046 // deprecated, same as GL_LUMINANCE12_ALPHA4_EXT +#endif +#if !defined(GL_LUMINANCE12_ALPHA12) +#define GL_LUMINANCE12_ALPHA12 0x8047 // deprecated, same as GL_LUMINANCE12_ALPHA12_EXT +#endif +#if !defined(GL_LUMINANCE16_ALPHA16) +#define GL_LUMINANCE16_ALPHA16 0x8048 // deprecated, same as GL_LUMINANCE16_ALPHA16_EXT +#endif +#if !defined(GL_LUMINANCE16_ALPHA16_SNORM) +#define GL_LUMINANCE16_ALPHA16_SNORM 0x901A // deprecated +#endif +#if !defined(GL_LUMINANCE_ALPHA16UI_EXT) +#define GL_LUMINANCE_ALPHA16UI_EXT 0x8D7B // deprecated +#endif +#if !defined(GL_LUMINANCE_ALPHA16I_EXT) +#define GL_LUMINANCE_ALPHA16I_EXT 0x8D8D // deprecated +#endif +#if !defined(GL_LUMINANCE_ALPHA16F_ARB) +#define GL_LUMINANCE_ALPHA16F_ARB \ + 0x881F // deprecated, same as GL_LUMINANCE_ALPHA_FLOAT16_APPLE and + // GL_LUMINANCE_ALPHA_FLOAT16_ATI +#endif +#if !defined(GL_LUMINANCE_ALPHA32UI_EXT) +#define GL_LUMINANCE_ALPHA32UI_EXT 0x8D75 // deprecated +#endif +#if !defined(GL_LUMINANCE_ALPHA32I_EXT) +#define GL_LUMINANCE_ALPHA32I_EXT 0x8D87 // deprecated +#endif +#if !defined(GL_LUMINANCE_ALPHA32F_ARB) +#define GL_LUMINANCE_ALPHA32F_ARB \ + 0x8819 // deprecated, same as GL_LUMINANCE_ALPHA_FLOAT32_APPLE and + // GL_LUMINANCE_ALPHA_FLOAT32_ATI +#endif + +// +// Intensity +// + +#if !defined(GL_INTENSITY4) +#define GL_INTENSITY4 0x804A // deprecated, same as GL_INTENSITY4_EXT +#endif +#if !defined(GL_INTENSITY8) +#define GL_INTENSITY8 0x804B // deprecated, same as GL_INTENSITY8_EXT +#endif +#if !defined(GL_INTENSITY8_SNORM) +#define GL_INTENSITY8_SNORM 0x9017 // deprecated +#endif +#if !defined(GL_INTENSITY8UI_EXT) +#define GL_INTENSITY8UI_EXT 0x8D7F // deprecated +#endif +#if !defined(GL_INTENSITY8I_EXT) +#define GL_INTENSITY8I_EXT 0x8D91 // deprecated +#endif +#if !defined(GL_INTENSITY12) +#define GL_INTENSITY12 0x804C // deprecated, same as GL_INTENSITY12_EXT +#endif +#if !defined(GL_INTENSITY16) +#define GL_INTENSITY16 0x804D // deprecated, same as GL_INTENSITY16_EXT +#endif +#if !defined(GL_INTENSITY16_SNORM) +#define GL_INTENSITY16_SNORM 0x901B // deprecated +#endif +#if !defined(GL_INTENSITY16UI_EXT) +#define GL_INTENSITY16UI_EXT 0x8D79 // deprecated +#endif +#if !defined(GL_INTENSITY16I_EXT) +#define GL_INTENSITY16I_EXT 0x8D8B // deprecated +#endif +#if !defined(GL_INTENSITY16F_ARB) +#define GL_INTENSITY16F_ARB \ + 0x881D // deprecated, same as GL_INTENSITY_FLOAT16_APPLE and GL_INTENSITY_FLOAT16_ATI +#endif +#if !defined(GL_INTENSITY32UI_EXT) +#define GL_INTENSITY32UI_EXT 0x8D73 // deprecated +#endif +#if !defined(GL_INTENSITY32I_EXT) +#define GL_INTENSITY32I_EXT 0x8D85 // deprecated +#endif +#if !defined(GL_INTENSITY32F_ARB) +#define GL_INTENSITY32F_ARB \ + 0x8817 // deprecated, same as GL_INTENSITY_FLOAT32_APPLE and GL_INTENSITY_FLOAT32_ATI +#endif + +// +// Generic compression +// + +#if !defined(GL_COMPRESSED_RED) +#define GL_COMPRESSED_RED 0x8225 +#endif +#if !defined(GL_COMPRESSED_ALPHA) +#define GL_COMPRESSED_ALPHA 0x84E9 // deprecated, same as GL_COMPRESSED_ALPHA_ARB +#endif +#if !defined(GL_COMPRESSED_LUMINANCE) +#define GL_COMPRESSED_LUMINANCE 0x84EA // deprecated, same as GL_COMPRESSED_LUMINANCE_ARB +#endif +#if !defined(GL_COMPRESSED_SLUMINANCE) +#define GL_COMPRESSED_SLUMINANCE 0x8C4A // deprecated, same as GL_COMPRESSED_SLUMINANCE_EXT +#endif +#if !defined(GL_COMPRESSED_LUMINANCE_ALPHA) +#define GL_COMPRESSED_LUMINANCE_ALPHA \ + 0x84EB // deprecated, same as GL_COMPRESSED_LUMINANCE_ALPHA_ARB +#endif +#if !defined(GL_COMPRESSED_SLUMINANCE_ALPHA) +#define GL_COMPRESSED_SLUMINANCE_ALPHA \ + 0x8C4B // deprecated, same as GL_COMPRESSED_SLUMINANCE_ALPHA_EXT +#endif +#if !defined(GL_COMPRESSED_INTENSITY) +#define GL_COMPRESSED_INTENSITY 0x84EC // deprecated, same as GL_COMPRESSED_INTENSITY_ARB +#endif +#if !defined(GL_COMPRESSED_RG) +#define GL_COMPRESSED_RG 0x8226 +#endif +#if !defined(GL_COMPRESSED_RGB) +#define GL_COMPRESSED_RGB 0x84ED // same as GL_COMPRESSED_RGB_ARB +#endif +#if !defined(GL_COMPRESSED_RGBA) +#define GL_COMPRESSED_RGBA 0x84EE // same as GL_COMPRESSED_RGBA_ARB +#endif +#if !defined(GL_COMPRESSED_SRGB) +#define GL_COMPRESSED_SRGB 0x8C48 // same as GL_COMPRESSED_SRGB_EXT +#endif +#if !defined(GL_COMPRESSED_SRGB_ALPHA) +#define GL_COMPRESSED_SRGB_ALPHA 0x8C49 // same as GL_COMPRESSED_SRGB_ALPHA_EXT +#endif + +// +// FXT1 +// + +#if !defined(GL_COMPRESSED_RGB_FXT1_3DFX) +#define GL_COMPRESSED_RGB_FXT1_3DFX 0x86B0 // deprecated +#endif +#if !defined(GL_COMPRESSED_RGBA_FXT1_3DFX) +#define GL_COMPRESSED_RGBA_FXT1_3DFX 0x86B1 // deprecated +#endif + +// +// S3TC/DXT/BC +// + +#if !defined(GL_COMPRESSED_RGB_S3TC_DXT1_EXT) +#define GL_COMPRESSED_RGB_S3TC_DXT1_EXT 0x83F0 +#endif +#if !defined(GL_COMPRESSED_RGBA_S3TC_DXT1_EXT) +#define GL_COMPRESSED_RGBA_S3TC_DXT1_EXT 0x83F1 +#endif +#if !defined(GL_COMPRESSED_RGBA_S3TC_DXT3_EXT) +#define GL_COMPRESSED_RGBA_S3TC_DXT3_EXT 0x83F2 +#endif +#if !defined(GL_COMPRESSED_RGBA_S3TC_DXT5_EXT) +#define GL_COMPRESSED_RGBA_S3TC_DXT5_EXT 0x83F3 +#endif + +#if !defined(GL_COMPRESSED_SRGB_S3TC_DXT1_EXT) +#define GL_COMPRESSED_SRGB_S3TC_DXT1_EXT 0x8C4C +#endif +#if !defined(GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT) +#define GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT 0x8C4D +#endif +#if !defined(GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT) +#define GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT 0x8C4E +#endif +#if !defined(GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT) +#define GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT 0x8C4F +#endif + +#if !defined(GL_COMPRESSED_LUMINANCE_LATC1_EXT) +#define GL_COMPRESSED_LUMINANCE_LATC1_EXT 0x8C70 +#endif +#if !defined(GL_COMPRESSED_LUMINANCE_ALPHA_LATC2_EXT) +#define GL_COMPRESSED_LUMINANCE_ALPHA_LATC2_EXT 0x8C72 +#endif +#if !defined(GL_COMPRESSED_SIGNED_LUMINANCE_LATC1_EXT) +#define GL_COMPRESSED_SIGNED_LUMINANCE_LATC1_EXT 0x8C71 +#endif +#if !defined(GL_COMPRESSED_SIGNED_LUMINANCE_ALPHA_LATC2_EXT) +#define GL_COMPRESSED_SIGNED_LUMINANCE_ALPHA_LATC2_EXT 0x8C73 +#endif + +#if !defined(GL_COMPRESSED_RED_RGTC1) +#define GL_COMPRESSED_RED_RGTC1 0x8DBB // same as GL_COMPRESSED_RED_RGTC1_EXT +#endif +#if !defined(GL_COMPRESSED_RG_RGTC2) +#define GL_COMPRESSED_RG_RGTC2 0x8DBD // same as GL_COMPRESSED_RG_RGTC2_EXT +#endif +#if !defined(GL_COMPRESSED_SIGNED_RED_RGTC1) +#define GL_COMPRESSED_SIGNED_RED_RGTC1 0x8DBC // same as GL_COMPRESSED_SIGNED_RED_RGTC1_EXT +#endif +#if !defined(GL_COMPRESSED_SIGNED_RG_RGTC2) +#define GL_COMPRESSED_SIGNED_RG_RGTC2 0x8DBE // same as GL_COMPRESSED_SIGNED_RG_RGTC2_EXT +#endif + +#if !defined(GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT) +#define GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT \ + 0x8E8E // same as GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_ARB +#endif +#if !defined(GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT) +#define GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT \ + 0x8E8F // same as GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT_ARB +#endif +#if !defined(GL_COMPRESSED_RGBA_BPTC_UNORM) +#define GL_COMPRESSED_RGBA_BPTC_UNORM 0x8E8C // same as GL_COMPRESSED_RGBA_BPTC_UNORM_ARB +#endif +#if !defined(GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM) +#define GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM \ + 0x8E8D // same as GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM_ARB +#endif + +// +// ETC +// + +#if !defined(GL_ETC1_RGB8_OES) +#define GL_ETC1_RGB8_OES 0x8D64 +#endif + +#if !defined(GL_COMPRESSED_RGB8_ETC2) +#define GL_COMPRESSED_RGB8_ETC2 0x9274 +#endif +#if !defined(GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2) +#define GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2 0x9276 +#endif +#if !defined(GL_COMPRESSED_RGBA8_ETC2_EAC) +#define GL_COMPRESSED_RGBA8_ETC2_EAC 0x9278 +#endif + +#if !defined(GL_COMPRESSED_SRGB8_ETC2) +#define GL_COMPRESSED_SRGB8_ETC2 0x9275 +#endif +#if !defined(GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2) +#define GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2 0x9277 +#endif +#if !defined(GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC) +#define GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC 0x9279 +#endif + +#if !defined(GL_COMPRESSED_R11_EAC) +#define GL_COMPRESSED_R11_EAC 0x9270 +#endif +#if !defined(GL_COMPRESSED_RG11_EAC) +#define GL_COMPRESSED_RG11_EAC 0x9272 +#endif +#if !defined(GL_COMPRESSED_SIGNED_R11_EAC) +#define GL_COMPRESSED_SIGNED_R11_EAC 0x9271 +#endif +#if !defined(GL_COMPRESSED_SIGNED_RG11_EAC) +#define GL_COMPRESSED_SIGNED_RG11_EAC 0x9273 +#endif + +// +// PVRTC +// + +#if !defined(GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG) +#define GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG 0x8C01 +#define GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG 0x8C00 +#define GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG 0x8C03 +#define GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG 0x8C02 +#endif +#if !defined(GL_COMPRESSED_RGBA_PVRTC_2BPPV2_IMG) +#define GL_COMPRESSED_RGBA_PVRTC_2BPPV2_IMG 0x9137 +#define GL_COMPRESSED_RGBA_PVRTC_4BPPV2_IMG 0x9138 +#endif +#if !defined(GL_COMPRESSED_SRGB_PVRTC_2BPPV1_EXT) +#define GL_COMPRESSED_SRGB_PVRTC_2BPPV1_EXT 0x8A54 +#define GL_COMPRESSED_SRGB_PVRTC_4BPPV1_EXT 0x8A55 +#define GL_COMPRESSED_SRGB_ALPHA_PVRTC_2BPPV1_EXT 0x8A56 +#define GL_COMPRESSED_SRGB_ALPHA_PVRTC_4BPPV1_EXT 0x8A57 +#endif +#if !defined(GL_COMPRESSED_SRGB_ALPHA_PVRTC_2BPPV2_IMG) +#define GL_COMPRESSED_SRGB_ALPHA_PVRTC_2BPPV2_IMG 0x93F0 +#define GL_COMPRESSED_SRGB_ALPHA_PVRTC_4BPPV2_IMG 0x93F1 +#endif + +// +// ASTC +// + +#if !defined(GL_COMPRESSED_RGBA_ASTC_4x4_KHR) +#define GL_COMPRESSED_RGBA_ASTC_4x4_KHR 0x93B0 +#define GL_COMPRESSED_RGBA_ASTC_5x4_KHR 0x93B1 +#define GL_COMPRESSED_RGBA_ASTC_5x5_KHR 0x93B2 +#define GL_COMPRESSED_RGBA_ASTC_6x5_KHR 0x93B3 +#define GL_COMPRESSED_RGBA_ASTC_6x6_KHR 0x93B4 +#define GL_COMPRESSED_RGBA_ASTC_8x5_KHR 0x93B5 +#define GL_COMPRESSED_RGBA_ASTC_8x6_KHR 0x93B6 +#define GL_COMPRESSED_RGBA_ASTC_8x8_KHR 0x93B7 +#define GL_COMPRESSED_RGBA_ASTC_10x5_KHR 0x93B8 +#define GL_COMPRESSED_RGBA_ASTC_10x6_KHR 0x93B9 +#define GL_COMPRESSED_RGBA_ASTC_10x8_KHR 0x93BA +#define GL_COMPRESSED_RGBA_ASTC_10x10_KHR 0x93BB +#define GL_COMPRESSED_RGBA_ASTC_12x10_KHR 0x93BC +#define GL_COMPRESSED_RGBA_ASTC_12x12_KHR 0x93BD +#endif + +#if !defined(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR) +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR 0x93D0 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR 0x93D1 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR 0x93D2 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR 0x93D3 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR 0x93D4 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR 0x93D5 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR 0x93D6 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR 0x93D7 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR 0x93D8 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR 0x93D9 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR 0x93DA +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR 0x93DB +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR 0x93DC +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR 0x93DD +#endif + +#if !defined(GL_COMPRESSED_RGBA_ASTC_3x3x3_OES) +#define GL_COMPRESSED_RGBA_ASTC_3x3x3_OES 0x93C0 +#define GL_COMPRESSED_RGBA_ASTC_4x3x3_OES 0x93C1 +#define GL_COMPRESSED_RGBA_ASTC_4x4x3_OES 0x93C2 +#define GL_COMPRESSED_RGBA_ASTC_4x4x4_OES 0x93C3 +#define GL_COMPRESSED_RGBA_ASTC_5x4x4_OES 0x93C4 +#define GL_COMPRESSED_RGBA_ASTC_5x5x4_OES 0x93C5 +#define GL_COMPRESSED_RGBA_ASTC_5x5x5_OES 0x93C6 +#define GL_COMPRESSED_RGBA_ASTC_6x5x5_OES 0x93C7 +#define GL_COMPRESSED_RGBA_ASTC_6x6x5_OES 0x93C8 +#define GL_COMPRESSED_RGBA_ASTC_6x6x6_OES 0x93C9 +#endif + +#if !defined(GL_COMPRESSED_SRGB8_ALPHA8_ASTC_3x3x3_OES) +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_3x3x3_OES 0x93E0 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x3x3_OES 0x93E1 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4x3_OES 0x93E2 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4x4_OES 0x93E3 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4x4_OES 0x93E4 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5x4_OES 0x93E5 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5x5_OES 0x93E6 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5x5_OES 0x93E7 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6x5_OES 0x93E8 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6x6_OES 0x93E9 +#endif + +// +// ATC +// + +#if !defined(GL_ATC_RGB_AMD) +#define GL_ATC_RGB_AMD 0x8C92 +#define GL_ATC_RGBA_EXPLICIT_ALPHA_AMD 0x8C93 +#define GL_ATC_RGBA_INTERPOLATED_ALPHA_AMD 0x87EE +#endif + +// +// Palletized (combined palette) +// + +#if !defined(GL_PALETTE4_RGB8_OES) +#define GL_PALETTE4_RGB8_OES 0x8B90 +#define GL_PALETTE4_RGBA8_OES 0x8B91 +#define GL_PALETTE4_R5_G6_B5_OES 0x8B92 +#define GL_PALETTE4_RGBA4_OES 0x8B93 +#define GL_PALETTE4_RGB5_A1_OES 0x8B94 +#define GL_PALETTE8_RGB8_OES 0x8B95 +#define GL_PALETTE8_RGBA8_OES 0x8B96 +#define GL_PALETTE8_R5_G6_B5_OES 0x8B97 +#define GL_PALETTE8_RGBA4_OES 0x8B98 +#define GL_PALETTE8_RGB5_A1_OES 0x8B99 +#endif + +// +// Palletized (separate palette) +// + +#if !defined(GL_COLOR_INDEX1_EXT) +#define GL_COLOR_INDEX1_EXT 0x80E2 // deprecated +#define GL_COLOR_INDEX2_EXT 0x80E3 // deprecated +#define GL_COLOR_INDEX4_EXT 0x80E4 // deprecated +#define GL_COLOR_INDEX8_EXT 0x80E5 // deprecated +#define GL_COLOR_INDEX12_EXT 0x80E6 // deprecated +#define GL_COLOR_INDEX16_EXT 0x80E7 // deprecated +#endif + +// +// Depth/stencil +// + +#if !defined(GL_DEPTH_COMPONENT16) +#define GL_DEPTH_COMPONENT16 \ + 0x81A5 // same as GL_DEPTH_COMPONENT16_SGIX and GL_DEPTH_COMPONENT16_ARB +#endif +#if !defined(GL_DEPTH_COMPONENT24) +#define GL_DEPTH_COMPONENT24 \ + 0x81A6 // same as GL_DEPTH_COMPONENT24_SGIX and GL_DEPTH_COMPONENT24_ARB +#endif +#if !defined(GL_DEPTH_COMPONENT32) +#define GL_DEPTH_COMPONENT32 \ + 0x81A7 // same as GL_DEPTH_COMPONENT32_SGIX and GL_DEPTH_COMPONENT32_ARB and + // GL_DEPTH_COMPONENT32_OES +#endif +#if !defined(GL_DEPTH_COMPONENT32F) +#define GL_DEPTH_COMPONENT32F 0x8CAC // same as GL_DEPTH_COMPONENT32F_ARB +#endif +#if !defined(GL_DEPTH_COMPONENT32F_NV) +#define GL_DEPTH_COMPONENT32F_NV 0x8DAB // note that this is different from GL_DEPTH_COMPONENT32F +#endif +#if !defined(GL_STENCIL_INDEX1) +#define GL_STENCIL_INDEX1 0x8D46 // same as GL_STENCIL_INDEX1_EXT +#endif +#if !defined(GL_STENCIL_INDEX4) +#define GL_STENCIL_INDEX4 0x8D47 // same as GL_STENCIL_INDEX4_EXT +#endif +#if !defined(GL_STENCIL_INDEX8) +#define GL_STENCIL_INDEX8 0x8D48 // same as GL_STENCIL_INDEX8_EXT +#endif +#if !defined(GL_STENCIL_INDEX16) +#define GL_STENCIL_INDEX16 0x8D49 // same as GL_STENCIL_INDEX16_EXT +#endif +#if !defined(GL_DEPTH24_STENCIL8) +#define GL_DEPTH24_STENCIL8 0x88F0 // same as GL_DEPTH24_STENCIL8_EXT and GL_DEPTH24_STENCIL8_OES +#endif +#if !defined(GL_DEPTH32F_STENCIL8) +#define GL_DEPTH32F_STENCIL8 0x8CAD // same as GL_DEPTH32F_STENCIL8_ARB +#endif +#if !defined(GL_DEPTH32F_STENCIL8_NV) +#define GL_DEPTH32F_STENCIL8_NV 0x8DAC // note that this is different from GL_DEPTH32F_STENCIL8 +#endif + +static inline GLenum glGetFormatFromInternalFormat(const GLenum internalFormat) { + switch (internalFormat) { + // + // 8 bits per component + // + case GL_R8: + return GL_RED; // 1-component, 8-bit unsigned normalized + case GL_RG8: + return GL_RG; // 2-component, 8-bit unsigned normalized + case GL_RGB8: + return GL_RGB; // 3-component, 8-bit unsigned normalized + case GL_RGBA8: + return GL_RGBA; // 4-component, 8-bit unsigned normalized + + case GL_R8_SNORM: + return GL_RED; // 1-component, 8-bit signed normalized + case GL_RG8_SNORM: + return GL_RG; // 2-component, 8-bit signed normalized + case GL_RGB8_SNORM: + return GL_RGB; // 3-component, 8-bit signed normalized + case GL_RGBA8_SNORM: + return GL_RGBA; // 4-component, 8-bit signed normalized + + case GL_R8UI: + return GL_RED; // 1-component, 8-bit unsigned integer + case GL_RG8UI: + return GL_RG; // 2-component, 8-bit unsigned integer + case GL_RGB8UI: + return GL_RGB; // 3-component, 8-bit unsigned integer + case GL_RGBA8UI: + return GL_RGBA; // 4-component, 8-bit unsigned integer + + case GL_R8I: + return GL_RED; // 1-component, 8-bit signed integer + case GL_RG8I: + return GL_RG; // 2-component, 8-bit signed integer + case GL_RGB8I: + return GL_RGB; // 3-component, 8-bit signed integer + case GL_RGBA8I: + return GL_RGBA; // 4-component, 8-bit signed integer + + case GL_SR8: + return GL_RED; // 1-component, 8-bit sRGB + case GL_SRG8: + return GL_RG; // 2-component, 8-bit sRGB + case GL_SRGB8: + return GL_RGB; // 3-component, 8-bit sRGB + case GL_SRGB8_ALPHA8: + return GL_RGBA; // 4-component, 8-bit sRGB + + // + // 16 bits per component + // + case GL_R16: + return GL_RED; // 1-component, 16-bit unsigned normalized + case GL_RG16: + return GL_RG; // 2-component, 16-bit unsigned normalized + case GL_RGB16: + return GL_RGB; // 3-component, 16-bit unsigned normalized + case GL_RGBA16: + return GL_RGBA; // 4-component, 16-bit unsigned normalized + + case GL_R16_SNORM: + return GL_RED; // 1-component, 16-bit signed normalized + case GL_RG16_SNORM: + return GL_RG; // 2-component, 16-bit signed normalized + case GL_RGB16_SNORM: + return GL_RGB; // 3-component, 16-bit signed normalized + case GL_RGBA16_SNORM: + return GL_RGBA; // 4-component, 16-bit signed normalized + + case GL_R16UI: + return GL_RED; // 1-component, 16-bit unsigned integer + case GL_RG16UI: + return GL_RG; // 2-component, 16-bit unsigned integer + case GL_RGB16UI: + return GL_RGB; // 3-component, 16-bit unsigned integer + case GL_RGBA16UI: + return GL_RGBA; // 4-component, 16-bit unsigned integer + + case GL_R16I: + return GL_RED; // 1-component, 16-bit signed integer + case GL_RG16I: + return GL_RG; // 2-component, 16-bit signed integer + case GL_RGB16I: + return GL_RGB; // 3-component, 16-bit signed integer + case GL_RGBA16I: + return GL_RGBA; // 4-component, 16-bit signed integer + + case GL_R16F: + return GL_RED; // 1-component, 16-bit floating-point + case GL_RG16F: + return GL_RG; // 2-component, 16-bit floating-point + case GL_RGB16F: + return GL_RGB; // 3-component, 16-bit floating-point + case GL_RGBA16F: + return GL_RGBA; // 4-component, 16-bit floating-point + + // + // 32 bits per component + // + case GL_R32UI: + return GL_RED; // 1-component, 32-bit unsigned integer + case GL_RG32UI: + return GL_RG; // 2-component, 32-bit unsigned integer + case GL_RGB32UI: + return GL_RGB; // 3-component, 32-bit unsigned integer + case GL_RGBA32UI: + return GL_RGBA; // 4-component, 32-bit unsigned integer + + case GL_R32I: + return GL_RED; // 1-component, 32-bit signed integer + case GL_RG32I: + return GL_RG; // 2-component, 32-bit signed integer + case GL_RGB32I: + return GL_RGB; // 3-component, 32-bit signed integer + case GL_RGBA32I: + return GL_RGBA; // 4-component, 32-bit signed integer + + case GL_R32F: + return GL_RED; // 1-component, 32-bit floating-point + case GL_RG32F: + return GL_RG; // 2-component, 32-bit floating-point + case GL_RGB32F: + return GL_RGB; // 3-component, 32-bit floating-point + case GL_RGBA32F: + return GL_RGBA; // 4-component, 32-bit floating-point + + // + // Packed + // + case GL_R3_G3_B2: + return GL_RGB; // 3-component 3:3:2, unsigned normalized + case GL_RGB4: + return GL_RGB; // 3-component 4:4:4, unsigned normalized + case GL_RGB5: + return GL_RGB; // 3-component 5:5:5, unsigned normalized + case GL_RGB565: + return GL_RGB; // 3-component 5:6:5, unsigned normalized + case GL_RGB10: + return GL_RGB; // 3-component 10:10:10, unsigned normalized + case GL_RGB12: + return GL_RGB; // 3-component 12:12:12, unsigned normalized + case GL_RGBA2: + return GL_RGBA; // 4-component 2:2:2:2, unsigned normalized + case GL_RGBA4: + return GL_RGBA; // 4-component 4:4:4:4, unsigned normalized + case GL_RGBA12: + return GL_RGBA; // 4-component 12:12:12:12, unsigned normalized + case GL_RGB5_A1: + return GL_RGBA; // 4-component 5:5:5:1, unsigned normalized + case GL_RGB10_A2: + return GL_RGBA; // 4-component 10:10:10:2, unsigned normalized + case GL_RGB10_A2UI: + return GL_RGBA; // 4-component 10:10:10:2, unsigned integer + case GL_R11F_G11F_B10F: + return GL_RGB; // 3-component 11:11:10, floating-point + case GL_RGB9_E5: + return GL_RGB; // 3-component/exp 9:9:9/5, floating-point + + // + // S3TC/DXT/BC + // + + case GL_COMPRESSED_RGB_S3TC_DXT1_EXT: + return GL_RGB; // line through 3D space, 4x4 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: + return GL_RGBA; // line through 3D space plus 1-bit alpha, 4x4 blocks, unsigned + // normalized + case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: + return GL_RGBA; // line through 3D space plus line through 1D space, 4x4 blocks, + // unsigned normalized + case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT: + return GL_RGBA; // line through 3D space plus 4-bit alpha, 4x4 blocks, unsigned + // normalized + + case GL_COMPRESSED_SRGB_S3TC_DXT1_EXT: + return GL_RGB; // line through 3D space, 4x4 blocks, sRGB + case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT: + return GL_RGBA; // line through 3D space plus 1-bit alpha, 4x4 blocks, sRGB + case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT: + return GL_RGBA; // line through 3D space plus line through 1D space, 4x4 blocks, sRGB + case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT: + return GL_RGBA; // line through 3D space plus 4-bit alpha, 4x4 blocks, sRGB + + case GL_COMPRESSED_LUMINANCE_LATC1_EXT: + return GL_RED; // line through 1D space, 4x4 blocks, unsigned normalized + case GL_COMPRESSED_LUMINANCE_ALPHA_LATC2_EXT: + return GL_RG; // two lines through 1D space, 4x4 blocks, unsigned normalized + case GL_COMPRESSED_SIGNED_LUMINANCE_LATC1_EXT: + return GL_RED; // line through 1D space, 4x4 blocks, signed normalized + case GL_COMPRESSED_SIGNED_LUMINANCE_ALPHA_LATC2_EXT: + return GL_RG; // two lines through 1D space, 4x4 blocks, signed normalized + + case GL_COMPRESSED_RED_RGTC1: + return GL_RED; // line through 1D space, 4x4 blocks, unsigned normalized + case GL_COMPRESSED_RG_RGTC2: + return GL_RG; // two lines through 1D space, 4x4 blocks, unsigned normalized + case GL_COMPRESSED_SIGNED_RED_RGTC1: + return GL_RED; // line through 1D space, 4x4 blocks, signed normalized + case GL_COMPRESSED_SIGNED_RG_RGTC2: + return GL_RG; // two lines through 1D space, 4x4 blocks, signed normalized + + case GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT: + return GL_RGB; // 3-component, 4x4 blocks, unsigned floating-point + case GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT: + return GL_RGB; // 3-component, 4x4 blocks, signed floating-point + case GL_COMPRESSED_RGBA_BPTC_UNORM: + return GL_RGBA; // 4-component, 4x4 blocks, unsigned normalized + case GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM: + return GL_RGBA; // 4-component, 4x4 blocks, sRGB + + // + // ETC + // + case GL_ETC1_RGB8_OES: + return GL_RGB; // 3-component ETC1, 4x4 blocks, unsigned normalized + + case GL_COMPRESSED_RGB8_ETC2: + return GL_RGB; // 3-component ETC2, 4x4 blocks, unsigned normalized + case GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2: + return GL_RGBA; // 4-component ETC2 with 1-bit alpha, 4x4 blocks, unsigned normalized + case GL_COMPRESSED_RGBA8_ETC2_EAC: + return GL_RGBA; // 4-component ETC2, 4x4 blocks, unsigned normalized + + case GL_COMPRESSED_SRGB8_ETC2: + return GL_RGB; // 3-component ETC2, 4x4 blocks, sRGB + case GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2: + return GL_RGBA; // 4-component ETC2 with 1-bit alpha, 4x4 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC: + return GL_RGBA; // 4-component ETC2, 4x4 blocks, sRGB + + case GL_COMPRESSED_R11_EAC: + return GL_RED; // 1-component ETC, 4x4 blocks, unsigned normalized + case GL_COMPRESSED_RG11_EAC: + return GL_RG; // 2-component ETC, 4x4 blocks, unsigned normalized + case GL_COMPRESSED_SIGNED_R11_EAC: + return GL_RED; // 1-component ETC, 4x4 blocks, signed normalized + case GL_COMPRESSED_SIGNED_RG11_EAC: + return GL_RG; // 2-component ETC, 4x4 blocks, signed normalized + + // + // PVRTC + // + case GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG: + return GL_RGB; // 3-component PVRTC, 16x8 blocks, unsigned normalized + case GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG: + return GL_RGB; // 3-component PVRTC, 8x8 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG: + return GL_RGBA; // 4-component PVRTC, 16x8 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG: + return GL_RGBA; // 4-component PVRTC, 8x8 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_PVRTC_2BPPV2_IMG: + return GL_RGBA; // 4-component PVRTC, 8x4 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_PVRTC_4BPPV2_IMG: + return GL_RGBA; // 4-component PVRTC, 4x4 blocks, unsigned normalized + + case GL_COMPRESSED_SRGB_PVRTC_2BPPV1_EXT: + return GL_RGB; // 3-component PVRTC, 16x8 blocks, sRGB + case GL_COMPRESSED_SRGB_PVRTC_4BPPV1_EXT: + return GL_RGB; // 3-component PVRTC, 8x8 blocks, sRGB + case GL_COMPRESSED_SRGB_ALPHA_PVRTC_2BPPV1_EXT: + return GL_RGBA; // 4-component PVRTC, 16x8 blocks, sRGB + case GL_COMPRESSED_SRGB_ALPHA_PVRTC_4BPPV1_EXT: + return GL_RGBA; // 4-component PVRTC, 8x8 blocks, sRGB + case GL_COMPRESSED_SRGB_ALPHA_PVRTC_2BPPV2_IMG: + return GL_RGBA; // 4-component PVRTC, 8x4 blocks, sRGB + case GL_COMPRESSED_SRGB_ALPHA_PVRTC_4BPPV2_IMG: + return GL_RGBA; // 4-component PVRTC, 4x4 blocks, sRGB + + // + // ASTC + // + case GL_COMPRESSED_RGBA_ASTC_4x4_KHR: + return GL_RGBA; // 4-component ASTC, 4x4 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_ASTC_5x4_KHR: + return GL_RGBA; // 4-component ASTC, 5x4 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_ASTC_5x5_KHR: + return GL_RGBA; // 4-component ASTC, 5x5 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_ASTC_6x5_KHR: + return GL_RGBA; // 4-component ASTC, 6x5 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_ASTC_6x6_KHR: + return GL_RGBA; // 4-component ASTC, 6x6 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_ASTC_8x5_KHR: + return GL_RGBA; // 4-component ASTC, 8x5 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_ASTC_8x6_KHR: + return GL_RGBA; // 4-component ASTC, 8x6 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_ASTC_8x8_KHR: + return GL_RGBA; // 4-component ASTC, 8x8 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_ASTC_10x5_KHR: + return GL_RGBA; // 4-component ASTC, 10x5 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_ASTC_10x6_KHR: + return GL_RGBA; // 4-component ASTC, 10x6 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_ASTC_10x8_KHR: + return GL_RGBA; // 4-component ASTC, 10x8 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_ASTC_10x10_KHR: + return GL_RGBA; // 4-component ASTC, 10x10 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_ASTC_12x10_KHR: + return GL_RGBA; // 4-component ASTC, 12x10 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_ASTC_12x12_KHR: + return GL_RGBA; // 4-component ASTC, 12x12 blocks, unsigned normalized + + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR: + return GL_RGBA; // 4-component ASTC, 4x4 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR: + return GL_RGBA; // 4-component ASTC, 5x4 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR: + return GL_RGBA; // 4-component ASTC, 5x5 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR: + return GL_RGBA; // 4-component ASTC, 6x5 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR: + return GL_RGBA; // 4-component ASTC, 6x6 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR: + return GL_RGBA; // 4-component ASTC, 8x5 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR: + return GL_RGBA; // 4-component ASTC, 8x6 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR: + return GL_RGBA; // 4-component ASTC, 8x8 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR: + return GL_RGBA; // 4-component ASTC, 10x5 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR: + return GL_RGBA; // 4-component ASTC, 10x6 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR: + return GL_RGBA; // 4-component ASTC, 10x8 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR: + return GL_RGBA; // 4-component ASTC, 10x10 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR: + return GL_RGBA; // 4-component ASTC, 12x10 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR: + return GL_RGBA; // 4-component ASTC, 12x12 blocks, sRGB + + case GL_COMPRESSED_RGBA_ASTC_3x3x3_OES: + return GL_RGBA; // 4-component ASTC, 3x3x3 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_ASTC_4x3x3_OES: + return GL_RGBA; // 4-component ASTC, 4x3x3 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_ASTC_4x4x3_OES: + return GL_RGBA; // 4-component ASTC, 4x4x3 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_ASTC_4x4x4_OES: + return GL_RGBA; // 4-component ASTC, 4x4x4 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_ASTC_5x4x4_OES: + return GL_RGBA; // 4-component ASTC, 5x4x4 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_ASTC_5x5x4_OES: + return GL_RGBA; // 4-component ASTC, 5x5x4 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_ASTC_5x5x5_OES: + return GL_RGBA; // 4-component ASTC, 5x5x5 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_ASTC_6x5x5_OES: + return GL_RGBA; // 4-component ASTC, 6x5x5 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_ASTC_6x6x5_OES: + return GL_RGBA; // 4-component ASTC, 6x6x5 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_ASTC_6x6x6_OES: + return GL_RGBA; // 4-component ASTC, 6x6x6 blocks, unsigned normalized + + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_3x3x3_OES: + return GL_RGBA; // 4-component ASTC, 3x3x3 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x3x3_OES: + return GL_RGBA; // 4-component ASTC, 4x3x3 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4x3_OES: + return GL_RGBA; // 4-component ASTC, 4x4x3 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4x4_OES: + return GL_RGBA; // 4-component ASTC, 4x4x4 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4x4_OES: + return GL_RGBA; // 4-component ASTC, 5x4x4 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5x4_OES: + return GL_RGBA; // 4-component ASTC, 5x5x4 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5x5_OES: + return GL_RGBA; // 4-component ASTC, 5x5x5 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5x5_OES: + return GL_RGBA; // 4-component ASTC, 6x5x5 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6x5_OES: + return GL_RGBA; // 4-component ASTC, 6x6x5 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6x6_OES: + return GL_RGBA; // 4-component ASTC, 6x6x6 blocks, sRGB + + // + // ATC + // + case GL_ATC_RGB_AMD: + return GL_RGB; // 3-component, 4x4 blocks, unsigned normalized + case GL_ATC_RGBA_EXPLICIT_ALPHA_AMD: + return GL_RGBA; // 4-component, 4x4 blocks, unsigned normalized + case GL_ATC_RGBA_INTERPOLATED_ALPHA_AMD: + return GL_RGBA; // 4-component, 4x4 blocks, unsigned normalized + + // + // Palletized + // + case GL_PALETTE4_RGB8_OES: + return GL_RGB; // 3-component 8:8:8, 4-bit palette, unsigned normalized + case GL_PALETTE4_RGBA8_OES: + return GL_RGBA; // 4-component 8:8:8:8, 4-bit palette, unsigned normalized + case GL_PALETTE4_R5_G6_B5_OES: + return GL_RGB; // 3-component 5:6:5, 4-bit palette, unsigned normalized + case GL_PALETTE4_RGBA4_OES: + return GL_RGBA; // 4-component 4:4:4:4, 4-bit palette, unsigned normalized + case GL_PALETTE4_RGB5_A1_OES: + return GL_RGBA; // 4-component 5:5:5:1, 4-bit palette, unsigned normalized + case GL_PALETTE8_RGB8_OES: + return GL_RGB; // 3-component 8:8:8, 8-bit palette, unsigned normalized + case GL_PALETTE8_RGBA8_OES: + return GL_RGBA; // 4-component 8:8:8:8, 8-bit palette, unsigned normalized + case GL_PALETTE8_R5_G6_B5_OES: + return GL_RGB; // 3-component 5:6:5, 8-bit palette, unsigned normalized + case GL_PALETTE8_RGBA4_OES: + return GL_RGBA; // 4-component 4:4:4:4, 8-bit palette, unsigned normalized + case GL_PALETTE8_RGB5_A1_OES: + return GL_RGBA; // 4-component 5:5:5:1, 8-bit palette, unsigned normalized + + // + // Depth/stencil + // + case GL_DEPTH_COMPONENT16: + return GL_DEPTH_COMPONENT; + case GL_DEPTH_COMPONENT24: + return GL_DEPTH_COMPONENT; + case GL_DEPTH_COMPONENT32: + return GL_DEPTH_COMPONENT; + case GL_DEPTH_COMPONENT32F: + return GL_DEPTH_COMPONENT; + case GL_DEPTH_COMPONENT32F_NV: + return GL_DEPTH_COMPONENT; + case GL_STENCIL_INDEX1: + return GL_STENCIL_INDEX; + case GL_STENCIL_INDEX4: + return GL_STENCIL_INDEX; + case GL_STENCIL_INDEX8: + return GL_STENCIL_INDEX; + case GL_STENCIL_INDEX16: + return GL_STENCIL_INDEX; + case GL_DEPTH24_STENCIL8: + return GL_DEPTH_STENCIL; + case GL_DEPTH32F_STENCIL8: + return GL_DEPTH_STENCIL; + case GL_DEPTH32F_STENCIL8_NV: + return GL_DEPTH_STENCIL; + + default: + return GL_INVALID_VALUE; + } +} + +static inline GLenum glGetTypeFromInternalFormat(const GLenum internalFormat) { + switch (internalFormat) { + // + // 8 bits per component + // + case GL_R8: + return GL_UNSIGNED_BYTE; // 1-component, 8-bit unsigned normalized + case GL_RG8: + return GL_UNSIGNED_BYTE; // 2-component, 8-bit unsigned normalized + case GL_RGB8: + return GL_UNSIGNED_BYTE; // 3-component, 8-bit unsigned normalized + case GL_RGBA8: + return GL_UNSIGNED_BYTE; // 4-component, 8-bit unsigned normalized + + case GL_R8_SNORM: + return GL_BYTE; // 1-component, 8-bit signed normalized + case GL_RG8_SNORM: + return GL_BYTE; // 2-component, 8-bit signed normalized + case GL_RGB8_SNORM: + return GL_BYTE; // 3-component, 8-bit signed normalized + case GL_RGBA8_SNORM: + return GL_BYTE; // 4-component, 8-bit signed normalized + + case GL_R8UI: + return GL_UNSIGNED_BYTE; // 1-component, 8-bit unsigned integer + case GL_RG8UI: + return GL_UNSIGNED_BYTE; // 2-component, 8-bit unsigned integer + case GL_RGB8UI: + return GL_UNSIGNED_BYTE; // 3-component, 8-bit unsigned integer + case GL_RGBA8UI: + return GL_UNSIGNED_BYTE; // 4-component, 8-bit unsigned integer + + case GL_R8I: + return GL_BYTE; // 1-component, 8-bit signed integer + case GL_RG8I: + return GL_BYTE; // 2-component, 8-bit signed integer + case GL_RGB8I: + return GL_BYTE; // 3-component, 8-bit signed integer + case GL_RGBA8I: + return GL_BYTE; // 4-component, 8-bit signed integer + + case GL_SR8: + return GL_UNSIGNED_BYTE; // 1-component, 8-bit sRGB + case GL_SRG8: + return GL_UNSIGNED_BYTE; // 2-component, 8-bit sRGB + case GL_SRGB8: + return GL_UNSIGNED_BYTE; // 3-component, 8-bit sRGB + case GL_SRGB8_ALPHA8: + return GL_UNSIGNED_BYTE; // 4-component, 8-bit sRGB + + // + // 16 bits per component + // + case GL_R16: + return GL_UNSIGNED_SHORT; // 1-component, 16-bit unsigned normalized + case GL_RG16: + return GL_UNSIGNED_SHORT; // 2-component, 16-bit unsigned normalized + case GL_RGB16: + return GL_UNSIGNED_SHORT; // 3-component, 16-bit unsigned normalized + case GL_RGBA16: + return GL_UNSIGNED_SHORT; // 4-component, 16-bit unsigned normalized + + case GL_R16_SNORM: + return GL_SHORT; // 1-component, 16-bit signed normalized + case GL_RG16_SNORM: + return GL_SHORT; // 2-component, 16-bit signed normalized + case GL_RGB16_SNORM: + return GL_SHORT; // 3-component, 16-bit signed normalized + case GL_RGBA16_SNORM: + return GL_SHORT; // 4-component, 16-bit signed normalized + + case GL_R16UI: + return GL_UNSIGNED_SHORT; // 1-component, 16-bit unsigned integer + case GL_RG16UI: + return GL_UNSIGNED_SHORT; // 2-component, 16-bit unsigned integer + case GL_RGB16UI: + return GL_UNSIGNED_SHORT; // 3-component, 16-bit unsigned integer + case GL_RGBA16UI: + return GL_UNSIGNED_SHORT; // 4-component, 16-bit unsigned integer + + case GL_R16I: + return GL_SHORT; // 1-component, 16-bit signed integer + case GL_RG16I: + return GL_SHORT; // 2-component, 16-bit signed integer + case GL_RGB16I: + return GL_SHORT; // 3-component, 16-bit signed integer + case GL_RGBA16I: + return GL_SHORT; // 4-component, 16-bit signed integer + + case GL_R16F: + return GL_HALF_FLOAT; // 1-component, 16-bit floating-point + case GL_RG16F: + return GL_HALF_FLOAT; // 2-component, 16-bit floating-point + case GL_RGB16F: + return GL_HALF_FLOAT; // 3-component, 16-bit floating-point + case GL_RGBA16F: + return GL_HALF_FLOAT; // 4-component, 16-bit floating-point + + // + // 32 bits per component + // + case GL_R32UI: + return GL_UNSIGNED_INT; // 1-component, 32-bit unsigned integer + case GL_RG32UI: + return GL_UNSIGNED_INT; // 2-component, 32-bit unsigned integer + case GL_RGB32UI: + return GL_UNSIGNED_INT; // 3-component, 32-bit unsigned integer + case GL_RGBA32UI: + return GL_UNSIGNED_INT; // 4-component, 32-bit unsigned integer + + case GL_R32I: + return GL_INT; // 1-component, 32-bit signed integer + case GL_RG32I: + return GL_INT; // 2-component, 32-bit signed integer + case GL_RGB32I: + return GL_INT; // 3-component, 32-bit signed integer + case GL_RGBA32I: + return GL_INT; // 4-component, 32-bit signed integer + + case GL_R32F: + return GL_FLOAT; // 1-component, 32-bit floating-point + case GL_RG32F: + return GL_FLOAT; // 2-component, 32-bit floating-point + case GL_RGB32F: + return GL_FLOAT; // 3-component, 32-bit floating-point + case GL_RGBA32F: + return GL_FLOAT; // 4-component, 32-bit floating-point + + // + // Packed + // + case GL_R3_G3_B2: + return GL_UNSIGNED_BYTE_2_3_3_REV; // 3-component 3:3:2, unsigned normalized + case GL_RGB4: + return GL_UNSIGNED_SHORT_4_4_4_4; // 3-component 4:4:4, unsigned normalized + case GL_RGB5: + return GL_UNSIGNED_SHORT_5_5_5_1; // 3-component 5:5:5, unsigned normalized + case GL_RGB565: + return GL_UNSIGNED_SHORT_5_6_5; // 3-component 5:6:5, unsigned normalized + case GL_RGB10: + return GL_UNSIGNED_INT_10_10_10_2; // 3-component 10:10:10, unsigned normalized + case GL_RGB12: + return GL_UNSIGNED_SHORT; // 3-component 12:12:12, unsigned normalized + case GL_RGBA2: + return GL_UNSIGNED_BYTE; // 4-component 2:2:2:2, unsigned normalized + case GL_RGBA4: + return GL_UNSIGNED_SHORT_4_4_4_4; // 4-component 4:4:4:4, unsigned normalized + case GL_RGBA12: + return GL_UNSIGNED_SHORT; // 4-component 12:12:12:12, unsigned normalized + case GL_RGB5_A1: + return GL_UNSIGNED_SHORT_5_5_5_1; // 4-component 5:5:5:1, unsigned normalized + case GL_RGB10_A2: + return GL_UNSIGNED_INT_2_10_10_10_REV; // 4-component 10:10:10:2, unsigned normalized + case GL_RGB10_A2UI: + return GL_UNSIGNED_INT_2_10_10_10_REV; // 4-component 10:10:10:2, unsigned integer + case GL_R11F_G11F_B10F: + return GL_UNSIGNED_INT_10F_11F_11F_REV; // 3-component 11:11:10, floating-point + case GL_RGB9_E5: + return GL_UNSIGNED_INT_5_9_9_9_REV; // 3-component/exp 9:9:9/5, floating-point + + // + // S3TC/DXT/BC + // + + case GL_COMPRESSED_RGB_S3TC_DXT1_EXT: + return GL_UNSIGNED_BYTE; // line through 3D space, 4x4 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: + return GL_UNSIGNED_BYTE; // line through 3D space plus 1-bit alpha, 4x4 blocks, unsigned + // normalized + case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: + return GL_UNSIGNED_BYTE; // line through 3D space plus line through 1D space, 4x4 + // blocks, unsigned normalized + case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT: + return GL_UNSIGNED_BYTE; // line through 3D space plus 4-bit alpha, 4x4 blocks, unsigned + // normalized + + case GL_COMPRESSED_SRGB_S3TC_DXT1_EXT: + return GL_UNSIGNED_BYTE; // line through 3D space, 4x4 blocks, sRGB + case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT: + return GL_UNSIGNED_BYTE; // line through 3D space plus 1-bit alpha, 4x4 blocks, sRGB + case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT: + return GL_UNSIGNED_BYTE; // line through 3D space plus line through 1D space, 4x4 + // blocks, sRGB + case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT: + return GL_UNSIGNED_BYTE; // line through 3D space plus 4-bit alpha, 4x4 blocks, sRGB + + case GL_COMPRESSED_LUMINANCE_LATC1_EXT: + return GL_UNSIGNED_BYTE; // line through 1D space, 4x4 blocks, unsigned normalized + case GL_COMPRESSED_LUMINANCE_ALPHA_LATC2_EXT: + return GL_UNSIGNED_BYTE; // two lines through 1D space, 4x4 blocks, unsigned normalized + case GL_COMPRESSED_SIGNED_LUMINANCE_LATC1_EXT: + return GL_UNSIGNED_BYTE; // line through 1D space, 4x4 blocks, signed normalized + case GL_COMPRESSED_SIGNED_LUMINANCE_ALPHA_LATC2_EXT: + return GL_UNSIGNED_BYTE; // two lines through 1D space, 4x4 blocks, signed normalized + + case GL_COMPRESSED_RED_RGTC1: + return GL_UNSIGNED_BYTE; // line through 1D space, 4x4 blocks, unsigned normalized + case GL_COMPRESSED_RG_RGTC2: + return GL_UNSIGNED_BYTE; // two lines through 1D space, 4x4 blocks, unsigned normalized + case GL_COMPRESSED_SIGNED_RED_RGTC1: + return GL_UNSIGNED_BYTE; // line through 1D space, 4x4 blocks, signed normalized + case GL_COMPRESSED_SIGNED_RG_RGTC2: + return GL_UNSIGNED_BYTE; // two lines through 1D space, 4x4 blocks, signed normalized + + case GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT: + return GL_FLOAT; // 3-component, 4x4 blocks, unsigned floating-point + case GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT: + return GL_FLOAT; // 3-component, 4x4 blocks, signed floating-point + case GL_COMPRESSED_RGBA_BPTC_UNORM: + return GL_UNSIGNED_BYTE; // 4-component, 4x4 blocks, unsigned normalized + case GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM: + return GL_UNSIGNED_BYTE; // 4-component, 4x4 blocks, sRGB + + // + // ETC + // + case GL_ETC1_RGB8_OES: + return GL_UNSIGNED_BYTE; // 3-component ETC1, 4x4 blocks, unsigned normalized" ), + + case GL_COMPRESSED_RGB8_ETC2: + return GL_UNSIGNED_BYTE; // 3-component ETC2, 4x4 blocks, unsigned normalized + case GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2: + return GL_UNSIGNED_BYTE; // 4-component ETC2 with 1-bit alpha, 4x4 blocks, unsigned + // normalized + case GL_COMPRESSED_RGBA8_ETC2_EAC: + return GL_UNSIGNED_BYTE; // 4-component ETC2, 4x4 blocks, unsigned normalized + + case GL_COMPRESSED_SRGB8_ETC2: + return GL_UNSIGNED_BYTE; // 3-component ETC2, 4x4 blocks, sRGB + case GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2: + return GL_UNSIGNED_BYTE; // 4-component ETC2 with 1-bit alpha, 4x4 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC: + return GL_UNSIGNED_BYTE; // 4-component ETC2, 4x4 blocks, sRGB + + case GL_COMPRESSED_R11_EAC: + return GL_UNSIGNED_BYTE; // 1-component ETC, 4x4 blocks, unsigned normalized + case GL_COMPRESSED_RG11_EAC: + return GL_UNSIGNED_BYTE; // 2-component ETC, 4x4 blocks, unsigned normalized + case GL_COMPRESSED_SIGNED_R11_EAC: + return GL_UNSIGNED_BYTE; // 1-component ETC, 4x4 blocks, signed normalized + case GL_COMPRESSED_SIGNED_RG11_EAC: + return GL_UNSIGNED_BYTE; // 2-component ETC, 4x4 blocks, signed normalized + + // + // PVRTC + // + case GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG: + return GL_UNSIGNED_BYTE; // 3-component PVRTC, 16x8 blocks, unsigned normalized + case GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG: + return GL_UNSIGNED_BYTE; // 3-component PVRTC, 8x8 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG: + return GL_UNSIGNED_BYTE; // 4-component PVRTC, 16x8 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG: + return GL_UNSIGNED_BYTE; // 4-component PVRTC, 8x8 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_PVRTC_2BPPV2_IMG: + return GL_UNSIGNED_BYTE; // 4-component PVRTC, 8x4 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_PVRTC_4BPPV2_IMG: + return GL_UNSIGNED_BYTE; // 4-component PVRTC, 4x4 blocks, unsigned normalized + + case GL_COMPRESSED_SRGB_PVRTC_2BPPV1_EXT: + return GL_UNSIGNED_BYTE; // 3-component PVRTC, 16x8 blocks, sRGB + case GL_COMPRESSED_SRGB_PVRTC_4BPPV1_EXT: + return GL_UNSIGNED_BYTE; // 3-component PVRTC, 8x8 blocks, sRGB + case GL_COMPRESSED_SRGB_ALPHA_PVRTC_2BPPV1_EXT: + return GL_UNSIGNED_BYTE; // 4-component PVRTC, 16x8 blocks, sRGB + case GL_COMPRESSED_SRGB_ALPHA_PVRTC_4BPPV1_EXT: + return GL_UNSIGNED_BYTE; // 4-component PVRTC, 8x8 blocks, sRGB + case GL_COMPRESSED_SRGB_ALPHA_PVRTC_2BPPV2_IMG: + return GL_UNSIGNED_BYTE; // 4-component PVRTC, 8x4 blocks, sRGB + case GL_COMPRESSED_SRGB_ALPHA_PVRTC_4BPPV2_IMG: + return GL_UNSIGNED_BYTE; // 4-component PVRTC, 4x4 blocks, sRGB + + // + // ASTC + // + case GL_COMPRESSED_RGBA_ASTC_4x4_KHR: + return GL_UNSIGNED_BYTE; // 4-component ASTC, 4x4 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_ASTC_5x4_KHR: + return GL_UNSIGNED_BYTE; // 4-component ASTC, 5x4 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_ASTC_5x5_KHR: + return GL_UNSIGNED_BYTE; // 4-component ASTC, 5x5 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_ASTC_6x5_KHR: + return GL_UNSIGNED_BYTE; // 4-component ASTC, 6x5 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_ASTC_6x6_KHR: + return GL_UNSIGNED_BYTE; // 4-component ASTC, 6x6 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_ASTC_8x5_KHR: + return GL_UNSIGNED_BYTE; // 4-component ASTC, 8x5 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_ASTC_8x6_KHR: + return GL_UNSIGNED_BYTE; // 4-component ASTC, 8x6 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_ASTC_8x8_KHR: + return GL_UNSIGNED_BYTE; // 4-component ASTC, 8x8 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_ASTC_10x5_KHR: + return GL_UNSIGNED_BYTE; // 4-component ASTC, 10x5 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_ASTC_10x6_KHR: + return GL_UNSIGNED_BYTE; // 4-component ASTC, 10x6 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_ASTC_10x8_KHR: + return GL_UNSIGNED_BYTE; // 4-component ASTC, 10x8 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_ASTC_10x10_KHR: + return GL_UNSIGNED_BYTE; // 4-component ASTC, 10x10 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_ASTC_12x10_KHR: + return GL_UNSIGNED_BYTE; // 4-component ASTC, 12x10 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_ASTC_12x12_KHR: + return GL_UNSIGNED_BYTE; // 4-component ASTC, 12x12 blocks, unsigned normalized + + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR: + return GL_UNSIGNED_BYTE; // 4-component ASTC, 4x4 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR: + return GL_UNSIGNED_BYTE; // 4-component ASTC, 5x4 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR: + return GL_UNSIGNED_BYTE; // 4-component ASTC, 5x5 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR: + return GL_UNSIGNED_BYTE; // 4-component ASTC, 6x5 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR: + return GL_UNSIGNED_BYTE; // 4-component ASTC, 6x6 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR: + return GL_UNSIGNED_BYTE; // 4-component ASTC, 8x5 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR: + return GL_UNSIGNED_BYTE; // 4-component ASTC, 8x6 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR: + return GL_UNSIGNED_BYTE; // 4-component ASTC, 8x8 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR: + return GL_UNSIGNED_BYTE; // 4-component ASTC, 10x5 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR: + return GL_UNSIGNED_BYTE; // 4-component ASTC, 10x6 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR: + return GL_UNSIGNED_BYTE; // 4-component ASTC, 10x8 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR: + return GL_UNSIGNED_BYTE; // 4-component ASTC, 10x10 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR: + return GL_UNSIGNED_BYTE; // 4-component ASTC, 12x10 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR: + return GL_UNSIGNED_BYTE; // 4-component ASTC, 12x12 blocks, sRGB + + case GL_COMPRESSED_RGBA_ASTC_3x3x3_OES: + return GL_UNSIGNED_BYTE; // 4-component ASTC, 3x3x3 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_ASTC_4x3x3_OES: + return GL_UNSIGNED_BYTE; // 4-component ASTC, 4x3x3 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_ASTC_4x4x3_OES: + return GL_UNSIGNED_BYTE; // 4-component ASTC, 4x4x3 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_ASTC_4x4x4_OES: + return GL_UNSIGNED_BYTE; // 4-component ASTC, 4x4x4 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_ASTC_5x4x4_OES: + return GL_UNSIGNED_BYTE; // 4-component ASTC, 5x4x4 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_ASTC_5x5x4_OES: + return GL_UNSIGNED_BYTE; // 4-component ASTC, 5x5x4 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_ASTC_5x5x5_OES: + return GL_UNSIGNED_BYTE; // 4-component ASTC, 5x5x5 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_ASTC_6x5x5_OES: + return GL_UNSIGNED_BYTE; // 4-component ASTC, 6x5x5 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_ASTC_6x6x5_OES: + return GL_UNSIGNED_BYTE; // 4-component ASTC, 6x6x5 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_ASTC_6x6x6_OES: + return GL_UNSIGNED_BYTE; // 4-component ASTC, 6x6x6 blocks, unsigned normalized + + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_3x3x3_OES: + return GL_UNSIGNED_BYTE; // 4-component ASTC, 3x3x3 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x3x3_OES: + return GL_UNSIGNED_BYTE; // 4-component ASTC, 4x3x3 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4x3_OES: + return GL_UNSIGNED_BYTE; // 4-component ASTC, 4x4x3 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4x4_OES: + return GL_UNSIGNED_BYTE; // 4-component ASTC, 4x4x4 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4x4_OES: + return GL_UNSIGNED_BYTE; // 4-component ASTC, 5x4x4 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5x4_OES: + return GL_UNSIGNED_BYTE; // 4-component ASTC, 5x5x4 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5x5_OES: + return GL_UNSIGNED_BYTE; // 4-component ASTC, 5x5x5 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5x5_OES: + return GL_UNSIGNED_BYTE; // 4-component ASTC, 6x5x5 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6x5_OES: + return GL_UNSIGNED_BYTE; // 4-component ASTC, 6x6x5 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6x6_OES: + return GL_UNSIGNED_BYTE; // 4-component ASTC, 6x6x6 blocks, sRGB + + // + // ATC + // + case GL_ATC_RGB_AMD: + return GL_UNSIGNED_BYTE; // 3-component, 4x4 blocks, unsigned normalized + case GL_ATC_RGBA_EXPLICIT_ALPHA_AMD: + return GL_UNSIGNED_BYTE; // 4-component, 4x4 blocks, unsigned normalized + case GL_ATC_RGBA_INTERPOLATED_ALPHA_AMD: + return GL_UNSIGNED_BYTE; // 4-component, 4x4 blocks, unsigned normalized + + // + // Palletized + // + case GL_PALETTE4_RGB8_OES: + return GL_UNSIGNED_BYTE; // 3-component 8:8:8, 4-bit palette, unsigned normalized + case GL_PALETTE4_RGBA8_OES: + return GL_UNSIGNED_BYTE; // 4-component 8:8:8:8, 4-bit palette, unsigned normalized + case GL_PALETTE4_R5_G6_B5_OES: + return GL_UNSIGNED_SHORT_5_6_5; // 3-component 5:6:5, 4-bit palette, unsigned + // normalized + case GL_PALETTE4_RGBA4_OES: + return GL_UNSIGNED_SHORT_4_4_4_4; // 4-component 4:4:4:4, 4-bit palette, unsigned + // normalized + case GL_PALETTE4_RGB5_A1_OES: + return GL_UNSIGNED_SHORT_5_5_5_1; // 4-component 5:5:5:1, 4-bit palette, unsigned + // normalized + case GL_PALETTE8_RGB8_OES: + return GL_UNSIGNED_BYTE; // 3-component 8:8:8, 8-bit palette, unsigned normalized + case GL_PALETTE8_RGBA8_OES: + return GL_UNSIGNED_BYTE; // 4-component 8:8:8:8, 8-bit palette, unsigned normalized + case GL_PALETTE8_R5_G6_B5_OES: + return GL_UNSIGNED_SHORT_5_6_5; // 3-component 5:6:5, 8-bit palette, unsigned + // normalized + case GL_PALETTE8_RGBA4_OES: + return GL_UNSIGNED_SHORT_4_4_4_4; // 4-component 4:4:4:4, 8-bit palette, unsigned + // normalized + case GL_PALETTE8_RGB5_A1_OES: + return GL_UNSIGNED_SHORT_5_5_5_1; // 4-component 5:5:5:1, 8-bit palette, unsigned + // normalized + + // + // Depth/stencil + // + case GL_DEPTH_COMPONENT16: + return GL_UNSIGNED_SHORT; + case GL_DEPTH_COMPONENT24: + return GL_UNSIGNED_INT_24_8; + case GL_DEPTH_COMPONENT32: + return GL_UNSIGNED_INT; + case GL_DEPTH_COMPONENT32F: + return GL_FLOAT; + case GL_DEPTH_COMPONENT32F_NV: + return GL_FLOAT; + case GL_STENCIL_INDEX1: + return GL_UNSIGNED_BYTE; + case GL_STENCIL_INDEX4: + return GL_UNSIGNED_BYTE; + case GL_STENCIL_INDEX8: + return GL_UNSIGNED_BYTE; + case GL_STENCIL_INDEX16: + return GL_UNSIGNED_SHORT; + case GL_DEPTH24_STENCIL8: + return GL_UNSIGNED_INT_24_8; + case GL_DEPTH32F_STENCIL8: + return GL_FLOAT_32_UNSIGNED_INT_24_8_REV; + case GL_DEPTH32F_STENCIL8_NV: + return GL_FLOAT_32_UNSIGNED_INT_24_8_REV; + + default: + return GL_INVALID_VALUE; + } +} + +typedef enum GlFormatSizeFlagBits { + GL_FORMAT_SIZE_PACKED_BIT = 0x00000001, + GL_FORMAT_SIZE_COMPRESSED_BIT = 0x00000002, + GL_FORMAT_SIZE_PALETTIZED_BIT = 0x00000004, + GL_FORMAT_SIZE_DEPTH_BIT = 0x00000008, + GL_FORMAT_SIZE_STENCIL_BIT = 0x00000010, +} GlFormatSizeFlagBits; + +typedef unsigned int GlFormatSizeFlags; + +typedef struct GlFormatSize { + GlFormatSizeFlags flags; + unsigned int paletteSizeInBits; + unsigned int blockSizeInBits; + unsigned int blockWidth; // in texels + unsigned int blockHeight; // in texels + unsigned int blockDepth; // in texels +} GlFormatSize; + +static inline void glGetFormatSize(const GLenum internalFormat, GlFormatSize* pFormatSize) { + switch (internalFormat) { + // + // 8 bits per component + // + case GL_R8: // 1-component, 8-bit unsigned normalized + case GL_R8_SNORM: // 1-component, 8-bit signed normalized + case GL_R8UI: // 1-component, 8-bit unsigned integer + case GL_R8I: // 1-component, 8-bit signed integer + case GL_SR8: // 1-component, 8-bit sRGB + pFormatSize->flags = 0; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 1 * 8; + pFormatSize->blockWidth = 1; + pFormatSize->blockHeight = 1; + pFormatSize->blockDepth = 1; + break; + case GL_RG8: // 2-component, 8-bit unsigned normalized + case GL_RG8_SNORM: // 2-component, 8-bit signed normalized + case GL_RG8UI: // 2-component, 8-bit unsigned integer + case GL_RG8I: // 2-component, 8-bit signed integer + case GL_SRG8: // 2-component, 8-bit sRGB + pFormatSize->flags = 0; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 2 * 8; + pFormatSize->blockWidth = 1; + pFormatSize->blockHeight = 1; + pFormatSize->blockDepth = 1; + break; + case GL_RGB8: // 3-component, 8-bit unsigned normalized + case GL_RGB8_SNORM: // 3-component, 8-bit signed normalized + case GL_RGB8UI: // 3-component, 8-bit unsigned integer + case GL_RGB8I: // 3-component, 8-bit signed integer + case GL_SRGB8: // 3-component, 8-bit sRGB + pFormatSize->flags = 0; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 3 * 8; + pFormatSize->blockWidth = 1; + pFormatSize->blockHeight = 1; + pFormatSize->blockDepth = 1; + break; + case GL_RGBA8: // 4-component, 8-bit unsigned normalized + case GL_RGBA8_SNORM: // 4-component, 8-bit signed normalized + case GL_RGBA8UI: // 4-component, 8-bit unsigned integer + case GL_RGBA8I: // 4-component, 8-bit signed integer + case GL_SRGB8_ALPHA8: // 4-component, 8-bit sRGB + pFormatSize->flags = 0; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 4 * 8; + pFormatSize->blockWidth = 1; + pFormatSize->blockHeight = 1; + pFormatSize->blockDepth = 1; + break; + + // + // 16 bits per component + // + case GL_R16: // 1-component, 16-bit unsigned normalized + case GL_R16_SNORM: // 1-component, 16-bit signed normalized + case GL_R16UI: // 1-component, 16-bit unsigned integer + case GL_R16I: // 1-component, 16-bit signed integer + case GL_R16F: // 1-component, 16-bit floating-point + pFormatSize->flags = 0; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 2 * 8; + pFormatSize->blockWidth = 1; + pFormatSize->blockHeight = 1; + pFormatSize->blockDepth = 1; + break; + case GL_RG16: // 2-component, 16-bit unsigned normalized + case GL_RG16_SNORM: // 2-component, 16-bit signed normalized + case GL_RG16UI: // 2-component, 16-bit unsigned integer + case GL_RG16I: // 2-component, 16-bit signed integer + case GL_RG16F: // 2-component, 16-bit floating-point + pFormatSize->flags = 0; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 4 * 8; + pFormatSize->blockWidth = 1; + pFormatSize->blockHeight = 1; + pFormatSize->blockDepth = 1; + break; + case GL_RGB16: // 3-component, 16-bit unsigned normalized + case GL_RGB16_SNORM: // 3-component, 16-bit signed normalized + case GL_RGB16UI: // 3-component, 16-bit unsigned integer + case GL_RGB16I: // 3-component, 16-bit signed integer + case GL_RGB16F: // 3-component, 16-bit floating-point + pFormatSize->flags = 0; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 6 * 8; + pFormatSize->blockWidth = 1; + pFormatSize->blockHeight = 1; + pFormatSize->blockDepth = 1; + break; + case GL_RGBA16: // 4-component, 16-bit unsigned normalized + case GL_RGBA16_SNORM: // 4-component, 16-bit signed normalized + case GL_RGBA16UI: // 4-component, 16-bit unsigned integer + case GL_RGBA16I: // 4-component, 16-bit signed integer + case GL_RGBA16F: // 4-component, 16-bit floating-point + pFormatSize->flags = 0; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 8 * 8; + pFormatSize->blockWidth = 1; + pFormatSize->blockHeight = 1; + pFormatSize->blockDepth = 1; + break; + + // + // 32 bits per component + // + case GL_R32UI: // 1-component, 32-bit unsigned integer + case GL_R32I: // 1-component, 32-bit signed integer + case GL_R32F: // 1-component, 32-bit floating-point + pFormatSize->flags = 0; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 4 * 8; + pFormatSize->blockWidth = 1; + pFormatSize->blockHeight = 1; + pFormatSize->blockDepth = 1; + break; + case GL_RG32UI: // 2-component, 32-bit unsigned integer + case GL_RG32I: // 2-component, 32-bit signed integer + case GL_RG32F: // 2-component, 32-bit floating-point + pFormatSize->flags = 0; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 8 * 8; + pFormatSize->blockWidth = 1; + pFormatSize->blockHeight = 1; + pFormatSize->blockDepth = 1; + break; + case GL_RGB32UI: // 3-component, 32-bit unsigned integer + case GL_RGB32I: // 3-component, 32-bit signed integer + case GL_RGB32F: // 3-component, 32-bit floating-point + pFormatSize->flags = 0; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 12 * 8; + pFormatSize->blockWidth = 1; + pFormatSize->blockHeight = 1; + pFormatSize->blockDepth = 1; + break; + case GL_RGBA32UI: // 4-component, 32-bit unsigned integer + case GL_RGBA32I: // 4-component, 32-bit signed integer + case GL_RGBA32F: // 4-component, 32-bit floating-point + pFormatSize->flags = 0; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 16 * 8; + pFormatSize->blockWidth = 1; + pFormatSize->blockHeight = 1; + pFormatSize->blockDepth = 1; + break; + + // + // Packed + // + case GL_R3_G3_B2: // 3-component 3:3:2, unsigned normalized + pFormatSize->flags = GL_FORMAT_SIZE_PACKED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 8; + pFormatSize->blockWidth = 1; + pFormatSize->blockHeight = 1; + pFormatSize->blockDepth = 1; + break; + case GL_RGB4: // 3-component 4:4:4, unsigned normalized + pFormatSize->flags = GL_FORMAT_SIZE_PACKED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 12; + pFormatSize->blockWidth = 1; + pFormatSize->blockHeight = 1; + pFormatSize->blockDepth = 1; + break; + case GL_RGB5: // 3-component 5:5:5, unsigned normalized + pFormatSize->flags = GL_FORMAT_SIZE_PACKED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 16; + pFormatSize->blockWidth = 1; + pFormatSize->blockHeight = 1; + pFormatSize->blockDepth = 1; + break; + case GL_RGB565: // 3-component 5:6:5, unsigned normalized + pFormatSize->flags = GL_FORMAT_SIZE_PACKED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 16; + pFormatSize->blockWidth = 1; + pFormatSize->blockHeight = 1; + pFormatSize->blockDepth = 1; + break; + case GL_RGB10: // 3-component 10:10:10, unsigned normalized + pFormatSize->flags = GL_FORMAT_SIZE_PACKED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 32; + pFormatSize->blockWidth = 1; + pFormatSize->blockHeight = 1; + pFormatSize->blockDepth = 1; + break; + case GL_RGB12: // 3-component 12:12:12, unsigned normalized + pFormatSize->flags = GL_FORMAT_SIZE_PACKED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 36; + pFormatSize->blockWidth = 1; + pFormatSize->blockHeight = 1; + pFormatSize->blockDepth = 1; + break; + case GL_RGBA2: // 4-component 2:2:2:2, unsigned normalized + pFormatSize->flags = GL_FORMAT_SIZE_PACKED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 8; + pFormatSize->blockWidth = 1; + pFormatSize->blockHeight = 1; + pFormatSize->blockDepth = 1; + break; + case GL_RGBA4: // 4-component 4:4:4:4, unsigned normalized + pFormatSize->flags = GL_FORMAT_SIZE_PACKED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 16; + pFormatSize->blockWidth = 1; + pFormatSize->blockHeight = 1; + pFormatSize->blockDepth = 1; + break; + case GL_RGBA12: // 4-component 12:12:12:12, unsigned normalized + pFormatSize->flags = GL_FORMAT_SIZE_PACKED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 48; + pFormatSize->blockWidth = 1; + pFormatSize->blockHeight = 1; + pFormatSize->blockDepth = 1; + break; + case GL_RGB5_A1: // 4-component 5:5:5:1, unsigned normalized + pFormatSize->flags = GL_FORMAT_SIZE_PACKED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 32; + pFormatSize->blockWidth = 1; + pFormatSize->blockHeight = 1; + pFormatSize->blockDepth = 1; + break; + case GL_RGB10_A2: // 4-component 10:10:10:2, unsigned normalized + pFormatSize->flags = GL_FORMAT_SIZE_PACKED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 32; + pFormatSize->blockWidth = 1; + pFormatSize->blockHeight = 1; + pFormatSize->blockDepth = 1; + break; + case GL_RGB10_A2UI: // 4-component 10:10:10:2, unsigned integer + pFormatSize->flags = GL_FORMAT_SIZE_PACKED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 32; + pFormatSize->blockWidth = 1; + pFormatSize->blockHeight = 1; + pFormatSize->blockDepth = 1; + break; + case GL_R11F_G11F_B10F: // 3-component 11:11:10, floating-point + case GL_RGB9_E5: // 3-component/exp 9:9:9/5, floating-point + pFormatSize->flags = GL_FORMAT_SIZE_PACKED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 32; + pFormatSize->blockWidth = 1; + pFormatSize->blockHeight = 1; + pFormatSize->blockDepth = 1; + break; + + // + // S3TC/DXT/BC + // + case GL_COMPRESSED_RGB_S3TC_DXT1_EXT: // line through 3D space, 4x4 blocks, unsigned + // normalized + case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: // line through 3D space plus 1-bit alpha, 4x4 + // blocks, unsigned normalized + case GL_COMPRESSED_SRGB_S3TC_DXT1_EXT: // line through 3D space, 4x4 blocks, sRGB + case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT: // line through 3D space plus 1-bit alpha, 4x4 + // blocks, sRGB + pFormatSize->flags = GL_FORMAT_SIZE_COMPRESSED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 64; + pFormatSize->blockWidth = 4; + pFormatSize->blockHeight = 4; + pFormatSize->blockDepth = 1; + break; + case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: // line through 3D space plus line through 1D space, + // 4x4 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT: // line through 3D space plus 4-bit alpha, 4x4 + // blocks, unsigned normalized + case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT: // line through 3D space plus line through 1D + // space, 4x4 blocks, sRGB + case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT: // line through 3D space plus 4-bit alpha, 4x4 + // blocks, sRGB + pFormatSize->flags = GL_FORMAT_SIZE_COMPRESSED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 128; + pFormatSize->blockWidth = 4; + pFormatSize->blockHeight = 4; + pFormatSize->blockDepth = 1; + break; + + case GL_COMPRESSED_LUMINANCE_LATC1_EXT: // line through 1D space, 4x4 blocks, unsigned + // normalized + case GL_COMPRESSED_SIGNED_LUMINANCE_LATC1_EXT: // line through 1D space, 4x4 blocks, signed + // normalized + pFormatSize->flags = GL_FORMAT_SIZE_COMPRESSED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 64; + pFormatSize->blockWidth = 4; + pFormatSize->blockHeight = 4; + pFormatSize->blockDepth = 1; + break; + case GL_COMPRESSED_LUMINANCE_ALPHA_LATC2_EXT: // two lines through 1D space, 4x4 blocks, + // unsigned normalized + case GL_COMPRESSED_SIGNED_LUMINANCE_ALPHA_LATC2_EXT: // two lines through 1D space, 4x4 + // blocks, signed normalized + pFormatSize->flags = GL_FORMAT_SIZE_COMPRESSED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 128; + pFormatSize->blockWidth = 4; + pFormatSize->blockHeight = 4; + pFormatSize->blockDepth = 1; + break; + + case GL_COMPRESSED_RED_RGTC1: // line through 1D space, 4x4 blocks, unsigned normalized + case GL_COMPRESSED_SIGNED_RED_RGTC1: // line through 1D space, 4x4 blocks, signed normalized + pFormatSize->flags = GL_FORMAT_SIZE_COMPRESSED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 64; + pFormatSize->blockWidth = 4; + pFormatSize->blockHeight = 4; + pFormatSize->blockDepth = 1; + break; + case GL_COMPRESSED_RG_RGTC2: // two lines through 1D space, 4x4 blocks, unsigned normalized + case GL_COMPRESSED_SIGNED_RG_RGTC2: // two lines through 1D space, 4x4 blocks, signed + // normalized + pFormatSize->flags = GL_FORMAT_SIZE_COMPRESSED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 128; + pFormatSize->blockWidth = 4; + pFormatSize->blockHeight = 4; + pFormatSize->blockDepth = 1; + break; + + case GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT: // 3-component, 4x4 blocks, unsigned + // floating-point + case GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT: // 3-component, 4x4 blocks, signed floating-point + case GL_COMPRESSED_RGBA_BPTC_UNORM: // 4-component, 4x4 blocks, unsigned normalized + case GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM: // 4-component, 4x4 blocks, sRGB + pFormatSize->flags = GL_FORMAT_SIZE_COMPRESSED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 128; + pFormatSize->blockWidth = 4; + pFormatSize->blockHeight = 4; + pFormatSize->blockDepth = 1; + break; + + // + // ETC + // + case GL_ETC1_RGB8_OES: // 3-component ETC1, 4x4 blocks, unsigned normalized" ), + case GL_COMPRESSED_RGB8_ETC2: // 3-component ETC2, 4x4 blocks, unsigned normalized + case GL_COMPRESSED_SRGB8_ETC2: // 3-component ETC2, 4x4 blocks, sRGB + case GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2: // 4-component ETC2 with 1-bit alpha, 4x4 + // blocks, unsigned normalized + case GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2: // 4-component ETC2 with 1-bit alpha, 4x4 + // blocks, sRGB + pFormatSize->flags = GL_FORMAT_SIZE_COMPRESSED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 64; + pFormatSize->blockWidth = 4; + pFormatSize->blockHeight = 4; + pFormatSize->blockDepth = 1; + break; + case GL_COMPRESSED_RGBA8_ETC2_EAC: // 4-component ETC2, 4x4 blocks, unsigned normalized + case GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC: // 4-component ETC2, 4x4 blocks, sRGB + pFormatSize->flags = GL_FORMAT_SIZE_COMPRESSED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 128; + pFormatSize->blockWidth = 4; + pFormatSize->blockHeight = 4; + pFormatSize->blockDepth = 1; + break; + + case GL_COMPRESSED_R11_EAC: // 1-component ETC, 4x4 blocks, unsigned normalized + case GL_COMPRESSED_SIGNED_R11_EAC: // 1-component ETC, 4x4 blocks, signed normalized + pFormatSize->flags = GL_FORMAT_SIZE_COMPRESSED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 64; + pFormatSize->blockWidth = 4; + pFormatSize->blockHeight = 4; + pFormatSize->blockDepth = 1; + break; + case GL_COMPRESSED_RG11_EAC: // 2-component ETC, 4x4 blocks, unsigned normalized + case GL_COMPRESSED_SIGNED_RG11_EAC: // 2-component ETC, 4x4 blocks, signed normalized + pFormatSize->flags = GL_FORMAT_SIZE_COMPRESSED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 128; + pFormatSize->blockWidth = 4; + pFormatSize->blockHeight = 4; + pFormatSize->blockDepth = 1; + break; + + // + // PVRTC + // + case GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG: // 3-component PVRTC, 16x8 blocks, unsigned + // normalized + case GL_COMPRESSED_SRGB_PVRTC_2BPPV1_EXT: // 3-component PVRTC, 16x8 blocks, sRGB + case GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG: // 4-component PVRTC, 16x8 blocks, unsigned + // normalized + case GL_COMPRESSED_SRGB_ALPHA_PVRTC_2BPPV1_EXT: // 4-component PVRTC, 16x8 blocks, sRGB + pFormatSize->flags = GL_FORMAT_SIZE_COMPRESSED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 64; + pFormatSize->blockWidth = 16; + pFormatSize->blockHeight = 8; + pFormatSize->blockDepth = 1; + break; + case GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG: // 3-component PVRTC, 8x8 blocks, unsigned + // normalized + case GL_COMPRESSED_SRGB_PVRTC_4BPPV1_EXT: // 3-component PVRTC, 8x8 blocks, sRGB + case GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG: // 4-component PVRTC, 8x8 blocks, unsigned + // normalized + case GL_COMPRESSED_SRGB_ALPHA_PVRTC_4BPPV1_EXT: // 4-component PVRTC, 8x8 blocks, sRGB + pFormatSize->flags = GL_FORMAT_SIZE_COMPRESSED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 64; + pFormatSize->blockWidth = 8; + pFormatSize->blockHeight = 8; + pFormatSize->blockDepth = 1; + break; + case GL_COMPRESSED_RGBA_PVRTC_2BPPV2_IMG: // 4-component PVRTC, 8x4 blocks, unsigned + // normalized + case GL_COMPRESSED_SRGB_ALPHA_PVRTC_2BPPV2_IMG: // 4-component PVRTC, 8x4 blocks, sRGB + pFormatSize->flags = GL_FORMAT_SIZE_COMPRESSED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 64; + pFormatSize->blockWidth = 8; + pFormatSize->blockHeight = 4; + pFormatSize->blockDepth = 1; + break; + case GL_COMPRESSED_RGBA_PVRTC_4BPPV2_IMG: // 4-component PVRTC, 4x4 blocks, unsigned + // normalized + case GL_COMPRESSED_SRGB_ALPHA_PVRTC_4BPPV2_IMG: // 4-component PVRTC, 4x4 blocks, sRGB + pFormatSize->flags = GL_FORMAT_SIZE_COMPRESSED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 64; + pFormatSize->blockWidth = 4; + pFormatSize->blockHeight = 4; + pFormatSize->blockDepth = 1; + break; + + // + // ASTC + // + case GL_COMPRESSED_RGBA_ASTC_4x4_KHR: // 4-component ASTC, 4x4 blocks, unsigned normalized + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR: // 4-component ASTC, 4x4 blocks, sRGB + pFormatSize->flags = GL_FORMAT_SIZE_COMPRESSED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 128; + pFormatSize->blockWidth = 4; + pFormatSize->blockHeight = 4; + pFormatSize->blockDepth = 1; + break; + case GL_COMPRESSED_RGBA_ASTC_5x4_KHR: // 4-component ASTC, 5x4 blocks, unsigned normalized + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR: // 4-component ASTC, 5x4 blocks, sRGB + pFormatSize->flags = GL_FORMAT_SIZE_COMPRESSED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 128; + pFormatSize->blockWidth = 5; + pFormatSize->blockHeight = 4; + pFormatSize->blockDepth = 1; + break; + case GL_COMPRESSED_RGBA_ASTC_5x5_KHR: // 4-component ASTC, 5x5 blocks, unsigned normalized + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR: // 4-component ASTC, 5x5 blocks, sRGB + pFormatSize->flags = GL_FORMAT_SIZE_COMPRESSED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 128; + pFormatSize->blockWidth = 5; + pFormatSize->blockHeight = 6; + pFormatSize->blockDepth = 1; + break; + case GL_COMPRESSED_RGBA_ASTC_6x5_KHR: // 4-component ASTC, 6x5 blocks, unsigned normalized + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR: // 4-component ASTC, 6x5 blocks, sRGB + pFormatSize->flags = GL_FORMAT_SIZE_COMPRESSED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 128; + pFormatSize->blockWidth = 6; + pFormatSize->blockHeight = 5; + pFormatSize->blockDepth = 1; + break; + case GL_COMPRESSED_RGBA_ASTC_6x6_KHR: // 4-component ASTC, 6x6 blocks, unsigned normalized + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR: // 4-component ASTC, 6x6 blocks, sRGB + pFormatSize->flags = GL_FORMAT_SIZE_COMPRESSED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 128; + pFormatSize->blockWidth = 6; + pFormatSize->blockHeight = 6; + pFormatSize->blockDepth = 1; + break; + case GL_COMPRESSED_RGBA_ASTC_8x5_KHR: // 4-component ASTC, 8x5 blocks, unsigned normalized + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR: // 4-component ASTC, 8x5 blocks, sRGB + pFormatSize->flags = GL_FORMAT_SIZE_COMPRESSED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 128; + pFormatSize->blockWidth = 8; + pFormatSize->blockHeight = 5; + pFormatSize->blockDepth = 1; + break; + case GL_COMPRESSED_RGBA_ASTC_8x6_KHR: // 4-component ASTC, 8x6 blocks, unsigned normalized + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR: // 4-component ASTC, 8x6 blocks, sRGB + pFormatSize->flags = GL_FORMAT_SIZE_COMPRESSED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 128; + pFormatSize->blockWidth = 8; + pFormatSize->blockHeight = 6; + pFormatSize->blockDepth = 1; + break; + case GL_COMPRESSED_RGBA_ASTC_8x8_KHR: // 4-component ASTC, 8x8 blocks, unsigned normalized + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR: // 4-component ASTC, 8x8 blocks, sRGB + pFormatSize->flags = GL_FORMAT_SIZE_COMPRESSED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 128; + pFormatSize->blockWidth = 8; + pFormatSize->blockHeight = 8; + pFormatSize->blockDepth = 1; + break; + case GL_COMPRESSED_RGBA_ASTC_10x5_KHR: // 4-component ASTC, 10x5 blocks, unsigned normalized + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR: // 4-component ASTC, 10x5 blocks, sRGB + pFormatSize->flags = GL_FORMAT_SIZE_COMPRESSED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 128; + pFormatSize->blockWidth = 10; + pFormatSize->blockHeight = 5; + pFormatSize->blockDepth = 1; + break; + case GL_COMPRESSED_RGBA_ASTC_10x6_KHR: // 4-component ASTC, 10x6 blocks, unsigned normalized + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR: // 4-component ASTC, 10x6 blocks, sRGB + pFormatSize->flags = GL_FORMAT_SIZE_COMPRESSED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 128; + pFormatSize->blockWidth = 10; + pFormatSize->blockHeight = 6; + pFormatSize->blockDepth = 1; + break; + case GL_COMPRESSED_RGBA_ASTC_10x8_KHR: // 4-component ASTC, 10x8 blocks, unsigned normalized + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR: // 4-component ASTC, 10x8 blocks, sRGB + pFormatSize->flags = GL_FORMAT_SIZE_COMPRESSED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 128; + pFormatSize->blockWidth = 10; + pFormatSize->blockHeight = 8; + pFormatSize->blockDepth = 1; + break; + case GL_COMPRESSED_RGBA_ASTC_10x10_KHR: // 4-component ASTC, 10x10 blocks, unsigned + // normalized + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR: // 4-component ASTC, 10x10 blocks, sRGB + pFormatSize->flags = GL_FORMAT_SIZE_COMPRESSED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 128; + pFormatSize->blockWidth = 10; + pFormatSize->blockHeight = 10; + pFormatSize->blockDepth = 1; + break; + case GL_COMPRESSED_RGBA_ASTC_12x10_KHR: // 4-component ASTC, 12x10 blocks, unsigned + // normalized + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR: // 4-component ASTC, 12x10 blocks, sRGB + pFormatSize->flags = GL_FORMAT_SIZE_COMPRESSED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 128; + pFormatSize->blockWidth = 12; + pFormatSize->blockHeight = 10; + pFormatSize->blockDepth = 1; + break; + case GL_COMPRESSED_RGBA_ASTC_12x12_KHR: // 4-component ASTC, 12x12 blocks, unsigned + // normalized + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR: // 4-component ASTC, 12x12 blocks, sRGB + pFormatSize->flags = GL_FORMAT_SIZE_COMPRESSED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 128; + pFormatSize->blockWidth = 12; + pFormatSize->blockHeight = 12; + pFormatSize->blockDepth = 1; + break; + + case GL_COMPRESSED_RGBA_ASTC_3x3x3_OES: // 4-component ASTC, 3x3x3 blocks, unsigned + // normalized + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_3x3x3_OES: // 4-component ASTC, 3x3x3 blocks, sRGB + pFormatSize->flags = GL_FORMAT_SIZE_COMPRESSED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 128; + pFormatSize->blockWidth = 3; + pFormatSize->blockHeight = 3; + pFormatSize->blockDepth = 3; + break; + case GL_COMPRESSED_RGBA_ASTC_4x3x3_OES: // 4-component ASTC, 4x3x3 blocks, unsigned + // normalized + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x3x3_OES: // 4-component ASTC, 4x3x3 blocks, sRGB + pFormatSize->flags = GL_FORMAT_SIZE_COMPRESSED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 128; + pFormatSize->blockWidth = 4; + pFormatSize->blockHeight = 3; + pFormatSize->blockDepth = 3; + break; + case GL_COMPRESSED_RGBA_ASTC_4x4x3_OES: // 4-component ASTC, 4x4x3 blocks, unsigned + // normalized + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4x3_OES: // 4-component ASTC, 4x4x3 blocks, sRGB + pFormatSize->flags = GL_FORMAT_SIZE_COMPRESSED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 128; + pFormatSize->blockWidth = 4; + pFormatSize->blockHeight = 4; + pFormatSize->blockDepth = 3; + break; + case GL_COMPRESSED_RGBA_ASTC_4x4x4_OES: // 4-component ASTC, 4x4x4 blocks, unsigned + // normalized + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4x4_OES: // 4-component ASTC, 4x4x4 blocks, sRGB + pFormatSize->flags = GL_FORMAT_SIZE_COMPRESSED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 128; + pFormatSize->blockWidth = 4; + pFormatSize->blockHeight = 4; + pFormatSize->blockDepth = 4; + break; + case GL_COMPRESSED_RGBA_ASTC_5x4x4_OES: // 4-component ASTC, 5x4x4 blocks, unsigned + // normalized + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4x4_OES: // 4-component ASTC, 5x4x4 blocks, sRGB + pFormatSize->flags = GL_FORMAT_SIZE_COMPRESSED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 128; + pFormatSize->blockWidth = 5; + pFormatSize->blockHeight = 4; + pFormatSize->blockDepth = 4; + break; + case GL_COMPRESSED_RGBA_ASTC_5x5x4_OES: // 4-component ASTC, 5x5x4 blocks, unsigned + // normalized + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5x4_OES: // 4-component ASTC, 5x5x4 blocks, sRGB + pFormatSize->flags = GL_FORMAT_SIZE_COMPRESSED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 128; + pFormatSize->blockWidth = 5; + pFormatSize->blockHeight = 5; + pFormatSize->blockDepth = 4; + break; + case GL_COMPRESSED_RGBA_ASTC_5x5x5_OES: // 4-component ASTC, 5x5x5 blocks, unsigned + // normalized + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5x5_OES: // 4-component ASTC, 5x5x5 blocks, sRGB + pFormatSize->flags = GL_FORMAT_SIZE_COMPRESSED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 128; + pFormatSize->blockWidth = 5; + pFormatSize->blockHeight = 5; + pFormatSize->blockDepth = 5; + break; + case GL_COMPRESSED_RGBA_ASTC_6x5x5_OES: // 4-component ASTC, 6x5x5 blocks, unsigned + // normalized + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5x5_OES: // 4-component ASTC, 6x5x5 blocks, sRGB + pFormatSize->flags = GL_FORMAT_SIZE_COMPRESSED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 128; + pFormatSize->blockWidth = 6; + pFormatSize->blockHeight = 5; + pFormatSize->blockDepth = 5; + break; + case GL_COMPRESSED_RGBA_ASTC_6x6x5_OES: // 4-component ASTC, 6x6x5 blocks, unsigned + // normalized + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6x5_OES: // 4-component ASTC, 6x6x5 blocks, sRGB + pFormatSize->flags = GL_FORMAT_SIZE_COMPRESSED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 128; + pFormatSize->blockWidth = 6; + pFormatSize->blockHeight = 6; + pFormatSize->blockDepth = 5; + break; + case GL_COMPRESSED_RGBA_ASTC_6x6x6_OES: // 4-component ASTC, 6x6x6 blocks, unsigned + // normalized + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6x6_OES: // 4-component ASTC, 6x6x6 blocks, sRGB + pFormatSize->flags = GL_FORMAT_SIZE_COMPRESSED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 128; + pFormatSize->blockWidth = 6; + pFormatSize->blockHeight = 6; + pFormatSize->blockDepth = 6; + break; + + // + // ATC + // + case GL_ATC_RGB_AMD: // 3-component, 4x4 blocks, unsigned normalized + pFormatSize->flags = GL_FORMAT_SIZE_COMPRESSED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 64; + pFormatSize->blockWidth = 4; + pFormatSize->blockHeight = 4; + pFormatSize->blockDepth = 1; + break; + case GL_ATC_RGBA_EXPLICIT_ALPHA_AMD: // 4-component, 4x4 blocks, unsigned normalized + case GL_ATC_RGBA_INTERPOLATED_ALPHA_AMD: // 4-component, 4x4 blocks, unsigned normalized + pFormatSize->flags = GL_FORMAT_SIZE_COMPRESSED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 128; + pFormatSize->blockWidth = 4; + pFormatSize->blockHeight = 4; + pFormatSize->blockDepth = 1; + break; + + // + // Palletized + // + case GL_PALETTE4_RGB8_OES: // 3-component 8:8:8, 4-bit palette, unsigned normalized + pFormatSize->flags = GL_FORMAT_SIZE_PALETTIZED_BIT; + pFormatSize->paletteSizeInBits = 16 * 24; + pFormatSize->blockSizeInBits = 4; + pFormatSize->blockWidth = 1; + pFormatSize->blockHeight = 1; + pFormatSize->blockDepth = 1; + break; + case GL_PALETTE4_RGBA8_OES: // 4-component 8:8:8:8, 4-bit palette, unsigned normalized + pFormatSize->flags = GL_FORMAT_SIZE_PALETTIZED_BIT; + pFormatSize->paletteSizeInBits = 16 * 32; + pFormatSize->blockSizeInBits = 4; + pFormatSize->blockWidth = 1; + pFormatSize->blockHeight = 1; + pFormatSize->blockDepth = 1; + break; + case GL_PALETTE4_R5_G6_B5_OES: // 3-component 5:6:5, 4-bit palette, unsigned normalized + case GL_PALETTE4_RGBA4_OES: // 4-component 4:4:4:4, 4-bit palette, unsigned normalized + case GL_PALETTE4_RGB5_A1_OES: // 4-component 5:5:5:1, 4-bit palette, unsigned normalized + pFormatSize->flags = GL_FORMAT_SIZE_PALETTIZED_BIT; + pFormatSize->paletteSizeInBits = 16 * 16; + pFormatSize->blockSizeInBits = 4; + pFormatSize->blockWidth = 1; + pFormatSize->blockHeight = 1; + pFormatSize->blockDepth = 1; + break; + case GL_PALETTE8_RGB8_OES: // 3-component 8:8:8, 8-bit palette, unsigned normalized + pFormatSize->flags = GL_FORMAT_SIZE_PALETTIZED_BIT; + pFormatSize->paletteSizeInBits = 256 * 24; + pFormatSize->blockSizeInBits = 8; + pFormatSize->blockWidth = 1; + pFormatSize->blockHeight = 1; + pFormatSize->blockDepth = 1; + break; + case GL_PALETTE8_RGBA8_OES: // 4-component 8:8:8:8, 8-bit palette, unsigned normalized + pFormatSize->flags = GL_FORMAT_SIZE_PALETTIZED_BIT; + pFormatSize->paletteSizeInBits = 256 * 32; + pFormatSize->blockSizeInBits = 8; + pFormatSize->blockWidth = 1; + pFormatSize->blockHeight = 1; + pFormatSize->blockDepth = 1; + break; + case GL_PALETTE8_R5_G6_B5_OES: // 3-component 5:6:5, 8-bit palette, unsigned normalized + case GL_PALETTE8_RGBA4_OES: // 4-component 4:4:4:4, 8-bit palette, unsigned normalized + case GL_PALETTE8_RGB5_A1_OES: // 4-component 5:5:5:1, 8-bit palette, unsigned normalized + pFormatSize->flags = GL_FORMAT_SIZE_PALETTIZED_BIT; + pFormatSize->paletteSizeInBits = 256 * 16; + pFormatSize->blockSizeInBits = 8; + pFormatSize->blockWidth = 1; + pFormatSize->blockHeight = 1; + pFormatSize->blockDepth = 1; + break; + + // + // Depth/stencil + // + case GL_DEPTH_COMPONENT16: + pFormatSize->flags = GL_FORMAT_SIZE_DEPTH_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 16; + pFormatSize->blockWidth = 1; + pFormatSize->blockHeight = 1; + pFormatSize->blockDepth = 1; + break; + case GL_DEPTH_COMPONENT24: + case GL_DEPTH_COMPONENT32: + case GL_DEPTH_COMPONENT32F: + case GL_DEPTH_COMPONENT32F_NV: + pFormatSize->flags = GL_FORMAT_SIZE_DEPTH_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 32; + pFormatSize->blockWidth = 1; + pFormatSize->blockHeight = 1; + pFormatSize->blockDepth = 1; + break; + case GL_STENCIL_INDEX1: + pFormatSize->flags = GL_FORMAT_SIZE_STENCIL_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 1; + pFormatSize->blockWidth = 1; + pFormatSize->blockHeight = 1; + pFormatSize->blockDepth = 1; + break; + case GL_STENCIL_INDEX4: + pFormatSize->flags = GL_FORMAT_SIZE_STENCIL_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 4; + pFormatSize->blockWidth = 1; + pFormatSize->blockHeight = 1; + pFormatSize->blockDepth = 1; + break; + case GL_STENCIL_INDEX8: + pFormatSize->flags = GL_FORMAT_SIZE_STENCIL_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 8; + pFormatSize->blockWidth = 1; + pFormatSize->blockHeight = 1; + pFormatSize->blockDepth = 1; + break; + case GL_STENCIL_INDEX16: + pFormatSize->flags = GL_FORMAT_SIZE_STENCIL_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 16; + pFormatSize->blockWidth = 1; + pFormatSize->blockHeight = 1; + pFormatSize->blockDepth = 1; + break; + case GL_DEPTH24_STENCIL8: + pFormatSize->flags = GL_FORMAT_SIZE_DEPTH_BIT | GL_FORMAT_SIZE_STENCIL_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 32; + pFormatSize->blockWidth = 1; + pFormatSize->blockHeight = 1; + pFormatSize->blockDepth = 1; + break; + case GL_DEPTH32F_STENCIL8: + case GL_DEPTH32F_STENCIL8_NV: + pFormatSize->flags = GL_FORMAT_SIZE_DEPTH_BIT | GL_FORMAT_SIZE_STENCIL_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 64; + pFormatSize->blockWidth = 1; + pFormatSize->blockHeight = 1; + pFormatSize->blockDepth = 1; + break; + + default: + pFormatSize->flags = 0; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 8; + pFormatSize->blockWidth = 1; + pFormatSize->blockHeight = 1; + pFormatSize->blockDepth = 1; + break; + } +} + +#endif // !GL_FORMAT_H diff --git a/Samples/1stParty/utilities/include/vulkan/vk_format.h b/Samples/1stParty/utilities/include/vulkan/vk_format.h new file mode 100755 index 0000000..26433a7 --- /dev/null +++ b/Samples/1stParty/utilities/include/vulkan/vk_format.h @@ -0,0 +1,1794 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * 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. + */ + +// clang-format off +/* +================================================================================================ + +Description : Vulkan format properties and conversion from OpenGL. +Author : J.M.P. van Waveren +Date : 07/17/2016 +Language : C99 +Format : Real tabs with the tab size equal to 4 spaces. + + +DESCRIPTION +============== + +This header implements several support routines to convert OpenGL formats/types +to Vulkan formats. These routines are particularly useful for loading file +formats that store OpenGL formats/types such as KTX and glTF. + +The functions in this header file convert the format, internalFormat and type +that are used as parameters to the following OpenGL functions: + +void glTexImage2D( GLenum target, GLint level, GLint internalFormat, + GLsizei width, GLsizei height, GLint border, + GLenum format, GLenum type, const GLvoid * data ); +void glTexImage3D( GLenum target, GLint level, GLint internalFormat, + GLsizei width, GLsizei height, GLsizei depth, GLint border, + GLenum format, GLenum type, const GLvoid * data ); +void glCompressedTexImage2D( GLenum target, GLint level, GLenum internalformat, + GLsizei width, GLsizei height, GLint border, + GLsizei imageSize, const GLvoid * data ); +void glCompressedTexImage3D( GLenum target, GLint level, GLenum internalformat, + GLsizei width, GLsizei height, GLsizei depth, GLint border, + GLsizei imageSize, const GLvoid * data ); +void glTexStorage2D( GLenum target, GLsizei levels, GLenum internalformat, + GLsizei width, GLsizei height ); +void glTexStorage3D( GLenum target, GLsizei levels, GLenum internalformat, + GLsizei width, GLsizei height, GLsizei depth ); +void glVertexAttribPointer( GLuint index, GLint size, GLenum type, GLboolean normalized, + GLsizei stride, const GLvoid * pointer); + + +IMPLEMENTATION +============== + +This file does not include OpenGL / OpenGL ES headers because: + + 1. Including OpenGL / OpenGL ES headers is platform dependent and + may require a separate installation of an OpenGL SDK. + 2. The OpenGL format/type constants are the same between extensions and core. + 3. The OpenGL format/type constants are the same between OpenGL and OpenGL ES. + 4. File formats like KTX and glTF may use OpenGL formats and types that + are not supported by the OpenGL implementation on the platform but are + supported by the Vulkan implementation. + + +ENTRY POINTS +============== + +static inline VkFormat vkGetFormatFromOpenGLFormat( const GLenum format, const GLenum type ); +static inline VkFormat vkGetFormatFromOpenGLType( const GLenum type, const GLuint numComponents, const GLboolean normalized ); +static inline VkFormat vkGetFormatFromOpenGLInternalFormat( const GLenum internalFormat ); +static inline void vkGetFormatSize( const VkFormat format, VkFormatSize * pFormatSize ); + +================================================================================================ +*/ +// clang-format on + +#if !defined(VK_FORMAT_H) +#define VK_FORMAT_H + +#include "../GL/gl_format.h" + +static inline VkFormat vkGetFormatFromOpenGLFormat(const GLenum format, const GLenum type) { + switch (type) { + // + // 8 bits per component + // + case GL_UNSIGNED_BYTE: { + switch (format) { + case GL_RED: + return VK_FORMAT_R8_UNORM; + case GL_RG: + return VK_FORMAT_R8G8_UNORM; + case GL_RGB: + return VK_FORMAT_R8G8B8_UNORM; + case GL_BGR: + return VK_FORMAT_B8G8R8_UNORM; + case GL_RGBA: + return VK_FORMAT_R8G8B8A8_UNORM; + case GL_BGRA: + return VK_FORMAT_B8G8R8A8_UNORM; + case GL_RED_INTEGER: + return VK_FORMAT_R8_UINT; + case GL_RG_INTEGER: + return VK_FORMAT_R8G8_UINT; + case GL_RGB_INTEGER: + return VK_FORMAT_R8G8B8_UINT; + case GL_BGR_INTEGER: + return VK_FORMAT_B8G8R8_UINT; + case GL_RGBA_INTEGER: + return VK_FORMAT_R8G8B8A8_UINT; + case GL_BGRA_INTEGER: + return VK_FORMAT_B8G8R8A8_UINT; + case GL_STENCIL_INDEX: + return VK_FORMAT_S8_UINT; + case GL_DEPTH_COMPONENT: + return VK_FORMAT_UNDEFINED; + case GL_DEPTH_STENCIL: + return VK_FORMAT_UNDEFINED; + } + break; + } + case GL_BYTE: { + switch (format) { + case GL_RED: + return VK_FORMAT_R8_SNORM; + case GL_RG: + return VK_FORMAT_R8G8_SNORM; + case GL_RGB: + return VK_FORMAT_R8G8B8_SNORM; + case GL_BGR: + return VK_FORMAT_B8G8R8_SNORM; + case GL_RGBA: + return VK_FORMAT_R8G8B8A8_SNORM; + case GL_BGRA: + return VK_FORMAT_B8G8R8A8_SNORM; + case GL_RED_INTEGER: + return VK_FORMAT_R8_SINT; + case GL_RG_INTEGER: + return VK_FORMAT_R8G8_SINT; + case GL_RGB_INTEGER: + return VK_FORMAT_R8G8B8_SINT; + case GL_BGR_INTEGER: + return VK_FORMAT_B8G8R8_SINT; + case GL_RGBA_INTEGER: + return VK_FORMAT_R8G8B8A8_SINT; + case GL_BGRA_INTEGER: + return VK_FORMAT_B8G8R8A8_SINT; + case GL_STENCIL_INDEX: + return VK_FORMAT_UNDEFINED; + case GL_DEPTH_COMPONENT: + return VK_FORMAT_UNDEFINED; + case GL_DEPTH_STENCIL: + return VK_FORMAT_UNDEFINED; + } + break; + } + + // + // 16 bits per component + // + case GL_UNSIGNED_SHORT: { + switch (format) { + case GL_RED: + return VK_FORMAT_R16_UNORM; + case GL_RG: + return VK_FORMAT_R16G16_UNORM; + case GL_RGB: + return VK_FORMAT_R16G16B16_UNORM; + case GL_BGR: + return VK_FORMAT_UNDEFINED; + case GL_RGBA: + return VK_FORMAT_R16G16B16A16_UNORM; + case GL_BGRA: + return VK_FORMAT_UNDEFINED; + case GL_RED_INTEGER: + return VK_FORMAT_R16_UINT; + case GL_RG_INTEGER: + return VK_FORMAT_R16G16_UINT; + case GL_RGB_INTEGER: + return VK_FORMAT_R16G16B16_UINT; + case GL_BGR_INTEGER: + return VK_FORMAT_UNDEFINED; + case GL_RGBA_INTEGER: + return VK_FORMAT_R16G16B16A16_UINT; + case GL_BGRA_INTEGER: + return VK_FORMAT_UNDEFINED; + case GL_STENCIL_INDEX: + return VK_FORMAT_UNDEFINED; + case GL_DEPTH_COMPONENT: + return VK_FORMAT_D16_UNORM; + case GL_DEPTH_STENCIL: + return VK_FORMAT_D16_UNORM_S8_UINT; + } + break; + } + case GL_SHORT: { + switch (format) { + case GL_RED: + return VK_FORMAT_R16_SNORM; + case GL_RG: + return VK_FORMAT_R16G16_SNORM; + case GL_RGB: + return VK_FORMAT_R16G16B16_SNORM; + case GL_BGR: + return VK_FORMAT_UNDEFINED; + case GL_RGBA: + return VK_FORMAT_R16G16B16A16_SNORM; + case GL_BGRA: + return VK_FORMAT_UNDEFINED; + case GL_RED_INTEGER: + return VK_FORMAT_R16_SINT; + case GL_RG_INTEGER: + return VK_FORMAT_R16G16_SINT; + case GL_RGB_INTEGER: + return VK_FORMAT_R16G16B16_SINT; + case GL_BGR_INTEGER: + return VK_FORMAT_UNDEFINED; + case GL_RGBA_INTEGER: + return VK_FORMAT_R16G16B16A16_SINT; + case GL_BGRA_INTEGER: + return VK_FORMAT_UNDEFINED; + case GL_STENCIL_INDEX: + return VK_FORMAT_UNDEFINED; + case GL_DEPTH_COMPONENT: + return VK_FORMAT_UNDEFINED; + case GL_DEPTH_STENCIL: + return VK_FORMAT_UNDEFINED; + } + break; + } + case GL_HALF_FLOAT: + case GL_HALF_FLOAT_OES: { + switch (format) { + case GL_RED: + return VK_FORMAT_R16_SFLOAT; + case GL_RG: + return VK_FORMAT_R16G16_SFLOAT; + case GL_RGB: + return VK_FORMAT_R16G16B16_SFLOAT; + case GL_BGR: + return VK_FORMAT_UNDEFINED; + case GL_RGBA: + return VK_FORMAT_R16G16B16A16_SFLOAT; + case GL_BGRA: + return VK_FORMAT_UNDEFINED; + case GL_RED_INTEGER: + return VK_FORMAT_UNDEFINED; + case GL_RG_INTEGER: + return VK_FORMAT_UNDEFINED; + case GL_RGB_INTEGER: + return VK_FORMAT_UNDEFINED; + case GL_BGR_INTEGER: + return VK_FORMAT_UNDEFINED; + case GL_RGBA_INTEGER: + return VK_FORMAT_UNDEFINED; + case GL_BGRA_INTEGER: + return VK_FORMAT_UNDEFINED; + case GL_STENCIL_INDEX: + return VK_FORMAT_UNDEFINED; + case GL_DEPTH_COMPONENT: + return VK_FORMAT_UNDEFINED; + case GL_DEPTH_STENCIL: + return VK_FORMAT_UNDEFINED; + } + break; + } + + // + // 32 bits per component + // + case GL_UNSIGNED_INT: { + switch (format) { + case GL_RED: + return VK_FORMAT_R32_UINT; + case GL_RG: + return VK_FORMAT_R32G32_UINT; + case GL_RGB: + return VK_FORMAT_R32G32B32_UINT; + case GL_BGR: + return VK_FORMAT_UNDEFINED; + case GL_RGBA: + return VK_FORMAT_R32G32B32A32_UINT; + case GL_BGRA: + return VK_FORMAT_UNDEFINED; + case GL_RED_INTEGER: + return VK_FORMAT_R32_UINT; + case GL_RG_INTEGER: + return VK_FORMAT_R32G32_UINT; + case GL_RGB_INTEGER: + return VK_FORMAT_R32G32B32_UINT; + case GL_BGR_INTEGER: + return VK_FORMAT_UNDEFINED; + case GL_RGBA_INTEGER: + return VK_FORMAT_R32G32B32A32_UINT; + case GL_BGRA_INTEGER: + return VK_FORMAT_UNDEFINED; + case GL_STENCIL_INDEX: + return VK_FORMAT_UNDEFINED; + case GL_DEPTH_COMPONENT: + return VK_FORMAT_X8_D24_UNORM_PACK32; + case GL_DEPTH_STENCIL: + return VK_FORMAT_D24_UNORM_S8_UINT; + } + break; + } + case GL_INT: { + switch (format) { + case GL_RED: + return VK_FORMAT_R32_SINT; + case GL_RG: + return VK_FORMAT_R32G32_SINT; + case GL_RGB: + return VK_FORMAT_R32G32B32_SINT; + case GL_BGR: + return VK_FORMAT_UNDEFINED; + case GL_RGBA: + return VK_FORMAT_R32G32B32A32_SINT; + case GL_BGRA: + return VK_FORMAT_UNDEFINED; + case GL_RED_INTEGER: + return VK_FORMAT_R32_SINT; + case GL_RG_INTEGER: + return VK_FORMAT_R32G32_SINT; + case GL_RGB_INTEGER: + return VK_FORMAT_R32G32B32_SINT; + case GL_BGR_INTEGER: + return VK_FORMAT_UNDEFINED; + case GL_RGBA_INTEGER: + return VK_FORMAT_R32G32B32A32_SINT; + case GL_BGRA_INTEGER: + return VK_FORMAT_UNDEFINED; + case GL_STENCIL_INDEX: + return VK_FORMAT_UNDEFINED; + case GL_DEPTH_COMPONENT: + return VK_FORMAT_UNDEFINED; + case GL_DEPTH_STENCIL: + return VK_FORMAT_UNDEFINED; + } + break; + } + case GL_FLOAT: { + switch (format) { + case GL_RED: + return VK_FORMAT_R32_SFLOAT; + case GL_RG: + return VK_FORMAT_R32G32_SFLOAT; + case GL_RGB: + return VK_FORMAT_R32G32B32_SFLOAT; + case GL_BGR: + return VK_FORMAT_UNDEFINED; + case GL_RGBA: + return VK_FORMAT_R32G32B32A32_SFLOAT; + case GL_BGRA: + return VK_FORMAT_UNDEFINED; + case GL_RED_INTEGER: + return VK_FORMAT_UNDEFINED; + case GL_RG_INTEGER: + return VK_FORMAT_UNDEFINED; + case GL_RGB_INTEGER: + return VK_FORMAT_UNDEFINED; + case GL_BGR_INTEGER: + return VK_FORMAT_UNDEFINED; + case GL_RGBA_INTEGER: + return VK_FORMAT_UNDEFINED; + case GL_BGRA_INTEGER: + return VK_FORMAT_UNDEFINED; + case GL_STENCIL_INDEX: + return VK_FORMAT_UNDEFINED; + case GL_DEPTH_COMPONENT: + return VK_FORMAT_D32_SFLOAT; + case GL_DEPTH_STENCIL: + return VK_FORMAT_D32_SFLOAT_S8_UINT; + } + break; + } + + // + // 64 bits per component + // + case GL_UNSIGNED_INT64: { + switch (format) { + case GL_RED: + return VK_FORMAT_R64_UINT; + case GL_RG: + return VK_FORMAT_R64G64_UINT; + case GL_RGB: + return VK_FORMAT_R64G64B64_UINT; + case GL_BGR: + return VK_FORMAT_UNDEFINED; + case GL_RGBA: + return VK_FORMAT_R64G64B64A64_UINT; + case GL_BGRA: + return VK_FORMAT_UNDEFINED; + case GL_RED_INTEGER: + return VK_FORMAT_UNDEFINED; + case GL_RG_INTEGER: + return VK_FORMAT_UNDEFINED; + case GL_RGB_INTEGER: + return VK_FORMAT_UNDEFINED; + case GL_BGR_INTEGER: + return VK_FORMAT_UNDEFINED; + case GL_RGBA_INTEGER: + return VK_FORMAT_UNDEFINED; + case GL_BGRA_INTEGER: + return VK_FORMAT_UNDEFINED; + case GL_STENCIL_INDEX: + return VK_FORMAT_UNDEFINED; + case GL_DEPTH_COMPONENT: + return VK_FORMAT_UNDEFINED; + case GL_DEPTH_STENCIL: + return VK_FORMAT_UNDEFINED; + } + break; + } + case GL_INT64: { + switch (format) { + case GL_RED: + return VK_FORMAT_R64_SINT; + case GL_RG: + return VK_FORMAT_R64G64_SINT; + case GL_RGB: + return VK_FORMAT_R64G64B64_SINT; + case GL_BGR: + return VK_FORMAT_UNDEFINED; + case GL_RGBA: + return VK_FORMAT_R64G64B64A64_SINT; + case GL_BGRA: + return VK_FORMAT_UNDEFINED; + case GL_RED_INTEGER: + return VK_FORMAT_R64_SINT; + case GL_RG_INTEGER: + return VK_FORMAT_R64G64_SINT; + case GL_RGB_INTEGER: + return VK_FORMAT_R64G64B64_SINT; + case GL_BGR_INTEGER: + return VK_FORMAT_UNDEFINED; + case GL_RGBA_INTEGER: + return VK_FORMAT_R64G64B64A64_SINT; + case GL_BGRA_INTEGER: + return VK_FORMAT_UNDEFINED; + case GL_STENCIL_INDEX: + return VK_FORMAT_UNDEFINED; + case GL_DEPTH_COMPONENT: + return VK_FORMAT_UNDEFINED; + case GL_DEPTH_STENCIL: + return VK_FORMAT_UNDEFINED; + } + break; + } + case GL_DOUBLE: { + switch (format) { + case GL_RED: + return VK_FORMAT_R64_SFLOAT; + case GL_RG: + return VK_FORMAT_R64G64_SFLOAT; + case GL_RGB: + return VK_FORMAT_R64G64B64_SFLOAT; + case GL_BGR: + return VK_FORMAT_UNDEFINED; + case GL_RGBA: + return VK_FORMAT_R64G64B64A64_SFLOAT; + case GL_BGRA: + return VK_FORMAT_UNDEFINED; + case GL_RED_INTEGER: + return VK_FORMAT_R64_SFLOAT; + case GL_RG_INTEGER: + return VK_FORMAT_R64G64_SFLOAT; + case GL_RGB_INTEGER: + return VK_FORMAT_R64G64B64_SFLOAT; + case GL_BGR_INTEGER: + return VK_FORMAT_UNDEFINED; + case GL_RGBA_INTEGER: + return VK_FORMAT_R64G64B64A64_SFLOAT; + case GL_BGRA_INTEGER: + return VK_FORMAT_UNDEFINED; + case GL_STENCIL_INDEX: + return VK_FORMAT_UNDEFINED; + case GL_DEPTH_COMPONENT: + return VK_FORMAT_UNDEFINED; + case GL_DEPTH_STENCIL: + return VK_FORMAT_UNDEFINED; + } + break; + } + + // + // Packed + // + case GL_UNSIGNED_BYTE_3_3_2: + assert(format == GL_RGB || format == GL_RGB_INTEGER); + return VK_FORMAT_UNDEFINED; + case GL_UNSIGNED_BYTE_2_3_3_REV: + assert(format == GL_BGR || format == GL_BGR_INTEGER); + return VK_FORMAT_UNDEFINED; + case GL_UNSIGNED_SHORT_5_6_5: + assert(format == GL_RGB || format == GL_RGB_INTEGER); + return VK_FORMAT_R5G6B5_UNORM_PACK16; + case GL_UNSIGNED_SHORT_5_6_5_REV: + assert(format == GL_BGR || format == GL_BGR_INTEGER); + return VK_FORMAT_B5G6R5_UNORM_PACK16; + case GL_UNSIGNED_SHORT_4_4_4_4: + assert( + format == GL_RGB || format == GL_BGRA || format == GL_RGB_INTEGER || + format == GL_BGRA_INTEGER); + return VK_FORMAT_R4G4B4A4_UNORM_PACK16; + case GL_UNSIGNED_SHORT_4_4_4_4_REV: + assert( + format == GL_RGB || format == GL_BGRA || format == GL_RGB_INTEGER || + format == GL_BGRA_INTEGER); + return VK_FORMAT_B4G4R4A4_UNORM_PACK16; + case GL_UNSIGNED_SHORT_5_5_5_1: + assert( + format == GL_RGB || format == GL_BGRA || format == GL_RGB_INTEGER || + format == GL_BGRA_INTEGER); + return VK_FORMAT_R5G5B5A1_UNORM_PACK16; + case GL_UNSIGNED_SHORT_1_5_5_5_REV: + assert( + format == GL_RGB || format == GL_BGRA || format == GL_RGB_INTEGER || + format == GL_BGRA_INTEGER); + return VK_FORMAT_A1R5G5B5_UNORM_PACK16; + case GL_UNSIGNED_INT_8_8_8_8: + assert( + format == GL_RGB || format == GL_BGRA || format == GL_RGB_INTEGER || + format == GL_BGRA_INTEGER); + return (format == GL_RGB_INTEGER || format == GL_BGRA_INTEGER) + ? VK_FORMAT_R8G8B8A8_UINT + : VK_FORMAT_R8G8B8A8_UNORM; + case GL_UNSIGNED_INT_8_8_8_8_REV: + assert( + format == GL_RGB || format == GL_BGRA || format == GL_RGB_INTEGER || + format == GL_BGRA_INTEGER); + return (format == GL_RGB_INTEGER || format == GL_BGRA_INTEGER) + ? VK_FORMAT_A8B8G8R8_UINT_PACK32 + : VK_FORMAT_A8B8G8R8_UNORM_PACK32; + case GL_UNSIGNED_INT_10_10_10_2: + assert( + format == GL_RGB || format == GL_BGRA || format == GL_RGB_INTEGER || + format == GL_BGRA_INTEGER); + return (format == GL_RGB_INTEGER || format == GL_BGRA_INTEGER) + ? VK_FORMAT_A2R10G10B10_UINT_PACK32 + : VK_FORMAT_A2R10G10B10_UNORM_PACK32; + case GL_UNSIGNED_INT_2_10_10_10_REV: + assert( + format == GL_RGB || format == GL_BGRA || format == GL_RGB_INTEGER || + format == GL_BGRA_INTEGER); + return (format == GL_RGB_INTEGER || format == GL_BGRA_INTEGER) + ? VK_FORMAT_A2B10G10R10_UINT_PACK32 + : VK_FORMAT_A2B10G10R10_UNORM_PACK32; + case GL_UNSIGNED_INT_10F_11F_11F_REV: + assert(format == GL_RGB || format == GL_BGR); + return VK_FORMAT_B10G11R11_UFLOAT_PACK32; + case GL_UNSIGNED_INT_5_9_9_9_REV: + assert(format == GL_RGB || format == GL_BGR); + return VK_FORMAT_E5B9G9R9_UFLOAT_PACK32; + case GL_UNSIGNED_INT_24_8: + assert(format == GL_DEPTH_STENCIL); + return VK_FORMAT_D24_UNORM_S8_UINT; + case GL_FLOAT_32_UNSIGNED_INT_24_8_REV: + assert(format == GL_DEPTH_STENCIL); + return VK_FORMAT_D32_SFLOAT_S8_UINT; + } + + return VK_FORMAT_UNDEFINED; +} + +static inline VkFormat vkGetFormatFromOpenGLType( + const GLenum type, + const GLuint numComponents, + const GLboolean normalized) { + switch (type) { + // + // 8 bits per component + // + case GL_UNSIGNED_BYTE: { + switch (numComponents) { + case 1: + return normalized ? VK_FORMAT_R8_UNORM : VK_FORMAT_R8_UINT; + case 2: + return normalized ? VK_FORMAT_R8G8_UNORM : VK_FORMAT_R8G8_UINT; + case 3: + return normalized ? VK_FORMAT_R8G8B8_UNORM : VK_FORMAT_R8G8B8_UINT; + case 4: + return normalized ? VK_FORMAT_R8G8B8A8_UNORM : VK_FORMAT_R8G8B8A8_UINT; + } + break; + } + case GL_BYTE: { + switch (numComponents) { + case 1: + return normalized ? VK_FORMAT_R8_SNORM : VK_FORMAT_R8_SINT; + case 2: + return normalized ? VK_FORMAT_R8G8_SNORM : VK_FORMAT_R8G8_SINT; + case 3: + return normalized ? VK_FORMAT_R8G8B8_SNORM : VK_FORMAT_R8G8B8_SINT; + case 4: + return normalized ? VK_FORMAT_R8G8B8A8_SNORM : VK_FORMAT_R8G8B8A8_SINT; + } + break; + } + + // + // 16 bits per component + // + case GL_UNSIGNED_SHORT: { + switch (numComponents) { + case 1: + return normalized ? VK_FORMAT_R16_UNORM : VK_FORMAT_R16_UINT; + case 2: + return normalized ? VK_FORMAT_R16G16_UNORM : VK_FORMAT_R16G16_UINT; + case 3: + return normalized ? VK_FORMAT_R16G16B16_UNORM : VK_FORMAT_R16G16B16_UINT; + case 4: + return normalized ? VK_FORMAT_R16G16B16A16_UNORM : VK_FORMAT_R16G16B16A16_UINT; + } + break; + } + case GL_SHORT: { + switch (numComponents) { + case 1: + return normalized ? VK_FORMAT_R16_SNORM : VK_FORMAT_R16_SINT; + case 2: + return normalized ? VK_FORMAT_R16G16_SNORM : VK_FORMAT_R16G16_SINT; + case 3: + return normalized ? VK_FORMAT_R16G16B16_SNORM : VK_FORMAT_R16G16B16_SINT; + case 4: + return normalized ? VK_FORMAT_R16G16B16A16_SNORM : VK_FORMAT_R16G16B16A16_SINT; + } + break; + } + case GL_HALF_FLOAT: + case GL_HALF_FLOAT_OES: { + switch (numComponents) { + case 1: + return VK_FORMAT_R16_SFLOAT; + case 2: + return VK_FORMAT_R16G16_SFLOAT; + case 3: + return VK_FORMAT_R16G16B16_SFLOAT; + case 4: + return VK_FORMAT_R16G16B16A16_SFLOAT; + } + break; + } + + // + // 32 bits per component + // + case GL_UNSIGNED_INT: { + switch (numComponents) { + case 1: + return VK_FORMAT_R32_UINT; + case 2: + return VK_FORMAT_R32G32_UINT; + case 3: + return VK_FORMAT_R32G32B32_UINT; + case 4: + return VK_FORMAT_R32G32B32A32_UINT; + } + break; + } + case GL_INT: { + switch (numComponents) { + case 1: + return VK_FORMAT_R32_SINT; + case 2: + return VK_FORMAT_R32G32_SINT; + case 3: + return VK_FORMAT_R32G32B32_SINT; + case 4: + return VK_FORMAT_R32G32B32A32_SINT; + } + break; + } + case GL_FLOAT: { + switch (numComponents) { + case 1: + return VK_FORMAT_R32_SFLOAT; + case 2: + return VK_FORMAT_R32G32_SFLOAT; + case 3: + return VK_FORMAT_R32G32B32_SFLOAT; + case 4: + return VK_FORMAT_R32G32B32A32_SFLOAT; + } + break; + } + + // + // 64 bits per component + // + case GL_UNSIGNED_INT64: { + switch (numComponents) { + case 1: + return VK_FORMAT_R64_UINT; + case 2: + return VK_FORMAT_R64G64_UINT; + case 3: + return VK_FORMAT_R64G64B64_UINT; + case 4: + return VK_FORMAT_R64G64B64A64_UINT; + } + break; + } + case GL_INT64: { + switch (numComponents) { + case 1: + return VK_FORMAT_R64_SINT; + case 2: + return VK_FORMAT_R64G64_SINT; + case 3: + return VK_FORMAT_R64G64B64_SINT; + case 4: + return VK_FORMAT_R64G64B64A64_SINT; + } + break; + } + case GL_DOUBLE: { + switch (numComponents) { + case 1: + return VK_FORMAT_R64_SFLOAT; + case 2: + return VK_FORMAT_R64G64_SFLOAT; + case 3: + return VK_FORMAT_R64G64B64_SFLOAT; + case 4: + return VK_FORMAT_R64G64B64A64_SFLOAT; + } + break; + } + + // + // Packed + // + case GL_UNSIGNED_BYTE_3_3_2: + return VK_FORMAT_UNDEFINED; + case GL_UNSIGNED_BYTE_2_3_3_REV: + return VK_FORMAT_UNDEFINED; + case GL_UNSIGNED_SHORT_5_6_5: + return VK_FORMAT_R5G6B5_UNORM_PACK16; + case GL_UNSIGNED_SHORT_5_6_5_REV: + return VK_FORMAT_B5G6R5_UNORM_PACK16; + case GL_UNSIGNED_SHORT_4_4_4_4: + return VK_FORMAT_R4G4B4A4_UNORM_PACK16; + case GL_UNSIGNED_SHORT_4_4_4_4_REV: + return VK_FORMAT_B4G4R4A4_UNORM_PACK16; + case GL_UNSIGNED_SHORT_5_5_5_1: + return VK_FORMAT_R5G5B5A1_UNORM_PACK16; + case GL_UNSIGNED_SHORT_1_5_5_5_REV: + return VK_FORMAT_A1R5G5B5_UNORM_PACK16; + case GL_UNSIGNED_INT_8_8_8_8: + return normalized ? VK_FORMAT_R8G8B8A8_UNORM : VK_FORMAT_R8G8B8A8_UINT; + case GL_UNSIGNED_INT_8_8_8_8_REV: + return normalized ? VK_FORMAT_A8B8G8R8_UNORM_PACK32 : VK_FORMAT_A8B8G8R8_UINT_PACK32; + case GL_UNSIGNED_INT_10_10_10_2: + return normalized ? VK_FORMAT_A2R10G10B10_UNORM_PACK32 + : VK_FORMAT_A2R10G10B10_UINT_PACK32; + case GL_UNSIGNED_INT_2_10_10_10_REV: + return normalized ? VK_FORMAT_A2B10G10R10_UNORM_PACK32 + : VK_FORMAT_A2B10G10R10_UINT_PACK32; + case GL_UNSIGNED_INT_10F_11F_11F_REV: + return VK_FORMAT_B10G11R11_UFLOAT_PACK32; + case GL_UNSIGNED_INT_5_9_9_9_REV: + return VK_FORMAT_E5B9G9R9_UFLOAT_PACK32; + case GL_UNSIGNED_INT_24_8: + return VK_FORMAT_D24_UNORM_S8_UINT; + case GL_FLOAT_32_UNSIGNED_INT_24_8_REV: + return VK_FORMAT_D32_SFLOAT_S8_UINT; + } + + return VK_FORMAT_UNDEFINED; +} + +static inline VkFormat vkGetFormatFromOpenGLInternalFormat(const GLenum internalFormat) { + switch (internalFormat) { + // + // 8 bits per component + // + case GL_R8: + return VK_FORMAT_R8_UNORM; // 1-component, 8-bit unsigned normalized + case GL_RG8: + return VK_FORMAT_R8G8_UNORM; // 2-component, 8-bit unsigned normalized + case GL_RGB8: + return VK_FORMAT_R8G8B8_UNORM; // 3-component, 8-bit unsigned normalized + case GL_RGBA8: + return VK_FORMAT_R8G8B8A8_UNORM; // 4-component, 8-bit unsigned normalized + + case GL_R8_SNORM: + return VK_FORMAT_R8_SNORM; // 1-component, 8-bit signed normalized + case GL_RG8_SNORM: + return VK_FORMAT_R8G8_SNORM; // 2-component, 8-bit signed normalized + case GL_RGB8_SNORM: + return VK_FORMAT_R8G8B8_SNORM; // 3-component, 8-bit signed normalized + case GL_RGBA8_SNORM: + return VK_FORMAT_R8G8B8A8_SNORM; // 4-component, 8-bit signed normalized + + case GL_R8UI: + return VK_FORMAT_R8_UINT; // 1-component, 8-bit unsigned integer + case GL_RG8UI: + return VK_FORMAT_R8G8_UINT; // 2-component, 8-bit unsigned integer + case GL_RGB8UI: + return VK_FORMAT_R8G8B8_UINT; // 3-component, 8-bit unsigned integer + case GL_RGBA8UI: + return VK_FORMAT_R8G8B8A8_UINT; // 4-component, 8-bit unsigned integer + + case GL_R8I: + return VK_FORMAT_R8_SINT; // 1-component, 8-bit signed integer + case GL_RG8I: + return VK_FORMAT_R8G8_SINT; // 2-component, 8-bit signed integer + case GL_RGB8I: + return VK_FORMAT_R8G8B8_SINT; // 3-component, 8-bit signed integer + case GL_RGBA8I: + return VK_FORMAT_R8G8B8A8_SINT; // 4-component, 8-bit signed integer + + case GL_SR8: + return VK_FORMAT_R8_SRGB; // 1-component, 8-bit sRGB + case GL_SRG8: + return VK_FORMAT_R8G8_SRGB; // 2-component, 8-bit sRGB + case GL_SRGB8: + return VK_FORMAT_R8G8B8_SRGB; // 3-component, 8-bit sRGB + case GL_SRGB8_ALPHA8: + return VK_FORMAT_R8G8B8A8_SRGB; // 4-component, 8-bit sRGB + + // + // 16 bits per component + // + case GL_R16: + return VK_FORMAT_R16_UNORM; // 1-component, 16-bit unsigned normalized + case GL_RG16: + return VK_FORMAT_R16G16_UNORM; // 2-component, 16-bit unsigned normalized + case GL_RGB16: + return VK_FORMAT_R16G16B16_UNORM; // 3-component, 16-bit unsigned normalized + case GL_RGBA16: + return VK_FORMAT_R16G16B16A16_UNORM; // 4-component, 16-bit unsigned normalized + + case GL_R16_SNORM: + return VK_FORMAT_R16_SNORM; // 1-component, 16-bit signed normalized + case GL_RG16_SNORM: + return VK_FORMAT_R16G16_SNORM; // 2-component, 16-bit signed normalized + case GL_RGB16_SNORM: + return VK_FORMAT_R16G16B16_SNORM; // 3-component, 16-bit signed normalized + case GL_RGBA16_SNORM: + return VK_FORMAT_R16G16B16A16_SNORM; // 4-component, 16-bit signed normalized + + case GL_R16UI: + return VK_FORMAT_R16_UINT; // 1-component, 16-bit unsigned integer + case GL_RG16UI: + return VK_FORMAT_R16G16_UINT; // 2-component, 16-bit unsigned integer + case GL_RGB16UI: + return VK_FORMAT_R16G16B16_UINT; // 3-component, 16-bit unsigned integer + case GL_RGBA16UI: + return VK_FORMAT_R16G16B16A16_UINT; // 4-component, 16-bit unsigned integer + + case GL_R16I: + return VK_FORMAT_R16_SINT; // 1-component, 16-bit signed integer + case GL_RG16I: + return VK_FORMAT_R16G16_SINT; // 2-component, 16-bit signed integer + case GL_RGB16I: + return VK_FORMAT_R16G16B16_SINT; // 3-component, 16-bit signed integer + case GL_RGBA16I: + return VK_FORMAT_R16G16B16A16_SINT; // 4-component, 16-bit signed integer + + case GL_R16F: + return VK_FORMAT_R16_SFLOAT; // 1-component, 16-bit floating-point + case GL_RG16F: + return VK_FORMAT_R16G16_SFLOAT; // 2-component, 16-bit floating-point + case GL_RGB16F: + return VK_FORMAT_R16G16B16_SFLOAT; // 3-component, 16-bit floating-point + case GL_RGBA16F: + return VK_FORMAT_R16G16B16A16_SFLOAT; // 4-component, 16-bit floating-point + + // + // 32 bits per component + // + case GL_R32UI: + return VK_FORMAT_R32_UINT; // 1-component, 32-bit unsigned integer + case GL_RG32UI: + return VK_FORMAT_R32G32_UINT; // 2-component, 32-bit unsigned integer + case GL_RGB32UI: + return VK_FORMAT_R32G32B32_UINT; // 3-component, 32-bit unsigned integer + case GL_RGBA32UI: + return VK_FORMAT_R32G32B32A32_UINT; // 4-component, 32-bit unsigned integer + + case GL_R32I: + return VK_FORMAT_R32_SINT; // 1-component, 32-bit signed integer + case GL_RG32I: + return VK_FORMAT_R32G32_SINT; // 2-component, 32-bit signed integer + case GL_RGB32I: + return VK_FORMAT_R32G32B32_SINT; // 3-component, 32-bit signed integer + case GL_RGBA32I: + return VK_FORMAT_R32G32B32A32_SINT; // 4-component, 32-bit signed integer + + case GL_R32F: + return VK_FORMAT_R32_SFLOAT; // 1-component, 32-bit floating-point + case GL_RG32F: + return VK_FORMAT_R32G32_SFLOAT; // 2-component, 32-bit floating-point + case GL_RGB32F: + return VK_FORMAT_R32G32B32_SFLOAT; // 3-component, 32-bit floating-point + case GL_RGBA32F: + return VK_FORMAT_R32G32B32A32_SFLOAT; // 4-component, 32-bit floating-point + + // + // Packed + // + case GL_R3_G3_B2: + return VK_FORMAT_UNDEFINED; // 3-component 3:3:2, unsigned normalized + case GL_RGB4: + return VK_FORMAT_UNDEFINED; // 3-component 4:4:4, unsigned normalized + case GL_RGB5: + return VK_FORMAT_R5G5B5A1_UNORM_PACK16; // 3-component 5:5:5, unsigned normalized + case GL_RGB565: + return VK_FORMAT_R5G6B5_UNORM_PACK16; // 3-component 5:6:5, unsigned normalized + case GL_RGB10: + return VK_FORMAT_A2R10G10B10_UNORM_PACK32; // 3-component 10:10:10, unsigned + // normalized + case GL_RGB12: + return VK_FORMAT_UNDEFINED; // 3-component 12:12:12, unsigned normalized + case GL_RGBA2: + return VK_FORMAT_UNDEFINED; // 4-component 2:2:2:2, unsigned normalized + case GL_RGBA4: + return VK_FORMAT_R4G4B4A4_UNORM_PACK16; // 4-component 4:4:4:4, unsigned normalized + case GL_RGBA12: + return VK_FORMAT_UNDEFINED; // 4-component 12:12:12:12, unsigned normalized + case GL_RGB5_A1: + return VK_FORMAT_A1R5G5B5_UNORM_PACK16; // 4-component 5:5:5:1, unsigned normalized + case GL_RGB10_A2: + return VK_FORMAT_A2R10G10B10_UNORM_PACK32; // 4-component 10:10:10:2, unsigned + // normalized + case GL_RGB10_A2UI: + return VK_FORMAT_A2R10G10B10_UINT_PACK32; // 4-component 10:10:10:2, unsigned integer + case GL_R11F_G11F_B10F: + return VK_FORMAT_B10G11R11_UFLOAT_PACK32; // 3-component 11:11:10, floating-point + case GL_RGB9_E5: + return VK_FORMAT_E5B9G9R9_UFLOAT_PACK32; // 3-component/exp 9:9:9/5, floating-point + + // + // S3TC/DXT/BC + // + + case GL_COMPRESSED_RGB_S3TC_DXT1_EXT: + return VK_FORMAT_BC1_RGB_UNORM_BLOCK; // line through 3D space, 4x4 blocks, unsigned + // normalized + case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: + return VK_FORMAT_BC1_RGBA_UNORM_BLOCK; // line through 3D space plus 1-bit alpha, 4x4 + // blocks, unsigned normalized + case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: + return VK_FORMAT_BC2_UNORM_BLOCK; // line through 3D space plus line through 1D space, + // 4x4 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT: + return VK_FORMAT_BC3_UNORM_BLOCK; // line through 3D space plus 4-bit alpha, 4x4 blocks, + // unsigned normalized + + case GL_COMPRESSED_SRGB_S3TC_DXT1_EXT: + return VK_FORMAT_BC1_RGB_SRGB_BLOCK; // line through 3D space, 4x4 blocks, sRGB + case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT: + return VK_FORMAT_BC1_RGBA_SRGB_BLOCK; // line through 3D space plus 1-bit alpha, 4x4 + // blocks, sRGB + case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT: + return VK_FORMAT_BC2_SRGB_BLOCK; // line through 3D space plus line through 1D space, + // 4x4 blocks, sRGB + case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT: + return VK_FORMAT_BC3_SRGB_BLOCK; // line through 3D space plus 4-bit alpha, 4x4 blocks, + // sRGB + + case GL_COMPRESSED_LUMINANCE_LATC1_EXT: + return VK_FORMAT_BC4_UNORM_BLOCK; // line through 1D space, 4x4 blocks, unsigned + // normalized + case GL_COMPRESSED_LUMINANCE_ALPHA_LATC2_EXT: + return VK_FORMAT_BC5_UNORM_BLOCK; // two lines through 1D space, 4x4 blocks, unsigned + // normalized + case GL_COMPRESSED_SIGNED_LUMINANCE_LATC1_EXT: + return VK_FORMAT_BC4_SNORM_BLOCK; // line through 1D space, 4x4 blocks, signed + // normalized + case GL_COMPRESSED_SIGNED_LUMINANCE_ALPHA_LATC2_EXT: + return VK_FORMAT_BC5_SNORM_BLOCK; // two lines through 1D space, 4x4 blocks, signed + // normalized + + case GL_COMPRESSED_RED_RGTC1: + return VK_FORMAT_BC4_UNORM_BLOCK; // line through 1D space, 4x4 blocks, unsigned + // normalized + case GL_COMPRESSED_RG_RGTC2: + return VK_FORMAT_BC5_UNORM_BLOCK; // two lines through 1D space, 4x4 blocks, unsigned + // normalized + case GL_COMPRESSED_SIGNED_RED_RGTC1: + return VK_FORMAT_BC4_SNORM_BLOCK; // line through 1D space, 4x4 blocks, signed + // normalized + case GL_COMPRESSED_SIGNED_RG_RGTC2: + return VK_FORMAT_BC5_SNORM_BLOCK; // two lines through 1D space, 4x4 blocks, signed + // normalized + + case GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT: + return VK_FORMAT_BC6H_UFLOAT_BLOCK; // 3-component, 4x4 blocks, unsigned floating-point + case GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT: + return VK_FORMAT_BC6H_SFLOAT_BLOCK; // 3-component, 4x4 blocks, signed floating-point + case GL_COMPRESSED_RGBA_BPTC_UNORM: + return VK_FORMAT_BC7_UNORM_BLOCK; // 4-component, 4x4 blocks, unsigned normalized + case GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM: + return VK_FORMAT_BC7_SRGB_BLOCK; // 4-component, 4x4 blocks, sRGB + + // + // ETC + // + case GL_ETC1_RGB8_OES: + return VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK; // 3-component ETC1, 4x4 blocks, unsigned + // normalized + + case GL_COMPRESSED_RGB8_ETC2: + return VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK; // 3-component ETC2, 4x4 blocks, unsigned + // normalized + case GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2: + return VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK; // 4-component ETC2 with 1-bit alpha, 4x4 + // blocks, unsigned normalized + case GL_COMPRESSED_RGBA8_ETC2_EAC: + return VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK; // 4-component ETC2, 4x4 blocks, unsigned + // normalized + + case GL_COMPRESSED_SRGB8_ETC2: + return VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK; // 3-component ETC2, 4x4 blocks, sRGB + case GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2: + return VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK; // 4-component ETC2 with 1-bit alpha, 4x4 + // blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC: + return VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK; // 4-component ETC2, 4x4 blocks, sRGB + + case GL_COMPRESSED_R11_EAC: + return VK_FORMAT_EAC_R11_UNORM_BLOCK; // 1-component ETC, 4x4 blocks, unsigned + // normalized + case GL_COMPRESSED_RG11_EAC: + return VK_FORMAT_EAC_R11G11_UNORM_BLOCK; // 2-component ETC, 4x4 blocks, unsigned + // normalized + case GL_COMPRESSED_SIGNED_R11_EAC: + return VK_FORMAT_EAC_R11_SNORM_BLOCK; // 1-component ETC, 4x4 blocks, signed normalized + case GL_COMPRESSED_SIGNED_RG11_EAC: + return VK_FORMAT_EAC_R11G11_SNORM_BLOCK; // 2-component ETC, 4x4 blocks, signed + // normalized + + // + // PVRTC + // + case GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG: + return VK_FORMAT_UNDEFINED; // 3-component PVRTC, 16x8 blocks, unsigned normalized + case GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG: + return VK_FORMAT_UNDEFINED; // 3-component PVRTC, 8x8 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG: + return VK_FORMAT_UNDEFINED; // 4-component PVRTC, 16x8 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG: + return VK_FORMAT_UNDEFINED; // 4-component PVRTC, 8x8 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_PVRTC_2BPPV2_IMG: + return VK_FORMAT_UNDEFINED; // 4-component PVRTC, 8x4 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_PVRTC_4BPPV2_IMG: + return VK_FORMAT_UNDEFINED; // 4-component PVRTC, 4x4 blocks, unsigned normalized + + case GL_COMPRESSED_SRGB_PVRTC_2BPPV1_EXT: + return VK_FORMAT_UNDEFINED; // 3-component PVRTC, 16x8 blocks, sRGB + case GL_COMPRESSED_SRGB_PVRTC_4BPPV1_EXT: + return VK_FORMAT_UNDEFINED; // 3-component PVRTC, 8x8 blocks, sRGB + case GL_COMPRESSED_SRGB_ALPHA_PVRTC_2BPPV1_EXT: + return VK_FORMAT_UNDEFINED; // 4-component PVRTC, 16x8 blocks, sRGB + case GL_COMPRESSED_SRGB_ALPHA_PVRTC_4BPPV1_EXT: + return VK_FORMAT_UNDEFINED; // 4-component PVRTC, 8x8 blocks, sRGB + case GL_COMPRESSED_SRGB_ALPHA_PVRTC_2BPPV2_IMG: + return VK_FORMAT_UNDEFINED; // 4-component PVRTC, 8x4 blocks, sRGB + case GL_COMPRESSED_SRGB_ALPHA_PVRTC_4BPPV2_IMG: + return VK_FORMAT_UNDEFINED; // 4-component PVRTC, 4x4 blocks, sRGB + + // + // ASTC + // + case GL_COMPRESSED_RGBA_ASTC_4x4_KHR: + return VK_FORMAT_ASTC_4x4_UNORM_BLOCK; // 4-component ASTC, 4x4 blocks, unsigned + // normalized + case GL_COMPRESSED_RGBA_ASTC_5x4_KHR: + return VK_FORMAT_ASTC_5x4_UNORM_BLOCK; // 4-component ASTC, 5x4 blocks, unsigned + // normalized + case GL_COMPRESSED_RGBA_ASTC_5x5_KHR: + return VK_FORMAT_ASTC_5x5_UNORM_BLOCK; // 4-component ASTC, 5x5 blocks, unsigned + // normalized + case GL_COMPRESSED_RGBA_ASTC_6x5_KHR: + return VK_FORMAT_ASTC_6x5_UNORM_BLOCK; // 4-component ASTC, 6x5 blocks, unsigned + // normalized + case GL_COMPRESSED_RGBA_ASTC_6x6_KHR: + return VK_FORMAT_ASTC_6x6_UNORM_BLOCK; // 4-component ASTC, 6x6 blocks, unsigned + // normalized + case GL_COMPRESSED_RGBA_ASTC_8x5_KHR: + return VK_FORMAT_ASTC_8x5_UNORM_BLOCK; // 4-component ASTC, 8x5 blocks, unsigned + // normalized + case GL_COMPRESSED_RGBA_ASTC_8x6_KHR: + return VK_FORMAT_ASTC_8x6_UNORM_BLOCK; // 4-component ASTC, 8x6 blocks, unsigned + // normalized + case GL_COMPRESSED_RGBA_ASTC_8x8_KHR: + return VK_FORMAT_ASTC_8x8_UNORM_BLOCK; // 4-component ASTC, 8x8 blocks, unsigned + // normalized + case GL_COMPRESSED_RGBA_ASTC_10x5_KHR: + return VK_FORMAT_ASTC_10x5_UNORM_BLOCK; // 4-component ASTC, 10x5 blocks, unsigned + // normalized + case GL_COMPRESSED_RGBA_ASTC_10x6_KHR: + return VK_FORMAT_ASTC_10x6_UNORM_BLOCK; // 4-component ASTC, 10x6 blocks, unsigned + // normalized + case GL_COMPRESSED_RGBA_ASTC_10x8_KHR: + return VK_FORMAT_ASTC_10x8_UNORM_BLOCK; // 4-component ASTC, 10x8 blocks, unsigned + // normalized + case GL_COMPRESSED_RGBA_ASTC_10x10_KHR: + return VK_FORMAT_ASTC_10x10_UNORM_BLOCK; // 4-component ASTC, 10x10 blocks, unsigned + // normalized + case GL_COMPRESSED_RGBA_ASTC_12x10_KHR: + return VK_FORMAT_ASTC_12x10_UNORM_BLOCK; // 4-component ASTC, 12x10 blocks, unsigned + // normalized + case GL_COMPRESSED_RGBA_ASTC_12x12_KHR: + return VK_FORMAT_ASTC_12x12_UNORM_BLOCK; // 4-component ASTC, 12x12 blocks, unsigned + // normalized + + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR: + return VK_FORMAT_ASTC_4x4_SRGB_BLOCK; // 4-component ASTC, 4x4 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR: + return VK_FORMAT_ASTC_5x4_SRGB_BLOCK; // 4-component ASTC, 5x4 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR: + return VK_FORMAT_ASTC_5x5_SRGB_BLOCK; // 4-component ASTC, 5x5 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR: + return VK_FORMAT_ASTC_6x5_SRGB_BLOCK; // 4-component ASTC, 6x5 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR: + return VK_FORMAT_ASTC_6x6_SRGB_BLOCK; // 4-component ASTC, 6x6 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR: + return VK_FORMAT_ASTC_8x5_SRGB_BLOCK; // 4-component ASTC, 8x5 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR: + return VK_FORMAT_ASTC_8x6_SRGB_BLOCK; // 4-component ASTC, 8x6 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR: + return VK_FORMAT_ASTC_8x8_SRGB_BLOCK; // 4-component ASTC, 8x8 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR: + return VK_FORMAT_ASTC_10x5_SRGB_BLOCK; // 4-component ASTC, 10x5 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR: + return VK_FORMAT_ASTC_10x6_SRGB_BLOCK; // 4-component ASTC, 10x6 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR: + return VK_FORMAT_ASTC_10x8_SRGB_BLOCK; // 4-component ASTC, 10x8 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR: + return VK_FORMAT_ASTC_10x10_SRGB_BLOCK; // 4-component ASTC, 10x10 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR: + return VK_FORMAT_ASTC_12x10_SRGB_BLOCK; // 4-component ASTC, 12x10 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR: + return VK_FORMAT_ASTC_12x12_SRGB_BLOCK; // 4-component ASTC, 12x12 blocks, sRGB + + case GL_COMPRESSED_RGBA_ASTC_3x3x3_OES: + return VK_FORMAT_UNDEFINED; // 4-component ASTC, 3x3x3 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_ASTC_4x3x3_OES: + return VK_FORMAT_UNDEFINED; // 4-component ASTC, 4x3x3 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_ASTC_4x4x3_OES: + return VK_FORMAT_UNDEFINED; // 4-component ASTC, 4x4x3 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_ASTC_4x4x4_OES: + return VK_FORMAT_UNDEFINED; // 4-component ASTC, 4x4x4 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_ASTC_5x4x4_OES: + return VK_FORMAT_UNDEFINED; // 4-component ASTC, 5x4x4 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_ASTC_5x5x4_OES: + return VK_FORMAT_UNDEFINED; // 4-component ASTC, 5x5x4 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_ASTC_5x5x5_OES: + return VK_FORMAT_UNDEFINED; // 4-component ASTC, 5x5x5 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_ASTC_6x5x5_OES: + return VK_FORMAT_UNDEFINED; // 4-component ASTC, 6x5x5 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_ASTC_6x6x5_OES: + return VK_FORMAT_UNDEFINED; // 4-component ASTC, 6x6x5 blocks, unsigned normalized + case GL_COMPRESSED_RGBA_ASTC_6x6x6_OES: + return VK_FORMAT_UNDEFINED; // 4-component ASTC, 6x6x6 blocks, unsigned normalized + + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_3x3x3_OES: + return VK_FORMAT_UNDEFINED; // 4-component ASTC, 3x3x3 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x3x3_OES: + return VK_FORMAT_UNDEFINED; // 4-component ASTC, 4x3x3 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4x3_OES: + return VK_FORMAT_UNDEFINED; // 4-component ASTC, 4x4x3 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4x4_OES: + return VK_FORMAT_UNDEFINED; // 4-component ASTC, 4x4x4 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4x4_OES: + return VK_FORMAT_UNDEFINED; // 4-component ASTC, 5x4x4 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5x4_OES: + return VK_FORMAT_UNDEFINED; // 4-component ASTC, 5x5x4 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5x5_OES: + return VK_FORMAT_UNDEFINED; // 4-component ASTC, 5x5x5 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5x5_OES: + return VK_FORMAT_UNDEFINED; // 4-component ASTC, 6x5x5 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6x5_OES: + return VK_FORMAT_UNDEFINED; // 4-component ASTC, 6x6x5 blocks, sRGB + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6x6_OES: + return VK_FORMAT_UNDEFINED; // 4-component ASTC, 6x6x6 blocks, sRGB + + // + // ATC + // + case GL_ATC_RGB_AMD: + return VK_FORMAT_UNDEFINED; // 3-component, 4x4 blocks, unsigned normalized + case GL_ATC_RGBA_EXPLICIT_ALPHA_AMD: + return VK_FORMAT_UNDEFINED; // 4-component, 4x4 blocks, unsigned normalized + case GL_ATC_RGBA_INTERPOLATED_ALPHA_AMD: + return VK_FORMAT_UNDEFINED; // 4-component, 4x4 blocks, unsigned normalized + + // + // Palletized + // + case GL_PALETTE4_RGB8_OES: + return VK_FORMAT_UNDEFINED; // 3-component 8:8:8, 4-bit palette, unsigned normalized + case GL_PALETTE4_RGBA8_OES: + return VK_FORMAT_UNDEFINED; // 4-component 8:8:8:8, 4-bit palette, unsigned normalized + case GL_PALETTE4_R5_G6_B5_OES: + return VK_FORMAT_UNDEFINED; // 3-component 5:6:5, 4-bit palette, unsigned normalized + case GL_PALETTE4_RGBA4_OES: + return VK_FORMAT_UNDEFINED; // 4-component 4:4:4:4, 4-bit palette, unsigned normalized + case GL_PALETTE4_RGB5_A1_OES: + return VK_FORMAT_UNDEFINED; // 4-component 5:5:5:1, 4-bit palette, unsigned normalized + case GL_PALETTE8_RGB8_OES: + return VK_FORMAT_UNDEFINED; // 3-component 8:8:8, 8-bit palette, unsigned normalized + case GL_PALETTE8_RGBA8_OES: + return VK_FORMAT_UNDEFINED; // 4-component 8:8:8:8, 8-bit palette, unsigned normalized + case GL_PALETTE8_R5_G6_B5_OES: + return VK_FORMAT_UNDEFINED; // 3-component 5:6:5, 8-bit palette, unsigned normalized + case GL_PALETTE8_RGBA4_OES: + return VK_FORMAT_UNDEFINED; // 4-component 4:4:4:4, 8-bit palette, unsigned normalized + case GL_PALETTE8_RGB5_A1_OES: + return VK_FORMAT_UNDEFINED; // 4-component 5:5:5:1, 8-bit palette, unsigned normalized + + // + // Depth/stencil + // + case GL_DEPTH_COMPONENT16: + return VK_FORMAT_D16_UNORM; + case GL_DEPTH_COMPONENT24: + return VK_FORMAT_X8_D24_UNORM_PACK32; + case GL_DEPTH_COMPONENT32: + return VK_FORMAT_UNDEFINED; + case GL_DEPTH_COMPONENT32F: + return VK_FORMAT_D32_SFLOAT; + case GL_DEPTH_COMPONENT32F_NV: + return VK_FORMAT_D32_SFLOAT; + case GL_STENCIL_INDEX1: + return VK_FORMAT_UNDEFINED; + case GL_STENCIL_INDEX4: + return VK_FORMAT_UNDEFINED; + case GL_STENCIL_INDEX8: + return VK_FORMAT_S8_UINT; + case GL_STENCIL_INDEX16: + return VK_FORMAT_UNDEFINED; + case GL_DEPTH24_STENCIL8: + return VK_FORMAT_D24_UNORM_S8_UINT; + case GL_DEPTH32F_STENCIL8: + return VK_FORMAT_D32_SFLOAT_S8_UINT; + case GL_DEPTH32F_STENCIL8_NV: + return VK_FORMAT_D32_SFLOAT_S8_UINT; + + default: + return VK_FORMAT_UNDEFINED; + } +} + +typedef enum VkFormatSizeFlagBits { + VK_FORMAT_SIZE_PACKED_BIT = 0x00000001, + VK_FORMAT_SIZE_COMPRESSED_BIT = 0x00000002, + VK_FORMAT_SIZE_PALETTIZED_BIT = 0x00000004, + VK_FORMAT_SIZE_DEPTH_BIT = 0x00000008, + VK_FORMAT_SIZE_STENCIL_BIT = 0x00000010, +} VkFormatSizeFlagBits; + +typedef VkFlags VkFormatSizeFlags; + +typedef struct VkFormatSize { + VkFormatSizeFlags flags; + unsigned int paletteSizeInBits; + unsigned int blockSizeInBits; + unsigned int blockWidth; // in texels + unsigned int blockHeight; // in texels + unsigned int blockDepth; // in texels +} VkFormatSize; + +static inline void vkGetFormatSize(const VkFormat format, VkFormatSize* pFormatSize) { + switch (format) { + case VK_FORMAT_R4G4_UNORM_PACK8: + pFormatSize->flags = VK_FORMAT_SIZE_PACKED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 1 * 8; + pFormatSize->blockWidth = 1; + pFormatSize->blockHeight = 1; + pFormatSize->blockDepth = 1; + break; + case VK_FORMAT_R4G4B4A4_UNORM_PACK16: + case VK_FORMAT_B4G4R4A4_UNORM_PACK16: + case VK_FORMAT_R5G6B5_UNORM_PACK16: + case VK_FORMAT_B5G6R5_UNORM_PACK16: + case VK_FORMAT_R5G5B5A1_UNORM_PACK16: + case VK_FORMAT_B5G5R5A1_UNORM_PACK16: + case VK_FORMAT_A1R5G5B5_UNORM_PACK16: + pFormatSize->flags = VK_FORMAT_SIZE_PACKED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 2 * 8; + pFormatSize->blockWidth = 1; + pFormatSize->blockHeight = 1; + pFormatSize->blockDepth = 1; + break; + case VK_FORMAT_R8_UNORM: + case VK_FORMAT_R8_SNORM: + case VK_FORMAT_R8_USCALED: + case VK_FORMAT_R8_SSCALED: + case VK_FORMAT_R8_UINT: + case VK_FORMAT_R8_SINT: + case VK_FORMAT_R8_SRGB: + pFormatSize->flags = 0; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 1 * 8; + pFormatSize->blockWidth = 1; + pFormatSize->blockHeight = 1; + pFormatSize->blockDepth = 1; + break; + case VK_FORMAT_R8G8_UNORM: + case VK_FORMAT_R8G8_SNORM: + case VK_FORMAT_R8G8_USCALED: + case VK_FORMAT_R8G8_SSCALED: + case VK_FORMAT_R8G8_UINT: + case VK_FORMAT_R8G8_SINT: + case VK_FORMAT_R8G8_SRGB: + pFormatSize->flags = 0; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 2 * 8; + pFormatSize->blockWidth = 1; + pFormatSize->blockHeight = 1; + pFormatSize->blockDepth = 1; + break; + case VK_FORMAT_R8G8B8_UNORM: + case VK_FORMAT_R8G8B8_SNORM: + case VK_FORMAT_R8G8B8_USCALED: + case VK_FORMAT_R8G8B8_SSCALED: + case VK_FORMAT_R8G8B8_UINT: + case VK_FORMAT_R8G8B8_SINT: + case VK_FORMAT_R8G8B8_SRGB: + case VK_FORMAT_B8G8R8_UNORM: + case VK_FORMAT_B8G8R8_SNORM: + case VK_FORMAT_B8G8R8_USCALED: + case VK_FORMAT_B8G8R8_SSCALED: + case VK_FORMAT_B8G8R8_UINT: + case VK_FORMAT_B8G8R8_SINT: + case VK_FORMAT_B8G8R8_SRGB: + pFormatSize->flags = 0; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 3 * 8; + pFormatSize->blockWidth = 1; + pFormatSize->blockHeight = 1; + pFormatSize->blockDepth = 1; + break; + case VK_FORMAT_R8G8B8A8_UNORM: + case VK_FORMAT_R8G8B8A8_SNORM: + case VK_FORMAT_R8G8B8A8_USCALED: + case VK_FORMAT_R8G8B8A8_SSCALED: + case VK_FORMAT_R8G8B8A8_UINT: + case VK_FORMAT_R8G8B8A8_SINT: + case VK_FORMAT_R8G8B8A8_SRGB: + case VK_FORMAT_B8G8R8A8_UNORM: + case VK_FORMAT_B8G8R8A8_SNORM: + case VK_FORMAT_B8G8R8A8_USCALED: + case VK_FORMAT_B8G8R8A8_SSCALED: + case VK_FORMAT_B8G8R8A8_UINT: + case VK_FORMAT_B8G8R8A8_SINT: + case VK_FORMAT_B8G8R8A8_SRGB: + pFormatSize->flags = 0; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 4 * 8; + pFormatSize->blockWidth = 1; + pFormatSize->blockHeight = 1; + pFormatSize->blockDepth = 1; + break; + case VK_FORMAT_A8B8G8R8_UNORM_PACK32: + case VK_FORMAT_A8B8G8R8_SNORM_PACK32: + case VK_FORMAT_A8B8G8R8_USCALED_PACK32: + case VK_FORMAT_A8B8G8R8_SSCALED_PACK32: + case VK_FORMAT_A8B8G8R8_UINT_PACK32: + case VK_FORMAT_A8B8G8R8_SINT_PACK32: + case VK_FORMAT_A8B8G8R8_SRGB_PACK32: + pFormatSize->flags = VK_FORMAT_SIZE_PACKED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 4 * 8; + pFormatSize->blockWidth = 1; + pFormatSize->blockHeight = 1; + pFormatSize->blockDepth = 1; + break; + case VK_FORMAT_A2R10G10B10_UNORM_PACK32: + case VK_FORMAT_A2R10G10B10_SNORM_PACK32: + case VK_FORMAT_A2R10G10B10_USCALED_PACK32: + case VK_FORMAT_A2R10G10B10_SSCALED_PACK32: + case VK_FORMAT_A2R10G10B10_UINT_PACK32: + case VK_FORMAT_A2R10G10B10_SINT_PACK32: + case VK_FORMAT_A2B10G10R10_UNORM_PACK32: + case VK_FORMAT_A2B10G10R10_SNORM_PACK32: + case VK_FORMAT_A2B10G10R10_USCALED_PACK32: + case VK_FORMAT_A2B10G10R10_SSCALED_PACK32: + case VK_FORMAT_A2B10G10R10_UINT_PACK32: + case VK_FORMAT_A2B10G10R10_SINT_PACK32: + pFormatSize->flags = VK_FORMAT_SIZE_PACKED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 4 * 8; + pFormatSize->blockWidth = 1; + pFormatSize->blockHeight = 1; + pFormatSize->blockDepth = 1; + break; + case VK_FORMAT_R16_UNORM: + case VK_FORMAT_R16_SNORM: + case VK_FORMAT_R16_USCALED: + case VK_FORMAT_R16_SSCALED: + case VK_FORMAT_R16_UINT: + case VK_FORMAT_R16_SINT: + case VK_FORMAT_R16_SFLOAT: + pFormatSize->flags = 0; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 2 * 8; + pFormatSize->blockWidth = 1; + pFormatSize->blockHeight = 1; + pFormatSize->blockDepth = 1; + break; + case VK_FORMAT_R16G16_UNORM: + case VK_FORMAT_R16G16_SNORM: + case VK_FORMAT_R16G16_USCALED: + case VK_FORMAT_R16G16_SSCALED: + case VK_FORMAT_R16G16_UINT: + case VK_FORMAT_R16G16_SINT: + case VK_FORMAT_R16G16_SFLOAT: + pFormatSize->flags = 0; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 4 * 8; + pFormatSize->blockWidth = 1; + pFormatSize->blockHeight = 1; + pFormatSize->blockDepth = 1; + break; + case VK_FORMAT_R16G16B16_UNORM: + case VK_FORMAT_R16G16B16_SNORM: + case VK_FORMAT_R16G16B16_USCALED: + case VK_FORMAT_R16G16B16_SSCALED: + case VK_FORMAT_R16G16B16_UINT: + case VK_FORMAT_R16G16B16_SINT: + case VK_FORMAT_R16G16B16_SFLOAT: + pFormatSize->flags = 0; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 6 * 8; + pFormatSize->blockWidth = 1; + pFormatSize->blockHeight = 1; + pFormatSize->blockDepth = 1; + break; + case VK_FORMAT_R16G16B16A16_UNORM: + case VK_FORMAT_R16G16B16A16_SNORM: + case VK_FORMAT_R16G16B16A16_USCALED: + case VK_FORMAT_R16G16B16A16_SSCALED: + case VK_FORMAT_R16G16B16A16_UINT: + case VK_FORMAT_R16G16B16A16_SINT: + case VK_FORMAT_R16G16B16A16_SFLOAT: + pFormatSize->flags = 0; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 8 * 8; + pFormatSize->blockWidth = 1; + pFormatSize->blockHeight = 1; + pFormatSize->blockDepth = 1; + break; + case VK_FORMAT_R32_UINT: + case VK_FORMAT_R32_SINT: + case VK_FORMAT_R32_SFLOAT: + pFormatSize->flags = 0; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 4 * 8; + pFormatSize->blockWidth = 1; + pFormatSize->blockHeight = 1; + pFormatSize->blockDepth = 1; + break; + case VK_FORMAT_R32G32_UINT: + case VK_FORMAT_R32G32_SINT: + case VK_FORMAT_R32G32_SFLOAT: + pFormatSize->flags = 0; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 8 * 8; + pFormatSize->blockWidth = 1; + pFormatSize->blockHeight = 1; + pFormatSize->blockDepth = 1; + break; + case VK_FORMAT_R32G32B32_UINT: + case VK_FORMAT_R32G32B32_SINT: + case VK_FORMAT_R32G32B32_SFLOAT: + pFormatSize->flags = 0; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 12 * 8; + pFormatSize->blockWidth = 1; + pFormatSize->blockHeight = 1; + pFormatSize->blockDepth = 1; + break; + case VK_FORMAT_R32G32B32A32_UINT: + case VK_FORMAT_R32G32B32A32_SINT: + case VK_FORMAT_R32G32B32A32_SFLOAT: + pFormatSize->flags = 0; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 16 * 8; + pFormatSize->blockWidth = 1; + pFormatSize->blockHeight = 1; + pFormatSize->blockDepth = 1; + break; + case VK_FORMAT_R64_UINT: + case VK_FORMAT_R64_SINT: + case VK_FORMAT_R64_SFLOAT: + pFormatSize->flags = 0; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 8 * 8; + pFormatSize->blockWidth = 1; + pFormatSize->blockHeight = 1; + pFormatSize->blockDepth = 1; + break; + case VK_FORMAT_R64G64_UINT: + case VK_FORMAT_R64G64_SINT: + case VK_FORMAT_R64G64_SFLOAT: + pFormatSize->flags = 0; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 16 * 8; + pFormatSize->blockWidth = 1; + pFormatSize->blockHeight = 1; + pFormatSize->blockDepth = 1; + break; + case VK_FORMAT_R64G64B64_UINT: + case VK_FORMAT_R64G64B64_SINT: + case VK_FORMAT_R64G64B64_SFLOAT: + pFormatSize->flags = 0; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 24 * 8; + pFormatSize->blockWidth = 1; + pFormatSize->blockHeight = 1; + pFormatSize->blockDepth = 1; + break; + case VK_FORMAT_R64G64B64A64_UINT: + case VK_FORMAT_R64G64B64A64_SINT: + case VK_FORMAT_R64G64B64A64_SFLOAT: + pFormatSize->flags = 0; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 32 * 8; + pFormatSize->blockWidth = 1; + pFormatSize->blockHeight = 1; + pFormatSize->blockDepth = 1; + break; + case VK_FORMAT_B10G11R11_UFLOAT_PACK32: + case VK_FORMAT_E5B9G9R9_UFLOAT_PACK32: + pFormatSize->flags = VK_FORMAT_SIZE_PACKED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 4 * 8; + pFormatSize->blockWidth = 1; + pFormatSize->blockHeight = 1; + pFormatSize->blockDepth = 1; + break; + case VK_FORMAT_D16_UNORM: + pFormatSize->flags = VK_FORMAT_SIZE_DEPTH_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 2 * 8; + pFormatSize->blockWidth = 1; + pFormatSize->blockHeight = 1; + pFormatSize->blockDepth = 1; + break; + case VK_FORMAT_X8_D24_UNORM_PACK32: + pFormatSize->flags = VK_FORMAT_SIZE_PACKED_BIT | VK_FORMAT_SIZE_DEPTH_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 4 * 8; + pFormatSize->blockWidth = 1; + pFormatSize->blockHeight = 1; + pFormatSize->blockDepth = 1; + break; + case VK_FORMAT_D32_SFLOAT: + pFormatSize->flags = VK_FORMAT_SIZE_DEPTH_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 4 * 8; + pFormatSize->blockWidth = 1; + pFormatSize->blockHeight = 1; + pFormatSize->blockDepth = 1; + break; + case VK_FORMAT_S8_UINT: + pFormatSize->flags = VK_FORMAT_SIZE_STENCIL_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 1 * 8; + pFormatSize->blockWidth = 1; + pFormatSize->blockHeight = 1; + pFormatSize->blockDepth = 1; + break; + case VK_FORMAT_D16_UNORM_S8_UINT: + pFormatSize->flags = VK_FORMAT_SIZE_DEPTH_BIT | VK_FORMAT_SIZE_STENCIL_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 3 * 8; + pFormatSize->blockWidth = 1; + pFormatSize->blockHeight = 1; + pFormatSize->blockDepth = 1; + break; + case VK_FORMAT_D24_UNORM_S8_UINT: + pFormatSize->flags = VK_FORMAT_SIZE_DEPTH_BIT | VK_FORMAT_SIZE_STENCIL_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 4 * 8; + pFormatSize->blockWidth = 1; + pFormatSize->blockHeight = 1; + pFormatSize->blockDepth = 1; + break; + case VK_FORMAT_D32_SFLOAT_S8_UINT: + pFormatSize->flags = VK_FORMAT_SIZE_DEPTH_BIT | VK_FORMAT_SIZE_STENCIL_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 8 * 8; + pFormatSize->blockWidth = 1; + pFormatSize->blockHeight = 1; + pFormatSize->blockDepth = 1; + break; + case VK_FORMAT_BC1_RGB_UNORM_BLOCK: + case VK_FORMAT_BC1_RGB_SRGB_BLOCK: + case VK_FORMAT_BC1_RGBA_UNORM_BLOCK: + case VK_FORMAT_BC1_RGBA_SRGB_BLOCK: + pFormatSize->flags = VK_FORMAT_SIZE_COMPRESSED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 8 * 8; + pFormatSize->blockWidth = 4; + pFormatSize->blockHeight = 4; + pFormatSize->blockDepth = 1; + break; + case VK_FORMAT_BC2_UNORM_BLOCK: + case VK_FORMAT_BC2_SRGB_BLOCK: + case VK_FORMAT_BC3_UNORM_BLOCK: + case VK_FORMAT_BC3_SRGB_BLOCK: + case VK_FORMAT_BC4_UNORM_BLOCK: + case VK_FORMAT_BC4_SNORM_BLOCK: + case VK_FORMAT_BC5_UNORM_BLOCK: + case VK_FORMAT_BC5_SNORM_BLOCK: + case VK_FORMAT_BC6H_UFLOAT_BLOCK: + case VK_FORMAT_BC6H_SFLOAT_BLOCK: + case VK_FORMAT_BC7_UNORM_BLOCK: + case VK_FORMAT_BC7_SRGB_BLOCK: + pFormatSize->flags = VK_FORMAT_SIZE_COMPRESSED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 16 * 8; + pFormatSize->blockWidth = 4; + pFormatSize->blockHeight = 4; + pFormatSize->blockDepth = 1; + break; + case VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK: + case VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK: + case VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK: + case VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK: + pFormatSize->flags = VK_FORMAT_SIZE_COMPRESSED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 8 * 8; + pFormatSize->blockWidth = 4; + pFormatSize->blockHeight = 4; + pFormatSize->blockDepth = 1; + break; + case VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK: + case VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK: + case VK_FORMAT_EAC_R11_UNORM_BLOCK: + case VK_FORMAT_EAC_R11_SNORM_BLOCK: + case VK_FORMAT_EAC_R11G11_UNORM_BLOCK: + case VK_FORMAT_EAC_R11G11_SNORM_BLOCK: + pFormatSize->flags = VK_FORMAT_SIZE_COMPRESSED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 16 * 8; + pFormatSize->blockWidth = 4; + pFormatSize->blockHeight = 4; + pFormatSize->blockDepth = 1; + break; + case VK_FORMAT_ASTC_4x4_UNORM_BLOCK: + case VK_FORMAT_ASTC_4x4_SRGB_BLOCK: + pFormatSize->flags = VK_FORMAT_SIZE_COMPRESSED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 16 * 8; + pFormatSize->blockWidth = 4; + pFormatSize->blockHeight = 4; + pFormatSize->blockDepth = 1; + break; + case VK_FORMAT_ASTC_5x4_UNORM_BLOCK: + case VK_FORMAT_ASTC_5x4_SRGB_BLOCK: + pFormatSize->flags = VK_FORMAT_SIZE_COMPRESSED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 16 * 8; + pFormatSize->blockWidth = 5; + pFormatSize->blockHeight = 4; + pFormatSize->blockDepth = 1; + break; + case VK_FORMAT_ASTC_5x5_UNORM_BLOCK: + case VK_FORMAT_ASTC_5x5_SRGB_BLOCK: + pFormatSize->flags = VK_FORMAT_SIZE_COMPRESSED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 16 * 8; + pFormatSize->blockWidth = 5; + pFormatSize->blockHeight = 5; + pFormatSize->blockDepth = 1; + break; + case VK_FORMAT_ASTC_6x5_UNORM_BLOCK: + case VK_FORMAT_ASTC_6x5_SRGB_BLOCK: + pFormatSize->flags = VK_FORMAT_SIZE_COMPRESSED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 16 * 8; + pFormatSize->blockWidth = 6; + pFormatSize->blockHeight = 5; + pFormatSize->blockDepth = 1; + break; + case VK_FORMAT_ASTC_6x6_UNORM_BLOCK: + case VK_FORMAT_ASTC_6x6_SRGB_BLOCK: + pFormatSize->flags = VK_FORMAT_SIZE_COMPRESSED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 16 * 8; + pFormatSize->blockWidth = 6; + pFormatSize->blockHeight = 6; + pFormatSize->blockDepth = 1; + break; + case VK_FORMAT_ASTC_8x5_UNORM_BLOCK: + case VK_FORMAT_ASTC_8x5_SRGB_BLOCK: + pFormatSize->flags = VK_FORMAT_SIZE_COMPRESSED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 16 * 8; + pFormatSize->blockWidth = 8; + pFormatSize->blockHeight = 5; + pFormatSize->blockDepth = 1; + break; + case VK_FORMAT_ASTC_8x6_UNORM_BLOCK: + case VK_FORMAT_ASTC_8x6_SRGB_BLOCK: + pFormatSize->flags = VK_FORMAT_SIZE_COMPRESSED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 16 * 8; + pFormatSize->blockWidth = 8; + pFormatSize->blockHeight = 6; + pFormatSize->blockDepth = 1; + break; + case VK_FORMAT_ASTC_8x8_UNORM_BLOCK: + case VK_FORMAT_ASTC_8x8_SRGB_BLOCK: + pFormatSize->flags = VK_FORMAT_SIZE_COMPRESSED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 16 * 8; + pFormatSize->blockWidth = 8; + pFormatSize->blockHeight = 8; + pFormatSize->blockDepth = 1; + break; + case VK_FORMAT_ASTC_10x5_UNORM_BLOCK: + case VK_FORMAT_ASTC_10x5_SRGB_BLOCK: + pFormatSize->flags = VK_FORMAT_SIZE_COMPRESSED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 16 * 8; + pFormatSize->blockWidth = 10; + pFormatSize->blockHeight = 5; + pFormatSize->blockDepth = 1; + break; + case VK_FORMAT_ASTC_10x6_UNORM_BLOCK: + case VK_FORMAT_ASTC_10x6_SRGB_BLOCK: + pFormatSize->flags = VK_FORMAT_SIZE_COMPRESSED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 16 * 8; + pFormatSize->blockWidth = 10; + pFormatSize->blockHeight = 6; + pFormatSize->blockDepth = 1; + break; + case VK_FORMAT_ASTC_10x8_UNORM_BLOCK: + case VK_FORMAT_ASTC_10x8_SRGB_BLOCK: + pFormatSize->flags = VK_FORMAT_SIZE_COMPRESSED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 16 * 8; + pFormatSize->blockWidth = 10; + pFormatSize->blockHeight = 8; + pFormatSize->blockDepth = 1; + break; + case VK_FORMAT_ASTC_10x10_UNORM_BLOCK: + case VK_FORMAT_ASTC_10x10_SRGB_BLOCK: + pFormatSize->flags = VK_FORMAT_SIZE_COMPRESSED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 16 * 8; + pFormatSize->blockWidth = 10; + pFormatSize->blockHeight = 10; + pFormatSize->blockDepth = 1; + break; + case VK_FORMAT_ASTC_12x10_UNORM_BLOCK: + case VK_FORMAT_ASTC_12x10_SRGB_BLOCK: + pFormatSize->flags = VK_FORMAT_SIZE_COMPRESSED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 16 * 8; + pFormatSize->blockWidth = 12; + pFormatSize->blockHeight = 10; + pFormatSize->blockDepth = 1; + break; + case VK_FORMAT_ASTC_12x12_UNORM_BLOCK: + case VK_FORMAT_ASTC_12x12_SRGB_BLOCK: + pFormatSize->flags = VK_FORMAT_SIZE_COMPRESSED_BIT; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 16 * 8; + pFormatSize->blockWidth = 12; + pFormatSize->blockHeight = 12; + pFormatSize->blockDepth = 1; + break; + default: + pFormatSize->flags = 0; + pFormatSize->paletteSizeInBits = 0; + pFormatSize->blockSizeInBits = 0 * 8; + pFormatSize->blockWidth = 1; + pFormatSize->blockHeight = 1; + pFormatSize->blockDepth = 1; + break; + } +} + +#endif // !VK_FORMAT_H diff --git a/Samples/3rdParty/CMakeLists.txt b/Samples/3rdParty/CMakeLists.txt new file mode 100755 index 0000000..4214f3f --- /dev/null +++ b/Samples/3rdParty/CMakeLists.txt @@ -0,0 +1,141 @@ +# Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved. + +function(find_ktx) + # use prebuild library for Android to avoid runtime crash, further + # investigation needed on building from ktx source + if (ANDROID) + set(KTX_DIR ${CMAKE_CURRENT_LIST_DIR}/khronos/ktx) + + add_library(ktx SHARED IMPORTED GLOBAL) + set(KTXLIB ${KTX_DIR}/lib/android/${ANDROID_ABI}/libktx.so) + set_target_properties(ktx PROPERTIES + IMPORTED_LOCATION ${KTXLIB} + INTERFACE_INCLUDE_DIRECTORIES ${KTX_DIR}/include) + elseif (WIN32) + set(KTX_DIR ${CMAKE_CURRENT_LIST_DIR}/khronos/ktx_src) + include(FetchContent) + + FetchContent_Declare( + libktx + GIT_REPOSITORY https://github.com/KhronosGroup/KTX-Software.git + GIT_TAG v4.2.1 + SOURCE_DIR ${KTX_DIR} + ) + + FetchContent_GetProperties(libktx) + if(NOT libktx_POPULATED) + FetchContent_Populate(libktx) + endif() + + if (WIN32) + set(KTX_FEATURE_STATIC_LIBRARY ON CACHE BOOL "Build static ktx target to avoid DLL hell on Windows") + endif() + + + # Common compile definitions + add_subdirectory(${libktx_SOURCE_DIR} ${libktx_BINARY_DIR}) + endif() +endfunction() + +function(find_zlib) + set(Z_DIR ${CMAKE_CURRENT_LIST_DIR}/zlib) + + include(FetchContent) + + FetchContent_Declare( + zlib + GIT_REPOSITORY https://github.com/madler/zlib.git + GIT_TAG v1.2.13 + SOURCE_DIR ${Z_DIR} + ) + + FetchContent_GetProperties(zlib) + if(NOT zlib_POPULATED) + FetchContent_Populate(zlib) + endif() + + # Common compile definitions + add_subdirectory(${zlib_SOURCE_DIR} ${zlib_BINARY_DIR}) +endfunction() + +function(find_minizip) + # Define the source files + set(MINIZIP_SRCS + ${CMAKE_CURRENT_LIST_DIR}/minizip/src/ioapi.c + ${CMAKE_CURRENT_LIST_DIR}/minizip/src/miniunz.c + ${CMAKE_CURRENT_LIST_DIR}/minizip/src/mztools.c + ${CMAKE_CURRENT_LIST_DIR}/minizip/src/unzip.c + ${CMAKE_CURRENT_LIST_DIR}/minizip/src/zip.c + ) + + # Add the library + add_library(minizip STATIC ${MINIZIP_SRCS}) + + # Add the include directories + target_include_directories(minizip PUBLIC ${CMAKE_CURRENT_LIST_DIR}/minizip/src) + + # no-shadow doesn't exist on MSVC + if(NOT MSVC) + target_compile_options(minizip PRIVATE + $<$:-Wno-shadow> + ) + endif() + + if(ANDROID) + target_compile_options(minizip PRIVATE + $<$:-Wno-unused-command-line-argument;-marm;-mfpu=neon> + $<$:-Wno-unused-command-line-argument;-marm> + ) + target_link_libraries(minizip z) + elseif (WIN32) + target_include_directories(minizip + PUBLIC + ${CMAKE_CURRENT_LIST_DIR}/zlib + ${zlib_BINARY_DIR}) + target_link_libraries(minizip zlibstatic) + endif() +endfunction() + +function(find_stb) + set(STB_SRC + ${CMAKE_CURRENT_LIST_DIR}/stb/src/stb_image.c + ${CMAKE_CURRENT_LIST_DIR}/stb/src/stb_image_write.c + ${CMAKE_CURRENT_LIST_DIR}/stb/src/stb_vorbis.c + ) + + add_library(stb STATIC ${STB_SRC}) + + target_include_directories(stb PUBLIC ${CMAKE_CURRENT_LIST_DIR}/stb/src) +endfunction() + +function(find_openxr_loader) + # Pull openxr loader for windows build as there is no aar method on windows + if(WIN32) + include(FetchContent) + + FetchContent_Declare( + openxr + GIT_REPOSITORY https://github.com/KhronosGroup/OpenXR-SDK.git + GIT_TAG release-1.1.36 + SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}/openxr + ) + + FetchContent_GetProperties(openxr) + if(NOT openxr_POPULATED) + FetchContent_Populate(openxr) + endif() + + # Common compile definitions + add_subdirectory(${openxr_SOURCE_DIR} ${openxr_BINARY_DIR}) + endif() +endfunction() + +find_ktx() +find_stb() + +# this need to be defined before minizip since minizip depends on zlib +if (WIN32) + find_zlib() +endif() +find_minizip() +find_openxr_loader() diff --git a/Samples/3rdParty/glext/GL/glext.h b/Samples/3rdParty/glext/GL/glext.h new file mode 100755 index 0000000..e77694e --- /dev/null +++ b/Samples/3rdParty/glext/GL/glext.h @@ -0,0 +1,11863 @@ +#ifndef __glext_h_ +#define __glext_h_ 1 + +#ifdef __cplusplus +extern "C" { +#endif + +/* +** Copyright (c) 2013-2014 The Khronos Group Inc. +** +** Permission is hereby granted, free of charge, to any person obtaining a +** copy of this software and/or associated documentation files (the +** "Materials"), to deal in the Materials without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Materials, and to +** permit persons to whom the Materials are furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be included +** in all copies or substantial portions of the Materials. +** +** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. +*/ +/* +** This header is generated from the Khronos OpenGL / OpenGL ES XML +** API Registry. The current version of the Registry, generator scripts +** used to make the header, and the header can be found at +** http://www.opengl.org/registry/ +** +** Khronos $Revision: 31191 $ on $Date: 2015-05-14 06:31:39 -0400 (Thu, 14 May 2015) $ +*/ + +#if defined(_WIN32) && !defined(APIENTRY) && !defined(__CYGWIN__) && !defined(__SCITECH_SNAP__) +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN 1 +#endif +#include +#endif + +#ifndef APIENTRY +#define APIENTRY +#endif +#ifndef APIENTRYP +#define APIENTRYP APIENTRY * +#endif +#ifndef GLAPI +#define GLAPI extern +#endif + +#define GL_GLEXT_VERSION 20150514 + +/* Generated C header for: + * API: gl + * Profile: compatibility + * Versions considered: .* + * Versions emitted: 1\.[2-9]|[234]\.[0-9] + * Default extensions included: gl + * Additional extensions included: _nomatch_^ + * Extensions removed: _nomatch_^ + */ + +#ifndef GL_VERSION_1_2 +#define GL_VERSION_1_2 1 +#define GL_UNSIGNED_BYTE_3_3_2 0x8032 +#define GL_UNSIGNED_SHORT_4_4_4_4 0x8033 +#define GL_UNSIGNED_SHORT_5_5_5_1 0x8034 +#define GL_UNSIGNED_INT_8_8_8_8 0x8035 +#define GL_UNSIGNED_INT_10_10_10_2 0x8036 +#define GL_TEXTURE_BINDING_3D 0x806A +#define GL_PACK_SKIP_IMAGES 0x806B +#define GL_PACK_IMAGE_HEIGHT 0x806C +#define GL_UNPACK_SKIP_IMAGES 0x806D +#define GL_UNPACK_IMAGE_HEIGHT 0x806E +#define GL_TEXTURE_3D 0x806F +#define GL_PROXY_TEXTURE_3D 0x8070 +#define GL_TEXTURE_DEPTH 0x8071 +#define GL_TEXTURE_WRAP_R 0x8072 +#define GL_MAX_3D_TEXTURE_SIZE 0x8073 +#define GL_UNSIGNED_BYTE_2_3_3_REV 0x8362 +#define GL_UNSIGNED_SHORT_5_6_5 0x8363 +#define GL_UNSIGNED_SHORT_5_6_5_REV 0x8364 +#define GL_UNSIGNED_SHORT_4_4_4_4_REV 0x8365 +#define GL_UNSIGNED_SHORT_1_5_5_5_REV 0x8366 +#define GL_UNSIGNED_INT_8_8_8_8_REV 0x8367 +#define GL_UNSIGNED_INT_2_10_10_10_REV 0x8368 +#define GL_BGR 0x80E0 +#define GL_BGRA 0x80E1 +#define GL_MAX_ELEMENTS_VERTICES 0x80E8 +#define GL_MAX_ELEMENTS_INDICES 0x80E9 +#define GL_CLAMP_TO_EDGE 0x812F +#define GL_TEXTURE_MIN_LOD 0x813A +#define GL_TEXTURE_MAX_LOD 0x813B +#define GL_TEXTURE_BASE_LEVEL 0x813C +#define GL_TEXTURE_MAX_LEVEL 0x813D +#define GL_SMOOTH_POINT_SIZE_RANGE 0x0B12 +#define GL_SMOOTH_POINT_SIZE_GRANULARITY 0x0B13 +#define GL_SMOOTH_LINE_WIDTH_RANGE 0x0B22 +#define GL_SMOOTH_LINE_WIDTH_GRANULARITY 0x0B23 +#define GL_ALIASED_LINE_WIDTH_RANGE 0x846E +#define GL_RESCALE_NORMAL 0x803A +#define GL_LIGHT_MODEL_COLOR_CONTROL 0x81F8 +#define GL_SINGLE_COLOR 0x81F9 +#define GL_SEPARATE_SPECULAR_COLOR 0x81FA +#define GL_ALIASED_POINT_SIZE_RANGE 0x846D +typedef void (APIENTRYP PFNGLDRAWRANGEELEMENTSPROC) (GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void *indices); +typedef void (APIENTRYP PFNGLTEXIMAGE3DPROC) (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void *pixels); +typedef void (APIENTRYP PFNGLTEXSUBIMAGE3DPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *pixels); +typedef void (APIENTRYP PFNGLCOPYTEXSUBIMAGE3DPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glDrawRangeElements (GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void *indices); +GLAPI void APIENTRY glTexImage3D (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void *pixels); +GLAPI void APIENTRY glTexSubImage3D (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *pixels); +GLAPI void APIENTRY glCopyTexSubImage3D (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height); +#endif +#endif /* GL_VERSION_1_2 */ + +#ifndef GL_VERSION_1_3 +#define GL_VERSION_1_3 1 +#define GL_TEXTURE0 0x84C0 +#define GL_TEXTURE1 0x84C1 +#define GL_TEXTURE2 0x84C2 +#define GL_TEXTURE3 0x84C3 +#define GL_TEXTURE4 0x84C4 +#define GL_TEXTURE5 0x84C5 +#define GL_TEXTURE6 0x84C6 +#define GL_TEXTURE7 0x84C7 +#define GL_TEXTURE8 0x84C8 +#define GL_TEXTURE9 0x84C9 +#define GL_TEXTURE10 0x84CA +#define GL_TEXTURE11 0x84CB +#define GL_TEXTURE12 0x84CC +#define GL_TEXTURE13 0x84CD +#define GL_TEXTURE14 0x84CE +#define GL_TEXTURE15 0x84CF +#define GL_TEXTURE16 0x84D0 +#define GL_TEXTURE17 0x84D1 +#define GL_TEXTURE18 0x84D2 +#define GL_TEXTURE19 0x84D3 +#define GL_TEXTURE20 0x84D4 +#define GL_TEXTURE21 0x84D5 +#define GL_TEXTURE22 0x84D6 +#define GL_TEXTURE23 0x84D7 +#define GL_TEXTURE24 0x84D8 +#define GL_TEXTURE25 0x84D9 +#define GL_TEXTURE26 0x84DA +#define GL_TEXTURE27 0x84DB +#define GL_TEXTURE28 0x84DC +#define GL_TEXTURE29 0x84DD +#define GL_TEXTURE30 0x84DE +#define GL_TEXTURE31 0x84DF +#define GL_ACTIVE_TEXTURE 0x84E0 +#define GL_MULTISAMPLE 0x809D +#define GL_SAMPLE_ALPHA_TO_COVERAGE 0x809E +#define GL_SAMPLE_ALPHA_TO_ONE 0x809F +#define GL_SAMPLE_COVERAGE 0x80A0 +#define GL_SAMPLE_BUFFERS 0x80A8 +#define GL_SAMPLES 0x80A9 +#define GL_SAMPLE_COVERAGE_VALUE 0x80AA +#define GL_SAMPLE_COVERAGE_INVERT 0x80AB +#define GL_TEXTURE_CUBE_MAP 0x8513 +#define GL_TEXTURE_BINDING_CUBE_MAP 0x8514 +#define GL_TEXTURE_CUBE_MAP_POSITIVE_X 0x8515 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_X 0x8516 +#define GL_TEXTURE_CUBE_MAP_POSITIVE_Y 0x8517 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Y 0x8518 +#define GL_TEXTURE_CUBE_MAP_POSITIVE_Z 0x8519 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Z 0x851A +#define GL_PROXY_TEXTURE_CUBE_MAP 0x851B +#define GL_MAX_CUBE_MAP_TEXTURE_SIZE 0x851C +#define GL_COMPRESSED_RGB 0x84ED +#define GL_COMPRESSED_RGBA 0x84EE +#define GL_TEXTURE_COMPRESSION_HINT 0x84EF +#define GL_TEXTURE_COMPRESSED_IMAGE_SIZE 0x86A0 +#define GL_TEXTURE_COMPRESSED 0x86A1 +#define GL_NUM_COMPRESSED_TEXTURE_FORMATS 0x86A2 +#define GL_COMPRESSED_TEXTURE_FORMATS 0x86A3 +#define GL_CLAMP_TO_BORDER 0x812D +#define GL_CLIENT_ACTIVE_TEXTURE 0x84E1 +#define GL_MAX_TEXTURE_UNITS 0x84E2 +#define GL_TRANSPOSE_MODELVIEW_MATRIX 0x84E3 +#define GL_TRANSPOSE_PROJECTION_MATRIX 0x84E4 +#define GL_TRANSPOSE_TEXTURE_MATRIX 0x84E5 +#define GL_TRANSPOSE_COLOR_MATRIX 0x84E6 +#define GL_MULTISAMPLE_BIT 0x20000000 +#define GL_NORMAL_MAP 0x8511 +#define GL_REFLECTION_MAP 0x8512 +#define GL_COMPRESSED_ALPHA 0x84E9 +#define GL_COMPRESSED_LUMINANCE 0x84EA +#define GL_COMPRESSED_LUMINANCE_ALPHA 0x84EB +#define GL_COMPRESSED_INTENSITY 0x84EC +#define GL_COMBINE 0x8570 +#define GL_COMBINE_RGB 0x8571 +#define GL_COMBINE_ALPHA 0x8572 +#define GL_SOURCE0_RGB 0x8580 +#define GL_SOURCE1_RGB 0x8581 +#define GL_SOURCE2_RGB 0x8582 +#define GL_SOURCE0_ALPHA 0x8588 +#define GL_SOURCE1_ALPHA 0x8589 +#define GL_SOURCE2_ALPHA 0x858A +#define GL_OPERAND0_RGB 0x8590 +#define GL_OPERAND1_RGB 0x8591 +#define GL_OPERAND2_RGB 0x8592 +#define GL_OPERAND0_ALPHA 0x8598 +#define GL_OPERAND1_ALPHA 0x8599 +#define GL_OPERAND2_ALPHA 0x859A +#define GL_RGB_SCALE 0x8573 +#define GL_ADD_SIGNED 0x8574 +#define GL_INTERPOLATE 0x8575 +#define GL_SUBTRACT 0x84E7 +#define GL_CONSTANT 0x8576 +#define GL_PRIMARY_COLOR 0x8577 +#define GL_PREVIOUS 0x8578 +#define GL_DOT3_RGB 0x86AE +#define GL_DOT3_RGBA 0x86AF +typedef void (APIENTRYP PFNGLACTIVETEXTUREPROC) (GLenum texture); +typedef void (APIENTRYP PFNGLSAMPLECOVERAGEPROC) (GLfloat value, GLboolean invert); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXIMAGE3DPROC) (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const void *data); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXIMAGE2DPROC) (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void *data); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXIMAGE1DPROC) (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLint border, GLsizei imageSize, const void *data); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXSUBIMAGE3DPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *data); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *data); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXSUBIMAGE1DPROC) (GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const void *data); +typedef void (APIENTRYP PFNGLGETCOMPRESSEDTEXIMAGEPROC) (GLenum target, GLint level, void *img); +typedef void (APIENTRYP PFNGLCLIENTACTIVETEXTUREPROC) (GLenum texture); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1DPROC) (GLenum target, GLdouble s); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1DVPROC) (GLenum target, const GLdouble *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1FPROC) (GLenum target, GLfloat s); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1FVPROC) (GLenum target, const GLfloat *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1IPROC) (GLenum target, GLint s); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1IVPROC) (GLenum target, const GLint *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1SPROC) (GLenum target, GLshort s); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1SVPROC) (GLenum target, const GLshort *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2DPROC) (GLenum target, GLdouble s, GLdouble t); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2DVPROC) (GLenum target, const GLdouble *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2FPROC) (GLenum target, GLfloat s, GLfloat t); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2FVPROC) (GLenum target, const GLfloat *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2IPROC) (GLenum target, GLint s, GLint t); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2IVPROC) (GLenum target, const GLint *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2SPROC) (GLenum target, GLshort s, GLshort t); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2SVPROC) (GLenum target, const GLshort *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3DPROC) (GLenum target, GLdouble s, GLdouble t, GLdouble r); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3DVPROC) (GLenum target, const GLdouble *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3FPROC) (GLenum target, GLfloat s, GLfloat t, GLfloat r); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3FVPROC) (GLenum target, const GLfloat *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3IPROC) (GLenum target, GLint s, GLint t, GLint r); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3IVPROC) (GLenum target, const GLint *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3SPROC) (GLenum target, GLshort s, GLshort t, GLshort r); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3SVPROC) (GLenum target, const GLshort *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4DPROC) (GLenum target, GLdouble s, GLdouble t, GLdouble r, GLdouble q); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4DVPROC) (GLenum target, const GLdouble *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4FPROC) (GLenum target, GLfloat s, GLfloat t, GLfloat r, GLfloat q); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4FVPROC) (GLenum target, const GLfloat *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4IPROC) (GLenum target, GLint s, GLint t, GLint r, GLint q); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4IVPROC) (GLenum target, const GLint *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4SPROC) (GLenum target, GLshort s, GLshort t, GLshort r, GLshort q); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4SVPROC) (GLenum target, const GLshort *v); +typedef void (APIENTRYP PFNGLLOADTRANSPOSEMATRIXFPROC) (const GLfloat *m); +typedef void (APIENTRYP PFNGLLOADTRANSPOSEMATRIXDPROC) (const GLdouble *m); +typedef void (APIENTRYP PFNGLMULTTRANSPOSEMATRIXFPROC) (const GLfloat *m); +typedef void (APIENTRYP PFNGLMULTTRANSPOSEMATRIXDPROC) (const GLdouble *m); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glActiveTexture (GLenum texture); +GLAPI void APIENTRY glSampleCoverage (GLfloat value, GLboolean invert); +GLAPI void APIENTRY glCompressedTexImage3D (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const void *data); +GLAPI void APIENTRY glCompressedTexImage2D (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void *data); +GLAPI void APIENTRY glCompressedTexImage1D (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLint border, GLsizei imageSize, const void *data); +GLAPI void APIENTRY glCompressedTexSubImage3D (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *data); +GLAPI void APIENTRY glCompressedTexSubImage2D (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *data); +GLAPI void APIENTRY glCompressedTexSubImage1D (GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const void *data); +GLAPI void APIENTRY glGetCompressedTexImage (GLenum target, GLint level, void *img); +GLAPI void APIENTRY glClientActiveTexture (GLenum texture); +GLAPI void APIENTRY glMultiTexCoord1d (GLenum target, GLdouble s); +GLAPI void APIENTRY glMultiTexCoord1dv (GLenum target, const GLdouble *v); +GLAPI void APIENTRY glMultiTexCoord1f (GLenum target, GLfloat s); +GLAPI void APIENTRY glMultiTexCoord1fv (GLenum target, const GLfloat *v); +GLAPI void APIENTRY glMultiTexCoord1i (GLenum target, GLint s); +GLAPI void APIENTRY glMultiTexCoord1iv (GLenum target, const GLint *v); +GLAPI void APIENTRY glMultiTexCoord1s (GLenum target, GLshort s); +GLAPI void APIENTRY glMultiTexCoord1sv (GLenum target, const GLshort *v); +GLAPI void APIENTRY glMultiTexCoord2d (GLenum target, GLdouble s, GLdouble t); +GLAPI void APIENTRY glMultiTexCoord2dv (GLenum target, const GLdouble *v); +GLAPI void APIENTRY glMultiTexCoord2f (GLenum target, GLfloat s, GLfloat t); +GLAPI void APIENTRY glMultiTexCoord2fv (GLenum target, const GLfloat *v); +GLAPI void APIENTRY glMultiTexCoord2i (GLenum target, GLint s, GLint t); +GLAPI void APIENTRY glMultiTexCoord2iv (GLenum target, const GLint *v); +GLAPI void APIENTRY glMultiTexCoord2s (GLenum target, GLshort s, GLshort t); +GLAPI void APIENTRY glMultiTexCoord2sv (GLenum target, const GLshort *v); +GLAPI void APIENTRY glMultiTexCoord3d (GLenum target, GLdouble s, GLdouble t, GLdouble r); +GLAPI void APIENTRY glMultiTexCoord3dv (GLenum target, const GLdouble *v); +GLAPI void APIENTRY glMultiTexCoord3f (GLenum target, GLfloat s, GLfloat t, GLfloat r); +GLAPI void APIENTRY glMultiTexCoord3fv (GLenum target, const GLfloat *v); +GLAPI void APIENTRY glMultiTexCoord3i (GLenum target, GLint s, GLint t, GLint r); +GLAPI void APIENTRY glMultiTexCoord3iv (GLenum target, const GLint *v); +GLAPI void APIENTRY glMultiTexCoord3s (GLenum target, GLshort s, GLshort t, GLshort r); +GLAPI void APIENTRY glMultiTexCoord3sv (GLenum target, const GLshort *v); +GLAPI void APIENTRY glMultiTexCoord4d (GLenum target, GLdouble s, GLdouble t, GLdouble r, GLdouble q); +GLAPI void APIENTRY glMultiTexCoord4dv (GLenum target, const GLdouble *v); +GLAPI void APIENTRY glMultiTexCoord4f (GLenum target, GLfloat s, GLfloat t, GLfloat r, GLfloat q); +GLAPI void APIENTRY glMultiTexCoord4fv (GLenum target, const GLfloat *v); +GLAPI void APIENTRY glMultiTexCoord4i (GLenum target, GLint s, GLint t, GLint r, GLint q); +GLAPI void APIENTRY glMultiTexCoord4iv (GLenum target, const GLint *v); +GLAPI void APIENTRY glMultiTexCoord4s (GLenum target, GLshort s, GLshort t, GLshort r, GLshort q); +GLAPI void APIENTRY glMultiTexCoord4sv (GLenum target, const GLshort *v); +GLAPI void APIENTRY glLoadTransposeMatrixf (const GLfloat *m); +GLAPI void APIENTRY glLoadTransposeMatrixd (const GLdouble *m); +GLAPI void APIENTRY glMultTransposeMatrixf (const GLfloat *m); +GLAPI void APIENTRY glMultTransposeMatrixd (const GLdouble *m); +#endif +#endif /* GL_VERSION_1_3 */ + +#ifndef GL_VERSION_1_4 +#define GL_VERSION_1_4 1 +#define GL_BLEND_DST_RGB 0x80C8 +#define GL_BLEND_SRC_RGB 0x80C9 +#define GL_BLEND_DST_ALPHA 0x80CA +#define GL_BLEND_SRC_ALPHA 0x80CB +#define GL_POINT_FADE_THRESHOLD_SIZE 0x8128 +#define GL_DEPTH_COMPONENT16 0x81A5 +#define GL_DEPTH_COMPONENT24 0x81A6 +#define GL_DEPTH_COMPONENT32 0x81A7 +#define GL_MIRRORED_REPEAT 0x8370 +#define GL_MAX_TEXTURE_LOD_BIAS 0x84FD +#define GL_TEXTURE_LOD_BIAS 0x8501 +#define GL_INCR_WRAP 0x8507 +#define GL_DECR_WRAP 0x8508 +#define GL_TEXTURE_DEPTH_SIZE 0x884A +#define GL_TEXTURE_COMPARE_MODE 0x884C +#define GL_TEXTURE_COMPARE_FUNC 0x884D +#define GL_POINT_SIZE_MIN 0x8126 +#define GL_POINT_SIZE_MAX 0x8127 +#define GL_POINT_DISTANCE_ATTENUATION 0x8129 +#define GL_GENERATE_MIPMAP 0x8191 +#define GL_GENERATE_MIPMAP_HINT 0x8192 +#define GL_FOG_COORDINATE_SOURCE 0x8450 +#define GL_FOG_COORDINATE 0x8451 +#define GL_FRAGMENT_DEPTH 0x8452 +#define GL_CURRENT_FOG_COORDINATE 0x8453 +#define GL_FOG_COORDINATE_ARRAY_TYPE 0x8454 +#define GL_FOG_COORDINATE_ARRAY_STRIDE 0x8455 +#define GL_FOG_COORDINATE_ARRAY_POINTER 0x8456 +#define GL_FOG_COORDINATE_ARRAY 0x8457 +#define GL_COLOR_SUM 0x8458 +#define GL_CURRENT_SECONDARY_COLOR 0x8459 +#define GL_SECONDARY_COLOR_ARRAY_SIZE 0x845A +#define GL_SECONDARY_COLOR_ARRAY_TYPE 0x845B +#define GL_SECONDARY_COLOR_ARRAY_STRIDE 0x845C +#define GL_SECONDARY_COLOR_ARRAY_POINTER 0x845D +#define GL_SECONDARY_COLOR_ARRAY 0x845E +#define GL_TEXTURE_FILTER_CONTROL 0x8500 +#define GL_DEPTH_TEXTURE_MODE 0x884B +#define GL_COMPARE_R_TO_TEXTURE 0x884E +#define GL_FUNC_ADD 0x8006 +#define GL_FUNC_SUBTRACT 0x800A +#define GL_FUNC_REVERSE_SUBTRACT 0x800B +#define GL_MIN 0x8007 +#define GL_MAX 0x8008 +#define GL_CONSTANT_COLOR 0x8001 +#define GL_ONE_MINUS_CONSTANT_COLOR 0x8002 +#define GL_CONSTANT_ALPHA 0x8003 +#define GL_ONE_MINUS_CONSTANT_ALPHA 0x8004 +typedef void (APIENTRYP PFNGLBLENDFUNCSEPARATEPROC) (GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha); +typedef void (APIENTRYP PFNGLMULTIDRAWARRAYSPROC) (GLenum mode, const GLint *first, const GLsizei *count, GLsizei drawcount); +typedef void (APIENTRYP PFNGLMULTIDRAWELEMENTSPROC) (GLenum mode, const GLsizei *count, GLenum type, const void *const*indices, GLsizei drawcount); +typedef void (APIENTRYP PFNGLPOINTPARAMETERFPROC) (GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLPOINTPARAMETERFVPROC) (GLenum pname, const GLfloat *params); +typedef void (APIENTRYP PFNGLPOINTPARAMETERIPROC) (GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLPOINTPARAMETERIVPROC) (GLenum pname, const GLint *params); +typedef void (APIENTRYP PFNGLFOGCOORDFPROC) (GLfloat coord); +typedef void (APIENTRYP PFNGLFOGCOORDFVPROC) (const GLfloat *coord); +typedef void (APIENTRYP PFNGLFOGCOORDDPROC) (GLdouble coord); +typedef void (APIENTRYP PFNGLFOGCOORDDVPROC) (const GLdouble *coord); +typedef void (APIENTRYP PFNGLFOGCOORDPOINTERPROC) (GLenum type, GLsizei stride, const void *pointer); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3BPROC) (GLbyte red, GLbyte green, GLbyte blue); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3BVPROC) (const GLbyte *v); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3DPROC) (GLdouble red, GLdouble green, GLdouble blue); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3DVPROC) (const GLdouble *v); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3FPROC) (GLfloat red, GLfloat green, GLfloat blue); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3FVPROC) (const GLfloat *v); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3IPROC) (GLint red, GLint green, GLint blue); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3IVPROC) (const GLint *v); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3SPROC) (GLshort red, GLshort green, GLshort blue); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3SVPROC) (const GLshort *v); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3UBPROC) (GLubyte red, GLubyte green, GLubyte blue); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3UBVPROC) (const GLubyte *v); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3UIPROC) (GLuint red, GLuint green, GLuint blue); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3UIVPROC) (const GLuint *v); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3USPROC) (GLushort red, GLushort green, GLushort blue); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3USVPROC) (const GLushort *v); +typedef void (APIENTRYP PFNGLSECONDARYCOLORPOINTERPROC) (GLint size, GLenum type, GLsizei stride, const void *pointer); +typedef void (APIENTRYP PFNGLWINDOWPOS2DPROC) (GLdouble x, GLdouble y); +typedef void (APIENTRYP PFNGLWINDOWPOS2DVPROC) (const GLdouble *v); +typedef void (APIENTRYP PFNGLWINDOWPOS2FPROC) (GLfloat x, GLfloat y); +typedef void (APIENTRYP PFNGLWINDOWPOS2FVPROC) (const GLfloat *v); +typedef void (APIENTRYP PFNGLWINDOWPOS2IPROC) (GLint x, GLint y); +typedef void (APIENTRYP PFNGLWINDOWPOS2IVPROC) (const GLint *v); +typedef void (APIENTRYP PFNGLWINDOWPOS2SPROC) (GLshort x, GLshort y); +typedef void (APIENTRYP PFNGLWINDOWPOS2SVPROC) (const GLshort *v); +typedef void (APIENTRYP PFNGLWINDOWPOS3DPROC) (GLdouble x, GLdouble y, GLdouble z); +typedef void (APIENTRYP PFNGLWINDOWPOS3DVPROC) (const GLdouble *v); +typedef void (APIENTRYP PFNGLWINDOWPOS3FPROC) (GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLWINDOWPOS3FVPROC) (const GLfloat *v); +typedef void (APIENTRYP PFNGLWINDOWPOS3IPROC) (GLint x, GLint y, GLint z); +typedef void (APIENTRYP PFNGLWINDOWPOS3IVPROC) (const GLint *v); +typedef void (APIENTRYP PFNGLWINDOWPOS3SPROC) (GLshort x, GLshort y, GLshort z); +typedef void (APIENTRYP PFNGLWINDOWPOS3SVPROC) (const GLshort *v); +typedef void (APIENTRYP PFNGLBLENDCOLORPROC) (GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); +typedef void (APIENTRYP PFNGLBLENDEQUATIONPROC) (GLenum mode); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBlendFuncSeparate (GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha); +GLAPI void APIENTRY glMultiDrawArrays (GLenum mode, const GLint *first, const GLsizei *count, GLsizei drawcount); +GLAPI void APIENTRY glMultiDrawElements (GLenum mode, const GLsizei *count, GLenum type, const void *const*indices, GLsizei drawcount); +GLAPI void APIENTRY glPointParameterf (GLenum pname, GLfloat param); +GLAPI void APIENTRY glPointParameterfv (GLenum pname, const GLfloat *params); +GLAPI void APIENTRY glPointParameteri (GLenum pname, GLint param); +GLAPI void APIENTRY glPointParameteriv (GLenum pname, const GLint *params); +GLAPI void APIENTRY glFogCoordf (GLfloat coord); +GLAPI void APIENTRY glFogCoordfv (const GLfloat *coord); +GLAPI void APIENTRY glFogCoordd (GLdouble coord); +GLAPI void APIENTRY glFogCoorddv (const GLdouble *coord); +GLAPI void APIENTRY glFogCoordPointer (GLenum type, GLsizei stride, const void *pointer); +GLAPI void APIENTRY glSecondaryColor3b (GLbyte red, GLbyte green, GLbyte blue); +GLAPI void APIENTRY glSecondaryColor3bv (const GLbyte *v); +GLAPI void APIENTRY glSecondaryColor3d (GLdouble red, GLdouble green, GLdouble blue); +GLAPI void APIENTRY glSecondaryColor3dv (const GLdouble *v); +GLAPI void APIENTRY glSecondaryColor3f (GLfloat red, GLfloat green, GLfloat blue); +GLAPI void APIENTRY glSecondaryColor3fv (const GLfloat *v); +GLAPI void APIENTRY glSecondaryColor3i (GLint red, GLint green, GLint blue); +GLAPI void APIENTRY glSecondaryColor3iv (const GLint *v); +GLAPI void APIENTRY glSecondaryColor3s (GLshort red, GLshort green, GLshort blue); +GLAPI void APIENTRY glSecondaryColor3sv (const GLshort *v); +GLAPI void APIENTRY glSecondaryColor3ub (GLubyte red, GLubyte green, GLubyte blue); +GLAPI void APIENTRY glSecondaryColor3ubv (const GLubyte *v); +GLAPI void APIENTRY glSecondaryColor3ui (GLuint red, GLuint green, GLuint blue); +GLAPI void APIENTRY glSecondaryColor3uiv (const GLuint *v); +GLAPI void APIENTRY glSecondaryColor3us (GLushort red, GLushort green, GLushort blue); +GLAPI void APIENTRY glSecondaryColor3usv (const GLushort *v); +GLAPI void APIENTRY glSecondaryColorPointer (GLint size, GLenum type, GLsizei stride, const void *pointer); +GLAPI void APIENTRY glWindowPos2d (GLdouble x, GLdouble y); +GLAPI void APIENTRY glWindowPos2dv (const GLdouble *v); +GLAPI void APIENTRY glWindowPos2f (GLfloat x, GLfloat y); +GLAPI void APIENTRY glWindowPos2fv (const GLfloat *v); +GLAPI void APIENTRY glWindowPos2i (GLint x, GLint y); +GLAPI void APIENTRY glWindowPos2iv (const GLint *v); +GLAPI void APIENTRY glWindowPos2s (GLshort x, GLshort y); +GLAPI void APIENTRY glWindowPos2sv (const GLshort *v); +GLAPI void APIENTRY glWindowPos3d (GLdouble x, GLdouble y, GLdouble z); +GLAPI void APIENTRY glWindowPos3dv (const GLdouble *v); +GLAPI void APIENTRY glWindowPos3f (GLfloat x, GLfloat y, GLfloat z); +GLAPI void APIENTRY glWindowPos3fv (const GLfloat *v); +GLAPI void APIENTRY glWindowPos3i (GLint x, GLint y, GLint z); +GLAPI void APIENTRY glWindowPos3iv (const GLint *v); +GLAPI void APIENTRY glWindowPos3s (GLshort x, GLshort y, GLshort z); +GLAPI void APIENTRY glWindowPos3sv (const GLshort *v); +GLAPI void APIENTRY glBlendColor (GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); +GLAPI void APIENTRY glBlendEquation (GLenum mode); +#endif +#endif /* GL_VERSION_1_4 */ + +#ifndef GL_VERSION_1_5 +#define GL_VERSION_1_5 1 +#include +typedef ptrdiff_t GLsizeiptr; +typedef ptrdiff_t GLintptr; +#define GL_BUFFER_SIZE 0x8764 +#define GL_BUFFER_USAGE 0x8765 +#define GL_QUERY_COUNTER_BITS 0x8864 +#define GL_CURRENT_QUERY 0x8865 +#define GL_QUERY_RESULT 0x8866 +#define GL_QUERY_RESULT_AVAILABLE 0x8867 +#define GL_ARRAY_BUFFER 0x8892 +#define GL_ELEMENT_ARRAY_BUFFER 0x8893 +#define GL_ARRAY_BUFFER_BINDING 0x8894 +#define GL_ELEMENT_ARRAY_BUFFER_BINDING 0x8895 +#define GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING 0x889F +#define GL_READ_ONLY 0x88B8 +#define GL_WRITE_ONLY 0x88B9 +#define GL_READ_WRITE 0x88BA +#define GL_BUFFER_ACCESS 0x88BB +#define GL_BUFFER_MAPPED 0x88BC +#define GL_BUFFER_MAP_POINTER 0x88BD +#define GL_STREAM_DRAW 0x88E0 +#define GL_STREAM_READ 0x88E1 +#define GL_STREAM_COPY 0x88E2 +#define GL_STATIC_DRAW 0x88E4 +#define GL_STATIC_READ 0x88E5 +#define GL_STATIC_COPY 0x88E6 +#define GL_DYNAMIC_DRAW 0x88E8 +#define GL_DYNAMIC_READ 0x88E9 +#define GL_DYNAMIC_COPY 0x88EA +#define GL_SAMPLES_PASSED 0x8914 +#define GL_SRC1_ALPHA 0x8589 +#define GL_VERTEX_ARRAY_BUFFER_BINDING 0x8896 +#define GL_NORMAL_ARRAY_BUFFER_BINDING 0x8897 +#define GL_COLOR_ARRAY_BUFFER_BINDING 0x8898 +#define GL_INDEX_ARRAY_BUFFER_BINDING 0x8899 +#define GL_TEXTURE_COORD_ARRAY_BUFFER_BINDING 0x889A +#define GL_EDGE_FLAG_ARRAY_BUFFER_BINDING 0x889B +#define GL_SECONDARY_COLOR_ARRAY_BUFFER_BINDING 0x889C +#define GL_FOG_COORDINATE_ARRAY_BUFFER_BINDING 0x889D +#define GL_WEIGHT_ARRAY_BUFFER_BINDING 0x889E +#define GL_FOG_COORD_SRC 0x8450 +#define GL_FOG_COORD 0x8451 +#define GL_CURRENT_FOG_COORD 0x8453 +#define GL_FOG_COORD_ARRAY_TYPE 0x8454 +#define GL_FOG_COORD_ARRAY_STRIDE 0x8455 +#define GL_FOG_COORD_ARRAY_POINTER 0x8456 +#define GL_FOG_COORD_ARRAY 0x8457 +#define GL_FOG_COORD_ARRAY_BUFFER_BINDING 0x889D +#define GL_SRC0_RGB 0x8580 +#define GL_SRC1_RGB 0x8581 +#define GL_SRC2_RGB 0x8582 +#define GL_SRC0_ALPHA 0x8588 +#define GL_SRC2_ALPHA 0x858A +typedef void (APIENTRYP PFNGLGENQUERIESPROC) (GLsizei n, GLuint *ids); +typedef void (APIENTRYP PFNGLDELETEQUERIESPROC) (GLsizei n, const GLuint *ids); +typedef GLboolean (APIENTRYP PFNGLISQUERYPROC) (GLuint id); +typedef void (APIENTRYP PFNGLBEGINQUERYPROC) (GLenum target, GLuint id); +typedef void (APIENTRYP PFNGLENDQUERYPROC) (GLenum target); +typedef void (APIENTRYP PFNGLGETQUERYIVPROC) (GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETQUERYOBJECTIVPROC) (GLuint id, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETQUERYOBJECTUIVPROC) (GLuint id, GLenum pname, GLuint *params); +typedef void (APIENTRYP PFNGLBINDBUFFERPROC) (GLenum target, GLuint buffer); +typedef void (APIENTRYP PFNGLDELETEBUFFERSPROC) (GLsizei n, const GLuint *buffers); +typedef void (APIENTRYP PFNGLGENBUFFERSPROC) (GLsizei n, GLuint *buffers); +typedef GLboolean (APIENTRYP PFNGLISBUFFERPROC) (GLuint buffer); +typedef void (APIENTRYP PFNGLBUFFERDATAPROC) (GLenum target, GLsizeiptr size, const void *data, GLenum usage); +typedef void (APIENTRYP PFNGLBUFFERSUBDATAPROC) (GLenum target, GLintptr offset, GLsizeiptr size, const void *data); +typedef void (APIENTRYP PFNGLGETBUFFERSUBDATAPROC) (GLenum target, GLintptr offset, GLsizeiptr size, void *data); +typedef void *(APIENTRYP PFNGLMAPBUFFERPROC) (GLenum target, GLenum access); +typedef GLboolean (APIENTRYP PFNGLUNMAPBUFFERPROC) (GLenum target); +typedef void (APIENTRYP PFNGLGETBUFFERPARAMETERIVPROC) (GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETBUFFERPOINTERVPROC) (GLenum target, GLenum pname, void **params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glGenQueries (GLsizei n, GLuint *ids); +GLAPI void APIENTRY glDeleteQueries (GLsizei n, const GLuint *ids); +GLAPI GLboolean APIENTRY glIsQuery (GLuint id); +GLAPI void APIENTRY glBeginQuery (GLenum target, GLuint id); +GLAPI void APIENTRY glEndQuery (GLenum target); +GLAPI void APIENTRY glGetQueryiv (GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetQueryObjectiv (GLuint id, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetQueryObjectuiv (GLuint id, GLenum pname, GLuint *params); +GLAPI void APIENTRY glBindBuffer (GLenum target, GLuint buffer); +GLAPI void APIENTRY glDeleteBuffers (GLsizei n, const GLuint *buffers); +GLAPI void APIENTRY glGenBuffers (GLsizei n, GLuint *buffers); +GLAPI GLboolean APIENTRY glIsBuffer (GLuint buffer); +GLAPI void APIENTRY glBufferData (GLenum target, GLsizeiptr size, const void *data, GLenum usage); +GLAPI void APIENTRY glBufferSubData (GLenum target, GLintptr offset, GLsizeiptr size, const void *data); +GLAPI void APIENTRY glGetBufferSubData (GLenum target, GLintptr offset, GLsizeiptr size, void *data); +GLAPI void *APIENTRY glMapBuffer (GLenum target, GLenum access); +GLAPI GLboolean APIENTRY glUnmapBuffer (GLenum target); +GLAPI void APIENTRY glGetBufferParameteriv (GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetBufferPointerv (GLenum target, GLenum pname, void **params); +#endif +#endif /* GL_VERSION_1_5 */ + +#ifndef GL_VERSION_2_0 +#define GL_VERSION_2_0 1 +typedef char GLchar; +#define GL_BLEND_EQUATION_RGB 0x8009 +#define GL_VERTEX_ATTRIB_ARRAY_ENABLED 0x8622 +#define GL_VERTEX_ATTRIB_ARRAY_SIZE 0x8623 +#define GL_VERTEX_ATTRIB_ARRAY_STRIDE 0x8624 +#define GL_VERTEX_ATTRIB_ARRAY_TYPE 0x8625 +#define GL_CURRENT_VERTEX_ATTRIB 0x8626 +#define GL_VERTEX_PROGRAM_POINT_SIZE 0x8642 +#define GL_VERTEX_ATTRIB_ARRAY_POINTER 0x8645 +#define GL_STENCIL_BACK_FUNC 0x8800 +#define GL_STENCIL_BACK_FAIL 0x8801 +#define GL_STENCIL_BACK_PASS_DEPTH_FAIL 0x8802 +#define GL_STENCIL_BACK_PASS_DEPTH_PASS 0x8803 +#define GL_MAX_DRAW_BUFFERS 0x8824 +#define GL_DRAW_BUFFER0 0x8825 +#define GL_DRAW_BUFFER1 0x8826 +#define GL_DRAW_BUFFER2 0x8827 +#define GL_DRAW_BUFFER3 0x8828 +#define GL_DRAW_BUFFER4 0x8829 +#define GL_DRAW_BUFFER5 0x882A +#define GL_DRAW_BUFFER6 0x882B +#define GL_DRAW_BUFFER7 0x882C +#define GL_DRAW_BUFFER8 0x882D +#define GL_DRAW_BUFFER9 0x882E +#define GL_DRAW_BUFFER10 0x882F +#define GL_DRAW_BUFFER11 0x8830 +#define GL_DRAW_BUFFER12 0x8831 +#define GL_DRAW_BUFFER13 0x8832 +#define GL_DRAW_BUFFER14 0x8833 +#define GL_DRAW_BUFFER15 0x8834 +#define GL_BLEND_EQUATION_ALPHA 0x883D +#define GL_MAX_VERTEX_ATTRIBS 0x8869 +#define GL_VERTEX_ATTRIB_ARRAY_NORMALIZED 0x886A +#define GL_MAX_TEXTURE_IMAGE_UNITS 0x8872 +#define GL_FRAGMENT_SHADER 0x8B30 +#define GL_VERTEX_SHADER 0x8B31 +#define GL_MAX_FRAGMENT_UNIFORM_COMPONENTS 0x8B49 +#define GL_MAX_VERTEX_UNIFORM_COMPONENTS 0x8B4A +#define GL_MAX_VARYING_FLOATS 0x8B4B +#define GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS 0x8B4C +#define GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS 0x8B4D +#define GL_SHADER_TYPE 0x8B4F +#define GL_FLOAT_VEC2 0x8B50 +#define GL_FLOAT_VEC3 0x8B51 +#define GL_FLOAT_VEC4 0x8B52 +#define GL_INT_VEC2 0x8B53 +#define GL_INT_VEC3 0x8B54 +#define GL_INT_VEC4 0x8B55 +#define GL_BOOL 0x8B56 +#define GL_BOOL_VEC2 0x8B57 +#define GL_BOOL_VEC3 0x8B58 +#define GL_BOOL_VEC4 0x8B59 +#define GL_FLOAT_MAT2 0x8B5A +#define GL_FLOAT_MAT3 0x8B5B +#define GL_FLOAT_MAT4 0x8B5C +#define GL_SAMPLER_1D 0x8B5D +#define GL_SAMPLER_2D 0x8B5E +#define GL_SAMPLER_3D 0x8B5F +#define GL_SAMPLER_CUBE 0x8B60 +#define GL_SAMPLER_1D_SHADOW 0x8B61 +#define GL_SAMPLER_2D_SHADOW 0x8B62 +#define GL_DELETE_STATUS 0x8B80 +#define GL_COMPILE_STATUS 0x8B81 +#define GL_LINK_STATUS 0x8B82 +#define GL_VALIDATE_STATUS 0x8B83 +#define GL_INFO_LOG_LENGTH 0x8B84 +#define GL_ATTACHED_SHADERS 0x8B85 +#define GL_ACTIVE_UNIFORMS 0x8B86 +#define GL_ACTIVE_UNIFORM_MAX_LENGTH 0x8B87 +#define GL_SHADER_SOURCE_LENGTH 0x8B88 +#define GL_ACTIVE_ATTRIBUTES 0x8B89 +#define GL_ACTIVE_ATTRIBUTE_MAX_LENGTH 0x8B8A +#define GL_FRAGMENT_SHADER_DERIVATIVE_HINT 0x8B8B +#define GL_SHADING_LANGUAGE_VERSION 0x8B8C +#define GL_CURRENT_PROGRAM 0x8B8D +#define GL_POINT_SPRITE_COORD_ORIGIN 0x8CA0 +#define GL_LOWER_LEFT 0x8CA1 +#define GL_UPPER_LEFT 0x8CA2 +#define GL_STENCIL_BACK_REF 0x8CA3 +#define GL_STENCIL_BACK_VALUE_MASK 0x8CA4 +#define GL_STENCIL_BACK_WRITEMASK 0x8CA5 +#define GL_VERTEX_PROGRAM_TWO_SIDE 0x8643 +#define GL_POINT_SPRITE 0x8861 +#define GL_COORD_REPLACE 0x8862 +#define GL_MAX_TEXTURE_COORDS 0x8871 +typedef void (APIENTRYP PFNGLBLENDEQUATIONSEPARATEPROC) (GLenum modeRGB, GLenum modeAlpha); +typedef void (APIENTRYP PFNGLDRAWBUFFERSPROC) (GLsizei n, const GLenum *bufs); +typedef void (APIENTRYP PFNGLSTENCILOPSEPARATEPROC) (GLenum face, GLenum sfail, GLenum dpfail, GLenum dppass); +typedef void (APIENTRYP PFNGLSTENCILFUNCSEPARATEPROC) (GLenum face, GLenum func, GLint ref, GLuint mask); +typedef void (APIENTRYP PFNGLSTENCILMASKSEPARATEPROC) (GLenum face, GLuint mask); +typedef void (APIENTRYP PFNGLATTACHSHADERPROC) (GLuint program, GLuint shader); +typedef void (APIENTRYP PFNGLBINDATTRIBLOCATIONPROC) (GLuint program, GLuint index, const GLchar *name); +typedef void (APIENTRYP PFNGLCOMPILESHADERPROC) (GLuint shader); +typedef GLuint (APIENTRYP PFNGLCREATEPROGRAMPROC) (void); +typedef GLuint (APIENTRYP PFNGLCREATESHADERPROC) (GLenum type); +typedef void (APIENTRYP PFNGLDELETEPROGRAMPROC) (GLuint program); +typedef void (APIENTRYP PFNGLDELETESHADERPROC) (GLuint shader); +typedef void (APIENTRYP PFNGLDETACHSHADERPROC) (GLuint program, GLuint shader); +typedef void (APIENTRYP PFNGLDISABLEVERTEXATTRIBARRAYPROC) (GLuint index); +typedef void (APIENTRYP PFNGLENABLEVERTEXATTRIBARRAYPROC) (GLuint index); +typedef void (APIENTRYP PFNGLGETACTIVEATTRIBPROC) (GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLint *size, GLenum *type, GLchar *name); +typedef void (APIENTRYP PFNGLGETACTIVEUNIFORMPROC) (GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLint *size, GLenum *type, GLchar *name); +typedef void (APIENTRYP PFNGLGETATTACHEDSHADERSPROC) (GLuint program, GLsizei maxCount, GLsizei *count, GLuint *shaders); +typedef GLint (APIENTRYP PFNGLGETATTRIBLOCATIONPROC) (GLuint program, const GLchar *name); +typedef void (APIENTRYP PFNGLGETPROGRAMIVPROC) (GLuint program, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETPROGRAMINFOLOGPROC) (GLuint program, GLsizei bufSize, GLsizei *length, GLchar *infoLog); +typedef void (APIENTRYP PFNGLGETSHADERIVPROC) (GLuint shader, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETSHADERINFOLOGPROC) (GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *infoLog); +typedef void (APIENTRYP PFNGLGETSHADERSOURCEPROC) (GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *source); +typedef GLint (APIENTRYP PFNGLGETUNIFORMLOCATIONPROC) (GLuint program, const GLchar *name); +typedef void (APIENTRYP PFNGLGETUNIFORMFVPROC) (GLuint program, GLint location, GLfloat *params); +typedef void (APIENTRYP PFNGLGETUNIFORMIVPROC) (GLuint program, GLint location, GLint *params); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBDVPROC) (GLuint index, GLenum pname, GLdouble *params); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBFVPROC) (GLuint index, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBIVPROC) (GLuint index, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBPOINTERVPROC) (GLuint index, GLenum pname, void **pointer); +typedef GLboolean (APIENTRYP PFNGLISPROGRAMPROC) (GLuint program); +typedef GLboolean (APIENTRYP PFNGLISSHADERPROC) (GLuint shader); +typedef void (APIENTRYP PFNGLLINKPROGRAMPROC) (GLuint program); +typedef void (APIENTRYP PFNGLSHADERSOURCEPROC) (GLuint shader, GLsizei count, const GLchar *const*string, const GLint *length); +typedef void (APIENTRYP PFNGLUSEPROGRAMPROC) (GLuint program); +typedef void (APIENTRYP PFNGLUNIFORM1FPROC) (GLint location, GLfloat v0); +typedef void (APIENTRYP PFNGLUNIFORM2FPROC) (GLint location, GLfloat v0, GLfloat v1); +typedef void (APIENTRYP PFNGLUNIFORM3FPROC) (GLint location, GLfloat v0, GLfloat v1, GLfloat v2); +typedef void (APIENTRYP PFNGLUNIFORM4FPROC) (GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); +typedef void (APIENTRYP PFNGLUNIFORM1IPROC) (GLint location, GLint v0); +typedef void (APIENTRYP PFNGLUNIFORM2IPROC) (GLint location, GLint v0, GLint v1); +typedef void (APIENTRYP PFNGLUNIFORM3IPROC) (GLint location, GLint v0, GLint v1, GLint v2); +typedef void (APIENTRYP PFNGLUNIFORM4IPROC) (GLint location, GLint v0, GLint v1, GLint v2, GLint v3); +typedef void (APIENTRYP PFNGLUNIFORM1FVPROC) (GLint location, GLsizei count, const GLfloat *value); +typedef void (APIENTRYP PFNGLUNIFORM2FVPROC) (GLint location, GLsizei count, const GLfloat *value); +typedef void (APIENTRYP PFNGLUNIFORM3FVPROC) (GLint location, GLsizei count, const GLfloat *value); +typedef void (APIENTRYP PFNGLUNIFORM4FVPROC) (GLint location, GLsizei count, const GLfloat *value); +typedef void (APIENTRYP PFNGLUNIFORM1IVPROC) (GLint location, GLsizei count, const GLint *value); +typedef void (APIENTRYP PFNGLUNIFORM2IVPROC) (GLint location, GLsizei count, const GLint *value); +typedef void (APIENTRYP PFNGLUNIFORM3IVPROC) (GLint location, GLsizei count, const GLint *value); +typedef void (APIENTRYP PFNGLUNIFORM4IVPROC) (GLint location, GLsizei count, const GLint *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX2FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX3FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX4FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLVALIDATEPROGRAMPROC) (GLuint program); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1DPROC) (GLuint index, GLdouble x); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1DVPROC) (GLuint index, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1FPROC) (GLuint index, GLfloat x); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1FVPROC) (GLuint index, const GLfloat *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1SPROC) (GLuint index, GLshort x); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1SVPROC) (GLuint index, const GLshort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2DPROC) (GLuint index, GLdouble x, GLdouble y); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2DVPROC) (GLuint index, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2FPROC) (GLuint index, GLfloat x, GLfloat y); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2FVPROC) (GLuint index, const GLfloat *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2SPROC) (GLuint index, GLshort x, GLshort y); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2SVPROC) (GLuint index, const GLshort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3DPROC) (GLuint index, GLdouble x, GLdouble y, GLdouble z); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3DVPROC) (GLuint index, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3FPROC) (GLuint index, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3FVPROC) (GLuint index, const GLfloat *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3SPROC) (GLuint index, GLshort x, GLshort y, GLshort z); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3SVPROC) (GLuint index, const GLshort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4NBVPROC) (GLuint index, const GLbyte *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4NIVPROC) (GLuint index, const GLint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4NSVPROC) (GLuint index, const GLshort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4NUBPROC) (GLuint index, GLubyte x, GLubyte y, GLubyte z, GLubyte w); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4NUBVPROC) (GLuint index, const GLubyte *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4NUIVPROC) (GLuint index, const GLuint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4NUSVPROC) (GLuint index, const GLushort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4BVPROC) (GLuint index, const GLbyte *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4DPROC) (GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4DVPROC) (GLuint index, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4FPROC) (GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4FVPROC) (GLuint index, const GLfloat *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4IVPROC) (GLuint index, const GLint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4SPROC) (GLuint index, GLshort x, GLshort y, GLshort z, GLshort w); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4SVPROC) (GLuint index, const GLshort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4UBVPROC) (GLuint index, const GLubyte *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4UIVPROC) (GLuint index, const GLuint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4USVPROC) (GLuint index, const GLushort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBPOINTERPROC) (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void *pointer); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBlendEquationSeparate (GLenum modeRGB, GLenum modeAlpha); +GLAPI void APIENTRY glDrawBuffers (GLsizei n, const GLenum *bufs); +GLAPI void APIENTRY glStencilOpSeparate (GLenum face, GLenum sfail, GLenum dpfail, GLenum dppass); +GLAPI void APIENTRY glStencilFuncSeparate (GLenum face, GLenum func, GLint ref, GLuint mask); +GLAPI void APIENTRY glStencilMaskSeparate (GLenum face, GLuint mask); +GLAPI void APIENTRY glAttachShader (GLuint program, GLuint shader); +GLAPI void APIENTRY glBindAttribLocation (GLuint program, GLuint index, const GLchar *name); +GLAPI void APIENTRY glCompileShader (GLuint shader); +GLAPI GLuint APIENTRY glCreateProgram (void); +GLAPI GLuint APIENTRY glCreateShader (GLenum type); +GLAPI void APIENTRY glDeleteProgram (GLuint program); +GLAPI void APIENTRY glDeleteShader (GLuint shader); +GLAPI void APIENTRY glDetachShader (GLuint program, GLuint shader); +GLAPI void APIENTRY glDisableVertexAttribArray (GLuint index); +GLAPI void APIENTRY glEnableVertexAttribArray (GLuint index); +GLAPI void APIENTRY glGetActiveAttrib (GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLint *size, GLenum *type, GLchar *name); +GLAPI void APIENTRY glGetActiveUniform (GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLint *size, GLenum *type, GLchar *name); +GLAPI void APIENTRY glGetAttachedShaders (GLuint program, GLsizei maxCount, GLsizei *count, GLuint *shaders); +GLAPI GLint APIENTRY glGetAttribLocation (GLuint program, const GLchar *name); +GLAPI void APIENTRY glGetProgramiv (GLuint program, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetProgramInfoLog (GLuint program, GLsizei bufSize, GLsizei *length, GLchar *infoLog); +GLAPI void APIENTRY glGetShaderiv (GLuint shader, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetShaderInfoLog (GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *infoLog); +GLAPI void APIENTRY glGetShaderSource (GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *source); +GLAPI GLint APIENTRY glGetUniformLocation (GLuint program, const GLchar *name); +GLAPI void APIENTRY glGetUniformfv (GLuint program, GLint location, GLfloat *params); +GLAPI void APIENTRY glGetUniformiv (GLuint program, GLint location, GLint *params); +GLAPI void APIENTRY glGetVertexAttribdv (GLuint index, GLenum pname, GLdouble *params); +GLAPI void APIENTRY glGetVertexAttribfv (GLuint index, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetVertexAttribiv (GLuint index, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetVertexAttribPointerv (GLuint index, GLenum pname, void **pointer); +GLAPI GLboolean APIENTRY glIsProgram (GLuint program); +GLAPI GLboolean APIENTRY glIsShader (GLuint shader); +GLAPI void APIENTRY glLinkProgram (GLuint program); +GLAPI void APIENTRY glShaderSource (GLuint shader, GLsizei count, const GLchar *const*string, const GLint *length); +GLAPI void APIENTRY glUseProgram (GLuint program); +GLAPI void APIENTRY glUniform1f (GLint location, GLfloat v0); +GLAPI void APIENTRY glUniform2f (GLint location, GLfloat v0, GLfloat v1); +GLAPI void APIENTRY glUniform3f (GLint location, GLfloat v0, GLfloat v1, GLfloat v2); +GLAPI void APIENTRY glUniform4f (GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); +GLAPI void APIENTRY glUniform1i (GLint location, GLint v0); +GLAPI void APIENTRY glUniform2i (GLint location, GLint v0, GLint v1); +GLAPI void APIENTRY glUniform3i (GLint location, GLint v0, GLint v1, GLint v2); +GLAPI void APIENTRY glUniform4i (GLint location, GLint v0, GLint v1, GLint v2, GLint v3); +GLAPI void APIENTRY glUniform1fv (GLint location, GLsizei count, const GLfloat *value); +GLAPI void APIENTRY glUniform2fv (GLint location, GLsizei count, const GLfloat *value); +GLAPI void APIENTRY glUniform3fv (GLint location, GLsizei count, const GLfloat *value); +GLAPI void APIENTRY glUniform4fv (GLint location, GLsizei count, const GLfloat *value); +GLAPI void APIENTRY glUniform1iv (GLint location, GLsizei count, const GLint *value); +GLAPI void APIENTRY glUniform2iv (GLint location, GLsizei count, const GLint *value); +GLAPI void APIENTRY glUniform3iv (GLint location, GLsizei count, const GLint *value); +GLAPI void APIENTRY glUniform4iv (GLint location, GLsizei count, const GLint *value); +GLAPI void APIENTRY glUniformMatrix2fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glUniformMatrix3fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glUniformMatrix4fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glValidateProgram (GLuint program); +GLAPI void APIENTRY glVertexAttrib1d (GLuint index, GLdouble x); +GLAPI void APIENTRY glVertexAttrib1dv (GLuint index, const GLdouble *v); +GLAPI void APIENTRY glVertexAttrib1f (GLuint index, GLfloat x); +GLAPI void APIENTRY glVertexAttrib1fv (GLuint index, const GLfloat *v); +GLAPI void APIENTRY glVertexAttrib1s (GLuint index, GLshort x); +GLAPI void APIENTRY glVertexAttrib1sv (GLuint index, const GLshort *v); +GLAPI void APIENTRY glVertexAttrib2d (GLuint index, GLdouble x, GLdouble y); +GLAPI void APIENTRY glVertexAttrib2dv (GLuint index, const GLdouble *v); +GLAPI void APIENTRY glVertexAttrib2f (GLuint index, GLfloat x, GLfloat y); +GLAPI void APIENTRY glVertexAttrib2fv (GLuint index, const GLfloat *v); +GLAPI void APIENTRY glVertexAttrib2s (GLuint index, GLshort x, GLshort y); +GLAPI void APIENTRY glVertexAttrib2sv (GLuint index, const GLshort *v); +GLAPI void APIENTRY glVertexAttrib3d (GLuint index, GLdouble x, GLdouble y, GLdouble z); +GLAPI void APIENTRY glVertexAttrib3dv (GLuint index, const GLdouble *v); +GLAPI void APIENTRY glVertexAttrib3f (GLuint index, GLfloat x, GLfloat y, GLfloat z); +GLAPI void APIENTRY glVertexAttrib3fv (GLuint index, const GLfloat *v); +GLAPI void APIENTRY glVertexAttrib3s (GLuint index, GLshort x, GLshort y, GLshort z); +GLAPI void APIENTRY glVertexAttrib3sv (GLuint index, const GLshort *v); +GLAPI void APIENTRY glVertexAttrib4Nbv (GLuint index, const GLbyte *v); +GLAPI void APIENTRY glVertexAttrib4Niv (GLuint index, const GLint *v); +GLAPI void APIENTRY glVertexAttrib4Nsv (GLuint index, const GLshort *v); +GLAPI void APIENTRY glVertexAttrib4Nub (GLuint index, GLubyte x, GLubyte y, GLubyte z, GLubyte w); +GLAPI void APIENTRY glVertexAttrib4Nubv (GLuint index, const GLubyte *v); +GLAPI void APIENTRY glVertexAttrib4Nuiv (GLuint index, const GLuint *v); +GLAPI void APIENTRY glVertexAttrib4Nusv (GLuint index, const GLushort *v); +GLAPI void APIENTRY glVertexAttrib4bv (GLuint index, const GLbyte *v); +GLAPI void APIENTRY glVertexAttrib4d (GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +GLAPI void APIENTRY glVertexAttrib4dv (GLuint index, const GLdouble *v); +GLAPI void APIENTRY glVertexAttrib4f (GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +GLAPI void APIENTRY glVertexAttrib4fv (GLuint index, const GLfloat *v); +GLAPI void APIENTRY glVertexAttrib4iv (GLuint index, const GLint *v); +GLAPI void APIENTRY glVertexAttrib4s (GLuint index, GLshort x, GLshort y, GLshort z, GLshort w); +GLAPI void APIENTRY glVertexAttrib4sv (GLuint index, const GLshort *v); +GLAPI void APIENTRY glVertexAttrib4ubv (GLuint index, const GLubyte *v); +GLAPI void APIENTRY glVertexAttrib4uiv (GLuint index, const GLuint *v); +GLAPI void APIENTRY glVertexAttrib4usv (GLuint index, const GLushort *v); +GLAPI void APIENTRY glVertexAttribPointer (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void *pointer); +#endif +#endif /* GL_VERSION_2_0 */ + +#ifndef GL_VERSION_2_1 +#define GL_VERSION_2_1 1 +#define GL_PIXEL_PACK_BUFFER 0x88EB +#define GL_PIXEL_UNPACK_BUFFER 0x88EC +#define GL_PIXEL_PACK_BUFFER_BINDING 0x88ED +#define GL_PIXEL_UNPACK_BUFFER_BINDING 0x88EF +#define GL_FLOAT_MAT2x3 0x8B65 +#define GL_FLOAT_MAT2x4 0x8B66 +#define GL_FLOAT_MAT3x2 0x8B67 +#define GL_FLOAT_MAT3x4 0x8B68 +#define GL_FLOAT_MAT4x2 0x8B69 +#define GL_FLOAT_MAT4x3 0x8B6A +#define GL_SRGB 0x8C40 +#define GL_SRGB8 0x8C41 +#define GL_SRGB_ALPHA 0x8C42 +#define GL_SRGB8_ALPHA8 0x8C43 +#define GL_COMPRESSED_SRGB 0x8C48 +#define GL_COMPRESSED_SRGB_ALPHA 0x8C49 +#define GL_CURRENT_RASTER_SECONDARY_COLOR 0x845F +#define GL_SLUMINANCE_ALPHA 0x8C44 +#define GL_SLUMINANCE8_ALPHA8 0x8C45 +#define GL_SLUMINANCE 0x8C46 +#define GL_SLUMINANCE8 0x8C47 +#define GL_COMPRESSED_SLUMINANCE 0x8C4A +#define GL_COMPRESSED_SLUMINANCE_ALPHA 0x8C4B +typedef void (APIENTRYP PFNGLUNIFORMMATRIX2X3FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX3X2FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX2X4FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX4X2FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX3X4FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX4X3FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glUniformMatrix2x3fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glUniformMatrix3x2fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glUniformMatrix2x4fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glUniformMatrix4x2fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glUniformMatrix3x4fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glUniformMatrix4x3fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +#endif +#endif /* GL_VERSION_2_1 */ + +#ifndef GL_VERSION_3_0 +#define GL_VERSION_3_0 1 +typedef unsigned short GLhalf; +#define GL_COMPARE_REF_TO_TEXTURE 0x884E +#define GL_CLIP_DISTANCE0 0x3000 +#define GL_CLIP_DISTANCE1 0x3001 +#define GL_CLIP_DISTANCE2 0x3002 +#define GL_CLIP_DISTANCE3 0x3003 +#define GL_CLIP_DISTANCE4 0x3004 +#define GL_CLIP_DISTANCE5 0x3005 +#define GL_CLIP_DISTANCE6 0x3006 +#define GL_CLIP_DISTANCE7 0x3007 +#define GL_MAX_CLIP_DISTANCES 0x0D32 +#define GL_MAJOR_VERSION 0x821B +#define GL_MINOR_VERSION 0x821C +#define GL_NUM_EXTENSIONS 0x821D +#define GL_CONTEXT_FLAGS 0x821E +#define GL_COMPRESSED_RED 0x8225 +#define GL_COMPRESSED_RG 0x8226 +#define GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT 0x00000001 +#define GL_RGBA32F 0x8814 +#define GL_RGB32F 0x8815 +#define GL_RGBA16F 0x881A +#define GL_RGB16F 0x881B +#define GL_VERTEX_ATTRIB_ARRAY_INTEGER 0x88FD +#define GL_MAX_ARRAY_TEXTURE_LAYERS 0x88FF +#define GL_MIN_PROGRAM_TEXEL_OFFSET 0x8904 +#define GL_MAX_PROGRAM_TEXEL_OFFSET 0x8905 +#define GL_CLAMP_READ_COLOR 0x891C +#define GL_FIXED_ONLY 0x891D +#define GL_MAX_VARYING_COMPONENTS 0x8B4B +#define GL_TEXTURE_1D_ARRAY 0x8C18 +#define GL_PROXY_TEXTURE_1D_ARRAY 0x8C19 +#define GL_TEXTURE_2D_ARRAY 0x8C1A +#define GL_PROXY_TEXTURE_2D_ARRAY 0x8C1B +#define GL_TEXTURE_BINDING_1D_ARRAY 0x8C1C +#define GL_TEXTURE_BINDING_2D_ARRAY 0x8C1D +#define GL_R11F_G11F_B10F 0x8C3A +#define GL_UNSIGNED_INT_10F_11F_11F_REV 0x8C3B +#define GL_RGB9_E5 0x8C3D +#define GL_UNSIGNED_INT_5_9_9_9_REV 0x8C3E +#define GL_TEXTURE_SHARED_SIZE 0x8C3F +#define GL_TRANSFORM_FEEDBACK_VARYING_MAX_LENGTH 0x8C76 +#define GL_TRANSFORM_FEEDBACK_BUFFER_MODE 0x8C7F +#define GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS 0x8C80 +#define GL_TRANSFORM_FEEDBACK_VARYINGS 0x8C83 +#define GL_TRANSFORM_FEEDBACK_BUFFER_START 0x8C84 +#define GL_TRANSFORM_FEEDBACK_BUFFER_SIZE 0x8C85 +#define GL_PRIMITIVES_GENERATED 0x8C87 +#define GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN 0x8C88 +#define GL_RASTERIZER_DISCARD 0x8C89 +#define GL_MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS 0x8C8A +#define GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS 0x8C8B +#define GL_INTERLEAVED_ATTRIBS 0x8C8C +#define GL_SEPARATE_ATTRIBS 0x8C8D +#define GL_TRANSFORM_FEEDBACK_BUFFER 0x8C8E +#define GL_TRANSFORM_FEEDBACK_BUFFER_BINDING 0x8C8F +#define GL_RGBA32UI 0x8D70 +#define GL_RGB32UI 0x8D71 +#define GL_RGBA16UI 0x8D76 +#define GL_RGB16UI 0x8D77 +#define GL_RGBA8UI 0x8D7C +#define GL_RGB8UI 0x8D7D +#define GL_RGBA32I 0x8D82 +#define GL_RGB32I 0x8D83 +#define GL_RGBA16I 0x8D88 +#define GL_RGB16I 0x8D89 +#define GL_RGBA8I 0x8D8E +#define GL_RGB8I 0x8D8F +#define GL_RED_INTEGER 0x8D94 +#define GL_GREEN_INTEGER 0x8D95 +#define GL_BLUE_INTEGER 0x8D96 +#define GL_RGB_INTEGER 0x8D98 +#define GL_RGBA_INTEGER 0x8D99 +#define GL_BGR_INTEGER 0x8D9A +#define GL_BGRA_INTEGER 0x8D9B +#define GL_SAMPLER_1D_ARRAY 0x8DC0 +#define GL_SAMPLER_2D_ARRAY 0x8DC1 +#define GL_SAMPLER_1D_ARRAY_SHADOW 0x8DC3 +#define GL_SAMPLER_2D_ARRAY_SHADOW 0x8DC4 +#define GL_SAMPLER_CUBE_SHADOW 0x8DC5 +#define GL_UNSIGNED_INT_VEC2 0x8DC6 +#define GL_UNSIGNED_INT_VEC3 0x8DC7 +#define GL_UNSIGNED_INT_VEC4 0x8DC8 +#define GL_INT_SAMPLER_1D 0x8DC9 +#define GL_INT_SAMPLER_2D 0x8DCA +#define GL_INT_SAMPLER_3D 0x8DCB +#define GL_INT_SAMPLER_CUBE 0x8DCC +#define GL_INT_SAMPLER_1D_ARRAY 0x8DCE +#define GL_INT_SAMPLER_2D_ARRAY 0x8DCF +#define GL_UNSIGNED_INT_SAMPLER_1D 0x8DD1 +#define GL_UNSIGNED_INT_SAMPLER_2D 0x8DD2 +#define GL_UNSIGNED_INT_SAMPLER_3D 0x8DD3 +#define GL_UNSIGNED_INT_SAMPLER_CUBE 0x8DD4 +#define GL_UNSIGNED_INT_SAMPLER_1D_ARRAY 0x8DD6 +#define GL_UNSIGNED_INT_SAMPLER_2D_ARRAY 0x8DD7 +#define GL_QUERY_WAIT 0x8E13 +#define GL_QUERY_NO_WAIT 0x8E14 +#define GL_QUERY_BY_REGION_WAIT 0x8E15 +#define GL_QUERY_BY_REGION_NO_WAIT 0x8E16 +#define GL_BUFFER_ACCESS_FLAGS 0x911F +#define GL_BUFFER_MAP_LENGTH 0x9120 +#define GL_BUFFER_MAP_OFFSET 0x9121 +#define GL_DEPTH_COMPONENT32F 0x8CAC +#define GL_DEPTH32F_STENCIL8 0x8CAD +#define GL_FLOAT_32_UNSIGNED_INT_24_8_REV 0x8DAD +#define GL_INVALID_FRAMEBUFFER_OPERATION 0x0506 +#define GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING 0x8210 +#define GL_FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE 0x8211 +#define GL_FRAMEBUFFER_ATTACHMENT_RED_SIZE 0x8212 +#define GL_FRAMEBUFFER_ATTACHMENT_GREEN_SIZE 0x8213 +#define GL_FRAMEBUFFER_ATTACHMENT_BLUE_SIZE 0x8214 +#define GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE 0x8215 +#define GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE 0x8216 +#define GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE 0x8217 +#define GL_FRAMEBUFFER_DEFAULT 0x8218 +#define GL_FRAMEBUFFER_UNDEFINED 0x8219 +#define GL_DEPTH_STENCIL_ATTACHMENT 0x821A +#define GL_MAX_RENDERBUFFER_SIZE 0x84E8 +#define GL_DEPTH_STENCIL 0x84F9 +#define GL_UNSIGNED_INT_24_8 0x84FA +#define GL_DEPTH24_STENCIL8 0x88F0 +#define GL_TEXTURE_STENCIL_SIZE 0x88F1 +#define GL_TEXTURE_RED_TYPE 0x8C10 +#define GL_TEXTURE_GREEN_TYPE 0x8C11 +#define GL_TEXTURE_BLUE_TYPE 0x8C12 +#define GL_TEXTURE_ALPHA_TYPE 0x8C13 +#define GL_TEXTURE_DEPTH_TYPE 0x8C16 +#define GL_UNSIGNED_NORMALIZED 0x8C17 +#define GL_FRAMEBUFFER_BINDING 0x8CA6 +#define GL_DRAW_FRAMEBUFFER_BINDING 0x8CA6 +#define GL_RENDERBUFFER_BINDING 0x8CA7 +#define GL_READ_FRAMEBUFFER 0x8CA8 +#define GL_DRAW_FRAMEBUFFER 0x8CA9 +#define GL_READ_FRAMEBUFFER_BINDING 0x8CAA +#define GL_RENDERBUFFER_SAMPLES 0x8CAB +#define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE 0x8CD0 +#define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME 0x8CD1 +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL 0x8CD2 +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE 0x8CD3 +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER 0x8CD4 +#define GL_FRAMEBUFFER_COMPLETE 0x8CD5 +#define GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT 0x8CD6 +#define GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT 0x8CD7 +#define GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER 0x8CDB +#define GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER 0x8CDC +#define GL_FRAMEBUFFER_UNSUPPORTED 0x8CDD +#define GL_MAX_COLOR_ATTACHMENTS 0x8CDF +#define GL_COLOR_ATTACHMENT0 0x8CE0 +#define GL_COLOR_ATTACHMENT1 0x8CE1 +#define GL_COLOR_ATTACHMENT2 0x8CE2 +#define GL_COLOR_ATTACHMENT3 0x8CE3 +#define GL_COLOR_ATTACHMENT4 0x8CE4 +#define GL_COLOR_ATTACHMENT5 0x8CE5 +#define GL_COLOR_ATTACHMENT6 0x8CE6 +#define GL_COLOR_ATTACHMENT7 0x8CE7 +#define GL_COLOR_ATTACHMENT8 0x8CE8 +#define GL_COLOR_ATTACHMENT9 0x8CE9 +#define GL_COLOR_ATTACHMENT10 0x8CEA +#define GL_COLOR_ATTACHMENT11 0x8CEB +#define GL_COLOR_ATTACHMENT12 0x8CEC +#define GL_COLOR_ATTACHMENT13 0x8CED +#define GL_COLOR_ATTACHMENT14 0x8CEE +#define GL_COLOR_ATTACHMENT15 0x8CEF +#define GL_COLOR_ATTACHMENT16 0x8CF0 +#define GL_COLOR_ATTACHMENT17 0x8CF1 +#define GL_COLOR_ATTACHMENT18 0x8CF2 +#define GL_COLOR_ATTACHMENT19 0x8CF3 +#define GL_COLOR_ATTACHMENT20 0x8CF4 +#define GL_COLOR_ATTACHMENT21 0x8CF5 +#define GL_COLOR_ATTACHMENT22 0x8CF6 +#define GL_COLOR_ATTACHMENT23 0x8CF7 +#define GL_COLOR_ATTACHMENT24 0x8CF8 +#define GL_COLOR_ATTACHMENT25 0x8CF9 +#define GL_COLOR_ATTACHMENT26 0x8CFA +#define GL_COLOR_ATTACHMENT27 0x8CFB +#define GL_COLOR_ATTACHMENT28 0x8CFC +#define GL_COLOR_ATTACHMENT29 0x8CFD +#define GL_COLOR_ATTACHMENT30 0x8CFE +#define GL_COLOR_ATTACHMENT31 0x8CFF +#define GL_DEPTH_ATTACHMENT 0x8D00 +#define GL_STENCIL_ATTACHMENT 0x8D20 +#define GL_FRAMEBUFFER 0x8D40 +#define GL_RENDERBUFFER 0x8D41 +#define GL_RENDERBUFFER_WIDTH 0x8D42 +#define GL_RENDERBUFFER_HEIGHT 0x8D43 +#define GL_RENDERBUFFER_INTERNAL_FORMAT 0x8D44 +#define GL_STENCIL_INDEX1 0x8D46 +#define GL_STENCIL_INDEX4 0x8D47 +#define GL_STENCIL_INDEX8 0x8D48 +#define GL_STENCIL_INDEX16 0x8D49 +#define GL_RENDERBUFFER_RED_SIZE 0x8D50 +#define GL_RENDERBUFFER_GREEN_SIZE 0x8D51 +#define GL_RENDERBUFFER_BLUE_SIZE 0x8D52 +#define GL_RENDERBUFFER_ALPHA_SIZE 0x8D53 +#define GL_RENDERBUFFER_DEPTH_SIZE 0x8D54 +#define GL_RENDERBUFFER_STENCIL_SIZE 0x8D55 +#define GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE 0x8D56 +#define GL_MAX_SAMPLES 0x8D57 +#define GL_INDEX 0x8222 +#define GL_TEXTURE_LUMINANCE_TYPE 0x8C14 +#define GL_TEXTURE_INTENSITY_TYPE 0x8C15 +#define GL_FRAMEBUFFER_SRGB 0x8DB9 +#define GL_HALF_FLOAT 0x140B +#define GL_MAP_READ_BIT 0x0001 +#define GL_MAP_WRITE_BIT 0x0002 +#define GL_MAP_INVALIDATE_RANGE_BIT 0x0004 +#define GL_MAP_INVALIDATE_BUFFER_BIT 0x0008 +#define GL_MAP_FLUSH_EXPLICIT_BIT 0x0010 +#define GL_MAP_UNSYNCHRONIZED_BIT 0x0020 +#define GL_COMPRESSED_RED_RGTC1 0x8DBB +#define GL_COMPRESSED_SIGNED_RED_RGTC1 0x8DBC +#define GL_COMPRESSED_RG_RGTC2 0x8DBD +#define GL_COMPRESSED_SIGNED_RG_RGTC2 0x8DBE +#define GL_RG 0x8227 +#define GL_RG_INTEGER 0x8228 +#define GL_R8 0x8229 +#define GL_R16 0x822A +#define GL_RG8 0x822B +#define GL_RG16 0x822C +#define GL_R16F 0x822D +#define GL_R32F 0x822E +#define GL_RG16F 0x822F +#define GL_RG32F 0x8230 +#define GL_R8I 0x8231 +#define GL_R8UI 0x8232 +#define GL_R16I 0x8233 +#define GL_R16UI 0x8234 +#define GL_R32I 0x8235 +#define GL_R32UI 0x8236 +#define GL_RG8I 0x8237 +#define GL_RG8UI 0x8238 +#define GL_RG16I 0x8239 +#define GL_RG16UI 0x823A +#define GL_RG32I 0x823B +#define GL_RG32UI 0x823C +#define GL_VERTEX_ARRAY_BINDING 0x85B5 +#define GL_CLAMP_VERTEX_COLOR 0x891A +#define GL_CLAMP_FRAGMENT_COLOR 0x891B +#define GL_ALPHA_INTEGER 0x8D97 +typedef void (APIENTRYP PFNGLCOLORMASKIPROC) (GLuint index, GLboolean r, GLboolean g, GLboolean b, GLboolean a); +typedef void (APIENTRYP PFNGLGETBOOLEANI_VPROC) (GLenum target, GLuint index, GLboolean *data); +typedef void (APIENTRYP PFNGLGETINTEGERI_VPROC) (GLenum target, GLuint index, GLint *data); +typedef void (APIENTRYP PFNGLENABLEIPROC) (GLenum target, GLuint index); +typedef void (APIENTRYP PFNGLDISABLEIPROC) (GLenum target, GLuint index); +typedef GLboolean (APIENTRYP PFNGLISENABLEDIPROC) (GLenum target, GLuint index); +typedef void (APIENTRYP PFNGLBEGINTRANSFORMFEEDBACKPROC) (GLenum primitiveMode); +typedef void (APIENTRYP PFNGLENDTRANSFORMFEEDBACKPROC) (void); +typedef void (APIENTRYP PFNGLBINDBUFFERRANGEPROC) (GLenum target, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size); +typedef void (APIENTRYP PFNGLBINDBUFFERBASEPROC) (GLenum target, GLuint index, GLuint buffer); +typedef void (APIENTRYP PFNGLTRANSFORMFEEDBACKVARYINGSPROC) (GLuint program, GLsizei count, const GLchar *const*varyings, GLenum bufferMode); +typedef void (APIENTRYP PFNGLGETTRANSFORMFEEDBACKVARYINGPROC) (GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLsizei *size, GLenum *type, GLchar *name); +typedef void (APIENTRYP PFNGLCLAMPCOLORPROC) (GLenum target, GLenum clamp); +typedef void (APIENTRYP PFNGLBEGINCONDITIONALRENDERPROC) (GLuint id, GLenum mode); +typedef void (APIENTRYP PFNGLENDCONDITIONALRENDERPROC) (void); +typedef void (APIENTRYP PFNGLVERTEXATTRIBIPOINTERPROC) (GLuint index, GLint size, GLenum type, GLsizei stride, const void *pointer); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBIIVPROC) (GLuint index, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBIUIVPROC) (GLuint index, GLenum pname, GLuint *params); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI1IPROC) (GLuint index, GLint x); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI2IPROC) (GLuint index, GLint x, GLint y); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI3IPROC) (GLuint index, GLint x, GLint y, GLint z); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI4IPROC) (GLuint index, GLint x, GLint y, GLint z, GLint w); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI1UIPROC) (GLuint index, GLuint x); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI2UIPROC) (GLuint index, GLuint x, GLuint y); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI3UIPROC) (GLuint index, GLuint x, GLuint y, GLuint z); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI4UIPROC) (GLuint index, GLuint x, GLuint y, GLuint z, GLuint w); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI1IVPROC) (GLuint index, const GLint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI2IVPROC) (GLuint index, const GLint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI3IVPROC) (GLuint index, const GLint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI4IVPROC) (GLuint index, const GLint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI1UIVPROC) (GLuint index, const GLuint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI2UIVPROC) (GLuint index, const GLuint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI3UIVPROC) (GLuint index, const GLuint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI4UIVPROC) (GLuint index, const GLuint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI4BVPROC) (GLuint index, const GLbyte *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI4SVPROC) (GLuint index, const GLshort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI4UBVPROC) (GLuint index, const GLubyte *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI4USVPROC) (GLuint index, const GLushort *v); +typedef void (APIENTRYP PFNGLGETUNIFORMUIVPROC) (GLuint program, GLint location, GLuint *params); +typedef void (APIENTRYP PFNGLBINDFRAGDATALOCATIONPROC) (GLuint program, GLuint color, const GLchar *name); +typedef GLint (APIENTRYP PFNGLGETFRAGDATALOCATIONPROC) (GLuint program, const GLchar *name); +typedef void (APIENTRYP PFNGLUNIFORM1UIPROC) (GLint location, GLuint v0); +typedef void (APIENTRYP PFNGLUNIFORM2UIPROC) (GLint location, GLuint v0, GLuint v1); +typedef void (APIENTRYP PFNGLUNIFORM3UIPROC) (GLint location, GLuint v0, GLuint v1, GLuint v2); +typedef void (APIENTRYP PFNGLUNIFORM4UIPROC) (GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3); +typedef void (APIENTRYP PFNGLUNIFORM1UIVPROC) (GLint location, GLsizei count, const GLuint *value); +typedef void (APIENTRYP PFNGLUNIFORM2UIVPROC) (GLint location, GLsizei count, const GLuint *value); +typedef void (APIENTRYP PFNGLUNIFORM3UIVPROC) (GLint location, GLsizei count, const GLuint *value); +typedef void (APIENTRYP PFNGLUNIFORM4UIVPROC) (GLint location, GLsizei count, const GLuint *value); +typedef void (APIENTRYP PFNGLTEXPARAMETERIIVPROC) (GLenum target, GLenum pname, const GLint *params); +typedef void (APIENTRYP PFNGLTEXPARAMETERIUIVPROC) (GLenum target, GLenum pname, const GLuint *params); +typedef void (APIENTRYP PFNGLGETTEXPARAMETERIIVPROC) (GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETTEXPARAMETERIUIVPROC) (GLenum target, GLenum pname, GLuint *params); +typedef void (APIENTRYP PFNGLCLEARBUFFERIVPROC) (GLenum buffer, GLint drawbuffer, const GLint *value); +typedef void (APIENTRYP PFNGLCLEARBUFFERUIVPROC) (GLenum buffer, GLint drawbuffer, const GLuint *value); +typedef void (APIENTRYP PFNGLCLEARBUFFERFVPROC) (GLenum buffer, GLint drawbuffer, const GLfloat *value); +typedef void (APIENTRYP PFNGLCLEARBUFFERFIPROC) (GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil); +typedef const GLubyte *(APIENTRYP PFNGLGETSTRINGIPROC) (GLenum name, GLuint index); +typedef GLboolean (APIENTRYP PFNGLISRENDERBUFFERPROC) (GLuint renderbuffer); +typedef void (APIENTRYP PFNGLBINDRENDERBUFFERPROC) (GLenum target, GLuint renderbuffer); +typedef void (APIENTRYP PFNGLDELETERENDERBUFFERSPROC) (GLsizei n, const GLuint *renderbuffers); +typedef void (APIENTRYP PFNGLGENRENDERBUFFERSPROC) (GLsizei n, GLuint *renderbuffers); +typedef void (APIENTRYP PFNGLRENDERBUFFERSTORAGEPROC) (GLenum target, GLenum internalformat, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLGETRENDERBUFFERPARAMETERIVPROC) (GLenum target, GLenum pname, GLint *params); +typedef GLboolean (APIENTRYP PFNGLISFRAMEBUFFERPROC) (GLuint framebuffer); +typedef void (APIENTRYP PFNGLBINDFRAMEBUFFERPROC) (GLenum target, GLuint framebuffer); +typedef void (APIENTRYP PFNGLDELETEFRAMEBUFFERSPROC) (GLsizei n, const GLuint *framebuffers); +typedef void (APIENTRYP PFNGLGENFRAMEBUFFERSPROC) (GLsizei n, GLuint *framebuffers); +typedef GLenum (APIENTRYP PFNGLCHECKFRAMEBUFFERSTATUSPROC) (GLenum target); +typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURE1DPROC) (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); +typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURE2DPROC) (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); +typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURE3DPROC) (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset); +typedef void (APIENTRYP PFNGLFRAMEBUFFERRENDERBUFFERPROC) (GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); +typedef void (APIENTRYP PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC) (GLenum target, GLenum attachment, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGENERATEMIPMAPPROC) (GLenum target); +typedef void (APIENTRYP PFNGLBLITFRAMEBUFFERPROC) (GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter); +typedef void (APIENTRYP PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC) (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURELAYERPROC) (GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer); +typedef void *(APIENTRYP PFNGLMAPBUFFERRANGEPROC) (GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access); +typedef void (APIENTRYP PFNGLFLUSHMAPPEDBUFFERRANGEPROC) (GLenum target, GLintptr offset, GLsizeiptr length); +typedef void (APIENTRYP PFNGLBINDVERTEXARRAYPROC) (GLuint array); +typedef void (APIENTRYP PFNGLDELETEVERTEXARRAYSPROC) (GLsizei n, const GLuint *arrays); +typedef void (APIENTRYP PFNGLGENVERTEXARRAYSPROC) (GLsizei n, GLuint *arrays); +typedef GLboolean (APIENTRYP PFNGLISVERTEXARRAYPROC) (GLuint array); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glColorMaski (GLuint index, GLboolean r, GLboolean g, GLboolean b, GLboolean a); +GLAPI void APIENTRY glGetBooleani_v (GLenum target, GLuint index, GLboolean *data); +GLAPI void APIENTRY glGetIntegeri_v (GLenum target, GLuint index, GLint *data); +GLAPI void APIENTRY glEnablei (GLenum target, GLuint index); +GLAPI void APIENTRY glDisablei (GLenum target, GLuint index); +GLAPI GLboolean APIENTRY glIsEnabledi (GLenum target, GLuint index); +GLAPI void APIENTRY glBeginTransformFeedback (GLenum primitiveMode); +GLAPI void APIENTRY glEndTransformFeedback (void); +GLAPI void APIENTRY glBindBufferRange (GLenum target, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size); +GLAPI void APIENTRY glBindBufferBase (GLenum target, GLuint index, GLuint buffer); +GLAPI void APIENTRY glTransformFeedbackVaryings (GLuint program, GLsizei count, const GLchar *const*varyings, GLenum bufferMode); +GLAPI void APIENTRY glGetTransformFeedbackVarying (GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLsizei *size, GLenum *type, GLchar *name); +GLAPI void APIENTRY glClampColor (GLenum target, GLenum clamp); +GLAPI void APIENTRY glBeginConditionalRender (GLuint id, GLenum mode); +GLAPI void APIENTRY glEndConditionalRender (void); +GLAPI void APIENTRY glVertexAttribIPointer (GLuint index, GLint size, GLenum type, GLsizei stride, const void *pointer); +GLAPI void APIENTRY glGetVertexAttribIiv (GLuint index, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetVertexAttribIuiv (GLuint index, GLenum pname, GLuint *params); +GLAPI void APIENTRY glVertexAttribI1i (GLuint index, GLint x); +GLAPI void APIENTRY glVertexAttribI2i (GLuint index, GLint x, GLint y); +GLAPI void APIENTRY glVertexAttribI3i (GLuint index, GLint x, GLint y, GLint z); +GLAPI void APIENTRY glVertexAttribI4i (GLuint index, GLint x, GLint y, GLint z, GLint w); +GLAPI void APIENTRY glVertexAttribI1ui (GLuint index, GLuint x); +GLAPI void APIENTRY glVertexAttribI2ui (GLuint index, GLuint x, GLuint y); +GLAPI void APIENTRY glVertexAttribI3ui (GLuint index, GLuint x, GLuint y, GLuint z); +GLAPI void APIENTRY glVertexAttribI4ui (GLuint index, GLuint x, GLuint y, GLuint z, GLuint w); +GLAPI void APIENTRY glVertexAttribI1iv (GLuint index, const GLint *v); +GLAPI void APIENTRY glVertexAttribI2iv (GLuint index, const GLint *v); +GLAPI void APIENTRY glVertexAttribI3iv (GLuint index, const GLint *v); +GLAPI void APIENTRY glVertexAttribI4iv (GLuint index, const GLint *v); +GLAPI void APIENTRY glVertexAttribI1uiv (GLuint index, const GLuint *v); +GLAPI void APIENTRY glVertexAttribI2uiv (GLuint index, const GLuint *v); +GLAPI void APIENTRY glVertexAttribI3uiv (GLuint index, const GLuint *v); +GLAPI void APIENTRY glVertexAttribI4uiv (GLuint index, const GLuint *v); +GLAPI void APIENTRY glVertexAttribI4bv (GLuint index, const GLbyte *v); +GLAPI void APIENTRY glVertexAttribI4sv (GLuint index, const GLshort *v); +GLAPI void APIENTRY glVertexAttribI4ubv (GLuint index, const GLubyte *v); +GLAPI void APIENTRY glVertexAttribI4usv (GLuint index, const GLushort *v); +GLAPI void APIENTRY glGetUniformuiv (GLuint program, GLint location, GLuint *params); +GLAPI void APIENTRY glBindFragDataLocation (GLuint program, GLuint color, const GLchar *name); +GLAPI GLint APIENTRY glGetFragDataLocation (GLuint program, const GLchar *name); +GLAPI void APIENTRY glUniform1ui (GLint location, GLuint v0); +GLAPI void APIENTRY glUniform2ui (GLint location, GLuint v0, GLuint v1); +GLAPI void APIENTRY glUniform3ui (GLint location, GLuint v0, GLuint v1, GLuint v2); +GLAPI void APIENTRY glUniform4ui (GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3); +GLAPI void APIENTRY glUniform1uiv (GLint location, GLsizei count, const GLuint *value); +GLAPI void APIENTRY glUniform2uiv (GLint location, GLsizei count, const GLuint *value); +GLAPI void APIENTRY glUniform3uiv (GLint location, GLsizei count, const GLuint *value); +GLAPI void APIENTRY glUniform4uiv (GLint location, GLsizei count, const GLuint *value); +GLAPI void APIENTRY glTexParameterIiv (GLenum target, GLenum pname, const GLint *params); +GLAPI void APIENTRY glTexParameterIuiv (GLenum target, GLenum pname, const GLuint *params); +GLAPI void APIENTRY glGetTexParameterIiv (GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetTexParameterIuiv (GLenum target, GLenum pname, GLuint *params); +GLAPI void APIENTRY glClearBufferiv (GLenum buffer, GLint drawbuffer, const GLint *value); +GLAPI void APIENTRY glClearBufferuiv (GLenum buffer, GLint drawbuffer, const GLuint *value); +GLAPI void APIENTRY glClearBufferfv (GLenum buffer, GLint drawbuffer, const GLfloat *value); +GLAPI void APIENTRY glClearBufferfi (GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil); +GLAPI const GLubyte *APIENTRY glGetStringi (GLenum name, GLuint index); +GLAPI GLboolean APIENTRY glIsRenderbuffer (GLuint renderbuffer); +GLAPI void APIENTRY glBindRenderbuffer (GLenum target, GLuint renderbuffer); +GLAPI void APIENTRY glDeleteRenderbuffers (GLsizei n, const GLuint *renderbuffers); +GLAPI void APIENTRY glGenRenderbuffers (GLsizei n, GLuint *renderbuffers); +GLAPI void APIENTRY glRenderbufferStorage (GLenum target, GLenum internalformat, GLsizei width, GLsizei height); +GLAPI void APIENTRY glGetRenderbufferParameteriv (GLenum target, GLenum pname, GLint *params); +GLAPI GLboolean APIENTRY glIsFramebuffer (GLuint framebuffer); +GLAPI void APIENTRY glBindFramebuffer (GLenum target, GLuint framebuffer); +GLAPI void APIENTRY glDeleteFramebuffers (GLsizei n, const GLuint *framebuffers); +GLAPI void APIENTRY glGenFramebuffers (GLsizei n, GLuint *framebuffers); +GLAPI GLenum APIENTRY glCheckFramebufferStatus (GLenum target); +GLAPI void APIENTRY glFramebufferTexture1D (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); +GLAPI void APIENTRY glFramebufferTexture2D (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); +GLAPI void APIENTRY glFramebufferTexture3D (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset); +GLAPI void APIENTRY glFramebufferRenderbuffer (GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); +GLAPI void APIENTRY glGetFramebufferAttachmentParameteriv (GLenum target, GLenum attachment, GLenum pname, GLint *params); +GLAPI void APIENTRY glGenerateMipmap (GLenum target); +GLAPI void APIENTRY glBlitFramebuffer (GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter); +GLAPI void APIENTRY glRenderbufferStorageMultisample (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height); +GLAPI void APIENTRY glFramebufferTextureLayer (GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer); +GLAPI void *APIENTRY glMapBufferRange (GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access); +GLAPI void APIENTRY glFlushMappedBufferRange (GLenum target, GLintptr offset, GLsizeiptr length); +GLAPI void APIENTRY glBindVertexArray (GLuint array); +GLAPI void APIENTRY glDeleteVertexArrays (GLsizei n, const GLuint *arrays); +GLAPI void APIENTRY glGenVertexArrays (GLsizei n, GLuint *arrays); +GLAPI GLboolean APIENTRY glIsVertexArray (GLuint array); +#endif +#endif /* GL_VERSION_3_0 */ + +#ifndef GL_VERSION_3_1 +#define GL_VERSION_3_1 1 +#define GL_SAMPLER_2D_RECT 0x8B63 +#define GL_SAMPLER_2D_RECT_SHADOW 0x8B64 +#define GL_SAMPLER_BUFFER 0x8DC2 +#define GL_INT_SAMPLER_2D_RECT 0x8DCD +#define GL_INT_SAMPLER_BUFFER 0x8DD0 +#define GL_UNSIGNED_INT_SAMPLER_2D_RECT 0x8DD5 +#define GL_UNSIGNED_INT_SAMPLER_BUFFER 0x8DD8 +#define GL_TEXTURE_BUFFER 0x8C2A +#define GL_MAX_TEXTURE_BUFFER_SIZE 0x8C2B +#define GL_TEXTURE_BINDING_BUFFER 0x8C2C +#define GL_TEXTURE_BUFFER_DATA_STORE_BINDING 0x8C2D +#define GL_TEXTURE_RECTANGLE 0x84F5 +#define GL_TEXTURE_BINDING_RECTANGLE 0x84F6 +#define GL_PROXY_TEXTURE_RECTANGLE 0x84F7 +#define GL_MAX_RECTANGLE_TEXTURE_SIZE 0x84F8 +#define GL_R8_SNORM 0x8F94 +#define GL_RG8_SNORM 0x8F95 +#define GL_RGB8_SNORM 0x8F96 +#define GL_RGBA8_SNORM 0x8F97 +#define GL_R16_SNORM 0x8F98 +#define GL_RG16_SNORM 0x8F99 +#define GL_RGB16_SNORM 0x8F9A +#define GL_RGBA16_SNORM 0x8F9B +#define GL_SIGNED_NORMALIZED 0x8F9C +#define GL_PRIMITIVE_RESTART 0x8F9D +#define GL_PRIMITIVE_RESTART_INDEX 0x8F9E +#define GL_COPY_READ_BUFFER 0x8F36 +#define GL_COPY_WRITE_BUFFER 0x8F37 +#define GL_UNIFORM_BUFFER 0x8A11 +#define GL_UNIFORM_BUFFER_BINDING 0x8A28 +#define GL_UNIFORM_BUFFER_START 0x8A29 +#define GL_UNIFORM_BUFFER_SIZE 0x8A2A +#define GL_MAX_VERTEX_UNIFORM_BLOCKS 0x8A2B +#define GL_MAX_GEOMETRY_UNIFORM_BLOCKS 0x8A2C +#define GL_MAX_FRAGMENT_UNIFORM_BLOCKS 0x8A2D +#define GL_MAX_COMBINED_UNIFORM_BLOCKS 0x8A2E +#define GL_MAX_UNIFORM_BUFFER_BINDINGS 0x8A2F +#define GL_MAX_UNIFORM_BLOCK_SIZE 0x8A30 +#define GL_MAX_COMBINED_VERTEX_UNIFORM_COMPONENTS 0x8A31 +#define GL_MAX_COMBINED_GEOMETRY_UNIFORM_COMPONENTS 0x8A32 +#define GL_MAX_COMBINED_FRAGMENT_UNIFORM_COMPONENTS 0x8A33 +#define GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT 0x8A34 +#define GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH 0x8A35 +#define GL_ACTIVE_UNIFORM_BLOCKS 0x8A36 +#define GL_UNIFORM_TYPE 0x8A37 +#define GL_UNIFORM_SIZE 0x8A38 +#define GL_UNIFORM_NAME_LENGTH 0x8A39 +#define GL_UNIFORM_BLOCK_INDEX 0x8A3A +#define GL_UNIFORM_OFFSET 0x8A3B +#define GL_UNIFORM_ARRAY_STRIDE 0x8A3C +#define GL_UNIFORM_MATRIX_STRIDE 0x8A3D +#define GL_UNIFORM_IS_ROW_MAJOR 0x8A3E +#define GL_UNIFORM_BLOCK_BINDING 0x8A3F +#define GL_UNIFORM_BLOCK_DATA_SIZE 0x8A40 +#define GL_UNIFORM_BLOCK_NAME_LENGTH 0x8A41 +#define GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS 0x8A42 +#define GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES 0x8A43 +#define GL_UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER 0x8A44 +#define GL_UNIFORM_BLOCK_REFERENCED_BY_GEOMETRY_SHADER 0x8A45 +#define GL_UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER 0x8A46 +#define GL_INVALID_INDEX 0xFFFFFFFFu +typedef void (APIENTRYP PFNGLDRAWARRAYSINSTANCEDPROC) (GLenum mode, GLint first, GLsizei count, GLsizei instancecount); +typedef void (APIENTRYP PFNGLDRAWELEMENTSINSTANCEDPROC) (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount); +typedef void (APIENTRYP PFNGLTEXBUFFERPROC) (GLenum target, GLenum internalformat, GLuint buffer); +typedef void (APIENTRYP PFNGLPRIMITIVERESTARTINDEXPROC) (GLuint index); +typedef void (APIENTRYP PFNGLCOPYBUFFERSUBDATAPROC) (GLenum readTarget, GLenum writeTarget, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size); +typedef void (APIENTRYP PFNGLGETUNIFORMINDICESPROC) (GLuint program, GLsizei uniformCount, const GLchar *const*uniformNames, GLuint *uniformIndices); +typedef void (APIENTRYP PFNGLGETACTIVEUNIFORMSIVPROC) (GLuint program, GLsizei uniformCount, const GLuint *uniformIndices, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETACTIVEUNIFORMNAMEPROC) (GLuint program, GLuint uniformIndex, GLsizei bufSize, GLsizei *length, GLchar *uniformName); +typedef GLuint (APIENTRYP PFNGLGETUNIFORMBLOCKINDEXPROC) (GLuint program, const GLchar *uniformBlockName); +typedef void (APIENTRYP PFNGLGETACTIVEUNIFORMBLOCKIVPROC) (GLuint program, GLuint uniformBlockIndex, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETACTIVEUNIFORMBLOCKNAMEPROC) (GLuint program, GLuint uniformBlockIndex, GLsizei bufSize, GLsizei *length, GLchar *uniformBlockName); +typedef void (APIENTRYP PFNGLUNIFORMBLOCKBINDINGPROC) (GLuint program, GLuint uniformBlockIndex, GLuint uniformBlockBinding); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glDrawArraysInstanced (GLenum mode, GLint first, GLsizei count, GLsizei instancecount); +GLAPI void APIENTRY glDrawElementsInstanced (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount); +GLAPI void APIENTRY glTexBuffer (GLenum target, GLenum internalformat, GLuint buffer); +GLAPI void APIENTRY glPrimitiveRestartIndex (GLuint index); +GLAPI void APIENTRY glCopyBufferSubData (GLenum readTarget, GLenum writeTarget, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size); +GLAPI void APIENTRY glGetUniformIndices (GLuint program, GLsizei uniformCount, const GLchar *const*uniformNames, GLuint *uniformIndices); +GLAPI void APIENTRY glGetActiveUniformsiv (GLuint program, GLsizei uniformCount, const GLuint *uniformIndices, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetActiveUniformName (GLuint program, GLuint uniformIndex, GLsizei bufSize, GLsizei *length, GLchar *uniformName); +GLAPI GLuint APIENTRY glGetUniformBlockIndex (GLuint program, const GLchar *uniformBlockName); +GLAPI void APIENTRY glGetActiveUniformBlockiv (GLuint program, GLuint uniformBlockIndex, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetActiveUniformBlockName (GLuint program, GLuint uniformBlockIndex, GLsizei bufSize, GLsizei *length, GLchar *uniformBlockName); +GLAPI void APIENTRY glUniformBlockBinding (GLuint program, GLuint uniformBlockIndex, GLuint uniformBlockBinding); +#endif +#endif /* GL_VERSION_3_1 */ + +#ifndef GL_VERSION_3_2 +#define GL_VERSION_3_2 1 +typedef struct __GLsync *GLsync; +#ifndef GLEXT_64_TYPES_DEFINED +/* This code block is duplicated in glxext.h, so must be protected */ +#define GLEXT_64_TYPES_DEFINED +/* Define int32_t, int64_t, and uint64_t types for UST/MSC */ +/* (as used in the GL_EXT_timer_query extension). */ +#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L +#include +#elif defined(__sun__) || defined(__digital__) +#include +#if defined(__STDC__) +#if defined(__arch64__) || defined(_LP64) +typedef long int int64_t; +typedef unsigned long int uint64_t; +#else +typedef long long int int64_t; +typedef unsigned long long int uint64_t; +#endif /* __arch64__ */ +#endif /* __STDC__ */ +#elif defined( __VMS ) || defined(__sgi) +#include +#elif defined(__SCO__) || defined(__USLC__) +#include +#elif defined(__UNIXOS2__) || defined(__SOL64__) +typedef long int int32_t; +typedef long long int int64_t; +typedef unsigned long long int uint64_t; +#elif defined(_WIN32) && defined(__GNUC__) +#include +#elif defined(_WIN32) +typedef __int32 int32_t; +typedef __int64 int64_t; +typedef unsigned __int64 uint64_t; +#else +/* Fallback if nothing above works */ +#include +#endif +#endif +typedef uint64_t GLuint64; +typedef int64_t GLint64; +#define GL_CONTEXT_CORE_PROFILE_BIT 0x00000001 +#define GL_CONTEXT_COMPATIBILITY_PROFILE_BIT 0x00000002 +#define GL_LINES_ADJACENCY 0x000A +#define GL_LINE_STRIP_ADJACENCY 0x000B +#define GL_TRIANGLES_ADJACENCY 0x000C +#define GL_TRIANGLE_STRIP_ADJACENCY 0x000D +#define GL_PROGRAM_POINT_SIZE 0x8642 +#define GL_MAX_GEOMETRY_TEXTURE_IMAGE_UNITS 0x8C29 +#define GL_FRAMEBUFFER_ATTACHMENT_LAYERED 0x8DA7 +#define GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS 0x8DA8 +#define GL_GEOMETRY_SHADER 0x8DD9 +#define GL_GEOMETRY_VERTICES_OUT 0x8916 +#define GL_GEOMETRY_INPUT_TYPE 0x8917 +#define GL_GEOMETRY_OUTPUT_TYPE 0x8918 +#define GL_MAX_GEOMETRY_UNIFORM_COMPONENTS 0x8DDF +#define GL_MAX_GEOMETRY_OUTPUT_VERTICES 0x8DE0 +#define GL_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS 0x8DE1 +#define GL_MAX_VERTEX_OUTPUT_COMPONENTS 0x9122 +#define GL_MAX_GEOMETRY_INPUT_COMPONENTS 0x9123 +#define GL_MAX_GEOMETRY_OUTPUT_COMPONENTS 0x9124 +#define GL_MAX_FRAGMENT_INPUT_COMPONENTS 0x9125 +#define GL_CONTEXT_PROFILE_MASK 0x9126 +#define GL_DEPTH_CLAMP 0x864F +#define GL_QUADS_FOLLOW_PROVOKING_VERTEX_CONVENTION 0x8E4C +#define GL_FIRST_VERTEX_CONVENTION 0x8E4D +#define GL_LAST_VERTEX_CONVENTION 0x8E4E +#define GL_PROVOKING_VERTEX 0x8E4F +#define GL_TEXTURE_CUBE_MAP_SEAMLESS 0x884F +#define GL_MAX_SERVER_WAIT_TIMEOUT 0x9111 +#define GL_OBJECT_TYPE 0x9112 +#define GL_SYNC_CONDITION 0x9113 +#define GL_SYNC_STATUS 0x9114 +#define GL_SYNC_FLAGS 0x9115 +#define GL_SYNC_FENCE 0x9116 +#define GL_SYNC_GPU_COMMANDS_COMPLETE 0x9117 +#define GL_UNSIGNALED 0x9118 +#define GL_SIGNALED 0x9119 +#define GL_ALREADY_SIGNALED 0x911A +#define GL_TIMEOUT_EXPIRED 0x911B +#define GL_CONDITION_SATISFIED 0x911C +#define GL_WAIT_FAILED 0x911D +#define GL_TIMEOUT_IGNORED 0xFFFFFFFFFFFFFFFFull +#define GL_SYNC_FLUSH_COMMANDS_BIT 0x00000001 +#define GL_SAMPLE_POSITION 0x8E50 +#define GL_SAMPLE_MASK 0x8E51 +#define GL_SAMPLE_MASK_VALUE 0x8E52 +#define GL_MAX_SAMPLE_MASK_WORDS 0x8E59 +#define GL_TEXTURE_2D_MULTISAMPLE 0x9100 +#define GL_PROXY_TEXTURE_2D_MULTISAMPLE 0x9101 +#define GL_TEXTURE_2D_MULTISAMPLE_ARRAY 0x9102 +#define GL_PROXY_TEXTURE_2D_MULTISAMPLE_ARRAY 0x9103 +#define GL_TEXTURE_BINDING_2D_MULTISAMPLE 0x9104 +#define GL_TEXTURE_BINDING_2D_MULTISAMPLE_ARRAY 0x9105 +#define GL_TEXTURE_SAMPLES 0x9106 +#define GL_TEXTURE_FIXED_SAMPLE_LOCATIONS 0x9107 +#define GL_SAMPLER_2D_MULTISAMPLE 0x9108 +#define GL_INT_SAMPLER_2D_MULTISAMPLE 0x9109 +#define GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE 0x910A +#define GL_SAMPLER_2D_MULTISAMPLE_ARRAY 0x910B +#define GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY 0x910C +#define GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY 0x910D +#define GL_MAX_COLOR_TEXTURE_SAMPLES 0x910E +#define GL_MAX_DEPTH_TEXTURE_SAMPLES 0x910F +#define GL_MAX_INTEGER_SAMPLES 0x9110 +typedef void (APIENTRYP PFNGLDRAWELEMENTSBASEVERTEXPROC) (GLenum mode, GLsizei count, GLenum type, const void *indices, GLint basevertex); +typedef void (APIENTRYP PFNGLDRAWRANGEELEMENTSBASEVERTEXPROC) (GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void *indices, GLint basevertex); +typedef void (APIENTRYP PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXPROC) (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount, GLint basevertex); +typedef void (APIENTRYP PFNGLMULTIDRAWELEMENTSBASEVERTEXPROC) (GLenum mode, const GLsizei *count, GLenum type, const void *const*indices, GLsizei drawcount, const GLint *basevertex); +typedef void (APIENTRYP PFNGLPROVOKINGVERTEXPROC) (GLenum mode); +typedef GLsync (APIENTRYP PFNGLFENCESYNCPROC) (GLenum condition, GLbitfield flags); +typedef GLboolean (APIENTRYP PFNGLISSYNCPROC) (GLsync sync); +typedef void (APIENTRYP PFNGLDELETESYNCPROC) (GLsync sync); +typedef GLenum (APIENTRYP PFNGLCLIENTWAITSYNCPROC) (GLsync sync, GLbitfield flags, GLuint64 timeout); +typedef void (APIENTRYP PFNGLWAITSYNCPROC) (GLsync sync, GLbitfield flags, GLuint64 timeout); +typedef void (APIENTRYP PFNGLGETINTEGER64VPROC) (GLenum pname, GLint64 *data); +typedef void (APIENTRYP PFNGLGETSYNCIVPROC) (GLsync sync, GLenum pname, GLsizei bufSize, GLsizei *length, GLint *values); +typedef void (APIENTRYP PFNGLGETINTEGER64I_VPROC) (GLenum target, GLuint index, GLint64 *data); +typedef void (APIENTRYP PFNGLGETBUFFERPARAMETERI64VPROC) (GLenum target, GLenum pname, GLint64 *params); +typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTUREPROC) (GLenum target, GLenum attachment, GLuint texture, GLint level); +typedef void (APIENTRYP PFNGLTEXIMAGE2DMULTISAMPLEPROC) (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations); +typedef void (APIENTRYP PFNGLTEXIMAGE3DMULTISAMPLEPROC) (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations); +typedef void (APIENTRYP PFNGLGETMULTISAMPLEFVPROC) (GLenum pname, GLuint index, GLfloat *val); +typedef void (APIENTRYP PFNGLSAMPLEMASKIPROC) (GLuint maskNumber, GLbitfield mask); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glDrawElementsBaseVertex (GLenum mode, GLsizei count, GLenum type, const void *indices, GLint basevertex); +GLAPI void APIENTRY glDrawRangeElementsBaseVertex (GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void *indices, GLint basevertex); +GLAPI void APIENTRY glDrawElementsInstancedBaseVertex (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount, GLint basevertex); +GLAPI void APIENTRY glMultiDrawElementsBaseVertex (GLenum mode, const GLsizei *count, GLenum type, const void *const*indices, GLsizei drawcount, const GLint *basevertex); +GLAPI void APIENTRY glProvokingVertex (GLenum mode); +GLAPI GLsync APIENTRY glFenceSync (GLenum condition, GLbitfield flags); +GLAPI GLboolean APIENTRY glIsSync (GLsync sync); +GLAPI void APIENTRY glDeleteSync (GLsync sync); +GLAPI GLenum APIENTRY glClientWaitSync (GLsync sync, GLbitfield flags, GLuint64 timeout); +GLAPI void APIENTRY glWaitSync (GLsync sync, GLbitfield flags, GLuint64 timeout); +GLAPI void APIENTRY glGetInteger64v (GLenum pname, GLint64 *data); +GLAPI void APIENTRY glGetSynciv (GLsync sync, GLenum pname, GLsizei bufSize, GLsizei *length, GLint *values); +GLAPI void APIENTRY glGetInteger64i_v (GLenum target, GLuint index, GLint64 *data); +GLAPI void APIENTRY glGetBufferParameteri64v (GLenum target, GLenum pname, GLint64 *params); +GLAPI void APIENTRY glFramebufferTexture (GLenum target, GLenum attachment, GLuint texture, GLint level); +GLAPI void APIENTRY glTexImage2DMultisample (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations); +GLAPI void APIENTRY glTexImage3DMultisample (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations); +GLAPI void APIENTRY glGetMultisamplefv (GLenum pname, GLuint index, GLfloat *val); +GLAPI void APIENTRY glSampleMaski (GLuint maskNumber, GLbitfield mask); +#endif +#endif /* GL_VERSION_3_2 */ + +#ifndef GL_VERSION_3_3 +#define GL_VERSION_3_3 1 +#define GL_VERTEX_ATTRIB_ARRAY_DIVISOR 0x88FE +#define GL_SRC1_COLOR 0x88F9 +#define GL_ONE_MINUS_SRC1_COLOR 0x88FA +#define GL_ONE_MINUS_SRC1_ALPHA 0x88FB +#define GL_MAX_DUAL_SOURCE_DRAW_BUFFERS 0x88FC +#define GL_ANY_SAMPLES_PASSED 0x8C2F +#define GL_SAMPLER_BINDING 0x8919 +#define GL_RGB10_A2UI 0x906F +#define GL_TEXTURE_SWIZZLE_R 0x8E42 +#define GL_TEXTURE_SWIZZLE_G 0x8E43 +#define GL_TEXTURE_SWIZZLE_B 0x8E44 +#define GL_TEXTURE_SWIZZLE_A 0x8E45 +#define GL_TEXTURE_SWIZZLE_RGBA 0x8E46 +#define GL_TIME_ELAPSED 0x88BF +#define GL_TIMESTAMP 0x8E28 +#define GL_INT_2_10_10_10_REV 0x8D9F +typedef void (APIENTRYP PFNGLBINDFRAGDATALOCATIONINDEXEDPROC) (GLuint program, GLuint colorNumber, GLuint index, const GLchar *name); +typedef GLint (APIENTRYP PFNGLGETFRAGDATAINDEXPROC) (GLuint program, const GLchar *name); +typedef void (APIENTRYP PFNGLGENSAMPLERSPROC) (GLsizei count, GLuint *samplers); +typedef void (APIENTRYP PFNGLDELETESAMPLERSPROC) (GLsizei count, const GLuint *samplers); +typedef GLboolean (APIENTRYP PFNGLISSAMPLERPROC) (GLuint sampler); +typedef void (APIENTRYP PFNGLBINDSAMPLERPROC) (GLuint unit, GLuint sampler); +typedef void (APIENTRYP PFNGLSAMPLERPARAMETERIPROC) (GLuint sampler, GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLSAMPLERPARAMETERIVPROC) (GLuint sampler, GLenum pname, const GLint *param); +typedef void (APIENTRYP PFNGLSAMPLERPARAMETERFPROC) (GLuint sampler, GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLSAMPLERPARAMETERFVPROC) (GLuint sampler, GLenum pname, const GLfloat *param); +typedef void (APIENTRYP PFNGLSAMPLERPARAMETERIIVPROC) (GLuint sampler, GLenum pname, const GLint *param); +typedef void (APIENTRYP PFNGLSAMPLERPARAMETERIUIVPROC) (GLuint sampler, GLenum pname, const GLuint *param); +typedef void (APIENTRYP PFNGLGETSAMPLERPARAMETERIVPROC) (GLuint sampler, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETSAMPLERPARAMETERIIVPROC) (GLuint sampler, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETSAMPLERPARAMETERFVPROC) (GLuint sampler, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETSAMPLERPARAMETERIUIVPROC) (GLuint sampler, GLenum pname, GLuint *params); +typedef void (APIENTRYP PFNGLQUERYCOUNTERPROC) (GLuint id, GLenum target); +typedef void (APIENTRYP PFNGLGETQUERYOBJECTI64VPROC) (GLuint id, GLenum pname, GLint64 *params); +typedef void (APIENTRYP PFNGLGETQUERYOBJECTUI64VPROC) (GLuint id, GLenum pname, GLuint64 *params); +typedef void (APIENTRYP PFNGLVERTEXATTRIBDIVISORPROC) (GLuint index, GLuint divisor); +typedef void (APIENTRYP PFNGLVERTEXATTRIBP1UIPROC) (GLuint index, GLenum type, GLboolean normalized, GLuint value); +typedef void (APIENTRYP PFNGLVERTEXATTRIBP1UIVPROC) (GLuint index, GLenum type, GLboolean normalized, const GLuint *value); +typedef void (APIENTRYP PFNGLVERTEXATTRIBP2UIPROC) (GLuint index, GLenum type, GLboolean normalized, GLuint value); +typedef void (APIENTRYP PFNGLVERTEXATTRIBP2UIVPROC) (GLuint index, GLenum type, GLboolean normalized, const GLuint *value); +typedef void (APIENTRYP PFNGLVERTEXATTRIBP3UIPROC) (GLuint index, GLenum type, GLboolean normalized, GLuint value); +typedef void (APIENTRYP PFNGLVERTEXATTRIBP3UIVPROC) (GLuint index, GLenum type, GLboolean normalized, const GLuint *value); +typedef void (APIENTRYP PFNGLVERTEXATTRIBP4UIPROC) (GLuint index, GLenum type, GLboolean normalized, GLuint value); +typedef void (APIENTRYP PFNGLVERTEXATTRIBP4UIVPROC) (GLuint index, GLenum type, GLboolean normalized, const GLuint *value); +typedef void (APIENTRYP PFNGLVERTEXP2UIPROC) (GLenum type, GLuint value); +typedef void (APIENTRYP PFNGLVERTEXP2UIVPROC) (GLenum type, const GLuint *value); +typedef void (APIENTRYP PFNGLVERTEXP3UIPROC) (GLenum type, GLuint value); +typedef void (APIENTRYP PFNGLVERTEXP3UIVPROC) (GLenum type, const GLuint *value); +typedef void (APIENTRYP PFNGLVERTEXP4UIPROC) (GLenum type, GLuint value); +typedef void (APIENTRYP PFNGLVERTEXP4UIVPROC) (GLenum type, const GLuint *value); +typedef void (APIENTRYP PFNGLTEXCOORDP1UIPROC) (GLenum type, GLuint coords); +typedef void (APIENTRYP PFNGLTEXCOORDP1UIVPROC) (GLenum type, const GLuint *coords); +typedef void (APIENTRYP PFNGLTEXCOORDP2UIPROC) (GLenum type, GLuint coords); +typedef void (APIENTRYP PFNGLTEXCOORDP2UIVPROC) (GLenum type, const GLuint *coords); +typedef void (APIENTRYP PFNGLTEXCOORDP3UIPROC) (GLenum type, GLuint coords); +typedef void (APIENTRYP PFNGLTEXCOORDP3UIVPROC) (GLenum type, const GLuint *coords); +typedef void (APIENTRYP PFNGLTEXCOORDP4UIPROC) (GLenum type, GLuint coords); +typedef void (APIENTRYP PFNGLTEXCOORDP4UIVPROC) (GLenum type, const GLuint *coords); +typedef void (APIENTRYP PFNGLMULTITEXCOORDP1UIPROC) (GLenum texture, GLenum type, GLuint coords); +typedef void (APIENTRYP PFNGLMULTITEXCOORDP1UIVPROC) (GLenum texture, GLenum type, const GLuint *coords); +typedef void (APIENTRYP PFNGLMULTITEXCOORDP2UIPROC) (GLenum texture, GLenum type, GLuint coords); +typedef void (APIENTRYP PFNGLMULTITEXCOORDP2UIVPROC) (GLenum texture, GLenum type, const GLuint *coords); +typedef void (APIENTRYP PFNGLMULTITEXCOORDP3UIPROC) (GLenum texture, GLenum type, GLuint coords); +typedef void (APIENTRYP PFNGLMULTITEXCOORDP3UIVPROC) (GLenum texture, GLenum type, const GLuint *coords); +typedef void (APIENTRYP PFNGLMULTITEXCOORDP4UIPROC) (GLenum texture, GLenum type, GLuint coords); +typedef void (APIENTRYP PFNGLMULTITEXCOORDP4UIVPROC) (GLenum texture, GLenum type, const GLuint *coords); +typedef void (APIENTRYP PFNGLNORMALP3UIPROC) (GLenum type, GLuint coords); +typedef void (APIENTRYP PFNGLNORMALP3UIVPROC) (GLenum type, const GLuint *coords); +typedef void (APIENTRYP PFNGLCOLORP3UIPROC) (GLenum type, GLuint color); +typedef void (APIENTRYP PFNGLCOLORP3UIVPROC) (GLenum type, const GLuint *color); +typedef void (APIENTRYP PFNGLCOLORP4UIPROC) (GLenum type, GLuint color); +typedef void (APIENTRYP PFNGLCOLORP4UIVPROC) (GLenum type, const GLuint *color); +typedef void (APIENTRYP PFNGLSECONDARYCOLORP3UIPROC) (GLenum type, GLuint color); +typedef void (APIENTRYP PFNGLSECONDARYCOLORP3UIVPROC) (GLenum type, const GLuint *color); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBindFragDataLocationIndexed (GLuint program, GLuint colorNumber, GLuint index, const GLchar *name); +GLAPI GLint APIENTRY glGetFragDataIndex (GLuint program, const GLchar *name); +GLAPI void APIENTRY glGenSamplers (GLsizei count, GLuint *samplers); +GLAPI void APIENTRY glDeleteSamplers (GLsizei count, const GLuint *samplers); +GLAPI GLboolean APIENTRY glIsSampler (GLuint sampler); +GLAPI void APIENTRY glBindSampler (GLuint unit, GLuint sampler); +GLAPI void APIENTRY glSamplerParameteri (GLuint sampler, GLenum pname, GLint param); +GLAPI void APIENTRY glSamplerParameteriv (GLuint sampler, GLenum pname, const GLint *param); +GLAPI void APIENTRY glSamplerParameterf (GLuint sampler, GLenum pname, GLfloat param); +GLAPI void APIENTRY glSamplerParameterfv (GLuint sampler, GLenum pname, const GLfloat *param); +GLAPI void APIENTRY glSamplerParameterIiv (GLuint sampler, GLenum pname, const GLint *param); +GLAPI void APIENTRY glSamplerParameterIuiv (GLuint sampler, GLenum pname, const GLuint *param); +GLAPI void APIENTRY glGetSamplerParameteriv (GLuint sampler, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetSamplerParameterIiv (GLuint sampler, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetSamplerParameterfv (GLuint sampler, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetSamplerParameterIuiv (GLuint sampler, GLenum pname, GLuint *params); +GLAPI void APIENTRY glQueryCounter (GLuint id, GLenum target); +GLAPI void APIENTRY glGetQueryObjecti64v (GLuint id, GLenum pname, GLint64 *params); +GLAPI void APIENTRY glGetQueryObjectui64v (GLuint id, GLenum pname, GLuint64 *params); +GLAPI void APIENTRY glVertexAttribDivisor (GLuint index, GLuint divisor); +GLAPI void APIENTRY glVertexAttribP1ui (GLuint index, GLenum type, GLboolean normalized, GLuint value); +GLAPI void APIENTRY glVertexAttribP1uiv (GLuint index, GLenum type, GLboolean normalized, const GLuint *value); +GLAPI void APIENTRY glVertexAttribP2ui (GLuint index, GLenum type, GLboolean normalized, GLuint value); +GLAPI void APIENTRY glVertexAttribP2uiv (GLuint index, GLenum type, GLboolean normalized, const GLuint *value); +GLAPI void APIENTRY glVertexAttribP3ui (GLuint index, GLenum type, GLboolean normalized, GLuint value); +GLAPI void APIENTRY glVertexAttribP3uiv (GLuint index, GLenum type, GLboolean normalized, const GLuint *value); +GLAPI void APIENTRY glVertexAttribP4ui (GLuint index, GLenum type, GLboolean normalized, GLuint value); +GLAPI void APIENTRY glVertexAttribP4uiv (GLuint index, GLenum type, GLboolean normalized, const GLuint *value); +GLAPI void APIENTRY glVertexP2ui (GLenum type, GLuint value); +GLAPI void APIENTRY glVertexP2uiv (GLenum type, const GLuint *value); +GLAPI void APIENTRY glVertexP3ui (GLenum type, GLuint value); +GLAPI void APIENTRY glVertexP3uiv (GLenum type, const GLuint *value); +GLAPI void APIENTRY glVertexP4ui (GLenum type, GLuint value); +GLAPI void APIENTRY glVertexP4uiv (GLenum type, const GLuint *value); +GLAPI void APIENTRY glTexCoordP1ui (GLenum type, GLuint coords); +GLAPI void APIENTRY glTexCoordP1uiv (GLenum type, const GLuint *coords); +GLAPI void APIENTRY glTexCoordP2ui (GLenum type, GLuint coords); +GLAPI void APIENTRY glTexCoordP2uiv (GLenum type, const GLuint *coords); +GLAPI void APIENTRY glTexCoordP3ui (GLenum type, GLuint coords); +GLAPI void APIENTRY glTexCoordP3uiv (GLenum type, const GLuint *coords); +GLAPI void APIENTRY glTexCoordP4ui (GLenum type, GLuint coords); +GLAPI void APIENTRY glTexCoordP4uiv (GLenum type, const GLuint *coords); +GLAPI void APIENTRY glMultiTexCoordP1ui (GLenum texture, GLenum type, GLuint coords); +GLAPI void APIENTRY glMultiTexCoordP1uiv (GLenum texture, GLenum type, const GLuint *coords); +GLAPI void APIENTRY glMultiTexCoordP2ui (GLenum texture, GLenum type, GLuint coords); +GLAPI void APIENTRY glMultiTexCoordP2uiv (GLenum texture, GLenum type, const GLuint *coords); +GLAPI void APIENTRY glMultiTexCoordP3ui (GLenum texture, GLenum type, GLuint coords); +GLAPI void APIENTRY glMultiTexCoordP3uiv (GLenum texture, GLenum type, const GLuint *coords); +GLAPI void APIENTRY glMultiTexCoordP4ui (GLenum texture, GLenum type, GLuint coords); +GLAPI void APIENTRY glMultiTexCoordP4uiv (GLenum texture, GLenum type, const GLuint *coords); +GLAPI void APIENTRY glNormalP3ui (GLenum type, GLuint coords); +GLAPI void APIENTRY glNormalP3uiv (GLenum type, const GLuint *coords); +GLAPI void APIENTRY glColorP3ui (GLenum type, GLuint color); +GLAPI void APIENTRY glColorP3uiv (GLenum type, const GLuint *color); +GLAPI void APIENTRY glColorP4ui (GLenum type, GLuint color); +GLAPI void APIENTRY glColorP4uiv (GLenum type, const GLuint *color); +GLAPI void APIENTRY glSecondaryColorP3ui (GLenum type, GLuint color); +GLAPI void APIENTRY glSecondaryColorP3uiv (GLenum type, const GLuint *color); +#endif +#endif /* GL_VERSION_3_3 */ + +#ifndef GL_VERSION_4_0 +#define GL_VERSION_4_0 1 +#define GL_SAMPLE_SHADING 0x8C36 +#define GL_MIN_SAMPLE_SHADING_VALUE 0x8C37 +#define GL_MIN_PROGRAM_TEXTURE_GATHER_OFFSET 0x8E5E +#define GL_MAX_PROGRAM_TEXTURE_GATHER_OFFSET 0x8E5F +#define GL_TEXTURE_CUBE_MAP_ARRAY 0x9009 +#define GL_TEXTURE_BINDING_CUBE_MAP_ARRAY 0x900A +#define GL_PROXY_TEXTURE_CUBE_MAP_ARRAY 0x900B +#define GL_SAMPLER_CUBE_MAP_ARRAY 0x900C +#define GL_SAMPLER_CUBE_MAP_ARRAY_SHADOW 0x900D +#define GL_INT_SAMPLER_CUBE_MAP_ARRAY 0x900E +#define GL_UNSIGNED_INT_SAMPLER_CUBE_MAP_ARRAY 0x900F +#define GL_DRAW_INDIRECT_BUFFER 0x8F3F +#define GL_DRAW_INDIRECT_BUFFER_BINDING 0x8F43 +#define GL_GEOMETRY_SHADER_INVOCATIONS 0x887F +#define GL_MAX_GEOMETRY_SHADER_INVOCATIONS 0x8E5A +#define GL_MIN_FRAGMENT_INTERPOLATION_OFFSET 0x8E5B +#define GL_MAX_FRAGMENT_INTERPOLATION_OFFSET 0x8E5C +#define GL_FRAGMENT_INTERPOLATION_OFFSET_BITS 0x8E5D +#define GL_MAX_VERTEX_STREAMS 0x8E71 +#define GL_DOUBLE_VEC2 0x8FFC +#define GL_DOUBLE_VEC3 0x8FFD +#define GL_DOUBLE_VEC4 0x8FFE +#define GL_DOUBLE_MAT2 0x8F46 +#define GL_DOUBLE_MAT3 0x8F47 +#define GL_DOUBLE_MAT4 0x8F48 +#define GL_DOUBLE_MAT2x3 0x8F49 +#define GL_DOUBLE_MAT2x4 0x8F4A +#define GL_DOUBLE_MAT3x2 0x8F4B +#define GL_DOUBLE_MAT3x4 0x8F4C +#define GL_DOUBLE_MAT4x2 0x8F4D +#define GL_DOUBLE_MAT4x3 0x8F4E +#define GL_ACTIVE_SUBROUTINES 0x8DE5 +#define GL_ACTIVE_SUBROUTINE_UNIFORMS 0x8DE6 +#define GL_ACTIVE_SUBROUTINE_UNIFORM_LOCATIONS 0x8E47 +#define GL_ACTIVE_SUBROUTINE_MAX_LENGTH 0x8E48 +#define GL_ACTIVE_SUBROUTINE_UNIFORM_MAX_LENGTH 0x8E49 +#define GL_MAX_SUBROUTINES 0x8DE7 +#define GL_MAX_SUBROUTINE_UNIFORM_LOCATIONS 0x8DE8 +#define GL_NUM_COMPATIBLE_SUBROUTINES 0x8E4A +#define GL_COMPATIBLE_SUBROUTINES 0x8E4B +#define GL_PATCHES 0x000E +#define GL_PATCH_VERTICES 0x8E72 +#define GL_PATCH_DEFAULT_INNER_LEVEL 0x8E73 +#define GL_PATCH_DEFAULT_OUTER_LEVEL 0x8E74 +#define GL_TESS_CONTROL_OUTPUT_VERTICES 0x8E75 +#define GL_TESS_GEN_MODE 0x8E76 +#define GL_TESS_GEN_SPACING 0x8E77 +#define GL_TESS_GEN_VERTEX_ORDER 0x8E78 +#define GL_TESS_GEN_POINT_MODE 0x8E79 +#define GL_ISOLINES 0x8E7A +#define GL_FRACTIONAL_ODD 0x8E7B +#define GL_FRACTIONAL_EVEN 0x8E7C +#define GL_MAX_PATCH_VERTICES 0x8E7D +#define GL_MAX_TESS_GEN_LEVEL 0x8E7E +#define GL_MAX_TESS_CONTROL_UNIFORM_COMPONENTS 0x8E7F +#define GL_MAX_TESS_EVALUATION_UNIFORM_COMPONENTS 0x8E80 +#define GL_MAX_TESS_CONTROL_TEXTURE_IMAGE_UNITS 0x8E81 +#define GL_MAX_TESS_EVALUATION_TEXTURE_IMAGE_UNITS 0x8E82 +#define GL_MAX_TESS_CONTROL_OUTPUT_COMPONENTS 0x8E83 +#define GL_MAX_TESS_PATCH_COMPONENTS 0x8E84 +#define GL_MAX_TESS_CONTROL_TOTAL_OUTPUT_COMPONENTS 0x8E85 +#define GL_MAX_TESS_EVALUATION_OUTPUT_COMPONENTS 0x8E86 +#define GL_MAX_TESS_CONTROL_UNIFORM_BLOCKS 0x8E89 +#define GL_MAX_TESS_EVALUATION_UNIFORM_BLOCKS 0x8E8A +#define GL_MAX_TESS_CONTROL_INPUT_COMPONENTS 0x886C +#define GL_MAX_TESS_EVALUATION_INPUT_COMPONENTS 0x886D +#define GL_MAX_COMBINED_TESS_CONTROL_UNIFORM_COMPONENTS 0x8E1E +#define GL_MAX_COMBINED_TESS_EVALUATION_UNIFORM_COMPONENTS 0x8E1F +#define GL_UNIFORM_BLOCK_REFERENCED_BY_TESS_CONTROL_SHADER 0x84F0 +#define GL_UNIFORM_BLOCK_REFERENCED_BY_TESS_EVALUATION_SHADER 0x84F1 +#define GL_TESS_EVALUATION_SHADER 0x8E87 +#define GL_TESS_CONTROL_SHADER 0x8E88 +#define GL_TRANSFORM_FEEDBACK 0x8E22 +#define GL_TRANSFORM_FEEDBACK_BUFFER_PAUSED 0x8E23 +#define GL_TRANSFORM_FEEDBACK_BUFFER_ACTIVE 0x8E24 +#define GL_TRANSFORM_FEEDBACK_BINDING 0x8E25 +#define GL_MAX_TRANSFORM_FEEDBACK_BUFFERS 0x8E70 +typedef void (APIENTRYP PFNGLMINSAMPLESHADINGPROC) (GLfloat value); +typedef void (APIENTRYP PFNGLBLENDEQUATIONIPROC) (GLuint buf, GLenum mode); +typedef void (APIENTRYP PFNGLBLENDEQUATIONSEPARATEIPROC) (GLuint buf, GLenum modeRGB, GLenum modeAlpha); +typedef void (APIENTRYP PFNGLBLENDFUNCIPROC) (GLuint buf, GLenum src, GLenum dst); +typedef void (APIENTRYP PFNGLBLENDFUNCSEPARATEIPROC) (GLuint buf, GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha); +typedef void (APIENTRYP PFNGLDRAWARRAYSINDIRECTPROC) (GLenum mode, const void *indirect); +typedef void (APIENTRYP PFNGLDRAWELEMENTSINDIRECTPROC) (GLenum mode, GLenum type, const void *indirect); +typedef void (APIENTRYP PFNGLUNIFORM1DPROC) (GLint location, GLdouble x); +typedef void (APIENTRYP PFNGLUNIFORM2DPROC) (GLint location, GLdouble x, GLdouble y); +typedef void (APIENTRYP PFNGLUNIFORM3DPROC) (GLint location, GLdouble x, GLdouble y, GLdouble z); +typedef void (APIENTRYP PFNGLUNIFORM4DPROC) (GLint location, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +typedef void (APIENTRYP PFNGLUNIFORM1DVPROC) (GLint location, GLsizei count, const GLdouble *value); +typedef void (APIENTRYP PFNGLUNIFORM2DVPROC) (GLint location, GLsizei count, const GLdouble *value); +typedef void (APIENTRYP PFNGLUNIFORM3DVPROC) (GLint location, GLsizei count, const GLdouble *value); +typedef void (APIENTRYP PFNGLUNIFORM4DVPROC) (GLint location, GLsizei count, const GLdouble *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX2DVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX3DVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX4DVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX2X3DVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX2X4DVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX3X2DVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX3X4DVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX4X2DVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX4X3DVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLGETUNIFORMDVPROC) (GLuint program, GLint location, GLdouble *params); +typedef GLint (APIENTRYP PFNGLGETSUBROUTINEUNIFORMLOCATIONPROC) (GLuint program, GLenum shadertype, const GLchar *name); +typedef GLuint (APIENTRYP PFNGLGETSUBROUTINEINDEXPROC) (GLuint program, GLenum shadertype, const GLchar *name); +typedef void (APIENTRYP PFNGLGETACTIVESUBROUTINEUNIFORMIVPROC) (GLuint program, GLenum shadertype, GLuint index, GLenum pname, GLint *values); +typedef void (APIENTRYP PFNGLGETACTIVESUBROUTINEUNIFORMNAMEPROC) (GLuint program, GLenum shadertype, GLuint index, GLsizei bufsize, GLsizei *length, GLchar *name); +typedef void (APIENTRYP PFNGLGETACTIVESUBROUTINENAMEPROC) (GLuint program, GLenum shadertype, GLuint index, GLsizei bufsize, GLsizei *length, GLchar *name); +typedef void (APIENTRYP PFNGLUNIFORMSUBROUTINESUIVPROC) (GLenum shadertype, GLsizei count, const GLuint *indices); +typedef void (APIENTRYP PFNGLGETUNIFORMSUBROUTINEUIVPROC) (GLenum shadertype, GLint location, GLuint *params); +typedef void (APIENTRYP PFNGLGETPROGRAMSTAGEIVPROC) (GLuint program, GLenum shadertype, GLenum pname, GLint *values); +typedef void (APIENTRYP PFNGLPATCHPARAMETERIPROC) (GLenum pname, GLint value); +typedef void (APIENTRYP PFNGLPATCHPARAMETERFVPROC) (GLenum pname, const GLfloat *values); +typedef void (APIENTRYP PFNGLBINDTRANSFORMFEEDBACKPROC) (GLenum target, GLuint id); +typedef void (APIENTRYP PFNGLDELETETRANSFORMFEEDBACKSPROC) (GLsizei n, const GLuint *ids); +typedef void (APIENTRYP PFNGLGENTRANSFORMFEEDBACKSPROC) (GLsizei n, GLuint *ids); +typedef GLboolean (APIENTRYP PFNGLISTRANSFORMFEEDBACKPROC) (GLuint id); +typedef void (APIENTRYP PFNGLPAUSETRANSFORMFEEDBACKPROC) (void); +typedef void (APIENTRYP PFNGLRESUMETRANSFORMFEEDBACKPROC) (void); +typedef void (APIENTRYP PFNGLDRAWTRANSFORMFEEDBACKPROC) (GLenum mode, GLuint id); +typedef void (APIENTRYP PFNGLDRAWTRANSFORMFEEDBACKSTREAMPROC) (GLenum mode, GLuint id, GLuint stream); +typedef void (APIENTRYP PFNGLBEGINQUERYINDEXEDPROC) (GLenum target, GLuint index, GLuint id); +typedef void (APIENTRYP PFNGLENDQUERYINDEXEDPROC) (GLenum target, GLuint index); +typedef void (APIENTRYP PFNGLGETQUERYINDEXEDIVPROC) (GLenum target, GLuint index, GLenum pname, GLint *params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glMinSampleShading (GLfloat value); +GLAPI void APIENTRY glBlendEquationi (GLuint buf, GLenum mode); +GLAPI void APIENTRY glBlendEquationSeparatei (GLuint buf, GLenum modeRGB, GLenum modeAlpha); +GLAPI void APIENTRY glBlendFunci (GLuint buf, GLenum src, GLenum dst); +GLAPI void APIENTRY glBlendFuncSeparatei (GLuint buf, GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha); +GLAPI void APIENTRY glDrawArraysIndirect (GLenum mode, const void *indirect); +GLAPI void APIENTRY glDrawElementsIndirect (GLenum mode, GLenum type, const void *indirect); +GLAPI void APIENTRY glUniform1d (GLint location, GLdouble x); +GLAPI void APIENTRY glUniform2d (GLint location, GLdouble x, GLdouble y); +GLAPI void APIENTRY glUniform3d (GLint location, GLdouble x, GLdouble y, GLdouble z); +GLAPI void APIENTRY glUniform4d (GLint location, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +GLAPI void APIENTRY glUniform1dv (GLint location, GLsizei count, const GLdouble *value); +GLAPI void APIENTRY glUniform2dv (GLint location, GLsizei count, const GLdouble *value); +GLAPI void APIENTRY glUniform3dv (GLint location, GLsizei count, const GLdouble *value); +GLAPI void APIENTRY glUniform4dv (GLint location, GLsizei count, const GLdouble *value); +GLAPI void APIENTRY glUniformMatrix2dv (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glUniformMatrix3dv (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glUniformMatrix4dv (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glUniformMatrix2x3dv (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glUniformMatrix2x4dv (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glUniformMatrix3x2dv (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glUniformMatrix3x4dv (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glUniformMatrix4x2dv (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glUniformMatrix4x3dv (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glGetUniformdv (GLuint program, GLint location, GLdouble *params); +GLAPI GLint APIENTRY glGetSubroutineUniformLocation (GLuint program, GLenum shadertype, const GLchar *name); +GLAPI GLuint APIENTRY glGetSubroutineIndex (GLuint program, GLenum shadertype, const GLchar *name); +GLAPI void APIENTRY glGetActiveSubroutineUniformiv (GLuint program, GLenum shadertype, GLuint index, GLenum pname, GLint *values); +GLAPI void APIENTRY glGetActiveSubroutineUniformName (GLuint program, GLenum shadertype, GLuint index, GLsizei bufsize, GLsizei *length, GLchar *name); +GLAPI void APIENTRY glGetActiveSubroutineName (GLuint program, GLenum shadertype, GLuint index, GLsizei bufsize, GLsizei *length, GLchar *name); +GLAPI void APIENTRY glUniformSubroutinesuiv (GLenum shadertype, GLsizei count, const GLuint *indices); +GLAPI void APIENTRY glGetUniformSubroutineuiv (GLenum shadertype, GLint location, GLuint *params); +GLAPI void APIENTRY glGetProgramStageiv (GLuint program, GLenum shadertype, GLenum pname, GLint *values); +GLAPI void APIENTRY glPatchParameteri (GLenum pname, GLint value); +GLAPI void APIENTRY glPatchParameterfv (GLenum pname, const GLfloat *values); +GLAPI void APIENTRY glBindTransformFeedback (GLenum target, GLuint id); +GLAPI void APIENTRY glDeleteTransformFeedbacks (GLsizei n, const GLuint *ids); +GLAPI void APIENTRY glGenTransformFeedbacks (GLsizei n, GLuint *ids); +GLAPI GLboolean APIENTRY glIsTransformFeedback (GLuint id); +GLAPI void APIENTRY glPauseTransformFeedback (void); +GLAPI void APIENTRY glResumeTransformFeedback (void); +GLAPI void APIENTRY glDrawTransformFeedback (GLenum mode, GLuint id); +GLAPI void APIENTRY glDrawTransformFeedbackStream (GLenum mode, GLuint id, GLuint stream); +GLAPI void APIENTRY glBeginQueryIndexed (GLenum target, GLuint index, GLuint id); +GLAPI void APIENTRY glEndQueryIndexed (GLenum target, GLuint index); +GLAPI void APIENTRY glGetQueryIndexediv (GLenum target, GLuint index, GLenum pname, GLint *params); +#endif +#endif /* GL_VERSION_4_0 */ + +#ifndef GL_VERSION_4_1 +#define GL_VERSION_4_1 1 +#define GL_FIXED 0x140C +#define GL_IMPLEMENTATION_COLOR_READ_TYPE 0x8B9A +#define GL_IMPLEMENTATION_COLOR_READ_FORMAT 0x8B9B +#define GL_LOW_FLOAT 0x8DF0 +#define GL_MEDIUM_FLOAT 0x8DF1 +#define GL_HIGH_FLOAT 0x8DF2 +#define GL_LOW_INT 0x8DF3 +#define GL_MEDIUM_INT 0x8DF4 +#define GL_HIGH_INT 0x8DF5 +#define GL_SHADER_COMPILER 0x8DFA +#define GL_SHADER_BINARY_FORMATS 0x8DF8 +#define GL_NUM_SHADER_BINARY_FORMATS 0x8DF9 +#define GL_MAX_VERTEX_UNIFORM_VECTORS 0x8DFB +#define GL_MAX_VARYING_VECTORS 0x8DFC +#define GL_MAX_FRAGMENT_UNIFORM_VECTORS 0x8DFD +#define GL_RGB565 0x8D62 +#define GL_PROGRAM_BINARY_RETRIEVABLE_HINT 0x8257 +#define GL_PROGRAM_BINARY_LENGTH 0x8741 +#define GL_NUM_PROGRAM_BINARY_FORMATS 0x87FE +#define GL_PROGRAM_BINARY_FORMATS 0x87FF +#define GL_VERTEX_SHADER_BIT 0x00000001 +#define GL_FRAGMENT_SHADER_BIT 0x00000002 +#define GL_GEOMETRY_SHADER_BIT 0x00000004 +#define GL_TESS_CONTROL_SHADER_BIT 0x00000008 +#define GL_TESS_EVALUATION_SHADER_BIT 0x00000010 +#define GL_ALL_SHADER_BITS 0xFFFFFFFF +#define GL_PROGRAM_SEPARABLE 0x8258 +#define GL_ACTIVE_PROGRAM 0x8259 +#define GL_PROGRAM_PIPELINE_BINDING 0x825A +#define GL_MAX_VIEWPORTS 0x825B +#define GL_VIEWPORT_SUBPIXEL_BITS 0x825C +#define GL_VIEWPORT_BOUNDS_RANGE 0x825D +#define GL_LAYER_PROVOKING_VERTEX 0x825E +#define GL_VIEWPORT_INDEX_PROVOKING_VERTEX 0x825F +#define GL_UNDEFINED_VERTEX 0x8260 +typedef void (APIENTRYP PFNGLRELEASESHADERCOMPILERPROC) (void); +typedef void (APIENTRYP PFNGLSHADERBINARYPROC) (GLsizei count, const GLuint *shaders, GLenum binaryformat, const void *binary, GLsizei length); +typedef void (APIENTRYP PFNGLGETSHADERPRECISIONFORMATPROC) (GLenum shadertype, GLenum precisiontype, GLint *range, GLint *precision); +typedef void (APIENTRYP PFNGLDEPTHRANGEFPROC) (GLfloat n, GLfloat f); +typedef void (APIENTRYP PFNGLCLEARDEPTHFPROC) (GLfloat d); +typedef void (APIENTRYP PFNGLGETPROGRAMBINARYPROC) (GLuint program, GLsizei bufSize, GLsizei *length, GLenum *binaryFormat, void *binary); +typedef void (APIENTRYP PFNGLPROGRAMBINARYPROC) (GLuint program, GLenum binaryFormat, const void *binary, GLsizei length); +typedef void (APIENTRYP PFNGLPROGRAMPARAMETERIPROC) (GLuint program, GLenum pname, GLint value); +typedef void (APIENTRYP PFNGLUSEPROGRAMSTAGESPROC) (GLuint pipeline, GLbitfield stages, GLuint program); +typedef void (APIENTRYP PFNGLACTIVESHADERPROGRAMPROC) (GLuint pipeline, GLuint program); +typedef GLuint (APIENTRYP PFNGLCREATESHADERPROGRAMVPROC) (GLenum type, GLsizei count, const GLchar *const*strings); +typedef void (APIENTRYP PFNGLBINDPROGRAMPIPELINEPROC) (GLuint pipeline); +typedef void (APIENTRYP PFNGLDELETEPROGRAMPIPELINESPROC) (GLsizei n, const GLuint *pipelines); +typedef void (APIENTRYP PFNGLGENPROGRAMPIPELINESPROC) (GLsizei n, GLuint *pipelines); +typedef GLboolean (APIENTRYP PFNGLISPROGRAMPIPELINEPROC) (GLuint pipeline); +typedef void (APIENTRYP PFNGLGETPROGRAMPIPELINEIVPROC) (GLuint pipeline, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1IPROC) (GLuint program, GLint location, GLint v0); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1IVPROC) (GLuint program, GLint location, GLsizei count, const GLint *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1FPROC) (GLuint program, GLint location, GLfloat v0); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1FVPROC) (GLuint program, GLint location, GLsizei count, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1DPROC) (GLuint program, GLint location, GLdouble v0); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1DVPROC) (GLuint program, GLint location, GLsizei count, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1UIPROC) (GLuint program, GLint location, GLuint v0); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1UIVPROC) (GLuint program, GLint location, GLsizei count, const GLuint *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2IPROC) (GLuint program, GLint location, GLint v0, GLint v1); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2IVPROC) (GLuint program, GLint location, GLsizei count, const GLint *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2FPROC) (GLuint program, GLint location, GLfloat v0, GLfloat v1); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2FVPROC) (GLuint program, GLint location, GLsizei count, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2DPROC) (GLuint program, GLint location, GLdouble v0, GLdouble v1); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2DVPROC) (GLuint program, GLint location, GLsizei count, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2UIPROC) (GLuint program, GLint location, GLuint v0, GLuint v1); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2UIVPROC) (GLuint program, GLint location, GLsizei count, const GLuint *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3IPROC) (GLuint program, GLint location, GLint v0, GLint v1, GLint v2); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3IVPROC) (GLuint program, GLint location, GLsizei count, const GLint *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3FPROC) (GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3FVPROC) (GLuint program, GLint location, GLsizei count, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3DPROC) (GLuint program, GLint location, GLdouble v0, GLdouble v1, GLdouble v2); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3DVPROC) (GLuint program, GLint location, GLsizei count, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3UIPROC) (GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3UIVPROC) (GLuint program, GLint location, GLsizei count, const GLuint *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4IPROC) (GLuint program, GLint location, GLint v0, GLint v1, GLint v2, GLint v3); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4IVPROC) (GLuint program, GLint location, GLsizei count, const GLint *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4FPROC) (GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4FVPROC) (GLuint program, GLint location, GLsizei count, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4DPROC) (GLuint program, GLint location, GLdouble v0, GLdouble v1, GLdouble v2, GLdouble v3); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4DVPROC) (GLuint program, GLint location, GLsizei count, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4UIPROC) (GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4UIVPROC) (GLuint program, GLint location, GLsizei count, const GLuint *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2FVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3FVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4FVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2DVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3DVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4DVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2X3FVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3X2FVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2X4FVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4X2FVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3X4FVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4X3FVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2X3DVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3X2DVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2X4DVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4X2DVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3X4DVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4X3DVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLVALIDATEPROGRAMPIPELINEPROC) (GLuint pipeline); +typedef void (APIENTRYP PFNGLGETPROGRAMPIPELINEINFOLOGPROC) (GLuint pipeline, GLsizei bufSize, GLsizei *length, GLchar *infoLog); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL1DPROC) (GLuint index, GLdouble x); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL2DPROC) (GLuint index, GLdouble x, GLdouble y); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL3DPROC) (GLuint index, GLdouble x, GLdouble y, GLdouble z); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL4DPROC) (GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL1DVPROC) (GLuint index, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL2DVPROC) (GLuint index, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL3DVPROC) (GLuint index, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL4DVPROC) (GLuint index, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBLPOINTERPROC) (GLuint index, GLint size, GLenum type, GLsizei stride, const void *pointer); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBLDVPROC) (GLuint index, GLenum pname, GLdouble *params); +typedef void (APIENTRYP PFNGLVIEWPORTARRAYVPROC) (GLuint first, GLsizei count, const GLfloat *v); +typedef void (APIENTRYP PFNGLVIEWPORTINDEXEDFPROC) (GLuint index, GLfloat x, GLfloat y, GLfloat w, GLfloat h); +typedef void (APIENTRYP PFNGLVIEWPORTINDEXEDFVPROC) (GLuint index, const GLfloat *v); +typedef void (APIENTRYP PFNGLSCISSORARRAYVPROC) (GLuint first, GLsizei count, const GLint *v); +typedef void (APIENTRYP PFNGLSCISSORINDEXEDPROC) (GLuint index, GLint left, GLint bottom, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLSCISSORINDEXEDVPROC) (GLuint index, const GLint *v); +typedef void (APIENTRYP PFNGLDEPTHRANGEARRAYVPROC) (GLuint first, GLsizei count, const GLdouble *v); +typedef void (APIENTRYP PFNGLDEPTHRANGEINDEXEDPROC) (GLuint index, GLdouble n, GLdouble f); +typedef void (APIENTRYP PFNGLGETFLOATI_VPROC) (GLenum target, GLuint index, GLfloat *data); +typedef void (APIENTRYP PFNGLGETDOUBLEI_VPROC) (GLenum target, GLuint index, GLdouble *data); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glReleaseShaderCompiler (void); +GLAPI void APIENTRY glShaderBinary (GLsizei count, const GLuint *shaders, GLenum binaryformat, const void *binary, GLsizei length); +GLAPI void APIENTRY glGetShaderPrecisionFormat (GLenum shadertype, GLenum precisiontype, GLint *range, GLint *precision); +GLAPI void APIENTRY glDepthRangef (GLfloat n, GLfloat f); +GLAPI void APIENTRY glClearDepthf (GLfloat d); +GLAPI void APIENTRY glGetProgramBinary (GLuint program, GLsizei bufSize, GLsizei *length, GLenum *binaryFormat, void *binary); +GLAPI void APIENTRY glProgramBinary (GLuint program, GLenum binaryFormat, const void *binary, GLsizei length); +GLAPI void APIENTRY glProgramParameteri (GLuint program, GLenum pname, GLint value); +GLAPI void APIENTRY glUseProgramStages (GLuint pipeline, GLbitfield stages, GLuint program); +GLAPI void APIENTRY glActiveShaderProgram (GLuint pipeline, GLuint program); +GLAPI GLuint APIENTRY glCreateShaderProgramv (GLenum type, GLsizei count, const GLchar *const*strings); +GLAPI void APIENTRY glBindProgramPipeline (GLuint pipeline); +GLAPI void APIENTRY glDeleteProgramPipelines (GLsizei n, const GLuint *pipelines); +GLAPI void APIENTRY glGenProgramPipelines (GLsizei n, GLuint *pipelines); +GLAPI GLboolean APIENTRY glIsProgramPipeline (GLuint pipeline); +GLAPI void APIENTRY glGetProgramPipelineiv (GLuint pipeline, GLenum pname, GLint *params); +GLAPI void APIENTRY glProgramUniform1i (GLuint program, GLint location, GLint v0); +GLAPI void APIENTRY glProgramUniform1iv (GLuint program, GLint location, GLsizei count, const GLint *value); +GLAPI void APIENTRY glProgramUniform1f (GLuint program, GLint location, GLfloat v0); +GLAPI void APIENTRY glProgramUniform1fv (GLuint program, GLint location, GLsizei count, const GLfloat *value); +GLAPI void APIENTRY glProgramUniform1d (GLuint program, GLint location, GLdouble v0); +GLAPI void APIENTRY glProgramUniform1dv (GLuint program, GLint location, GLsizei count, const GLdouble *value); +GLAPI void APIENTRY glProgramUniform1ui (GLuint program, GLint location, GLuint v0); +GLAPI void APIENTRY glProgramUniform1uiv (GLuint program, GLint location, GLsizei count, const GLuint *value); +GLAPI void APIENTRY glProgramUniform2i (GLuint program, GLint location, GLint v0, GLint v1); +GLAPI void APIENTRY glProgramUniform2iv (GLuint program, GLint location, GLsizei count, const GLint *value); +GLAPI void APIENTRY glProgramUniform2f (GLuint program, GLint location, GLfloat v0, GLfloat v1); +GLAPI void APIENTRY glProgramUniform2fv (GLuint program, GLint location, GLsizei count, const GLfloat *value); +GLAPI void APIENTRY glProgramUniform2d (GLuint program, GLint location, GLdouble v0, GLdouble v1); +GLAPI void APIENTRY glProgramUniform2dv (GLuint program, GLint location, GLsizei count, const GLdouble *value); +GLAPI void APIENTRY glProgramUniform2ui (GLuint program, GLint location, GLuint v0, GLuint v1); +GLAPI void APIENTRY glProgramUniform2uiv (GLuint program, GLint location, GLsizei count, const GLuint *value); +GLAPI void APIENTRY glProgramUniform3i (GLuint program, GLint location, GLint v0, GLint v1, GLint v2); +GLAPI void APIENTRY glProgramUniform3iv (GLuint program, GLint location, GLsizei count, const GLint *value); +GLAPI void APIENTRY glProgramUniform3f (GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2); +GLAPI void APIENTRY glProgramUniform3fv (GLuint program, GLint location, GLsizei count, const GLfloat *value); +GLAPI void APIENTRY glProgramUniform3d (GLuint program, GLint location, GLdouble v0, GLdouble v1, GLdouble v2); +GLAPI void APIENTRY glProgramUniform3dv (GLuint program, GLint location, GLsizei count, const GLdouble *value); +GLAPI void APIENTRY glProgramUniform3ui (GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2); +GLAPI void APIENTRY glProgramUniform3uiv (GLuint program, GLint location, GLsizei count, const GLuint *value); +GLAPI void APIENTRY glProgramUniform4i (GLuint program, GLint location, GLint v0, GLint v1, GLint v2, GLint v3); +GLAPI void APIENTRY glProgramUniform4iv (GLuint program, GLint location, GLsizei count, const GLint *value); +GLAPI void APIENTRY glProgramUniform4f (GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); +GLAPI void APIENTRY glProgramUniform4fv (GLuint program, GLint location, GLsizei count, const GLfloat *value); +GLAPI void APIENTRY glProgramUniform4d (GLuint program, GLint location, GLdouble v0, GLdouble v1, GLdouble v2, GLdouble v3); +GLAPI void APIENTRY glProgramUniform4dv (GLuint program, GLint location, GLsizei count, const GLdouble *value); +GLAPI void APIENTRY glProgramUniform4ui (GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3); +GLAPI void APIENTRY glProgramUniform4uiv (GLuint program, GLint location, GLsizei count, const GLuint *value); +GLAPI void APIENTRY glProgramUniformMatrix2fv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glProgramUniformMatrix3fv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glProgramUniformMatrix4fv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glProgramUniformMatrix2dv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glProgramUniformMatrix3dv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glProgramUniformMatrix4dv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glProgramUniformMatrix2x3fv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glProgramUniformMatrix3x2fv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glProgramUniformMatrix2x4fv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glProgramUniformMatrix4x2fv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glProgramUniformMatrix3x4fv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glProgramUniformMatrix4x3fv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glProgramUniformMatrix2x3dv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glProgramUniformMatrix3x2dv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glProgramUniformMatrix2x4dv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glProgramUniformMatrix4x2dv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glProgramUniformMatrix3x4dv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glProgramUniformMatrix4x3dv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glValidateProgramPipeline (GLuint pipeline); +GLAPI void APIENTRY glGetProgramPipelineInfoLog (GLuint pipeline, GLsizei bufSize, GLsizei *length, GLchar *infoLog); +GLAPI void APIENTRY glVertexAttribL1d (GLuint index, GLdouble x); +GLAPI void APIENTRY glVertexAttribL2d (GLuint index, GLdouble x, GLdouble y); +GLAPI void APIENTRY glVertexAttribL3d (GLuint index, GLdouble x, GLdouble y, GLdouble z); +GLAPI void APIENTRY glVertexAttribL4d (GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +GLAPI void APIENTRY glVertexAttribL1dv (GLuint index, const GLdouble *v); +GLAPI void APIENTRY glVertexAttribL2dv (GLuint index, const GLdouble *v); +GLAPI void APIENTRY glVertexAttribL3dv (GLuint index, const GLdouble *v); +GLAPI void APIENTRY glVertexAttribL4dv (GLuint index, const GLdouble *v); +GLAPI void APIENTRY glVertexAttribLPointer (GLuint index, GLint size, GLenum type, GLsizei stride, const void *pointer); +GLAPI void APIENTRY glGetVertexAttribLdv (GLuint index, GLenum pname, GLdouble *params); +GLAPI void APIENTRY glViewportArrayv (GLuint first, GLsizei count, const GLfloat *v); +GLAPI void APIENTRY glViewportIndexedf (GLuint index, GLfloat x, GLfloat y, GLfloat w, GLfloat h); +GLAPI void APIENTRY glViewportIndexedfv (GLuint index, const GLfloat *v); +GLAPI void APIENTRY glScissorArrayv (GLuint first, GLsizei count, const GLint *v); +GLAPI void APIENTRY glScissorIndexed (GLuint index, GLint left, GLint bottom, GLsizei width, GLsizei height); +GLAPI void APIENTRY glScissorIndexedv (GLuint index, const GLint *v); +GLAPI void APIENTRY glDepthRangeArrayv (GLuint first, GLsizei count, const GLdouble *v); +GLAPI void APIENTRY glDepthRangeIndexed (GLuint index, GLdouble n, GLdouble f); +GLAPI void APIENTRY glGetFloati_v (GLenum target, GLuint index, GLfloat *data); +GLAPI void APIENTRY glGetDoublei_v (GLenum target, GLuint index, GLdouble *data); +#endif +#endif /* GL_VERSION_4_1 */ + +#ifndef GL_VERSION_4_2 +#define GL_VERSION_4_2 1 +#define GL_COPY_READ_BUFFER_BINDING 0x8F36 +#define GL_COPY_WRITE_BUFFER_BINDING 0x8F37 +#define GL_TRANSFORM_FEEDBACK_ACTIVE 0x8E24 +#define GL_TRANSFORM_FEEDBACK_PAUSED 0x8E23 +#define GL_UNPACK_COMPRESSED_BLOCK_WIDTH 0x9127 +#define GL_UNPACK_COMPRESSED_BLOCK_HEIGHT 0x9128 +#define GL_UNPACK_COMPRESSED_BLOCK_DEPTH 0x9129 +#define GL_UNPACK_COMPRESSED_BLOCK_SIZE 0x912A +#define GL_PACK_COMPRESSED_BLOCK_WIDTH 0x912B +#define GL_PACK_COMPRESSED_BLOCK_HEIGHT 0x912C +#define GL_PACK_COMPRESSED_BLOCK_DEPTH 0x912D +#define GL_PACK_COMPRESSED_BLOCK_SIZE 0x912E +#define GL_NUM_SAMPLE_COUNTS 0x9380 +#define GL_MIN_MAP_BUFFER_ALIGNMENT 0x90BC +#define GL_ATOMIC_COUNTER_BUFFER 0x92C0 +#define GL_ATOMIC_COUNTER_BUFFER_BINDING 0x92C1 +#define GL_ATOMIC_COUNTER_BUFFER_START 0x92C2 +#define GL_ATOMIC_COUNTER_BUFFER_SIZE 0x92C3 +#define GL_ATOMIC_COUNTER_BUFFER_DATA_SIZE 0x92C4 +#define GL_ATOMIC_COUNTER_BUFFER_ACTIVE_ATOMIC_COUNTERS 0x92C5 +#define GL_ATOMIC_COUNTER_BUFFER_ACTIVE_ATOMIC_COUNTER_INDICES 0x92C6 +#define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_VERTEX_SHADER 0x92C7 +#define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_TESS_CONTROL_SHADER 0x92C8 +#define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_TESS_EVALUATION_SHADER 0x92C9 +#define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_GEOMETRY_SHADER 0x92CA +#define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_FRAGMENT_SHADER 0x92CB +#define GL_MAX_VERTEX_ATOMIC_COUNTER_BUFFERS 0x92CC +#define GL_MAX_TESS_CONTROL_ATOMIC_COUNTER_BUFFERS 0x92CD +#define GL_MAX_TESS_EVALUATION_ATOMIC_COUNTER_BUFFERS 0x92CE +#define GL_MAX_GEOMETRY_ATOMIC_COUNTER_BUFFERS 0x92CF +#define GL_MAX_FRAGMENT_ATOMIC_COUNTER_BUFFERS 0x92D0 +#define GL_MAX_COMBINED_ATOMIC_COUNTER_BUFFERS 0x92D1 +#define GL_MAX_VERTEX_ATOMIC_COUNTERS 0x92D2 +#define GL_MAX_TESS_CONTROL_ATOMIC_COUNTERS 0x92D3 +#define GL_MAX_TESS_EVALUATION_ATOMIC_COUNTERS 0x92D4 +#define GL_MAX_GEOMETRY_ATOMIC_COUNTERS 0x92D5 +#define GL_MAX_FRAGMENT_ATOMIC_COUNTERS 0x92D6 +#define GL_MAX_COMBINED_ATOMIC_COUNTERS 0x92D7 +#define GL_MAX_ATOMIC_COUNTER_BUFFER_SIZE 0x92D8 +#define GL_MAX_ATOMIC_COUNTER_BUFFER_BINDINGS 0x92DC +#define GL_ACTIVE_ATOMIC_COUNTER_BUFFERS 0x92D9 +#define GL_UNIFORM_ATOMIC_COUNTER_BUFFER_INDEX 0x92DA +#define GL_UNSIGNED_INT_ATOMIC_COUNTER 0x92DB +#define GL_VERTEX_ATTRIB_ARRAY_BARRIER_BIT 0x00000001 +#define GL_ELEMENT_ARRAY_BARRIER_BIT 0x00000002 +#define GL_UNIFORM_BARRIER_BIT 0x00000004 +#define GL_TEXTURE_FETCH_BARRIER_BIT 0x00000008 +#define GL_SHADER_IMAGE_ACCESS_BARRIER_BIT 0x00000020 +#define GL_COMMAND_BARRIER_BIT 0x00000040 +#define GL_PIXEL_BUFFER_BARRIER_BIT 0x00000080 +#define GL_TEXTURE_UPDATE_BARRIER_BIT 0x00000100 +#define GL_BUFFER_UPDATE_BARRIER_BIT 0x00000200 +#define GL_FRAMEBUFFER_BARRIER_BIT 0x00000400 +#define GL_TRANSFORM_FEEDBACK_BARRIER_BIT 0x00000800 +#define GL_ATOMIC_COUNTER_BARRIER_BIT 0x00001000 +#define GL_ALL_BARRIER_BITS 0xFFFFFFFF +#define GL_MAX_IMAGE_UNITS 0x8F38 +#define GL_MAX_COMBINED_IMAGE_UNITS_AND_FRAGMENT_OUTPUTS 0x8F39 +#define GL_IMAGE_BINDING_NAME 0x8F3A +#define GL_IMAGE_BINDING_LEVEL 0x8F3B +#define GL_IMAGE_BINDING_LAYERED 0x8F3C +#define GL_IMAGE_BINDING_LAYER 0x8F3D +#define GL_IMAGE_BINDING_ACCESS 0x8F3E +#define GL_IMAGE_1D 0x904C +#define GL_IMAGE_2D 0x904D +#define GL_IMAGE_3D 0x904E +#define GL_IMAGE_2D_RECT 0x904F +#define GL_IMAGE_CUBE 0x9050 +#define GL_IMAGE_BUFFER 0x9051 +#define GL_IMAGE_1D_ARRAY 0x9052 +#define GL_IMAGE_2D_ARRAY 0x9053 +#define GL_IMAGE_CUBE_MAP_ARRAY 0x9054 +#define GL_IMAGE_2D_MULTISAMPLE 0x9055 +#define GL_IMAGE_2D_MULTISAMPLE_ARRAY 0x9056 +#define GL_INT_IMAGE_1D 0x9057 +#define GL_INT_IMAGE_2D 0x9058 +#define GL_INT_IMAGE_3D 0x9059 +#define GL_INT_IMAGE_2D_RECT 0x905A +#define GL_INT_IMAGE_CUBE 0x905B +#define GL_INT_IMAGE_BUFFER 0x905C +#define GL_INT_IMAGE_1D_ARRAY 0x905D +#define GL_INT_IMAGE_2D_ARRAY 0x905E +#define GL_INT_IMAGE_CUBE_MAP_ARRAY 0x905F +#define GL_INT_IMAGE_2D_MULTISAMPLE 0x9060 +#define GL_INT_IMAGE_2D_MULTISAMPLE_ARRAY 0x9061 +#define GL_UNSIGNED_INT_IMAGE_1D 0x9062 +#define GL_UNSIGNED_INT_IMAGE_2D 0x9063 +#define GL_UNSIGNED_INT_IMAGE_3D 0x9064 +#define GL_UNSIGNED_INT_IMAGE_2D_RECT 0x9065 +#define GL_UNSIGNED_INT_IMAGE_CUBE 0x9066 +#define GL_UNSIGNED_INT_IMAGE_BUFFER 0x9067 +#define GL_UNSIGNED_INT_IMAGE_1D_ARRAY 0x9068 +#define GL_UNSIGNED_INT_IMAGE_2D_ARRAY 0x9069 +#define GL_UNSIGNED_INT_IMAGE_CUBE_MAP_ARRAY 0x906A +#define GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE 0x906B +#define GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE_ARRAY 0x906C +#define GL_MAX_IMAGE_SAMPLES 0x906D +#define GL_IMAGE_BINDING_FORMAT 0x906E +#define GL_IMAGE_FORMAT_COMPATIBILITY_TYPE 0x90C7 +#define GL_IMAGE_FORMAT_COMPATIBILITY_BY_SIZE 0x90C8 +#define GL_IMAGE_FORMAT_COMPATIBILITY_BY_CLASS 0x90C9 +#define GL_MAX_VERTEX_IMAGE_UNIFORMS 0x90CA +#define GL_MAX_TESS_CONTROL_IMAGE_UNIFORMS 0x90CB +#define GL_MAX_TESS_EVALUATION_IMAGE_UNIFORMS 0x90CC +#define GL_MAX_GEOMETRY_IMAGE_UNIFORMS 0x90CD +#define GL_MAX_FRAGMENT_IMAGE_UNIFORMS 0x90CE +#define GL_MAX_COMBINED_IMAGE_UNIFORMS 0x90CF +#define GL_COMPRESSED_RGBA_BPTC_UNORM 0x8E8C +#define GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM 0x8E8D +#define GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT 0x8E8E +#define GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT 0x8E8F +#define GL_TEXTURE_IMMUTABLE_FORMAT 0x912F +typedef void (APIENTRYP PFNGLDRAWARRAYSINSTANCEDBASEINSTANCEPROC) (GLenum mode, GLint first, GLsizei count, GLsizei instancecount, GLuint baseinstance); +typedef void (APIENTRYP PFNGLDRAWELEMENTSINSTANCEDBASEINSTANCEPROC) (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount, GLuint baseinstance); +typedef void (APIENTRYP PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXBASEINSTANCEPROC) (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount, GLint basevertex, GLuint baseinstance); +typedef void (APIENTRYP PFNGLGETINTERNALFORMATIVPROC) (GLenum target, GLenum internalformat, GLenum pname, GLsizei bufSize, GLint *params); +typedef void (APIENTRYP PFNGLGETACTIVEATOMICCOUNTERBUFFERIVPROC) (GLuint program, GLuint bufferIndex, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLBINDIMAGETEXTUREPROC) (GLuint unit, GLuint texture, GLint level, GLboolean layered, GLint layer, GLenum access, GLenum format); +typedef void (APIENTRYP PFNGLMEMORYBARRIERPROC) (GLbitfield barriers); +typedef void (APIENTRYP PFNGLTEXSTORAGE1DPROC) (GLenum target, GLsizei levels, GLenum internalformat, GLsizei width); +typedef void (APIENTRYP PFNGLTEXSTORAGE2DPROC) (GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLTEXSTORAGE3DPROC) (GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth); +typedef void (APIENTRYP PFNGLDRAWTRANSFORMFEEDBACKINSTANCEDPROC) (GLenum mode, GLuint id, GLsizei instancecount); +typedef void (APIENTRYP PFNGLDRAWTRANSFORMFEEDBACKSTREAMINSTANCEDPROC) (GLenum mode, GLuint id, GLuint stream, GLsizei instancecount); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glDrawArraysInstancedBaseInstance (GLenum mode, GLint first, GLsizei count, GLsizei instancecount, GLuint baseinstance); +GLAPI void APIENTRY glDrawElementsInstancedBaseInstance (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount, GLuint baseinstance); +GLAPI void APIENTRY glDrawElementsInstancedBaseVertexBaseInstance (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount, GLint basevertex, GLuint baseinstance); +GLAPI void APIENTRY glGetInternalformativ (GLenum target, GLenum internalformat, GLenum pname, GLsizei bufSize, GLint *params); +GLAPI void APIENTRY glGetActiveAtomicCounterBufferiv (GLuint program, GLuint bufferIndex, GLenum pname, GLint *params); +GLAPI void APIENTRY glBindImageTexture (GLuint unit, GLuint texture, GLint level, GLboolean layered, GLint layer, GLenum access, GLenum format); +GLAPI void APIENTRY glMemoryBarrier (GLbitfield barriers); +GLAPI void APIENTRY glTexStorage1D (GLenum target, GLsizei levels, GLenum internalformat, GLsizei width); +GLAPI void APIENTRY glTexStorage2D (GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height); +GLAPI void APIENTRY glTexStorage3D (GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth); +GLAPI void APIENTRY glDrawTransformFeedbackInstanced (GLenum mode, GLuint id, GLsizei instancecount); +GLAPI void APIENTRY glDrawTransformFeedbackStreamInstanced (GLenum mode, GLuint id, GLuint stream, GLsizei instancecount); +#endif +#endif /* GL_VERSION_4_2 */ + +#ifndef GL_VERSION_4_3 +#define GL_VERSION_4_3 1 +typedef void (APIENTRY *GLDEBUGPROC)(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar *message,const void *userParam); +#define GL_NUM_SHADING_LANGUAGE_VERSIONS 0x82E9 +#define GL_VERTEX_ATTRIB_ARRAY_LONG 0x874E +#define GL_COMPRESSED_RGB8_ETC2 0x9274 +#define GL_COMPRESSED_SRGB8_ETC2 0x9275 +#define GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2 0x9276 +#define GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2 0x9277 +#define GL_COMPRESSED_RGBA8_ETC2_EAC 0x9278 +#define GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC 0x9279 +#define GL_COMPRESSED_R11_EAC 0x9270 +#define GL_COMPRESSED_SIGNED_R11_EAC 0x9271 +#define GL_COMPRESSED_RG11_EAC 0x9272 +#define GL_COMPRESSED_SIGNED_RG11_EAC 0x9273 +#define GL_PRIMITIVE_RESTART_FIXED_INDEX 0x8D69 +#define GL_ANY_SAMPLES_PASSED_CONSERVATIVE 0x8D6A +#define GL_MAX_ELEMENT_INDEX 0x8D6B +#define GL_COMPUTE_SHADER 0x91B9 +#define GL_MAX_COMPUTE_UNIFORM_BLOCKS 0x91BB +#define GL_MAX_COMPUTE_TEXTURE_IMAGE_UNITS 0x91BC +#define GL_MAX_COMPUTE_IMAGE_UNIFORMS 0x91BD +#define GL_MAX_COMPUTE_SHARED_MEMORY_SIZE 0x8262 +#define GL_MAX_COMPUTE_UNIFORM_COMPONENTS 0x8263 +#define GL_MAX_COMPUTE_ATOMIC_COUNTER_BUFFERS 0x8264 +#define GL_MAX_COMPUTE_ATOMIC_COUNTERS 0x8265 +#define GL_MAX_COMBINED_COMPUTE_UNIFORM_COMPONENTS 0x8266 +#define GL_MAX_COMPUTE_WORK_GROUP_INVOCATIONS 0x90EB +#define GL_MAX_COMPUTE_WORK_GROUP_COUNT 0x91BE +#define GL_MAX_COMPUTE_WORK_GROUP_SIZE 0x91BF +#define GL_COMPUTE_WORK_GROUP_SIZE 0x8267 +#define GL_UNIFORM_BLOCK_REFERENCED_BY_COMPUTE_SHADER 0x90EC +#define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_COMPUTE_SHADER 0x90ED +#define GL_DISPATCH_INDIRECT_BUFFER 0x90EE +#define GL_DISPATCH_INDIRECT_BUFFER_BINDING 0x90EF +#define GL_COMPUTE_SHADER_BIT 0x00000020 +#define GL_DEBUG_OUTPUT_SYNCHRONOUS 0x8242 +#define GL_DEBUG_NEXT_LOGGED_MESSAGE_LENGTH 0x8243 +#define GL_DEBUG_CALLBACK_FUNCTION 0x8244 +#define GL_DEBUG_CALLBACK_USER_PARAM 0x8245 +#define GL_DEBUG_SOURCE_API 0x8246 +#define GL_DEBUG_SOURCE_WINDOW_SYSTEM 0x8247 +#define GL_DEBUG_SOURCE_SHADER_COMPILER 0x8248 +#define GL_DEBUG_SOURCE_THIRD_PARTY 0x8249 +#define GL_DEBUG_SOURCE_APPLICATION 0x824A +#define GL_DEBUG_SOURCE_OTHER 0x824B +#define GL_DEBUG_TYPE_ERROR 0x824C +#define GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR 0x824D +#define GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR 0x824E +#define GL_DEBUG_TYPE_PORTABILITY 0x824F +#define GL_DEBUG_TYPE_PERFORMANCE 0x8250 +#define GL_DEBUG_TYPE_OTHER 0x8251 +#define GL_MAX_DEBUG_MESSAGE_LENGTH 0x9143 +#define GL_MAX_DEBUG_LOGGED_MESSAGES 0x9144 +#define GL_DEBUG_LOGGED_MESSAGES 0x9145 +#define GL_DEBUG_SEVERITY_HIGH 0x9146 +#define GL_DEBUG_SEVERITY_MEDIUM 0x9147 +#define GL_DEBUG_SEVERITY_LOW 0x9148 +#define GL_DEBUG_TYPE_MARKER 0x8268 +#define GL_DEBUG_TYPE_PUSH_GROUP 0x8269 +#define GL_DEBUG_TYPE_POP_GROUP 0x826A +#define GL_DEBUG_SEVERITY_NOTIFICATION 0x826B +#define GL_MAX_DEBUG_GROUP_STACK_DEPTH 0x826C +#define GL_DEBUG_GROUP_STACK_DEPTH 0x826D +#define GL_BUFFER 0x82E0 +#define GL_SHADER 0x82E1 +#define GL_PROGRAM 0x82E2 +#define GL_QUERY 0x82E3 +#define GL_PROGRAM_PIPELINE 0x82E4 +#define GL_SAMPLER 0x82E6 +#define GL_MAX_LABEL_LENGTH 0x82E8 +#define GL_DEBUG_OUTPUT 0x92E0 +#define GL_CONTEXT_FLAG_DEBUG_BIT 0x00000002 +#define GL_MAX_UNIFORM_LOCATIONS 0x826E +#define GL_FRAMEBUFFER_DEFAULT_WIDTH 0x9310 +#define GL_FRAMEBUFFER_DEFAULT_HEIGHT 0x9311 +#define GL_FRAMEBUFFER_DEFAULT_LAYERS 0x9312 +#define GL_FRAMEBUFFER_DEFAULT_SAMPLES 0x9313 +#define GL_FRAMEBUFFER_DEFAULT_FIXED_SAMPLE_LOCATIONS 0x9314 +#define GL_MAX_FRAMEBUFFER_WIDTH 0x9315 +#define GL_MAX_FRAMEBUFFER_HEIGHT 0x9316 +#define GL_MAX_FRAMEBUFFER_LAYERS 0x9317 +#define GL_MAX_FRAMEBUFFER_SAMPLES 0x9318 +#define GL_INTERNALFORMAT_SUPPORTED 0x826F +#define GL_INTERNALFORMAT_PREFERRED 0x8270 +#define GL_INTERNALFORMAT_RED_SIZE 0x8271 +#define GL_INTERNALFORMAT_GREEN_SIZE 0x8272 +#define GL_INTERNALFORMAT_BLUE_SIZE 0x8273 +#define GL_INTERNALFORMAT_ALPHA_SIZE 0x8274 +#define GL_INTERNALFORMAT_DEPTH_SIZE 0x8275 +#define GL_INTERNALFORMAT_STENCIL_SIZE 0x8276 +#define GL_INTERNALFORMAT_SHARED_SIZE 0x8277 +#define GL_INTERNALFORMAT_RED_TYPE 0x8278 +#define GL_INTERNALFORMAT_GREEN_TYPE 0x8279 +#define GL_INTERNALFORMAT_BLUE_TYPE 0x827A +#define GL_INTERNALFORMAT_ALPHA_TYPE 0x827B +#define GL_INTERNALFORMAT_DEPTH_TYPE 0x827C +#define GL_INTERNALFORMAT_STENCIL_TYPE 0x827D +#define GL_MAX_WIDTH 0x827E +#define GL_MAX_HEIGHT 0x827F +#define GL_MAX_DEPTH 0x8280 +#define GL_MAX_LAYERS 0x8281 +#define GL_MAX_COMBINED_DIMENSIONS 0x8282 +#define GL_COLOR_COMPONENTS 0x8283 +#define GL_DEPTH_COMPONENTS 0x8284 +#define GL_STENCIL_COMPONENTS 0x8285 +#define GL_COLOR_RENDERABLE 0x8286 +#define GL_DEPTH_RENDERABLE 0x8287 +#define GL_STENCIL_RENDERABLE 0x8288 +#define GL_FRAMEBUFFER_RENDERABLE 0x8289 +#define GL_FRAMEBUFFER_RENDERABLE_LAYERED 0x828A +#define GL_FRAMEBUFFER_BLEND 0x828B +#define GL_READ_PIXELS 0x828C +#define GL_READ_PIXELS_FORMAT 0x828D +#define GL_READ_PIXELS_TYPE 0x828E +#define GL_TEXTURE_IMAGE_FORMAT 0x828F +#define GL_TEXTURE_IMAGE_TYPE 0x8290 +#define GL_GET_TEXTURE_IMAGE_FORMAT 0x8291 +#define GL_GET_TEXTURE_IMAGE_TYPE 0x8292 +#define GL_MIPMAP 0x8293 +#define GL_MANUAL_GENERATE_MIPMAP 0x8294 +#define GL_AUTO_GENERATE_MIPMAP 0x8295 +#define GL_COLOR_ENCODING 0x8296 +#define GL_SRGB_READ 0x8297 +#define GL_SRGB_WRITE 0x8298 +#define GL_FILTER 0x829A +#define GL_VERTEX_TEXTURE 0x829B +#define GL_TESS_CONTROL_TEXTURE 0x829C +#define GL_TESS_EVALUATION_TEXTURE 0x829D +#define GL_GEOMETRY_TEXTURE 0x829E +#define GL_FRAGMENT_TEXTURE 0x829F +#define GL_COMPUTE_TEXTURE 0x82A0 +#define GL_TEXTURE_SHADOW 0x82A1 +#define GL_TEXTURE_GATHER 0x82A2 +#define GL_TEXTURE_GATHER_SHADOW 0x82A3 +#define GL_SHADER_IMAGE_LOAD 0x82A4 +#define GL_SHADER_IMAGE_STORE 0x82A5 +#define GL_SHADER_IMAGE_ATOMIC 0x82A6 +#define GL_IMAGE_TEXEL_SIZE 0x82A7 +#define GL_IMAGE_COMPATIBILITY_CLASS 0x82A8 +#define GL_IMAGE_PIXEL_FORMAT 0x82A9 +#define GL_IMAGE_PIXEL_TYPE 0x82AA +#define GL_SIMULTANEOUS_TEXTURE_AND_DEPTH_TEST 0x82AC +#define GL_SIMULTANEOUS_TEXTURE_AND_STENCIL_TEST 0x82AD +#define GL_SIMULTANEOUS_TEXTURE_AND_DEPTH_WRITE 0x82AE +#define GL_SIMULTANEOUS_TEXTURE_AND_STENCIL_WRITE 0x82AF +#define GL_TEXTURE_COMPRESSED_BLOCK_WIDTH 0x82B1 +#define GL_TEXTURE_COMPRESSED_BLOCK_HEIGHT 0x82B2 +#define GL_TEXTURE_COMPRESSED_BLOCK_SIZE 0x82B3 +#define GL_CLEAR_BUFFER 0x82B4 +#define GL_TEXTURE_VIEW 0x82B5 +#define GL_VIEW_COMPATIBILITY_CLASS 0x82B6 +#define GL_FULL_SUPPORT 0x82B7 +#define GL_CAVEAT_SUPPORT 0x82B8 +#define GL_IMAGE_CLASS_4_X_32 0x82B9 +#define GL_IMAGE_CLASS_2_X_32 0x82BA +#define GL_IMAGE_CLASS_1_X_32 0x82BB +#define GL_IMAGE_CLASS_4_X_16 0x82BC +#define GL_IMAGE_CLASS_2_X_16 0x82BD +#define GL_IMAGE_CLASS_1_X_16 0x82BE +#define GL_IMAGE_CLASS_4_X_8 0x82BF +#define GL_IMAGE_CLASS_2_X_8 0x82C0 +#define GL_IMAGE_CLASS_1_X_8 0x82C1 +#define GL_IMAGE_CLASS_11_11_10 0x82C2 +#define GL_IMAGE_CLASS_10_10_10_2 0x82C3 +#define GL_VIEW_CLASS_128_BITS 0x82C4 +#define GL_VIEW_CLASS_96_BITS 0x82C5 +#define GL_VIEW_CLASS_64_BITS 0x82C6 +#define GL_VIEW_CLASS_48_BITS 0x82C7 +#define GL_VIEW_CLASS_32_BITS 0x82C8 +#define GL_VIEW_CLASS_24_BITS 0x82C9 +#define GL_VIEW_CLASS_16_BITS 0x82CA +#define GL_VIEW_CLASS_8_BITS 0x82CB +#define GL_VIEW_CLASS_S3TC_DXT1_RGB 0x82CC +#define GL_VIEW_CLASS_S3TC_DXT1_RGBA 0x82CD +#define GL_VIEW_CLASS_S3TC_DXT3_RGBA 0x82CE +#define GL_VIEW_CLASS_S3TC_DXT5_RGBA 0x82CF +#define GL_VIEW_CLASS_RGTC1_RED 0x82D0 +#define GL_VIEW_CLASS_RGTC2_RG 0x82D1 +#define GL_VIEW_CLASS_BPTC_UNORM 0x82D2 +#define GL_VIEW_CLASS_BPTC_FLOAT 0x82D3 +#define GL_UNIFORM 0x92E1 +#define GL_UNIFORM_BLOCK 0x92E2 +#define GL_PROGRAM_INPUT 0x92E3 +#define GL_PROGRAM_OUTPUT 0x92E4 +#define GL_BUFFER_VARIABLE 0x92E5 +#define GL_SHADER_STORAGE_BLOCK 0x92E6 +#define GL_VERTEX_SUBROUTINE 0x92E8 +#define GL_TESS_CONTROL_SUBROUTINE 0x92E9 +#define GL_TESS_EVALUATION_SUBROUTINE 0x92EA +#define GL_GEOMETRY_SUBROUTINE 0x92EB +#define GL_FRAGMENT_SUBROUTINE 0x92EC +#define GL_COMPUTE_SUBROUTINE 0x92ED +#define GL_VERTEX_SUBROUTINE_UNIFORM 0x92EE +#define GL_TESS_CONTROL_SUBROUTINE_UNIFORM 0x92EF +#define GL_TESS_EVALUATION_SUBROUTINE_UNIFORM 0x92F0 +#define GL_GEOMETRY_SUBROUTINE_UNIFORM 0x92F1 +#define GL_FRAGMENT_SUBROUTINE_UNIFORM 0x92F2 +#define GL_COMPUTE_SUBROUTINE_UNIFORM 0x92F3 +#define GL_TRANSFORM_FEEDBACK_VARYING 0x92F4 +#define GL_ACTIVE_RESOURCES 0x92F5 +#define GL_MAX_NAME_LENGTH 0x92F6 +#define GL_MAX_NUM_ACTIVE_VARIABLES 0x92F7 +#define GL_MAX_NUM_COMPATIBLE_SUBROUTINES 0x92F8 +#define GL_NAME_LENGTH 0x92F9 +#define GL_TYPE 0x92FA +#define GL_ARRAY_SIZE 0x92FB +#define GL_OFFSET 0x92FC +#define GL_BLOCK_INDEX 0x92FD +#define GL_ARRAY_STRIDE 0x92FE +#define GL_MATRIX_STRIDE 0x92FF +#define GL_IS_ROW_MAJOR 0x9300 +#define GL_ATOMIC_COUNTER_BUFFER_INDEX 0x9301 +#define GL_BUFFER_BINDING 0x9302 +#define GL_BUFFER_DATA_SIZE 0x9303 +#define GL_NUM_ACTIVE_VARIABLES 0x9304 +#define GL_ACTIVE_VARIABLES 0x9305 +#define GL_REFERENCED_BY_VERTEX_SHADER 0x9306 +#define GL_REFERENCED_BY_TESS_CONTROL_SHADER 0x9307 +#define GL_REFERENCED_BY_TESS_EVALUATION_SHADER 0x9308 +#define GL_REFERENCED_BY_GEOMETRY_SHADER 0x9309 +#define GL_REFERENCED_BY_FRAGMENT_SHADER 0x930A +#define GL_REFERENCED_BY_COMPUTE_SHADER 0x930B +#define GL_TOP_LEVEL_ARRAY_SIZE 0x930C +#define GL_TOP_LEVEL_ARRAY_STRIDE 0x930D +#define GL_LOCATION 0x930E +#define GL_LOCATION_INDEX 0x930F +#define GL_IS_PER_PATCH 0x92E7 +#define GL_SHADER_STORAGE_BUFFER 0x90D2 +#define GL_SHADER_STORAGE_BUFFER_BINDING 0x90D3 +#define GL_SHADER_STORAGE_BUFFER_START 0x90D4 +#define GL_SHADER_STORAGE_BUFFER_SIZE 0x90D5 +#define GL_MAX_VERTEX_SHADER_STORAGE_BLOCKS 0x90D6 +#define GL_MAX_GEOMETRY_SHADER_STORAGE_BLOCKS 0x90D7 +#define GL_MAX_TESS_CONTROL_SHADER_STORAGE_BLOCKS 0x90D8 +#define GL_MAX_TESS_EVALUATION_SHADER_STORAGE_BLOCKS 0x90D9 +#define GL_MAX_FRAGMENT_SHADER_STORAGE_BLOCKS 0x90DA +#define GL_MAX_COMPUTE_SHADER_STORAGE_BLOCKS 0x90DB +#define GL_MAX_COMBINED_SHADER_STORAGE_BLOCKS 0x90DC +#define GL_MAX_SHADER_STORAGE_BUFFER_BINDINGS 0x90DD +#define GL_MAX_SHADER_STORAGE_BLOCK_SIZE 0x90DE +#define GL_SHADER_STORAGE_BUFFER_OFFSET_ALIGNMENT 0x90DF +#define GL_SHADER_STORAGE_BARRIER_BIT 0x00002000 +#define GL_MAX_COMBINED_SHADER_OUTPUT_RESOURCES 0x8F39 +#define GL_DEPTH_STENCIL_TEXTURE_MODE 0x90EA +#define GL_TEXTURE_BUFFER_OFFSET 0x919D +#define GL_TEXTURE_BUFFER_SIZE 0x919E +#define GL_TEXTURE_BUFFER_OFFSET_ALIGNMENT 0x919F +#define GL_TEXTURE_VIEW_MIN_LEVEL 0x82DB +#define GL_TEXTURE_VIEW_NUM_LEVELS 0x82DC +#define GL_TEXTURE_VIEW_MIN_LAYER 0x82DD +#define GL_TEXTURE_VIEW_NUM_LAYERS 0x82DE +#define GL_TEXTURE_IMMUTABLE_LEVELS 0x82DF +#define GL_VERTEX_ATTRIB_BINDING 0x82D4 +#define GL_VERTEX_ATTRIB_RELATIVE_OFFSET 0x82D5 +#define GL_VERTEX_BINDING_DIVISOR 0x82D6 +#define GL_VERTEX_BINDING_OFFSET 0x82D7 +#define GL_VERTEX_BINDING_STRIDE 0x82D8 +#define GL_MAX_VERTEX_ATTRIB_RELATIVE_OFFSET 0x82D9 +#define GL_MAX_VERTEX_ATTRIB_BINDINGS 0x82DA +#define GL_VERTEX_BINDING_BUFFER 0x8F4F +#define GL_DISPLAY_LIST 0x82E7 +typedef void (APIENTRYP PFNGLCLEARBUFFERDATAPROC) (GLenum target, GLenum internalformat, GLenum format, GLenum type, const void *data); +typedef void (APIENTRYP PFNGLCLEARBUFFERSUBDATAPROC) (GLenum target, GLenum internalformat, GLintptr offset, GLsizeiptr size, GLenum format, GLenum type, const void *data); +typedef void (APIENTRYP PFNGLDISPATCHCOMPUTEPROC) (GLuint num_groups_x, GLuint num_groups_y, GLuint num_groups_z); +typedef void (APIENTRYP PFNGLDISPATCHCOMPUTEINDIRECTPROC) (GLintptr indirect); +typedef void (APIENTRYP PFNGLCOPYIMAGESUBDATAPROC) (GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srcY, GLint srcZ, GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei srcWidth, GLsizei srcHeight, GLsizei srcDepth); +typedef void (APIENTRYP PFNGLFRAMEBUFFERPARAMETERIPROC) (GLenum target, GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLGETFRAMEBUFFERPARAMETERIVPROC) (GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETINTERNALFORMATI64VPROC) (GLenum target, GLenum internalformat, GLenum pname, GLsizei bufSize, GLint64 *params); +typedef void (APIENTRYP PFNGLINVALIDATETEXSUBIMAGEPROC) (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth); +typedef void (APIENTRYP PFNGLINVALIDATETEXIMAGEPROC) (GLuint texture, GLint level); +typedef void (APIENTRYP PFNGLINVALIDATEBUFFERSUBDATAPROC) (GLuint buffer, GLintptr offset, GLsizeiptr length); +typedef void (APIENTRYP PFNGLINVALIDATEBUFFERDATAPROC) (GLuint buffer); +typedef void (APIENTRYP PFNGLINVALIDATEFRAMEBUFFERPROC) (GLenum target, GLsizei numAttachments, const GLenum *attachments); +typedef void (APIENTRYP PFNGLINVALIDATESUBFRAMEBUFFERPROC) (GLenum target, GLsizei numAttachments, const GLenum *attachments, GLint x, GLint y, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLMULTIDRAWARRAYSINDIRECTPROC) (GLenum mode, const void *indirect, GLsizei drawcount, GLsizei stride); +typedef void (APIENTRYP PFNGLMULTIDRAWELEMENTSINDIRECTPROC) (GLenum mode, GLenum type, const void *indirect, GLsizei drawcount, GLsizei stride); +typedef void (APIENTRYP PFNGLGETPROGRAMINTERFACEIVPROC) (GLuint program, GLenum programInterface, GLenum pname, GLint *params); +typedef GLuint (APIENTRYP PFNGLGETPROGRAMRESOURCEINDEXPROC) (GLuint program, GLenum programInterface, const GLchar *name); +typedef void (APIENTRYP PFNGLGETPROGRAMRESOURCENAMEPROC) (GLuint program, GLenum programInterface, GLuint index, GLsizei bufSize, GLsizei *length, GLchar *name); +typedef void (APIENTRYP PFNGLGETPROGRAMRESOURCEIVPROC) (GLuint program, GLenum programInterface, GLuint index, GLsizei propCount, const GLenum *props, GLsizei bufSize, GLsizei *length, GLint *params); +typedef GLint (APIENTRYP PFNGLGETPROGRAMRESOURCELOCATIONPROC) (GLuint program, GLenum programInterface, const GLchar *name); +typedef GLint (APIENTRYP PFNGLGETPROGRAMRESOURCELOCATIONINDEXPROC) (GLuint program, GLenum programInterface, const GLchar *name); +typedef void (APIENTRYP PFNGLSHADERSTORAGEBLOCKBINDINGPROC) (GLuint program, GLuint storageBlockIndex, GLuint storageBlockBinding); +typedef void (APIENTRYP PFNGLTEXBUFFERRANGEPROC) (GLenum target, GLenum internalformat, GLuint buffer, GLintptr offset, GLsizeiptr size); +typedef void (APIENTRYP PFNGLTEXSTORAGE2DMULTISAMPLEPROC) (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations); +typedef void (APIENTRYP PFNGLTEXSTORAGE3DMULTISAMPLEPROC) (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations); +typedef void (APIENTRYP PFNGLTEXTUREVIEWPROC) (GLuint texture, GLenum target, GLuint origtexture, GLenum internalformat, GLuint minlevel, GLuint numlevels, GLuint minlayer, GLuint numlayers); +typedef void (APIENTRYP PFNGLBINDVERTEXBUFFERPROC) (GLuint bindingindex, GLuint buffer, GLintptr offset, GLsizei stride); +typedef void (APIENTRYP PFNGLVERTEXATTRIBFORMATPROC) (GLuint attribindex, GLint size, GLenum type, GLboolean normalized, GLuint relativeoffset); +typedef void (APIENTRYP PFNGLVERTEXATTRIBIFORMATPROC) (GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset); +typedef void (APIENTRYP PFNGLVERTEXATTRIBLFORMATPROC) (GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset); +typedef void (APIENTRYP PFNGLVERTEXATTRIBBINDINGPROC) (GLuint attribindex, GLuint bindingindex); +typedef void (APIENTRYP PFNGLVERTEXBINDINGDIVISORPROC) (GLuint bindingindex, GLuint divisor); +typedef void (APIENTRYP PFNGLDEBUGMESSAGECONTROLPROC) (GLenum source, GLenum type, GLenum severity, GLsizei count, const GLuint *ids, GLboolean enabled); +typedef void (APIENTRYP PFNGLDEBUGMESSAGEINSERTPROC) (GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *buf); +typedef void (APIENTRYP PFNGLDEBUGMESSAGECALLBACKPROC) (GLDEBUGPROC callback, const void *userParam); +typedef GLuint (APIENTRYP PFNGLGETDEBUGMESSAGELOGPROC) (GLuint count, GLsizei bufSize, GLenum *sources, GLenum *types, GLuint *ids, GLenum *severities, GLsizei *lengths, GLchar *messageLog); +typedef void (APIENTRYP PFNGLPUSHDEBUGGROUPPROC) (GLenum source, GLuint id, GLsizei length, const GLchar *message); +typedef void (APIENTRYP PFNGLPOPDEBUGGROUPPROC) (void); +typedef void (APIENTRYP PFNGLOBJECTLABELPROC) (GLenum identifier, GLuint name, GLsizei length, const GLchar *label); +typedef void (APIENTRYP PFNGLGETOBJECTLABELPROC) (GLenum identifier, GLuint name, GLsizei bufSize, GLsizei *length, GLchar *label); +typedef void (APIENTRYP PFNGLOBJECTPTRLABELPROC) (const void *ptr, GLsizei length, const GLchar *label); +typedef void (APIENTRYP PFNGLGETOBJECTPTRLABELPROC) (const void *ptr, GLsizei bufSize, GLsizei *length, GLchar *label); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glClearBufferData (GLenum target, GLenum internalformat, GLenum format, GLenum type, const void *data); +GLAPI void APIENTRY glClearBufferSubData (GLenum target, GLenum internalformat, GLintptr offset, GLsizeiptr size, GLenum format, GLenum type, const void *data); +GLAPI void APIENTRY glDispatchCompute (GLuint num_groups_x, GLuint num_groups_y, GLuint num_groups_z); +GLAPI void APIENTRY glDispatchComputeIndirect (GLintptr indirect); +GLAPI void APIENTRY glCopyImageSubData (GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srcY, GLint srcZ, GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei srcWidth, GLsizei srcHeight, GLsizei srcDepth); +GLAPI void APIENTRY glFramebufferParameteri (GLenum target, GLenum pname, GLint param); +GLAPI void APIENTRY glGetFramebufferParameteriv (GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetInternalformati64v (GLenum target, GLenum internalformat, GLenum pname, GLsizei bufSize, GLint64 *params); +GLAPI void APIENTRY glInvalidateTexSubImage (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth); +GLAPI void APIENTRY glInvalidateTexImage (GLuint texture, GLint level); +GLAPI void APIENTRY glInvalidateBufferSubData (GLuint buffer, GLintptr offset, GLsizeiptr length); +GLAPI void APIENTRY glInvalidateBufferData (GLuint buffer); +GLAPI void APIENTRY glInvalidateFramebuffer (GLenum target, GLsizei numAttachments, const GLenum *attachments); +GLAPI void APIENTRY glInvalidateSubFramebuffer (GLenum target, GLsizei numAttachments, const GLenum *attachments, GLint x, GLint y, GLsizei width, GLsizei height); +GLAPI void APIENTRY glMultiDrawArraysIndirect (GLenum mode, const void *indirect, GLsizei drawcount, GLsizei stride); +GLAPI void APIENTRY glMultiDrawElementsIndirect (GLenum mode, GLenum type, const void *indirect, GLsizei drawcount, GLsizei stride); +GLAPI void APIENTRY glGetProgramInterfaceiv (GLuint program, GLenum programInterface, GLenum pname, GLint *params); +GLAPI GLuint APIENTRY glGetProgramResourceIndex (GLuint program, GLenum programInterface, const GLchar *name); +GLAPI void APIENTRY glGetProgramResourceName (GLuint program, GLenum programInterface, GLuint index, GLsizei bufSize, GLsizei *length, GLchar *name); +GLAPI void APIENTRY glGetProgramResourceiv (GLuint program, GLenum programInterface, GLuint index, GLsizei propCount, const GLenum *props, GLsizei bufSize, GLsizei *length, GLint *params); +GLAPI GLint APIENTRY glGetProgramResourceLocation (GLuint program, GLenum programInterface, const GLchar *name); +GLAPI GLint APIENTRY glGetProgramResourceLocationIndex (GLuint program, GLenum programInterface, const GLchar *name); +GLAPI void APIENTRY glShaderStorageBlockBinding (GLuint program, GLuint storageBlockIndex, GLuint storageBlockBinding); +GLAPI void APIENTRY glTexBufferRange (GLenum target, GLenum internalformat, GLuint buffer, GLintptr offset, GLsizeiptr size); +GLAPI void APIENTRY glTexStorage2DMultisample (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations); +GLAPI void APIENTRY glTexStorage3DMultisample (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations); +GLAPI void APIENTRY glTextureView (GLuint texture, GLenum target, GLuint origtexture, GLenum internalformat, GLuint minlevel, GLuint numlevels, GLuint minlayer, GLuint numlayers); +GLAPI void APIENTRY glBindVertexBuffer (GLuint bindingindex, GLuint buffer, GLintptr offset, GLsizei stride); +GLAPI void APIENTRY glVertexAttribFormat (GLuint attribindex, GLint size, GLenum type, GLboolean normalized, GLuint relativeoffset); +GLAPI void APIENTRY glVertexAttribIFormat (GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset); +GLAPI void APIENTRY glVertexAttribLFormat (GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset); +GLAPI void APIENTRY glVertexAttribBinding (GLuint attribindex, GLuint bindingindex); +GLAPI void APIENTRY glVertexBindingDivisor (GLuint bindingindex, GLuint divisor); +GLAPI void APIENTRY glDebugMessageControl (GLenum source, GLenum type, GLenum severity, GLsizei count, const GLuint *ids, GLboolean enabled); +GLAPI void APIENTRY glDebugMessageInsert (GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *buf); +GLAPI void APIENTRY glDebugMessageCallback (GLDEBUGPROC callback, const void *userParam); +GLAPI GLuint APIENTRY glGetDebugMessageLog (GLuint count, GLsizei bufSize, GLenum *sources, GLenum *types, GLuint *ids, GLenum *severities, GLsizei *lengths, GLchar *messageLog); +GLAPI void APIENTRY glPushDebugGroup (GLenum source, GLuint id, GLsizei length, const GLchar *message); +GLAPI void APIENTRY glPopDebugGroup (void); +GLAPI void APIENTRY glObjectLabel (GLenum identifier, GLuint name, GLsizei length, const GLchar *label); +GLAPI void APIENTRY glGetObjectLabel (GLenum identifier, GLuint name, GLsizei bufSize, GLsizei *length, GLchar *label); +GLAPI void APIENTRY glObjectPtrLabel (const void *ptr, GLsizei length, const GLchar *label); +GLAPI void APIENTRY glGetObjectPtrLabel (const void *ptr, GLsizei bufSize, GLsizei *length, GLchar *label); +#endif +#endif /* GL_VERSION_4_3 */ + +#ifndef GL_VERSION_4_4 +#define GL_VERSION_4_4 1 +#define GL_MAX_VERTEX_ATTRIB_STRIDE 0x82E5 +#define GL_PRIMITIVE_RESTART_FOR_PATCHES_SUPPORTED 0x8221 +#define GL_TEXTURE_BUFFER_BINDING 0x8C2A +#define GL_MAP_PERSISTENT_BIT 0x0040 +#define GL_MAP_COHERENT_BIT 0x0080 +#define GL_DYNAMIC_STORAGE_BIT 0x0100 +#define GL_CLIENT_STORAGE_BIT 0x0200 +#define GL_CLIENT_MAPPED_BUFFER_BARRIER_BIT 0x00004000 +#define GL_BUFFER_IMMUTABLE_STORAGE 0x821F +#define GL_BUFFER_STORAGE_FLAGS 0x8220 +#define GL_CLEAR_TEXTURE 0x9365 +#define GL_LOCATION_COMPONENT 0x934A +#define GL_TRANSFORM_FEEDBACK_BUFFER_INDEX 0x934B +#define GL_TRANSFORM_FEEDBACK_BUFFER_STRIDE 0x934C +#define GL_QUERY_BUFFER 0x9192 +#define GL_QUERY_BUFFER_BARRIER_BIT 0x00008000 +#define GL_QUERY_BUFFER_BINDING 0x9193 +#define GL_QUERY_RESULT_NO_WAIT 0x9194 +#define GL_MIRROR_CLAMP_TO_EDGE 0x8743 +typedef void (APIENTRYP PFNGLBUFFERSTORAGEPROC) (GLenum target, GLsizeiptr size, const void *data, GLbitfield flags); +typedef void (APIENTRYP PFNGLCLEARTEXIMAGEPROC) (GLuint texture, GLint level, GLenum format, GLenum type, const void *data); +typedef void (APIENTRYP PFNGLCLEARTEXSUBIMAGEPROC) (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *data); +typedef void (APIENTRYP PFNGLBINDBUFFERSBASEPROC) (GLenum target, GLuint first, GLsizei count, const GLuint *buffers); +typedef void (APIENTRYP PFNGLBINDBUFFERSRANGEPROC) (GLenum target, GLuint first, GLsizei count, const GLuint *buffers, const GLintptr *offsets, const GLsizeiptr *sizes); +typedef void (APIENTRYP PFNGLBINDTEXTURESPROC) (GLuint first, GLsizei count, const GLuint *textures); +typedef void (APIENTRYP PFNGLBINDSAMPLERSPROC) (GLuint first, GLsizei count, const GLuint *samplers); +typedef void (APIENTRYP PFNGLBINDIMAGETEXTURESPROC) (GLuint first, GLsizei count, const GLuint *textures); +typedef void (APIENTRYP PFNGLBINDVERTEXBUFFERSPROC) (GLuint first, GLsizei count, const GLuint *buffers, const GLintptr *offsets, const GLsizei *strides); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBufferStorage (GLenum target, GLsizeiptr size, const void *data, GLbitfield flags); +GLAPI void APIENTRY glClearTexImage (GLuint texture, GLint level, GLenum format, GLenum type, const void *data); +GLAPI void APIENTRY glClearTexSubImage (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *data); +GLAPI void APIENTRY glBindBuffersBase (GLenum target, GLuint first, GLsizei count, const GLuint *buffers); +GLAPI void APIENTRY glBindBuffersRange (GLenum target, GLuint first, GLsizei count, const GLuint *buffers, const GLintptr *offsets, const GLsizeiptr *sizes); +GLAPI void APIENTRY glBindTextures (GLuint first, GLsizei count, const GLuint *textures); +GLAPI void APIENTRY glBindSamplers (GLuint first, GLsizei count, const GLuint *samplers); +GLAPI void APIENTRY glBindImageTextures (GLuint first, GLsizei count, const GLuint *textures); +GLAPI void APIENTRY glBindVertexBuffers (GLuint first, GLsizei count, const GLuint *buffers, const GLintptr *offsets, const GLsizei *strides); +#endif +#endif /* GL_VERSION_4_4 */ + +#ifndef GL_VERSION_4_5 +#define GL_VERSION_4_5 1 +#define GL_CONTEXT_LOST 0x0507 +#define GL_NEGATIVE_ONE_TO_ONE 0x935E +#define GL_ZERO_TO_ONE 0x935F +#define GL_CLIP_ORIGIN 0x935C +#define GL_CLIP_DEPTH_MODE 0x935D +#define GL_QUERY_WAIT_INVERTED 0x8E17 +#define GL_QUERY_NO_WAIT_INVERTED 0x8E18 +#define GL_QUERY_BY_REGION_WAIT_INVERTED 0x8E19 +#define GL_QUERY_BY_REGION_NO_WAIT_INVERTED 0x8E1A +#define GL_MAX_CULL_DISTANCES 0x82F9 +#define GL_MAX_COMBINED_CLIP_AND_CULL_DISTANCES 0x82FA +#define GL_TEXTURE_TARGET 0x1006 +#define GL_QUERY_TARGET 0x82EA +#define GL_GUILTY_CONTEXT_RESET 0x8253 +#define GL_INNOCENT_CONTEXT_RESET 0x8254 +#define GL_UNKNOWN_CONTEXT_RESET 0x8255 +#define GL_RESET_NOTIFICATION_STRATEGY 0x8256 +#define GL_LOSE_CONTEXT_ON_RESET 0x8252 +#define GL_NO_RESET_NOTIFICATION 0x8261 +#define GL_CONTEXT_FLAG_ROBUST_ACCESS_BIT 0x00000004 +#define GL_CONTEXT_RELEASE_BEHAVIOR 0x82FB +#define GL_CONTEXT_RELEASE_BEHAVIOR_FLUSH 0x82FC +typedef void (APIENTRYP PFNGLCLIPCONTROLPROC) (GLenum origin, GLenum depth); +typedef void (APIENTRYP PFNGLCREATETRANSFORMFEEDBACKSPROC) (GLsizei n, GLuint *ids); +typedef void (APIENTRYP PFNGLTRANSFORMFEEDBACKBUFFERBASEPROC) (GLuint xfb, GLuint index, GLuint buffer); +typedef void (APIENTRYP PFNGLTRANSFORMFEEDBACKBUFFERRANGEPROC) (GLuint xfb, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size); +typedef void (APIENTRYP PFNGLGETTRANSFORMFEEDBACKIVPROC) (GLuint xfb, GLenum pname, GLint *param); +typedef void (APIENTRYP PFNGLGETTRANSFORMFEEDBACKI_VPROC) (GLuint xfb, GLenum pname, GLuint index, GLint *param); +typedef void (APIENTRYP PFNGLGETTRANSFORMFEEDBACKI64_VPROC) (GLuint xfb, GLenum pname, GLuint index, GLint64 *param); +typedef void (APIENTRYP PFNGLCREATEBUFFERSPROC) (GLsizei n, GLuint *buffers); +typedef void (APIENTRYP PFNGLNAMEDBUFFERSTORAGEPROC) (GLuint buffer, GLsizeiptr size, const void *data, GLbitfield flags); +typedef void (APIENTRYP PFNGLNAMEDBUFFERDATAPROC) (GLuint buffer, GLsizeiptr size, const void *data, GLenum usage); +typedef void (APIENTRYP PFNGLNAMEDBUFFERSUBDATAPROC) (GLuint buffer, GLintptr offset, GLsizeiptr size, const void *data); +typedef void (APIENTRYP PFNGLCOPYNAMEDBUFFERSUBDATAPROC) (GLuint readBuffer, GLuint writeBuffer, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size); +typedef void (APIENTRYP PFNGLCLEARNAMEDBUFFERDATAPROC) (GLuint buffer, GLenum internalformat, GLenum format, GLenum type, const void *data); +typedef void (APIENTRYP PFNGLCLEARNAMEDBUFFERSUBDATAPROC) (GLuint buffer, GLenum internalformat, GLintptr offset, GLsizeiptr size, GLenum format, GLenum type, const void *data); +typedef void *(APIENTRYP PFNGLMAPNAMEDBUFFERPROC) (GLuint buffer, GLenum access); +typedef void *(APIENTRYP PFNGLMAPNAMEDBUFFERRANGEPROC) (GLuint buffer, GLintptr offset, GLsizeiptr length, GLbitfield access); +typedef GLboolean (APIENTRYP PFNGLUNMAPNAMEDBUFFERPROC) (GLuint buffer); +typedef void (APIENTRYP PFNGLFLUSHMAPPEDNAMEDBUFFERRANGEPROC) (GLuint buffer, GLintptr offset, GLsizeiptr length); +typedef void (APIENTRYP PFNGLGETNAMEDBUFFERPARAMETERIVPROC) (GLuint buffer, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETNAMEDBUFFERPARAMETERI64VPROC) (GLuint buffer, GLenum pname, GLint64 *params); +typedef void (APIENTRYP PFNGLGETNAMEDBUFFERPOINTERVPROC) (GLuint buffer, GLenum pname, void **params); +typedef void (APIENTRYP PFNGLGETNAMEDBUFFERSUBDATAPROC) (GLuint buffer, GLintptr offset, GLsizeiptr size, void *data); +typedef void (APIENTRYP PFNGLCREATEFRAMEBUFFERSPROC) (GLsizei n, GLuint *framebuffers); +typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERRENDERBUFFERPROC) (GLuint framebuffer, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); +typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERPARAMETERIPROC) (GLuint framebuffer, GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERTEXTUREPROC) (GLuint framebuffer, GLenum attachment, GLuint texture, GLint level); +typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERTEXTURELAYERPROC) (GLuint framebuffer, GLenum attachment, GLuint texture, GLint level, GLint layer); +typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERDRAWBUFFERPROC) (GLuint framebuffer, GLenum buf); +typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERDRAWBUFFERSPROC) (GLuint framebuffer, GLsizei n, const GLenum *bufs); +typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERREADBUFFERPROC) (GLuint framebuffer, GLenum src); +typedef void (APIENTRYP PFNGLINVALIDATENAMEDFRAMEBUFFERDATAPROC) (GLuint framebuffer, GLsizei numAttachments, const GLenum *attachments); +typedef void (APIENTRYP PFNGLINVALIDATENAMEDFRAMEBUFFERSUBDATAPROC) (GLuint framebuffer, GLsizei numAttachments, const GLenum *attachments, GLint x, GLint y, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLCLEARNAMEDFRAMEBUFFERIVPROC) (GLuint framebuffer, GLenum buffer, GLint drawbuffer, const GLint *value); +typedef void (APIENTRYP PFNGLCLEARNAMEDFRAMEBUFFERUIVPROC) (GLuint framebuffer, GLenum buffer, GLint drawbuffer, const GLuint *value); +typedef void (APIENTRYP PFNGLCLEARNAMEDFRAMEBUFFERFVPROC) (GLuint framebuffer, GLenum buffer, GLint drawbuffer, const GLfloat *value); +typedef void (APIENTRYP PFNGLCLEARNAMEDFRAMEBUFFERFIPROC) (GLuint framebuffer, GLenum buffer, const GLfloat depth, GLint stencil); +typedef void (APIENTRYP PFNGLBLITNAMEDFRAMEBUFFERPROC) (GLuint readFramebuffer, GLuint drawFramebuffer, GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter); +typedef GLenum (APIENTRYP PFNGLCHECKNAMEDFRAMEBUFFERSTATUSPROC) (GLuint framebuffer, GLenum target); +typedef void (APIENTRYP PFNGLGETNAMEDFRAMEBUFFERPARAMETERIVPROC) (GLuint framebuffer, GLenum pname, GLint *param); +typedef void (APIENTRYP PFNGLGETNAMEDFRAMEBUFFERATTACHMENTPARAMETERIVPROC) (GLuint framebuffer, GLenum attachment, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLCREATERENDERBUFFERSPROC) (GLsizei n, GLuint *renderbuffers); +typedef void (APIENTRYP PFNGLNAMEDRENDERBUFFERSTORAGEPROC) (GLuint renderbuffer, GLenum internalformat, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLNAMEDRENDERBUFFERSTORAGEMULTISAMPLEPROC) (GLuint renderbuffer, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLGETNAMEDRENDERBUFFERPARAMETERIVPROC) (GLuint renderbuffer, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLCREATETEXTURESPROC) (GLenum target, GLsizei n, GLuint *textures); +typedef void (APIENTRYP PFNGLTEXTUREBUFFERPROC) (GLuint texture, GLenum internalformat, GLuint buffer); +typedef void (APIENTRYP PFNGLTEXTUREBUFFERRANGEPROC) (GLuint texture, GLenum internalformat, GLuint buffer, GLintptr offset, GLsizeiptr size); +typedef void (APIENTRYP PFNGLTEXTURESTORAGE1DPROC) (GLuint texture, GLsizei levels, GLenum internalformat, GLsizei width); +typedef void (APIENTRYP PFNGLTEXTURESTORAGE2DPROC) (GLuint texture, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLTEXTURESTORAGE3DPROC) (GLuint texture, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth); +typedef void (APIENTRYP PFNGLTEXTURESTORAGE2DMULTISAMPLEPROC) (GLuint texture, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations); +typedef void (APIENTRYP PFNGLTEXTURESTORAGE3DMULTISAMPLEPROC) (GLuint texture, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations); +typedef void (APIENTRYP PFNGLTEXTURESUBIMAGE1DPROC) (GLuint texture, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const void *pixels); +typedef void (APIENTRYP PFNGLTEXTURESUBIMAGE2DPROC) (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels); +typedef void (APIENTRYP PFNGLTEXTURESUBIMAGE3DPROC) (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *pixels); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXTURESUBIMAGE1DPROC) (GLuint texture, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const void *data); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXTURESUBIMAGE2DPROC) (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *data); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXTURESUBIMAGE3DPROC) (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *data); +typedef void (APIENTRYP PFNGLCOPYTEXTURESUBIMAGE1DPROC) (GLuint texture, GLint level, GLint xoffset, GLint x, GLint y, GLsizei width); +typedef void (APIENTRYP PFNGLCOPYTEXTURESUBIMAGE2DPROC) (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLCOPYTEXTURESUBIMAGE3DPROC) (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLTEXTUREPARAMETERFPROC) (GLuint texture, GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLTEXTUREPARAMETERFVPROC) (GLuint texture, GLenum pname, const GLfloat *param); +typedef void (APIENTRYP PFNGLTEXTUREPARAMETERIPROC) (GLuint texture, GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLTEXTUREPARAMETERIIVPROC) (GLuint texture, GLenum pname, const GLint *params); +typedef void (APIENTRYP PFNGLTEXTUREPARAMETERIUIVPROC) (GLuint texture, GLenum pname, const GLuint *params); +typedef void (APIENTRYP PFNGLTEXTUREPARAMETERIVPROC) (GLuint texture, GLenum pname, const GLint *param); +typedef void (APIENTRYP PFNGLGENERATETEXTUREMIPMAPPROC) (GLuint texture); +typedef void (APIENTRYP PFNGLBINDTEXTUREUNITPROC) (GLuint unit, GLuint texture); +typedef void (APIENTRYP PFNGLGETTEXTUREIMAGEPROC) (GLuint texture, GLint level, GLenum format, GLenum type, GLsizei bufSize, void *pixels); +typedef void (APIENTRYP PFNGLGETCOMPRESSEDTEXTUREIMAGEPROC) (GLuint texture, GLint level, GLsizei bufSize, void *pixels); +typedef void (APIENTRYP PFNGLGETTEXTURELEVELPARAMETERFVPROC) (GLuint texture, GLint level, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETTEXTURELEVELPARAMETERIVPROC) (GLuint texture, GLint level, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETTEXTUREPARAMETERFVPROC) (GLuint texture, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETTEXTUREPARAMETERIIVPROC) (GLuint texture, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETTEXTUREPARAMETERIUIVPROC) (GLuint texture, GLenum pname, GLuint *params); +typedef void (APIENTRYP PFNGLGETTEXTUREPARAMETERIVPROC) (GLuint texture, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLCREATEVERTEXARRAYSPROC) (GLsizei n, GLuint *arrays); +typedef void (APIENTRYP PFNGLDISABLEVERTEXARRAYATTRIBPROC) (GLuint vaobj, GLuint index); +typedef void (APIENTRYP PFNGLENABLEVERTEXARRAYATTRIBPROC) (GLuint vaobj, GLuint index); +typedef void (APIENTRYP PFNGLVERTEXARRAYELEMENTBUFFERPROC) (GLuint vaobj, GLuint buffer); +typedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXBUFFERPROC) (GLuint vaobj, GLuint bindingindex, GLuint buffer, GLintptr offset, GLsizei stride); +typedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXBUFFERSPROC) (GLuint vaobj, GLuint first, GLsizei count, const GLuint *buffers, const GLintptr *offsets, const GLsizei *strides); +typedef void (APIENTRYP PFNGLVERTEXARRAYATTRIBBINDINGPROC) (GLuint vaobj, GLuint attribindex, GLuint bindingindex); +typedef void (APIENTRYP PFNGLVERTEXARRAYATTRIBFORMATPROC) (GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLboolean normalized, GLuint relativeoffset); +typedef void (APIENTRYP PFNGLVERTEXARRAYATTRIBIFORMATPROC) (GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset); +typedef void (APIENTRYP PFNGLVERTEXARRAYATTRIBLFORMATPROC) (GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset); +typedef void (APIENTRYP PFNGLVERTEXARRAYBINDINGDIVISORPROC) (GLuint vaobj, GLuint bindingindex, GLuint divisor); +typedef void (APIENTRYP PFNGLGETVERTEXARRAYIVPROC) (GLuint vaobj, GLenum pname, GLint *param); +typedef void (APIENTRYP PFNGLGETVERTEXARRAYINDEXEDIVPROC) (GLuint vaobj, GLuint index, GLenum pname, GLint *param); +typedef void (APIENTRYP PFNGLGETVERTEXARRAYINDEXED64IVPROC) (GLuint vaobj, GLuint index, GLenum pname, GLint64 *param); +typedef void (APIENTRYP PFNGLCREATESAMPLERSPROC) (GLsizei n, GLuint *samplers); +typedef void (APIENTRYP PFNGLCREATEPROGRAMPIPELINESPROC) (GLsizei n, GLuint *pipelines); +typedef void (APIENTRYP PFNGLCREATEQUERIESPROC) (GLenum target, GLsizei n, GLuint *ids); +typedef void (APIENTRYP PFNGLGETQUERYBUFFEROBJECTI64VPROC) (GLuint id, GLuint buffer, GLenum pname, GLintptr offset); +typedef void (APIENTRYP PFNGLGETQUERYBUFFEROBJECTIVPROC) (GLuint id, GLuint buffer, GLenum pname, GLintptr offset); +typedef void (APIENTRYP PFNGLGETQUERYBUFFEROBJECTUI64VPROC) (GLuint id, GLuint buffer, GLenum pname, GLintptr offset); +typedef void (APIENTRYP PFNGLGETQUERYBUFFEROBJECTUIVPROC) (GLuint id, GLuint buffer, GLenum pname, GLintptr offset); +typedef void (APIENTRYP PFNGLMEMORYBARRIERBYREGIONPROC) (GLbitfield barriers); +typedef void (APIENTRYP PFNGLGETTEXTURESUBIMAGEPROC) (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, GLsizei bufSize, void *pixels); +typedef void (APIENTRYP PFNGLGETCOMPRESSEDTEXTURESUBIMAGEPROC) (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLsizei bufSize, void *pixels); +typedef GLenum (APIENTRYP PFNGLGETGRAPHICSRESETSTATUSPROC) (void); +typedef void (APIENTRYP PFNGLGETNCOMPRESSEDTEXIMAGEPROC) (GLenum target, GLint lod, GLsizei bufSize, void *pixels); +typedef void (APIENTRYP PFNGLGETNTEXIMAGEPROC) (GLenum target, GLint level, GLenum format, GLenum type, GLsizei bufSize, void *pixels); +typedef void (APIENTRYP PFNGLGETNUNIFORMDVPROC) (GLuint program, GLint location, GLsizei bufSize, GLdouble *params); +typedef void (APIENTRYP PFNGLGETNUNIFORMFVPROC) (GLuint program, GLint location, GLsizei bufSize, GLfloat *params); +typedef void (APIENTRYP PFNGLGETNUNIFORMIVPROC) (GLuint program, GLint location, GLsizei bufSize, GLint *params); +typedef void (APIENTRYP PFNGLGETNUNIFORMUIVPROC) (GLuint program, GLint location, GLsizei bufSize, GLuint *params); +typedef void (APIENTRYP PFNGLREADNPIXELSPROC) (GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLsizei bufSize, void *data); +typedef void (APIENTRYP PFNGLGETNMAPDVPROC) (GLenum target, GLenum query, GLsizei bufSize, GLdouble *v); +typedef void (APIENTRYP PFNGLGETNMAPFVPROC) (GLenum target, GLenum query, GLsizei bufSize, GLfloat *v); +typedef void (APIENTRYP PFNGLGETNMAPIVPROC) (GLenum target, GLenum query, GLsizei bufSize, GLint *v); +typedef void (APIENTRYP PFNGLGETNPIXELMAPFVPROC) (GLenum map, GLsizei bufSize, GLfloat *values); +typedef void (APIENTRYP PFNGLGETNPIXELMAPUIVPROC) (GLenum map, GLsizei bufSize, GLuint *values); +typedef void (APIENTRYP PFNGLGETNPIXELMAPUSVPROC) (GLenum map, GLsizei bufSize, GLushort *values); +typedef void (APIENTRYP PFNGLGETNPOLYGONSTIPPLEPROC) (GLsizei bufSize, GLubyte *pattern); +typedef void (APIENTRYP PFNGLGETNCOLORTABLEPROC) (GLenum target, GLenum format, GLenum type, GLsizei bufSize, void *table); +typedef void (APIENTRYP PFNGLGETNCONVOLUTIONFILTERPROC) (GLenum target, GLenum format, GLenum type, GLsizei bufSize, void *image); +typedef void (APIENTRYP PFNGLGETNSEPARABLEFILTERPROC) (GLenum target, GLenum format, GLenum type, GLsizei rowBufSize, void *row, GLsizei columnBufSize, void *column, void *span); +typedef void (APIENTRYP PFNGLGETNHISTOGRAMPROC) (GLenum target, GLboolean reset, GLenum format, GLenum type, GLsizei bufSize, void *values); +typedef void (APIENTRYP PFNGLGETNMINMAXPROC) (GLenum target, GLboolean reset, GLenum format, GLenum type, GLsizei bufSize, void *values); +typedef void (APIENTRYP PFNGLTEXTUREBARRIERPROC) (void); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glClipControl (GLenum origin, GLenum depth); +GLAPI void APIENTRY glCreateTransformFeedbacks (GLsizei n, GLuint *ids); +GLAPI void APIENTRY glTransformFeedbackBufferBase (GLuint xfb, GLuint index, GLuint buffer); +GLAPI void APIENTRY glTransformFeedbackBufferRange (GLuint xfb, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size); +GLAPI void APIENTRY glGetTransformFeedbackiv (GLuint xfb, GLenum pname, GLint *param); +GLAPI void APIENTRY glGetTransformFeedbacki_v (GLuint xfb, GLenum pname, GLuint index, GLint *param); +GLAPI void APIENTRY glGetTransformFeedbacki64_v (GLuint xfb, GLenum pname, GLuint index, GLint64 *param); +GLAPI void APIENTRY glCreateBuffers (GLsizei n, GLuint *buffers); +GLAPI void APIENTRY glNamedBufferStorage (GLuint buffer, GLsizeiptr size, const void *data, GLbitfield flags); +GLAPI void APIENTRY glNamedBufferData (GLuint buffer, GLsizeiptr size, const void *data, GLenum usage); +GLAPI void APIENTRY glNamedBufferSubData (GLuint buffer, GLintptr offset, GLsizeiptr size, const void *data); +GLAPI void APIENTRY glCopyNamedBufferSubData (GLuint readBuffer, GLuint writeBuffer, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size); +GLAPI void APIENTRY glClearNamedBufferData (GLuint buffer, GLenum internalformat, GLenum format, GLenum type, const void *data); +GLAPI void APIENTRY glClearNamedBufferSubData (GLuint buffer, GLenum internalformat, GLintptr offset, GLsizeiptr size, GLenum format, GLenum type, const void *data); +GLAPI void *APIENTRY glMapNamedBuffer (GLuint buffer, GLenum access); +GLAPI void *APIENTRY glMapNamedBufferRange (GLuint buffer, GLintptr offset, GLsizeiptr length, GLbitfield access); +GLAPI GLboolean APIENTRY glUnmapNamedBuffer (GLuint buffer); +GLAPI void APIENTRY glFlushMappedNamedBufferRange (GLuint buffer, GLintptr offset, GLsizeiptr length); +GLAPI void APIENTRY glGetNamedBufferParameteriv (GLuint buffer, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetNamedBufferParameteri64v (GLuint buffer, GLenum pname, GLint64 *params); +GLAPI void APIENTRY glGetNamedBufferPointerv (GLuint buffer, GLenum pname, void **params); +GLAPI void APIENTRY glGetNamedBufferSubData (GLuint buffer, GLintptr offset, GLsizeiptr size, void *data); +GLAPI void APIENTRY glCreateFramebuffers (GLsizei n, GLuint *framebuffers); +GLAPI void APIENTRY glNamedFramebufferRenderbuffer (GLuint framebuffer, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); +GLAPI void APIENTRY glNamedFramebufferParameteri (GLuint framebuffer, GLenum pname, GLint param); +GLAPI void APIENTRY glNamedFramebufferTexture (GLuint framebuffer, GLenum attachment, GLuint texture, GLint level); +GLAPI void APIENTRY glNamedFramebufferTextureLayer (GLuint framebuffer, GLenum attachment, GLuint texture, GLint level, GLint layer); +GLAPI void APIENTRY glNamedFramebufferDrawBuffer (GLuint framebuffer, GLenum buf); +GLAPI void APIENTRY glNamedFramebufferDrawBuffers (GLuint framebuffer, GLsizei n, const GLenum *bufs); +GLAPI void APIENTRY glNamedFramebufferReadBuffer (GLuint framebuffer, GLenum src); +GLAPI void APIENTRY glInvalidateNamedFramebufferData (GLuint framebuffer, GLsizei numAttachments, const GLenum *attachments); +GLAPI void APIENTRY glInvalidateNamedFramebufferSubData (GLuint framebuffer, GLsizei numAttachments, const GLenum *attachments, GLint x, GLint y, GLsizei width, GLsizei height); +GLAPI void APIENTRY glClearNamedFramebufferiv (GLuint framebuffer, GLenum buffer, GLint drawbuffer, const GLint *value); +GLAPI void APIENTRY glClearNamedFramebufferuiv (GLuint framebuffer, GLenum buffer, GLint drawbuffer, const GLuint *value); +GLAPI void APIENTRY glClearNamedFramebufferfv (GLuint framebuffer, GLenum buffer, GLint drawbuffer, const GLfloat *value); +GLAPI void APIENTRY glClearNamedFramebufferfi (GLuint framebuffer, GLenum buffer, const GLfloat depth, GLint stencil); +GLAPI void APIENTRY glBlitNamedFramebuffer (GLuint readFramebuffer, GLuint drawFramebuffer, GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter); +GLAPI GLenum APIENTRY glCheckNamedFramebufferStatus (GLuint framebuffer, GLenum target); +GLAPI void APIENTRY glGetNamedFramebufferParameteriv (GLuint framebuffer, GLenum pname, GLint *param); +GLAPI void APIENTRY glGetNamedFramebufferAttachmentParameteriv (GLuint framebuffer, GLenum attachment, GLenum pname, GLint *params); +GLAPI void APIENTRY glCreateRenderbuffers (GLsizei n, GLuint *renderbuffers); +GLAPI void APIENTRY glNamedRenderbufferStorage (GLuint renderbuffer, GLenum internalformat, GLsizei width, GLsizei height); +GLAPI void APIENTRY glNamedRenderbufferStorageMultisample (GLuint renderbuffer, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height); +GLAPI void APIENTRY glGetNamedRenderbufferParameteriv (GLuint renderbuffer, GLenum pname, GLint *params); +GLAPI void APIENTRY glCreateTextures (GLenum target, GLsizei n, GLuint *textures); +GLAPI void APIENTRY glTextureBuffer (GLuint texture, GLenum internalformat, GLuint buffer); +GLAPI void APIENTRY glTextureBufferRange (GLuint texture, GLenum internalformat, GLuint buffer, GLintptr offset, GLsizeiptr size); +GLAPI void APIENTRY glTextureStorage1D (GLuint texture, GLsizei levels, GLenum internalformat, GLsizei width); +GLAPI void APIENTRY glTextureStorage2D (GLuint texture, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height); +GLAPI void APIENTRY glTextureStorage3D (GLuint texture, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth); +GLAPI void APIENTRY glTextureStorage2DMultisample (GLuint texture, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations); +GLAPI void APIENTRY glTextureStorage3DMultisample (GLuint texture, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations); +GLAPI void APIENTRY glTextureSubImage1D (GLuint texture, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const void *pixels); +GLAPI void APIENTRY glTextureSubImage2D (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels); +GLAPI void APIENTRY glTextureSubImage3D (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *pixels); +GLAPI void APIENTRY glCompressedTextureSubImage1D (GLuint texture, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const void *data); +GLAPI void APIENTRY glCompressedTextureSubImage2D (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *data); +GLAPI void APIENTRY glCompressedTextureSubImage3D (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *data); +GLAPI void APIENTRY glCopyTextureSubImage1D (GLuint texture, GLint level, GLint xoffset, GLint x, GLint y, GLsizei width); +GLAPI void APIENTRY glCopyTextureSubImage2D (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height); +GLAPI void APIENTRY glCopyTextureSubImage3D (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height); +GLAPI void APIENTRY glTextureParameterf (GLuint texture, GLenum pname, GLfloat param); +GLAPI void APIENTRY glTextureParameterfv (GLuint texture, GLenum pname, const GLfloat *param); +GLAPI void APIENTRY glTextureParameteri (GLuint texture, GLenum pname, GLint param); +GLAPI void APIENTRY glTextureParameterIiv (GLuint texture, GLenum pname, const GLint *params); +GLAPI void APIENTRY glTextureParameterIuiv (GLuint texture, GLenum pname, const GLuint *params); +GLAPI void APIENTRY glTextureParameteriv (GLuint texture, GLenum pname, const GLint *param); +GLAPI void APIENTRY glGenerateTextureMipmap (GLuint texture); +GLAPI void APIENTRY glBindTextureUnit (GLuint unit, GLuint texture); +GLAPI void APIENTRY glGetTextureImage (GLuint texture, GLint level, GLenum format, GLenum type, GLsizei bufSize, void *pixels); +GLAPI void APIENTRY glGetCompressedTextureImage (GLuint texture, GLint level, GLsizei bufSize, void *pixels); +GLAPI void APIENTRY glGetTextureLevelParameterfv (GLuint texture, GLint level, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetTextureLevelParameteriv (GLuint texture, GLint level, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetTextureParameterfv (GLuint texture, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetTextureParameterIiv (GLuint texture, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetTextureParameterIuiv (GLuint texture, GLenum pname, GLuint *params); +GLAPI void APIENTRY glGetTextureParameteriv (GLuint texture, GLenum pname, GLint *params); +GLAPI void APIENTRY glCreateVertexArrays (GLsizei n, GLuint *arrays); +GLAPI void APIENTRY glDisableVertexArrayAttrib (GLuint vaobj, GLuint index); +GLAPI void APIENTRY glEnableVertexArrayAttrib (GLuint vaobj, GLuint index); +GLAPI void APIENTRY glVertexArrayElementBuffer (GLuint vaobj, GLuint buffer); +GLAPI void APIENTRY glVertexArrayVertexBuffer (GLuint vaobj, GLuint bindingindex, GLuint buffer, GLintptr offset, GLsizei stride); +GLAPI void APIENTRY glVertexArrayVertexBuffers (GLuint vaobj, GLuint first, GLsizei count, const GLuint *buffers, const GLintptr *offsets, const GLsizei *strides); +GLAPI void APIENTRY glVertexArrayAttribBinding (GLuint vaobj, GLuint attribindex, GLuint bindingindex); +GLAPI void APIENTRY glVertexArrayAttribFormat (GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLboolean normalized, GLuint relativeoffset); +GLAPI void APIENTRY glVertexArrayAttribIFormat (GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset); +GLAPI void APIENTRY glVertexArrayAttribLFormat (GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset); +GLAPI void APIENTRY glVertexArrayBindingDivisor (GLuint vaobj, GLuint bindingindex, GLuint divisor); +GLAPI void APIENTRY glGetVertexArrayiv (GLuint vaobj, GLenum pname, GLint *param); +GLAPI void APIENTRY glGetVertexArrayIndexediv (GLuint vaobj, GLuint index, GLenum pname, GLint *param); +GLAPI void APIENTRY glGetVertexArrayIndexed64iv (GLuint vaobj, GLuint index, GLenum pname, GLint64 *param); +GLAPI void APIENTRY glCreateSamplers (GLsizei n, GLuint *samplers); +GLAPI void APIENTRY glCreateProgramPipelines (GLsizei n, GLuint *pipelines); +GLAPI void APIENTRY glCreateQueries (GLenum target, GLsizei n, GLuint *ids); +GLAPI void APIENTRY glGetQueryBufferObjecti64v (GLuint id, GLuint buffer, GLenum pname, GLintptr offset); +GLAPI void APIENTRY glGetQueryBufferObjectiv (GLuint id, GLuint buffer, GLenum pname, GLintptr offset); +GLAPI void APIENTRY glGetQueryBufferObjectui64v (GLuint id, GLuint buffer, GLenum pname, GLintptr offset); +GLAPI void APIENTRY glGetQueryBufferObjectuiv (GLuint id, GLuint buffer, GLenum pname, GLintptr offset); +GLAPI void APIENTRY glMemoryBarrierByRegion (GLbitfield barriers); +GLAPI void APIENTRY glGetTextureSubImage (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, GLsizei bufSize, void *pixels); +GLAPI void APIENTRY glGetCompressedTextureSubImage (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLsizei bufSize, void *pixels); +GLAPI GLenum APIENTRY glGetGraphicsResetStatus (void); +GLAPI void APIENTRY glGetnCompressedTexImage (GLenum target, GLint lod, GLsizei bufSize, void *pixels); +GLAPI void APIENTRY glGetnTexImage (GLenum target, GLint level, GLenum format, GLenum type, GLsizei bufSize, void *pixels); +GLAPI void APIENTRY glGetnUniformdv (GLuint program, GLint location, GLsizei bufSize, GLdouble *params); +GLAPI void APIENTRY glGetnUniformfv (GLuint program, GLint location, GLsizei bufSize, GLfloat *params); +GLAPI void APIENTRY glGetnUniformiv (GLuint program, GLint location, GLsizei bufSize, GLint *params); +GLAPI void APIENTRY glGetnUniformuiv (GLuint program, GLint location, GLsizei bufSize, GLuint *params); +GLAPI void APIENTRY glReadnPixels (GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLsizei bufSize, void *data); +GLAPI void APIENTRY glGetnMapdv (GLenum target, GLenum query, GLsizei bufSize, GLdouble *v); +GLAPI void APIENTRY glGetnMapfv (GLenum target, GLenum query, GLsizei bufSize, GLfloat *v); +GLAPI void APIENTRY glGetnMapiv (GLenum target, GLenum query, GLsizei bufSize, GLint *v); +GLAPI void APIENTRY glGetnPixelMapfv (GLenum map, GLsizei bufSize, GLfloat *values); +GLAPI void APIENTRY glGetnPixelMapuiv (GLenum map, GLsizei bufSize, GLuint *values); +GLAPI void APIENTRY glGetnPixelMapusv (GLenum map, GLsizei bufSize, GLushort *values); +GLAPI void APIENTRY glGetnPolygonStipple (GLsizei bufSize, GLubyte *pattern); +GLAPI void APIENTRY glGetnColorTable (GLenum target, GLenum format, GLenum type, GLsizei bufSize, void *table); +GLAPI void APIENTRY glGetnConvolutionFilter (GLenum target, GLenum format, GLenum type, GLsizei bufSize, void *image); +GLAPI void APIENTRY glGetnSeparableFilter (GLenum target, GLenum format, GLenum type, GLsizei rowBufSize, void *row, GLsizei columnBufSize, void *column, void *span); +GLAPI void APIENTRY glGetnHistogram (GLenum target, GLboolean reset, GLenum format, GLenum type, GLsizei bufSize, void *values); +GLAPI void APIENTRY glGetnMinmax (GLenum target, GLboolean reset, GLenum format, GLenum type, GLsizei bufSize, void *values); +GLAPI void APIENTRY glTextureBarrier (void); +#endif +#endif /* GL_VERSION_4_5 */ + +#ifndef GL_ARB_ES2_compatibility +#define GL_ARB_ES2_compatibility 1 +#endif /* GL_ARB_ES2_compatibility */ + +#ifndef GL_ARB_ES3_1_compatibility +#define GL_ARB_ES3_1_compatibility 1 +#endif /* GL_ARB_ES3_1_compatibility */ + +#ifndef GL_ARB_ES3_compatibility +#define GL_ARB_ES3_compatibility 1 +#endif /* GL_ARB_ES3_compatibility */ + +#ifndef GL_ARB_arrays_of_arrays +#define GL_ARB_arrays_of_arrays 1 +#endif /* GL_ARB_arrays_of_arrays */ + +#ifndef GL_ARB_base_instance +#define GL_ARB_base_instance 1 +#endif /* GL_ARB_base_instance */ + +#ifndef GL_ARB_bindless_texture +#define GL_ARB_bindless_texture 1 +typedef uint64_t GLuint64EXT; +#define GL_UNSIGNED_INT64_ARB 0x140F +typedef GLuint64 (APIENTRYP PFNGLGETTEXTUREHANDLEARBPROC) (GLuint texture); +typedef GLuint64 (APIENTRYP PFNGLGETTEXTURESAMPLERHANDLEARBPROC) (GLuint texture, GLuint sampler); +typedef void (APIENTRYP PFNGLMAKETEXTUREHANDLERESIDENTARBPROC) (GLuint64 handle); +typedef void (APIENTRYP PFNGLMAKETEXTUREHANDLENONRESIDENTARBPROC) (GLuint64 handle); +typedef GLuint64 (APIENTRYP PFNGLGETIMAGEHANDLEARBPROC) (GLuint texture, GLint level, GLboolean layered, GLint layer, GLenum format); +typedef void (APIENTRYP PFNGLMAKEIMAGEHANDLERESIDENTARBPROC) (GLuint64 handle, GLenum access); +typedef void (APIENTRYP PFNGLMAKEIMAGEHANDLENONRESIDENTARBPROC) (GLuint64 handle); +typedef void (APIENTRYP PFNGLUNIFORMHANDLEUI64ARBPROC) (GLint location, GLuint64 value); +typedef void (APIENTRYP PFNGLUNIFORMHANDLEUI64VARBPROC) (GLint location, GLsizei count, const GLuint64 *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMHANDLEUI64ARBPROC) (GLuint program, GLint location, GLuint64 value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMHANDLEUI64VARBPROC) (GLuint program, GLint location, GLsizei count, const GLuint64 *values); +typedef GLboolean (APIENTRYP PFNGLISTEXTUREHANDLERESIDENTARBPROC) (GLuint64 handle); +typedef GLboolean (APIENTRYP PFNGLISIMAGEHANDLERESIDENTARBPROC) (GLuint64 handle); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL1UI64ARBPROC) (GLuint index, GLuint64EXT x); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL1UI64VARBPROC) (GLuint index, const GLuint64EXT *v); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBLUI64VARBPROC) (GLuint index, GLenum pname, GLuint64EXT *params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI GLuint64 APIENTRY glGetTextureHandleARB (GLuint texture); +GLAPI GLuint64 APIENTRY glGetTextureSamplerHandleARB (GLuint texture, GLuint sampler); +GLAPI void APIENTRY glMakeTextureHandleResidentARB (GLuint64 handle); +GLAPI void APIENTRY glMakeTextureHandleNonResidentARB (GLuint64 handle); +GLAPI GLuint64 APIENTRY glGetImageHandleARB (GLuint texture, GLint level, GLboolean layered, GLint layer, GLenum format); +GLAPI void APIENTRY glMakeImageHandleResidentARB (GLuint64 handle, GLenum access); +GLAPI void APIENTRY glMakeImageHandleNonResidentARB (GLuint64 handle); +GLAPI void APIENTRY glUniformHandleui64ARB (GLint location, GLuint64 value); +GLAPI void APIENTRY glUniformHandleui64vARB (GLint location, GLsizei count, const GLuint64 *value); +GLAPI void APIENTRY glProgramUniformHandleui64ARB (GLuint program, GLint location, GLuint64 value); +GLAPI void APIENTRY glProgramUniformHandleui64vARB (GLuint program, GLint location, GLsizei count, const GLuint64 *values); +GLAPI GLboolean APIENTRY glIsTextureHandleResidentARB (GLuint64 handle); +GLAPI GLboolean APIENTRY glIsImageHandleResidentARB (GLuint64 handle); +GLAPI void APIENTRY glVertexAttribL1ui64ARB (GLuint index, GLuint64EXT x); +GLAPI void APIENTRY glVertexAttribL1ui64vARB (GLuint index, const GLuint64EXT *v); +GLAPI void APIENTRY glGetVertexAttribLui64vARB (GLuint index, GLenum pname, GLuint64EXT *params); +#endif +#endif /* GL_ARB_bindless_texture */ + +#ifndef GL_ARB_blend_func_extended +#define GL_ARB_blend_func_extended 1 +#endif /* GL_ARB_blend_func_extended */ + +#ifndef GL_ARB_buffer_storage +#define GL_ARB_buffer_storage 1 +#endif /* GL_ARB_buffer_storage */ + +#ifndef GL_ARB_cl_event +#define GL_ARB_cl_event 1 +struct _cl_context; +struct _cl_event; +#define GL_SYNC_CL_EVENT_ARB 0x8240 +#define GL_SYNC_CL_EVENT_COMPLETE_ARB 0x8241 +typedef GLsync (APIENTRYP PFNGLCREATESYNCFROMCLEVENTARBPROC) (struct _cl_context *context, struct _cl_event *event, GLbitfield flags); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI GLsync APIENTRY glCreateSyncFromCLeventARB (struct _cl_context *context, struct _cl_event *event, GLbitfield flags); +#endif +#endif /* GL_ARB_cl_event */ + +#ifndef GL_ARB_clear_buffer_object +#define GL_ARB_clear_buffer_object 1 +#endif /* GL_ARB_clear_buffer_object */ + +#ifndef GL_ARB_clear_texture +#define GL_ARB_clear_texture 1 +#endif /* GL_ARB_clear_texture */ + +#ifndef GL_ARB_clip_control +#define GL_ARB_clip_control 1 +#endif /* GL_ARB_clip_control */ + +#ifndef GL_ARB_color_buffer_float +#define GL_ARB_color_buffer_float 1 +#define GL_RGBA_FLOAT_MODE_ARB 0x8820 +#define GL_CLAMP_VERTEX_COLOR_ARB 0x891A +#define GL_CLAMP_FRAGMENT_COLOR_ARB 0x891B +#define GL_CLAMP_READ_COLOR_ARB 0x891C +#define GL_FIXED_ONLY_ARB 0x891D +typedef void (APIENTRYP PFNGLCLAMPCOLORARBPROC) (GLenum target, GLenum clamp); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glClampColorARB (GLenum target, GLenum clamp); +#endif +#endif /* GL_ARB_color_buffer_float */ + +#ifndef GL_ARB_compatibility +#define GL_ARB_compatibility 1 +#endif /* GL_ARB_compatibility */ + +#ifndef GL_ARB_compressed_texture_pixel_storage +#define GL_ARB_compressed_texture_pixel_storage 1 +#endif /* GL_ARB_compressed_texture_pixel_storage */ + +#ifndef GL_ARB_compute_shader +#define GL_ARB_compute_shader 1 +#endif /* GL_ARB_compute_shader */ + +#ifndef GL_ARB_compute_variable_group_size +#define GL_ARB_compute_variable_group_size 1 +#define GL_MAX_COMPUTE_VARIABLE_GROUP_INVOCATIONS_ARB 0x9344 +#define GL_MAX_COMPUTE_FIXED_GROUP_INVOCATIONS_ARB 0x90EB +#define GL_MAX_COMPUTE_VARIABLE_GROUP_SIZE_ARB 0x9345 +#define GL_MAX_COMPUTE_FIXED_GROUP_SIZE_ARB 0x91BF +typedef void (APIENTRYP PFNGLDISPATCHCOMPUTEGROUPSIZEARBPROC) (GLuint num_groups_x, GLuint num_groups_y, GLuint num_groups_z, GLuint group_size_x, GLuint group_size_y, GLuint group_size_z); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glDispatchComputeGroupSizeARB (GLuint num_groups_x, GLuint num_groups_y, GLuint num_groups_z, GLuint group_size_x, GLuint group_size_y, GLuint group_size_z); +#endif +#endif /* GL_ARB_compute_variable_group_size */ + +#ifndef GL_ARB_conditional_render_inverted +#define GL_ARB_conditional_render_inverted 1 +#endif /* GL_ARB_conditional_render_inverted */ + +#ifndef GL_ARB_conservative_depth +#define GL_ARB_conservative_depth 1 +#endif /* GL_ARB_conservative_depth */ + +#ifndef GL_ARB_copy_buffer +#define GL_ARB_copy_buffer 1 +#endif /* GL_ARB_copy_buffer */ + +#ifndef GL_ARB_copy_image +#define GL_ARB_copy_image 1 +#endif /* GL_ARB_copy_image */ + +#ifndef GL_ARB_cull_distance +#define GL_ARB_cull_distance 1 +#endif /* GL_ARB_cull_distance */ + +#ifndef GL_ARB_debug_output +#define GL_ARB_debug_output 1 +typedef void (APIENTRY *GLDEBUGPROCARB)(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar *message,const void *userParam); +#define GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB 0x8242 +#define GL_DEBUG_NEXT_LOGGED_MESSAGE_LENGTH_ARB 0x8243 +#define GL_DEBUG_CALLBACK_FUNCTION_ARB 0x8244 +#define GL_DEBUG_CALLBACK_USER_PARAM_ARB 0x8245 +#define GL_DEBUG_SOURCE_API_ARB 0x8246 +#define GL_DEBUG_SOURCE_WINDOW_SYSTEM_ARB 0x8247 +#define GL_DEBUG_SOURCE_SHADER_COMPILER_ARB 0x8248 +#define GL_DEBUG_SOURCE_THIRD_PARTY_ARB 0x8249 +#define GL_DEBUG_SOURCE_APPLICATION_ARB 0x824A +#define GL_DEBUG_SOURCE_OTHER_ARB 0x824B +#define GL_DEBUG_TYPE_ERROR_ARB 0x824C +#define GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR_ARB 0x824D +#define GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR_ARB 0x824E +#define GL_DEBUG_TYPE_PORTABILITY_ARB 0x824F +#define GL_DEBUG_TYPE_PERFORMANCE_ARB 0x8250 +#define GL_DEBUG_TYPE_OTHER_ARB 0x8251 +#define GL_MAX_DEBUG_MESSAGE_LENGTH_ARB 0x9143 +#define GL_MAX_DEBUG_LOGGED_MESSAGES_ARB 0x9144 +#define GL_DEBUG_LOGGED_MESSAGES_ARB 0x9145 +#define GL_DEBUG_SEVERITY_HIGH_ARB 0x9146 +#define GL_DEBUG_SEVERITY_MEDIUM_ARB 0x9147 +#define GL_DEBUG_SEVERITY_LOW_ARB 0x9148 +typedef void (APIENTRYP PFNGLDEBUGMESSAGECONTROLARBPROC) (GLenum source, GLenum type, GLenum severity, GLsizei count, const GLuint *ids, GLboolean enabled); +typedef void (APIENTRYP PFNGLDEBUGMESSAGEINSERTARBPROC) (GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *buf); +typedef void (APIENTRYP PFNGLDEBUGMESSAGECALLBACKARBPROC) (GLDEBUGPROCARB callback, const void *userParam); +typedef GLuint (APIENTRYP PFNGLGETDEBUGMESSAGELOGARBPROC) (GLuint count, GLsizei bufSize, GLenum *sources, GLenum *types, GLuint *ids, GLenum *severities, GLsizei *lengths, GLchar *messageLog); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glDebugMessageControlARB (GLenum source, GLenum type, GLenum severity, GLsizei count, const GLuint *ids, GLboolean enabled); +GLAPI void APIENTRY glDebugMessageInsertARB (GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *buf); +GLAPI void APIENTRY glDebugMessageCallbackARB (GLDEBUGPROCARB callback, const void *userParam); +GLAPI GLuint APIENTRY glGetDebugMessageLogARB (GLuint count, GLsizei bufSize, GLenum *sources, GLenum *types, GLuint *ids, GLenum *severities, GLsizei *lengths, GLchar *messageLog); +#endif +#endif /* GL_ARB_debug_output */ + +#ifndef GL_ARB_depth_buffer_float +#define GL_ARB_depth_buffer_float 1 +#endif /* GL_ARB_depth_buffer_float */ + +#ifndef GL_ARB_depth_clamp +#define GL_ARB_depth_clamp 1 +#endif /* GL_ARB_depth_clamp */ + +#ifndef GL_ARB_depth_texture +#define GL_ARB_depth_texture 1 +#define GL_DEPTH_COMPONENT16_ARB 0x81A5 +#define GL_DEPTH_COMPONENT24_ARB 0x81A6 +#define GL_DEPTH_COMPONENT32_ARB 0x81A7 +#define GL_TEXTURE_DEPTH_SIZE_ARB 0x884A +#define GL_DEPTH_TEXTURE_MODE_ARB 0x884B +#endif /* GL_ARB_depth_texture */ + +#ifndef GL_ARB_derivative_control +#define GL_ARB_derivative_control 1 +#endif /* GL_ARB_derivative_control */ + +#ifndef GL_ARB_direct_state_access +#define GL_ARB_direct_state_access 1 +#endif /* GL_ARB_direct_state_access */ + +#ifndef GL_ARB_draw_buffers +#define GL_ARB_draw_buffers 1 +#define GL_MAX_DRAW_BUFFERS_ARB 0x8824 +#define GL_DRAW_BUFFER0_ARB 0x8825 +#define GL_DRAW_BUFFER1_ARB 0x8826 +#define GL_DRAW_BUFFER2_ARB 0x8827 +#define GL_DRAW_BUFFER3_ARB 0x8828 +#define GL_DRAW_BUFFER4_ARB 0x8829 +#define GL_DRAW_BUFFER5_ARB 0x882A +#define GL_DRAW_BUFFER6_ARB 0x882B +#define GL_DRAW_BUFFER7_ARB 0x882C +#define GL_DRAW_BUFFER8_ARB 0x882D +#define GL_DRAW_BUFFER9_ARB 0x882E +#define GL_DRAW_BUFFER10_ARB 0x882F +#define GL_DRAW_BUFFER11_ARB 0x8830 +#define GL_DRAW_BUFFER12_ARB 0x8831 +#define GL_DRAW_BUFFER13_ARB 0x8832 +#define GL_DRAW_BUFFER14_ARB 0x8833 +#define GL_DRAW_BUFFER15_ARB 0x8834 +typedef void (APIENTRYP PFNGLDRAWBUFFERSARBPROC) (GLsizei n, const GLenum *bufs); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glDrawBuffersARB (GLsizei n, const GLenum *bufs); +#endif +#endif /* GL_ARB_draw_buffers */ + +#ifndef GL_ARB_draw_buffers_blend +#define GL_ARB_draw_buffers_blend 1 +typedef void (APIENTRYP PFNGLBLENDEQUATIONIARBPROC) (GLuint buf, GLenum mode); +typedef void (APIENTRYP PFNGLBLENDEQUATIONSEPARATEIARBPROC) (GLuint buf, GLenum modeRGB, GLenum modeAlpha); +typedef void (APIENTRYP PFNGLBLENDFUNCIARBPROC) (GLuint buf, GLenum src, GLenum dst); +typedef void (APIENTRYP PFNGLBLENDFUNCSEPARATEIARBPROC) (GLuint buf, GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBlendEquationiARB (GLuint buf, GLenum mode); +GLAPI void APIENTRY glBlendEquationSeparateiARB (GLuint buf, GLenum modeRGB, GLenum modeAlpha); +GLAPI void APIENTRY glBlendFunciARB (GLuint buf, GLenum src, GLenum dst); +GLAPI void APIENTRY glBlendFuncSeparateiARB (GLuint buf, GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha); +#endif +#endif /* GL_ARB_draw_buffers_blend */ + +#ifndef GL_ARB_draw_elements_base_vertex +#define GL_ARB_draw_elements_base_vertex 1 +#endif /* GL_ARB_draw_elements_base_vertex */ + +#ifndef GL_ARB_draw_indirect +#define GL_ARB_draw_indirect 1 +#endif /* GL_ARB_draw_indirect */ + +#ifndef GL_ARB_draw_instanced +#define GL_ARB_draw_instanced 1 +typedef void (APIENTRYP PFNGLDRAWARRAYSINSTANCEDARBPROC) (GLenum mode, GLint first, GLsizei count, GLsizei primcount); +typedef void (APIENTRYP PFNGLDRAWELEMENTSINSTANCEDARBPROC) (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei primcount); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glDrawArraysInstancedARB (GLenum mode, GLint first, GLsizei count, GLsizei primcount); +GLAPI void APIENTRY glDrawElementsInstancedARB (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei primcount); +#endif +#endif /* GL_ARB_draw_instanced */ + +#ifndef GL_ARB_enhanced_layouts +#define GL_ARB_enhanced_layouts 1 +#endif /* GL_ARB_enhanced_layouts */ + +#ifndef GL_ARB_explicit_attrib_location +#define GL_ARB_explicit_attrib_location 1 +#endif /* GL_ARB_explicit_attrib_location */ + +#ifndef GL_ARB_explicit_uniform_location +#define GL_ARB_explicit_uniform_location 1 +#endif /* GL_ARB_explicit_uniform_location */ + +#ifndef GL_ARB_fragment_coord_conventions +#define GL_ARB_fragment_coord_conventions 1 +#endif /* GL_ARB_fragment_coord_conventions */ + +#ifndef GL_ARB_fragment_layer_viewport +#define GL_ARB_fragment_layer_viewport 1 +#endif /* GL_ARB_fragment_layer_viewport */ + +#ifndef GL_ARB_fragment_program +#define GL_ARB_fragment_program 1 +#define GL_FRAGMENT_PROGRAM_ARB 0x8804 +#define GL_PROGRAM_FORMAT_ASCII_ARB 0x8875 +#define GL_PROGRAM_LENGTH_ARB 0x8627 +#define GL_PROGRAM_FORMAT_ARB 0x8876 +#define GL_PROGRAM_BINDING_ARB 0x8677 +#define GL_PROGRAM_INSTRUCTIONS_ARB 0x88A0 +#define GL_MAX_PROGRAM_INSTRUCTIONS_ARB 0x88A1 +#define GL_PROGRAM_NATIVE_INSTRUCTIONS_ARB 0x88A2 +#define GL_MAX_PROGRAM_NATIVE_INSTRUCTIONS_ARB 0x88A3 +#define GL_PROGRAM_TEMPORARIES_ARB 0x88A4 +#define GL_MAX_PROGRAM_TEMPORARIES_ARB 0x88A5 +#define GL_PROGRAM_NATIVE_TEMPORARIES_ARB 0x88A6 +#define GL_MAX_PROGRAM_NATIVE_TEMPORARIES_ARB 0x88A7 +#define GL_PROGRAM_PARAMETERS_ARB 0x88A8 +#define GL_MAX_PROGRAM_PARAMETERS_ARB 0x88A9 +#define GL_PROGRAM_NATIVE_PARAMETERS_ARB 0x88AA +#define GL_MAX_PROGRAM_NATIVE_PARAMETERS_ARB 0x88AB +#define GL_PROGRAM_ATTRIBS_ARB 0x88AC +#define GL_MAX_PROGRAM_ATTRIBS_ARB 0x88AD +#define GL_PROGRAM_NATIVE_ATTRIBS_ARB 0x88AE +#define GL_MAX_PROGRAM_NATIVE_ATTRIBS_ARB 0x88AF +#define GL_MAX_PROGRAM_LOCAL_PARAMETERS_ARB 0x88B4 +#define GL_MAX_PROGRAM_ENV_PARAMETERS_ARB 0x88B5 +#define GL_PROGRAM_UNDER_NATIVE_LIMITS_ARB 0x88B6 +#define GL_PROGRAM_ALU_INSTRUCTIONS_ARB 0x8805 +#define GL_PROGRAM_TEX_INSTRUCTIONS_ARB 0x8806 +#define GL_PROGRAM_TEX_INDIRECTIONS_ARB 0x8807 +#define GL_PROGRAM_NATIVE_ALU_INSTRUCTIONS_ARB 0x8808 +#define GL_PROGRAM_NATIVE_TEX_INSTRUCTIONS_ARB 0x8809 +#define GL_PROGRAM_NATIVE_TEX_INDIRECTIONS_ARB 0x880A +#define GL_MAX_PROGRAM_ALU_INSTRUCTIONS_ARB 0x880B +#define GL_MAX_PROGRAM_TEX_INSTRUCTIONS_ARB 0x880C +#define GL_MAX_PROGRAM_TEX_INDIRECTIONS_ARB 0x880D +#define GL_MAX_PROGRAM_NATIVE_ALU_INSTRUCTIONS_ARB 0x880E +#define GL_MAX_PROGRAM_NATIVE_TEX_INSTRUCTIONS_ARB 0x880F +#define GL_MAX_PROGRAM_NATIVE_TEX_INDIRECTIONS_ARB 0x8810 +#define GL_PROGRAM_STRING_ARB 0x8628 +#define GL_PROGRAM_ERROR_POSITION_ARB 0x864B +#define GL_CURRENT_MATRIX_ARB 0x8641 +#define GL_TRANSPOSE_CURRENT_MATRIX_ARB 0x88B7 +#define GL_CURRENT_MATRIX_STACK_DEPTH_ARB 0x8640 +#define GL_MAX_PROGRAM_MATRICES_ARB 0x862F +#define GL_MAX_PROGRAM_MATRIX_STACK_DEPTH_ARB 0x862E +#define GL_MAX_TEXTURE_COORDS_ARB 0x8871 +#define GL_MAX_TEXTURE_IMAGE_UNITS_ARB 0x8872 +#define GL_PROGRAM_ERROR_STRING_ARB 0x8874 +#define GL_MATRIX0_ARB 0x88C0 +#define GL_MATRIX1_ARB 0x88C1 +#define GL_MATRIX2_ARB 0x88C2 +#define GL_MATRIX3_ARB 0x88C3 +#define GL_MATRIX4_ARB 0x88C4 +#define GL_MATRIX5_ARB 0x88C5 +#define GL_MATRIX6_ARB 0x88C6 +#define GL_MATRIX7_ARB 0x88C7 +#define GL_MATRIX8_ARB 0x88C8 +#define GL_MATRIX9_ARB 0x88C9 +#define GL_MATRIX10_ARB 0x88CA +#define GL_MATRIX11_ARB 0x88CB +#define GL_MATRIX12_ARB 0x88CC +#define GL_MATRIX13_ARB 0x88CD +#define GL_MATRIX14_ARB 0x88CE +#define GL_MATRIX15_ARB 0x88CF +#define GL_MATRIX16_ARB 0x88D0 +#define GL_MATRIX17_ARB 0x88D1 +#define GL_MATRIX18_ARB 0x88D2 +#define GL_MATRIX19_ARB 0x88D3 +#define GL_MATRIX20_ARB 0x88D4 +#define GL_MATRIX21_ARB 0x88D5 +#define GL_MATRIX22_ARB 0x88D6 +#define GL_MATRIX23_ARB 0x88D7 +#define GL_MATRIX24_ARB 0x88D8 +#define GL_MATRIX25_ARB 0x88D9 +#define GL_MATRIX26_ARB 0x88DA +#define GL_MATRIX27_ARB 0x88DB +#define GL_MATRIX28_ARB 0x88DC +#define GL_MATRIX29_ARB 0x88DD +#define GL_MATRIX30_ARB 0x88DE +#define GL_MATRIX31_ARB 0x88DF +typedef void (APIENTRYP PFNGLPROGRAMSTRINGARBPROC) (GLenum target, GLenum format, GLsizei len, const void *string); +typedef void (APIENTRYP PFNGLBINDPROGRAMARBPROC) (GLenum target, GLuint program); +typedef void (APIENTRYP PFNGLDELETEPROGRAMSARBPROC) (GLsizei n, const GLuint *programs); +typedef void (APIENTRYP PFNGLGENPROGRAMSARBPROC) (GLsizei n, GLuint *programs); +typedef void (APIENTRYP PFNGLPROGRAMENVPARAMETER4DARBPROC) (GLenum target, GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +typedef void (APIENTRYP PFNGLPROGRAMENVPARAMETER4DVARBPROC) (GLenum target, GLuint index, const GLdouble *params); +typedef void (APIENTRYP PFNGLPROGRAMENVPARAMETER4FARBPROC) (GLenum target, GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +typedef void (APIENTRYP PFNGLPROGRAMENVPARAMETER4FVARBPROC) (GLenum target, GLuint index, const GLfloat *params); +typedef void (APIENTRYP PFNGLPROGRAMLOCALPARAMETER4DARBPROC) (GLenum target, GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +typedef void (APIENTRYP PFNGLPROGRAMLOCALPARAMETER4DVARBPROC) (GLenum target, GLuint index, const GLdouble *params); +typedef void (APIENTRYP PFNGLPROGRAMLOCALPARAMETER4FARBPROC) (GLenum target, GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +typedef void (APIENTRYP PFNGLPROGRAMLOCALPARAMETER4FVARBPROC) (GLenum target, GLuint index, const GLfloat *params); +typedef void (APIENTRYP PFNGLGETPROGRAMENVPARAMETERDVARBPROC) (GLenum target, GLuint index, GLdouble *params); +typedef void (APIENTRYP PFNGLGETPROGRAMENVPARAMETERFVARBPROC) (GLenum target, GLuint index, GLfloat *params); +typedef void (APIENTRYP PFNGLGETPROGRAMLOCALPARAMETERDVARBPROC) (GLenum target, GLuint index, GLdouble *params); +typedef void (APIENTRYP PFNGLGETPROGRAMLOCALPARAMETERFVARBPROC) (GLenum target, GLuint index, GLfloat *params); +typedef void (APIENTRYP PFNGLGETPROGRAMIVARBPROC) (GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETPROGRAMSTRINGARBPROC) (GLenum target, GLenum pname, void *string); +typedef GLboolean (APIENTRYP PFNGLISPROGRAMARBPROC) (GLuint program); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glProgramStringARB (GLenum target, GLenum format, GLsizei len, const void *string); +GLAPI void APIENTRY glBindProgramARB (GLenum target, GLuint program); +GLAPI void APIENTRY glDeleteProgramsARB (GLsizei n, const GLuint *programs); +GLAPI void APIENTRY glGenProgramsARB (GLsizei n, GLuint *programs); +GLAPI void APIENTRY glProgramEnvParameter4dARB (GLenum target, GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +GLAPI void APIENTRY glProgramEnvParameter4dvARB (GLenum target, GLuint index, const GLdouble *params); +GLAPI void APIENTRY glProgramEnvParameter4fARB (GLenum target, GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +GLAPI void APIENTRY glProgramEnvParameter4fvARB (GLenum target, GLuint index, const GLfloat *params); +GLAPI void APIENTRY glProgramLocalParameter4dARB (GLenum target, GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +GLAPI void APIENTRY glProgramLocalParameter4dvARB (GLenum target, GLuint index, const GLdouble *params); +GLAPI void APIENTRY glProgramLocalParameter4fARB (GLenum target, GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +GLAPI void APIENTRY glProgramLocalParameter4fvARB (GLenum target, GLuint index, const GLfloat *params); +GLAPI void APIENTRY glGetProgramEnvParameterdvARB (GLenum target, GLuint index, GLdouble *params); +GLAPI void APIENTRY glGetProgramEnvParameterfvARB (GLenum target, GLuint index, GLfloat *params); +GLAPI void APIENTRY glGetProgramLocalParameterdvARB (GLenum target, GLuint index, GLdouble *params); +GLAPI void APIENTRY glGetProgramLocalParameterfvARB (GLenum target, GLuint index, GLfloat *params); +GLAPI void APIENTRY glGetProgramivARB (GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetProgramStringARB (GLenum target, GLenum pname, void *string); +GLAPI GLboolean APIENTRY glIsProgramARB (GLuint program); +#endif +#endif /* GL_ARB_fragment_program */ + +#ifndef GL_ARB_fragment_program_shadow +#define GL_ARB_fragment_program_shadow 1 +#endif /* GL_ARB_fragment_program_shadow */ + +#ifndef GL_ARB_fragment_shader +#define GL_ARB_fragment_shader 1 +#define GL_FRAGMENT_SHADER_ARB 0x8B30 +#define GL_MAX_FRAGMENT_UNIFORM_COMPONENTS_ARB 0x8B49 +#define GL_FRAGMENT_SHADER_DERIVATIVE_HINT_ARB 0x8B8B +#endif /* GL_ARB_fragment_shader */ + +#ifndef GL_ARB_framebuffer_no_attachments +#define GL_ARB_framebuffer_no_attachments 1 +#endif /* GL_ARB_framebuffer_no_attachments */ + +#ifndef GL_ARB_framebuffer_object +#define GL_ARB_framebuffer_object 1 +#endif /* GL_ARB_framebuffer_object */ + +#ifndef GL_ARB_framebuffer_sRGB +#define GL_ARB_framebuffer_sRGB 1 +#endif /* GL_ARB_framebuffer_sRGB */ + +#ifndef GL_ARB_geometry_shader4 +#define GL_ARB_geometry_shader4 1 +#define GL_LINES_ADJACENCY_ARB 0x000A +#define GL_LINE_STRIP_ADJACENCY_ARB 0x000B +#define GL_TRIANGLES_ADJACENCY_ARB 0x000C +#define GL_TRIANGLE_STRIP_ADJACENCY_ARB 0x000D +#define GL_PROGRAM_POINT_SIZE_ARB 0x8642 +#define GL_MAX_GEOMETRY_TEXTURE_IMAGE_UNITS_ARB 0x8C29 +#define GL_FRAMEBUFFER_ATTACHMENT_LAYERED_ARB 0x8DA7 +#define GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS_ARB 0x8DA8 +#define GL_FRAMEBUFFER_INCOMPLETE_LAYER_COUNT_ARB 0x8DA9 +#define GL_GEOMETRY_SHADER_ARB 0x8DD9 +#define GL_GEOMETRY_VERTICES_OUT_ARB 0x8DDA +#define GL_GEOMETRY_INPUT_TYPE_ARB 0x8DDB +#define GL_GEOMETRY_OUTPUT_TYPE_ARB 0x8DDC +#define GL_MAX_GEOMETRY_VARYING_COMPONENTS_ARB 0x8DDD +#define GL_MAX_VERTEX_VARYING_COMPONENTS_ARB 0x8DDE +#define GL_MAX_GEOMETRY_UNIFORM_COMPONENTS_ARB 0x8DDF +#define GL_MAX_GEOMETRY_OUTPUT_VERTICES_ARB 0x8DE0 +#define GL_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS_ARB 0x8DE1 +typedef void (APIENTRYP PFNGLPROGRAMPARAMETERIARBPROC) (GLuint program, GLenum pname, GLint value); +typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTUREARBPROC) (GLenum target, GLenum attachment, GLuint texture, GLint level); +typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURELAYERARBPROC) (GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer); +typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTUREFACEARBPROC) (GLenum target, GLenum attachment, GLuint texture, GLint level, GLenum face); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glProgramParameteriARB (GLuint program, GLenum pname, GLint value); +GLAPI void APIENTRY glFramebufferTextureARB (GLenum target, GLenum attachment, GLuint texture, GLint level); +GLAPI void APIENTRY glFramebufferTextureLayerARB (GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer); +GLAPI void APIENTRY glFramebufferTextureFaceARB (GLenum target, GLenum attachment, GLuint texture, GLint level, GLenum face); +#endif +#endif /* GL_ARB_geometry_shader4 */ + +#ifndef GL_ARB_get_program_binary +#define GL_ARB_get_program_binary 1 +#endif /* GL_ARB_get_program_binary */ + +#ifndef GL_ARB_get_texture_sub_image +#define GL_ARB_get_texture_sub_image 1 +#endif /* GL_ARB_get_texture_sub_image */ + +#ifndef GL_ARB_gpu_shader5 +#define GL_ARB_gpu_shader5 1 +#endif /* GL_ARB_gpu_shader5 */ + +#ifndef GL_ARB_gpu_shader_fp64 +#define GL_ARB_gpu_shader_fp64 1 +#endif /* GL_ARB_gpu_shader_fp64 */ + +#ifndef GL_ARB_half_float_pixel +#define GL_ARB_half_float_pixel 1 +typedef unsigned short GLhalfARB; +#define GL_HALF_FLOAT_ARB 0x140B +#endif /* GL_ARB_half_float_pixel */ + +#ifndef GL_ARB_half_float_vertex +#define GL_ARB_half_float_vertex 1 +#endif /* GL_ARB_half_float_vertex */ + +#ifndef GL_ARB_imaging +#define GL_ARB_imaging 1 +#define GL_BLEND_COLOR 0x8005 +#define GL_BLEND_EQUATION 0x8009 +#define GL_CONVOLUTION_1D 0x8010 +#define GL_CONVOLUTION_2D 0x8011 +#define GL_SEPARABLE_2D 0x8012 +#define GL_CONVOLUTION_BORDER_MODE 0x8013 +#define GL_CONVOLUTION_FILTER_SCALE 0x8014 +#define GL_CONVOLUTION_FILTER_BIAS 0x8015 +#define GL_REDUCE 0x8016 +#define GL_CONVOLUTION_FORMAT 0x8017 +#define GL_CONVOLUTION_WIDTH 0x8018 +#define GL_CONVOLUTION_HEIGHT 0x8019 +#define GL_MAX_CONVOLUTION_WIDTH 0x801A +#define GL_MAX_CONVOLUTION_HEIGHT 0x801B +#define GL_POST_CONVOLUTION_RED_SCALE 0x801C +#define GL_POST_CONVOLUTION_GREEN_SCALE 0x801D +#define GL_POST_CONVOLUTION_BLUE_SCALE 0x801E +#define GL_POST_CONVOLUTION_ALPHA_SCALE 0x801F +#define GL_POST_CONVOLUTION_RED_BIAS 0x8020 +#define GL_POST_CONVOLUTION_GREEN_BIAS 0x8021 +#define GL_POST_CONVOLUTION_BLUE_BIAS 0x8022 +#define GL_POST_CONVOLUTION_ALPHA_BIAS 0x8023 +#define GL_HISTOGRAM 0x8024 +#define GL_PROXY_HISTOGRAM 0x8025 +#define GL_HISTOGRAM_WIDTH 0x8026 +#define GL_HISTOGRAM_FORMAT 0x8027 +#define GL_HISTOGRAM_RED_SIZE 0x8028 +#define GL_HISTOGRAM_GREEN_SIZE 0x8029 +#define GL_HISTOGRAM_BLUE_SIZE 0x802A +#define GL_HISTOGRAM_ALPHA_SIZE 0x802B +#define GL_HISTOGRAM_LUMINANCE_SIZE 0x802C +#define GL_HISTOGRAM_SINK 0x802D +#define GL_MINMAX 0x802E +#define GL_MINMAX_FORMAT 0x802F +#define GL_MINMAX_SINK 0x8030 +#define GL_TABLE_TOO_LARGE 0x8031 +#define GL_COLOR_MATRIX 0x80B1 +#define GL_COLOR_MATRIX_STACK_DEPTH 0x80B2 +#define GL_MAX_COLOR_MATRIX_STACK_DEPTH 0x80B3 +#define GL_POST_COLOR_MATRIX_RED_SCALE 0x80B4 +#define GL_POST_COLOR_MATRIX_GREEN_SCALE 0x80B5 +#define GL_POST_COLOR_MATRIX_BLUE_SCALE 0x80B6 +#define GL_POST_COLOR_MATRIX_ALPHA_SCALE 0x80B7 +#define GL_POST_COLOR_MATRIX_RED_BIAS 0x80B8 +#define GL_POST_COLOR_MATRIX_GREEN_BIAS 0x80B9 +#define GL_POST_COLOR_MATRIX_BLUE_BIAS 0x80BA +#define GL_POST_COLOR_MATRIX_ALPHA_BIAS 0x80BB +#define GL_COLOR_TABLE 0x80D0 +#define GL_POST_CONVOLUTION_COLOR_TABLE 0x80D1 +#define GL_POST_COLOR_MATRIX_COLOR_TABLE 0x80D2 +#define GL_PROXY_COLOR_TABLE 0x80D3 +#define GL_PROXY_POST_CONVOLUTION_COLOR_TABLE 0x80D4 +#define GL_PROXY_POST_COLOR_MATRIX_COLOR_TABLE 0x80D5 +#define GL_COLOR_TABLE_SCALE 0x80D6 +#define GL_COLOR_TABLE_BIAS 0x80D7 +#define GL_COLOR_TABLE_FORMAT 0x80D8 +#define GL_COLOR_TABLE_WIDTH 0x80D9 +#define GL_COLOR_TABLE_RED_SIZE 0x80DA +#define GL_COLOR_TABLE_GREEN_SIZE 0x80DB +#define GL_COLOR_TABLE_BLUE_SIZE 0x80DC +#define GL_COLOR_TABLE_ALPHA_SIZE 0x80DD +#define GL_COLOR_TABLE_LUMINANCE_SIZE 0x80DE +#define GL_COLOR_TABLE_INTENSITY_SIZE 0x80DF +#define GL_CONSTANT_BORDER 0x8151 +#define GL_REPLICATE_BORDER 0x8153 +#define GL_CONVOLUTION_BORDER_COLOR 0x8154 +typedef void (APIENTRYP PFNGLCOLORTABLEPROC) (GLenum target, GLenum internalformat, GLsizei width, GLenum format, GLenum type, const void *table); +typedef void (APIENTRYP PFNGLCOLORTABLEPARAMETERFVPROC) (GLenum target, GLenum pname, const GLfloat *params); +typedef void (APIENTRYP PFNGLCOLORTABLEPARAMETERIVPROC) (GLenum target, GLenum pname, const GLint *params); +typedef void (APIENTRYP PFNGLCOPYCOLORTABLEPROC) (GLenum target, GLenum internalformat, GLint x, GLint y, GLsizei width); +typedef void (APIENTRYP PFNGLGETCOLORTABLEPROC) (GLenum target, GLenum format, GLenum type, void *table); +typedef void (APIENTRYP PFNGLGETCOLORTABLEPARAMETERFVPROC) (GLenum target, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETCOLORTABLEPARAMETERIVPROC) (GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLCOLORSUBTABLEPROC) (GLenum target, GLsizei start, GLsizei count, GLenum format, GLenum type, const void *data); +typedef void (APIENTRYP PFNGLCOPYCOLORSUBTABLEPROC) (GLenum target, GLsizei start, GLint x, GLint y, GLsizei width); +typedef void (APIENTRYP PFNGLCONVOLUTIONFILTER1DPROC) (GLenum target, GLenum internalformat, GLsizei width, GLenum format, GLenum type, const void *image); +typedef void (APIENTRYP PFNGLCONVOLUTIONFILTER2DPROC) (GLenum target, GLenum internalformat, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *image); +typedef void (APIENTRYP PFNGLCONVOLUTIONPARAMETERFPROC) (GLenum target, GLenum pname, GLfloat params); +typedef void (APIENTRYP PFNGLCONVOLUTIONPARAMETERFVPROC) (GLenum target, GLenum pname, const GLfloat *params); +typedef void (APIENTRYP PFNGLCONVOLUTIONPARAMETERIPROC) (GLenum target, GLenum pname, GLint params); +typedef void (APIENTRYP PFNGLCONVOLUTIONPARAMETERIVPROC) (GLenum target, GLenum pname, const GLint *params); +typedef void (APIENTRYP PFNGLCOPYCONVOLUTIONFILTER1DPROC) (GLenum target, GLenum internalformat, GLint x, GLint y, GLsizei width); +typedef void (APIENTRYP PFNGLCOPYCONVOLUTIONFILTER2DPROC) (GLenum target, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLGETCONVOLUTIONFILTERPROC) (GLenum target, GLenum format, GLenum type, void *image); +typedef void (APIENTRYP PFNGLGETCONVOLUTIONPARAMETERFVPROC) (GLenum target, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETCONVOLUTIONPARAMETERIVPROC) (GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETSEPARABLEFILTERPROC) (GLenum target, GLenum format, GLenum type, void *row, void *column, void *span); +typedef void (APIENTRYP PFNGLSEPARABLEFILTER2DPROC) (GLenum target, GLenum internalformat, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *row, const void *column); +typedef void (APIENTRYP PFNGLGETHISTOGRAMPROC) (GLenum target, GLboolean reset, GLenum format, GLenum type, void *values); +typedef void (APIENTRYP PFNGLGETHISTOGRAMPARAMETERFVPROC) (GLenum target, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETHISTOGRAMPARAMETERIVPROC) (GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETMINMAXPROC) (GLenum target, GLboolean reset, GLenum format, GLenum type, void *values); +typedef void (APIENTRYP PFNGLGETMINMAXPARAMETERFVPROC) (GLenum target, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETMINMAXPARAMETERIVPROC) (GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLHISTOGRAMPROC) (GLenum target, GLsizei width, GLenum internalformat, GLboolean sink); +typedef void (APIENTRYP PFNGLMINMAXPROC) (GLenum target, GLenum internalformat, GLboolean sink); +typedef void (APIENTRYP PFNGLRESETHISTOGRAMPROC) (GLenum target); +typedef void (APIENTRYP PFNGLRESETMINMAXPROC) (GLenum target); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glColorTable (GLenum target, GLenum internalformat, GLsizei width, GLenum format, GLenum type, const void *table); +GLAPI void APIENTRY glColorTableParameterfv (GLenum target, GLenum pname, const GLfloat *params); +GLAPI void APIENTRY glColorTableParameteriv (GLenum target, GLenum pname, const GLint *params); +GLAPI void APIENTRY glCopyColorTable (GLenum target, GLenum internalformat, GLint x, GLint y, GLsizei width); +GLAPI void APIENTRY glGetColorTable (GLenum target, GLenum format, GLenum type, void *table); +GLAPI void APIENTRY glGetColorTableParameterfv (GLenum target, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetColorTableParameteriv (GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glColorSubTable (GLenum target, GLsizei start, GLsizei count, GLenum format, GLenum type, const void *data); +GLAPI void APIENTRY glCopyColorSubTable (GLenum target, GLsizei start, GLint x, GLint y, GLsizei width); +GLAPI void APIENTRY glConvolutionFilter1D (GLenum target, GLenum internalformat, GLsizei width, GLenum format, GLenum type, const void *image); +GLAPI void APIENTRY glConvolutionFilter2D (GLenum target, GLenum internalformat, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *image); +GLAPI void APIENTRY glConvolutionParameterf (GLenum target, GLenum pname, GLfloat params); +GLAPI void APIENTRY glConvolutionParameterfv (GLenum target, GLenum pname, const GLfloat *params); +GLAPI void APIENTRY glConvolutionParameteri (GLenum target, GLenum pname, GLint params); +GLAPI void APIENTRY glConvolutionParameteriv (GLenum target, GLenum pname, const GLint *params); +GLAPI void APIENTRY glCopyConvolutionFilter1D (GLenum target, GLenum internalformat, GLint x, GLint y, GLsizei width); +GLAPI void APIENTRY glCopyConvolutionFilter2D (GLenum target, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height); +GLAPI void APIENTRY glGetConvolutionFilter (GLenum target, GLenum format, GLenum type, void *image); +GLAPI void APIENTRY glGetConvolutionParameterfv (GLenum target, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetConvolutionParameteriv (GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetSeparableFilter (GLenum target, GLenum format, GLenum type, void *row, void *column, void *span); +GLAPI void APIENTRY glSeparableFilter2D (GLenum target, GLenum internalformat, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *row, const void *column); +GLAPI void APIENTRY glGetHistogram (GLenum target, GLboolean reset, GLenum format, GLenum type, void *values); +GLAPI void APIENTRY glGetHistogramParameterfv (GLenum target, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetHistogramParameteriv (GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetMinmax (GLenum target, GLboolean reset, GLenum format, GLenum type, void *values); +GLAPI void APIENTRY glGetMinmaxParameterfv (GLenum target, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetMinmaxParameteriv (GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glHistogram (GLenum target, GLsizei width, GLenum internalformat, GLboolean sink); +GLAPI void APIENTRY glMinmax (GLenum target, GLenum internalformat, GLboolean sink); +GLAPI void APIENTRY glResetHistogram (GLenum target); +GLAPI void APIENTRY glResetMinmax (GLenum target); +#endif +#endif /* GL_ARB_imaging */ + +#ifndef GL_ARB_indirect_parameters +#define GL_ARB_indirect_parameters 1 +#define GL_PARAMETER_BUFFER_ARB 0x80EE +#define GL_PARAMETER_BUFFER_BINDING_ARB 0x80EF +typedef void (APIENTRYP PFNGLMULTIDRAWARRAYSINDIRECTCOUNTARBPROC) (GLenum mode, GLintptr indirect, GLintptr drawcount, GLsizei maxdrawcount, GLsizei stride); +typedef void (APIENTRYP PFNGLMULTIDRAWELEMENTSINDIRECTCOUNTARBPROC) (GLenum mode, GLenum type, GLintptr indirect, GLintptr drawcount, GLsizei maxdrawcount, GLsizei stride); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glMultiDrawArraysIndirectCountARB (GLenum mode, GLintptr indirect, GLintptr drawcount, GLsizei maxdrawcount, GLsizei stride); +GLAPI void APIENTRY glMultiDrawElementsIndirectCountARB (GLenum mode, GLenum type, GLintptr indirect, GLintptr drawcount, GLsizei maxdrawcount, GLsizei stride); +#endif +#endif /* GL_ARB_indirect_parameters */ + +#ifndef GL_ARB_instanced_arrays +#define GL_ARB_instanced_arrays 1 +#define GL_VERTEX_ATTRIB_ARRAY_DIVISOR_ARB 0x88FE +typedef void (APIENTRYP PFNGLVERTEXATTRIBDIVISORARBPROC) (GLuint index, GLuint divisor); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glVertexAttribDivisorARB (GLuint index, GLuint divisor); +#endif +#endif /* GL_ARB_instanced_arrays */ + +#ifndef GL_ARB_internalformat_query +#define GL_ARB_internalformat_query 1 +#endif /* GL_ARB_internalformat_query */ + +#ifndef GL_ARB_internalformat_query2 +#define GL_ARB_internalformat_query2 1 +#define GL_SRGB_DECODE_ARB 0x8299 +#endif /* GL_ARB_internalformat_query2 */ + +#ifndef GL_ARB_invalidate_subdata +#define GL_ARB_invalidate_subdata 1 +#endif /* GL_ARB_invalidate_subdata */ + +#ifndef GL_ARB_map_buffer_alignment +#define GL_ARB_map_buffer_alignment 1 +#endif /* GL_ARB_map_buffer_alignment */ + +#ifndef GL_ARB_map_buffer_range +#define GL_ARB_map_buffer_range 1 +#endif /* GL_ARB_map_buffer_range */ + +#ifndef GL_ARB_matrix_palette +#define GL_ARB_matrix_palette 1 +#define GL_MATRIX_PALETTE_ARB 0x8840 +#define GL_MAX_MATRIX_PALETTE_STACK_DEPTH_ARB 0x8841 +#define GL_MAX_PALETTE_MATRICES_ARB 0x8842 +#define GL_CURRENT_PALETTE_MATRIX_ARB 0x8843 +#define GL_MATRIX_INDEX_ARRAY_ARB 0x8844 +#define GL_CURRENT_MATRIX_INDEX_ARB 0x8845 +#define GL_MATRIX_INDEX_ARRAY_SIZE_ARB 0x8846 +#define GL_MATRIX_INDEX_ARRAY_TYPE_ARB 0x8847 +#define GL_MATRIX_INDEX_ARRAY_STRIDE_ARB 0x8848 +#define GL_MATRIX_INDEX_ARRAY_POINTER_ARB 0x8849 +typedef void (APIENTRYP PFNGLCURRENTPALETTEMATRIXARBPROC) (GLint index); +typedef void (APIENTRYP PFNGLMATRIXINDEXUBVARBPROC) (GLint size, const GLubyte *indices); +typedef void (APIENTRYP PFNGLMATRIXINDEXUSVARBPROC) (GLint size, const GLushort *indices); +typedef void (APIENTRYP PFNGLMATRIXINDEXUIVARBPROC) (GLint size, const GLuint *indices); +typedef void (APIENTRYP PFNGLMATRIXINDEXPOINTERARBPROC) (GLint size, GLenum type, GLsizei stride, const void *pointer); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glCurrentPaletteMatrixARB (GLint index); +GLAPI void APIENTRY glMatrixIndexubvARB (GLint size, const GLubyte *indices); +GLAPI void APIENTRY glMatrixIndexusvARB (GLint size, const GLushort *indices); +GLAPI void APIENTRY glMatrixIndexuivARB (GLint size, const GLuint *indices); +GLAPI void APIENTRY glMatrixIndexPointerARB (GLint size, GLenum type, GLsizei stride, const void *pointer); +#endif +#endif /* GL_ARB_matrix_palette */ + +#ifndef GL_ARB_multi_bind +#define GL_ARB_multi_bind 1 +#endif /* GL_ARB_multi_bind */ + +#ifndef GL_ARB_multi_draw_indirect +#define GL_ARB_multi_draw_indirect 1 +#endif /* GL_ARB_multi_draw_indirect */ + +#ifndef GL_ARB_multisample +#define GL_ARB_multisample 1 +#define GL_MULTISAMPLE_ARB 0x809D +#define GL_SAMPLE_ALPHA_TO_COVERAGE_ARB 0x809E +#define GL_SAMPLE_ALPHA_TO_ONE_ARB 0x809F +#define GL_SAMPLE_COVERAGE_ARB 0x80A0 +#define GL_SAMPLE_BUFFERS_ARB 0x80A8 +#define GL_SAMPLES_ARB 0x80A9 +#define GL_SAMPLE_COVERAGE_VALUE_ARB 0x80AA +#define GL_SAMPLE_COVERAGE_INVERT_ARB 0x80AB +#define GL_MULTISAMPLE_BIT_ARB 0x20000000 +typedef void (APIENTRYP PFNGLSAMPLECOVERAGEARBPROC) (GLfloat value, GLboolean invert); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glSampleCoverageARB (GLfloat value, GLboolean invert); +#endif +#endif /* GL_ARB_multisample */ + +#ifndef GL_ARB_multitexture +#define GL_ARB_multitexture 1 +#define GL_TEXTURE0_ARB 0x84C0 +#define GL_TEXTURE1_ARB 0x84C1 +#define GL_TEXTURE2_ARB 0x84C2 +#define GL_TEXTURE3_ARB 0x84C3 +#define GL_TEXTURE4_ARB 0x84C4 +#define GL_TEXTURE5_ARB 0x84C5 +#define GL_TEXTURE6_ARB 0x84C6 +#define GL_TEXTURE7_ARB 0x84C7 +#define GL_TEXTURE8_ARB 0x84C8 +#define GL_TEXTURE9_ARB 0x84C9 +#define GL_TEXTURE10_ARB 0x84CA +#define GL_TEXTURE11_ARB 0x84CB +#define GL_TEXTURE12_ARB 0x84CC +#define GL_TEXTURE13_ARB 0x84CD +#define GL_TEXTURE14_ARB 0x84CE +#define GL_TEXTURE15_ARB 0x84CF +#define GL_TEXTURE16_ARB 0x84D0 +#define GL_TEXTURE17_ARB 0x84D1 +#define GL_TEXTURE18_ARB 0x84D2 +#define GL_TEXTURE19_ARB 0x84D3 +#define GL_TEXTURE20_ARB 0x84D4 +#define GL_TEXTURE21_ARB 0x84D5 +#define GL_TEXTURE22_ARB 0x84D6 +#define GL_TEXTURE23_ARB 0x84D7 +#define GL_TEXTURE24_ARB 0x84D8 +#define GL_TEXTURE25_ARB 0x84D9 +#define GL_TEXTURE26_ARB 0x84DA +#define GL_TEXTURE27_ARB 0x84DB +#define GL_TEXTURE28_ARB 0x84DC +#define GL_TEXTURE29_ARB 0x84DD +#define GL_TEXTURE30_ARB 0x84DE +#define GL_TEXTURE31_ARB 0x84DF +#define GL_ACTIVE_TEXTURE_ARB 0x84E0 +#define GL_CLIENT_ACTIVE_TEXTURE_ARB 0x84E1 +#define GL_MAX_TEXTURE_UNITS_ARB 0x84E2 +typedef void (APIENTRYP PFNGLACTIVETEXTUREARBPROC) (GLenum texture); +typedef void (APIENTRYP PFNGLCLIENTACTIVETEXTUREARBPROC) (GLenum texture); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1DARBPROC) (GLenum target, GLdouble s); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1DVARBPROC) (GLenum target, const GLdouble *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1FARBPROC) (GLenum target, GLfloat s); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1FVARBPROC) (GLenum target, const GLfloat *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1IARBPROC) (GLenum target, GLint s); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1IVARBPROC) (GLenum target, const GLint *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1SARBPROC) (GLenum target, GLshort s); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1SVARBPROC) (GLenum target, const GLshort *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2DARBPROC) (GLenum target, GLdouble s, GLdouble t); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2DVARBPROC) (GLenum target, const GLdouble *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2FARBPROC) (GLenum target, GLfloat s, GLfloat t); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2FVARBPROC) (GLenum target, const GLfloat *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2IARBPROC) (GLenum target, GLint s, GLint t); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2IVARBPROC) (GLenum target, const GLint *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2SARBPROC) (GLenum target, GLshort s, GLshort t); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2SVARBPROC) (GLenum target, const GLshort *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3DARBPROC) (GLenum target, GLdouble s, GLdouble t, GLdouble r); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3DVARBPROC) (GLenum target, const GLdouble *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3FARBPROC) (GLenum target, GLfloat s, GLfloat t, GLfloat r); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3FVARBPROC) (GLenum target, const GLfloat *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3IARBPROC) (GLenum target, GLint s, GLint t, GLint r); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3IVARBPROC) (GLenum target, const GLint *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3SARBPROC) (GLenum target, GLshort s, GLshort t, GLshort r); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3SVARBPROC) (GLenum target, const GLshort *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4DARBPROC) (GLenum target, GLdouble s, GLdouble t, GLdouble r, GLdouble q); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4DVARBPROC) (GLenum target, const GLdouble *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4FARBPROC) (GLenum target, GLfloat s, GLfloat t, GLfloat r, GLfloat q); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4FVARBPROC) (GLenum target, const GLfloat *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4IARBPROC) (GLenum target, GLint s, GLint t, GLint r, GLint q); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4IVARBPROC) (GLenum target, const GLint *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4SARBPROC) (GLenum target, GLshort s, GLshort t, GLshort r, GLshort q); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4SVARBPROC) (GLenum target, const GLshort *v); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glActiveTextureARB (GLenum texture); +GLAPI void APIENTRY glClientActiveTextureARB (GLenum texture); +GLAPI void APIENTRY glMultiTexCoord1dARB (GLenum target, GLdouble s); +GLAPI void APIENTRY glMultiTexCoord1dvARB (GLenum target, const GLdouble *v); +GLAPI void APIENTRY glMultiTexCoord1fARB (GLenum target, GLfloat s); +GLAPI void APIENTRY glMultiTexCoord1fvARB (GLenum target, const GLfloat *v); +GLAPI void APIENTRY glMultiTexCoord1iARB (GLenum target, GLint s); +GLAPI void APIENTRY glMultiTexCoord1ivARB (GLenum target, const GLint *v); +GLAPI void APIENTRY glMultiTexCoord1sARB (GLenum target, GLshort s); +GLAPI void APIENTRY glMultiTexCoord1svARB (GLenum target, const GLshort *v); +GLAPI void APIENTRY glMultiTexCoord2dARB (GLenum target, GLdouble s, GLdouble t); +GLAPI void APIENTRY glMultiTexCoord2dvARB (GLenum target, const GLdouble *v); +GLAPI void APIENTRY glMultiTexCoord2fARB (GLenum target, GLfloat s, GLfloat t); +GLAPI void APIENTRY glMultiTexCoord2fvARB (GLenum target, const GLfloat *v); +GLAPI void APIENTRY glMultiTexCoord2iARB (GLenum target, GLint s, GLint t); +GLAPI void APIENTRY glMultiTexCoord2ivARB (GLenum target, const GLint *v); +GLAPI void APIENTRY glMultiTexCoord2sARB (GLenum target, GLshort s, GLshort t); +GLAPI void APIENTRY glMultiTexCoord2svARB (GLenum target, const GLshort *v); +GLAPI void APIENTRY glMultiTexCoord3dARB (GLenum target, GLdouble s, GLdouble t, GLdouble r); +GLAPI void APIENTRY glMultiTexCoord3dvARB (GLenum target, const GLdouble *v); +GLAPI void APIENTRY glMultiTexCoord3fARB (GLenum target, GLfloat s, GLfloat t, GLfloat r); +GLAPI void APIENTRY glMultiTexCoord3fvARB (GLenum target, const GLfloat *v); +GLAPI void APIENTRY glMultiTexCoord3iARB (GLenum target, GLint s, GLint t, GLint r); +GLAPI void APIENTRY glMultiTexCoord3ivARB (GLenum target, const GLint *v); +GLAPI void APIENTRY glMultiTexCoord3sARB (GLenum target, GLshort s, GLshort t, GLshort r); +GLAPI void APIENTRY glMultiTexCoord3svARB (GLenum target, const GLshort *v); +GLAPI void APIENTRY glMultiTexCoord4dARB (GLenum target, GLdouble s, GLdouble t, GLdouble r, GLdouble q); +GLAPI void APIENTRY glMultiTexCoord4dvARB (GLenum target, const GLdouble *v); +GLAPI void APIENTRY glMultiTexCoord4fARB (GLenum target, GLfloat s, GLfloat t, GLfloat r, GLfloat q); +GLAPI void APIENTRY glMultiTexCoord4fvARB (GLenum target, const GLfloat *v); +GLAPI void APIENTRY glMultiTexCoord4iARB (GLenum target, GLint s, GLint t, GLint r, GLint q); +GLAPI void APIENTRY glMultiTexCoord4ivARB (GLenum target, const GLint *v); +GLAPI void APIENTRY glMultiTexCoord4sARB (GLenum target, GLshort s, GLshort t, GLshort r, GLshort q); +GLAPI void APIENTRY glMultiTexCoord4svARB (GLenum target, const GLshort *v); +#endif +#endif /* GL_ARB_multitexture */ + +#ifndef GL_ARB_occlusion_query +#define GL_ARB_occlusion_query 1 +#define GL_QUERY_COUNTER_BITS_ARB 0x8864 +#define GL_CURRENT_QUERY_ARB 0x8865 +#define GL_QUERY_RESULT_ARB 0x8866 +#define GL_QUERY_RESULT_AVAILABLE_ARB 0x8867 +#define GL_SAMPLES_PASSED_ARB 0x8914 +typedef void (APIENTRYP PFNGLGENQUERIESARBPROC) (GLsizei n, GLuint *ids); +typedef void (APIENTRYP PFNGLDELETEQUERIESARBPROC) (GLsizei n, const GLuint *ids); +typedef GLboolean (APIENTRYP PFNGLISQUERYARBPROC) (GLuint id); +typedef void (APIENTRYP PFNGLBEGINQUERYARBPROC) (GLenum target, GLuint id); +typedef void (APIENTRYP PFNGLENDQUERYARBPROC) (GLenum target); +typedef void (APIENTRYP PFNGLGETQUERYIVARBPROC) (GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETQUERYOBJECTIVARBPROC) (GLuint id, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETQUERYOBJECTUIVARBPROC) (GLuint id, GLenum pname, GLuint *params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glGenQueriesARB (GLsizei n, GLuint *ids); +GLAPI void APIENTRY glDeleteQueriesARB (GLsizei n, const GLuint *ids); +GLAPI GLboolean APIENTRY glIsQueryARB (GLuint id); +GLAPI void APIENTRY glBeginQueryARB (GLenum target, GLuint id); +GLAPI void APIENTRY glEndQueryARB (GLenum target); +GLAPI void APIENTRY glGetQueryivARB (GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetQueryObjectivARB (GLuint id, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetQueryObjectuivARB (GLuint id, GLenum pname, GLuint *params); +#endif +#endif /* GL_ARB_occlusion_query */ + +#ifndef GL_ARB_occlusion_query2 +#define GL_ARB_occlusion_query2 1 +#endif /* GL_ARB_occlusion_query2 */ + +#ifndef GL_ARB_pipeline_statistics_query +#define GL_ARB_pipeline_statistics_query 1 +#define GL_VERTICES_SUBMITTED_ARB 0x82EE +#define GL_PRIMITIVES_SUBMITTED_ARB 0x82EF +#define GL_VERTEX_SHADER_INVOCATIONS_ARB 0x82F0 +#define GL_TESS_CONTROL_SHADER_PATCHES_ARB 0x82F1 +#define GL_TESS_EVALUATION_SHADER_INVOCATIONS_ARB 0x82F2 +#define GL_GEOMETRY_SHADER_PRIMITIVES_EMITTED_ARB 0x82F3 +#define GL_FRAGMENT_SHADER_INVOCATIONS_ARB 0x82F4 +#define GL_COMPUTE_SHADER_INVOCATIONS_ARB 0x82F5 +#define GL_CLIPPING_INPUT_PRIMITIVES_ARB 0x82F6 +#define GL_CLIPPING_OUTPUT_PRIMITIVES_ARB 0x82F7 +#endif /* GL_ARB_pipeline_statistics_query */ + +#ifndef GL_ARB_pixel_buffer_object +#define GL_ARB_pixel_buffer_object 1 +#define GL_PIXEL_PACK_BUFFER_ARB 0x88EB +#define GL_PIXEL_UNPACK_BUFFER_ARB 0x88EC +#define GL_PIXEL_PACK_BUFFER_BINDING_ARB 0x88ED +#define GL_PIXEL_UNPACK_BUFFER_BINDING_ARB 0x88EF +#endif /* GL_ARB_pixel_buffer_object */ + +#ifndef GL_ARB_point_parameters +#define GL_ARB_point_parameters 1 +#define GL_POINT_SIZE_MIN_ARB 0x8126 +#define GL_POINT_SIZE_MAX_ARB 0x8127 +#define GL_POINT_FADE_THRESHOLD_SIZE_ARB 0x8128 +#define GL_POINT_DISTANCE_ATTENUATION_ARB 0x8129 +typedef void (APIENTRYP PFNGLPOINTPARAMETERFARBPROC) (GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLPOINTPARAMETERFVARBPROC) (GLenum pname, const GLfloat *params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glPointParameterfARB (GLenum pname, GLfloat param); +GLAPI void APIENTRY glPointParameterfvARB (GLenum pname, const GLfloat *params); +#endif +#endif /* GL_ARB_point_parameters */ + +#ifndef GL_ARB_point_sprite +#define GL_ARB_point_sprite 1 +#define GL_POINT_SPRITE_ARB 0x8861 +#define GL_COORD_REPLACE_ARB 0x8862 +#endif /* GL_ARB_point_sprite */ + +#ifndef GL_ARB_program_interface_query +#define GL_ARB_program_interface_query 1 +#endif /* GL_ARB_program_interface_query */ + +#ifndef GL_ARB_provoking_vertex +#define GL_ARB_provoking_vertex 1 +#endif /* GL_ARB_provoking_vertex */ + +#ifndef GL_ARB_query_buffer_object +#define GL_ARB_query_buffer_object 1 +#endif /* GL_ARB_query_buffer_object */ + +#ifndef GL_ARB_robust_buffer_access_behavior +#define GL_ARB_robust_buffer_access_behavior 1 +#endif /* GL_ARB_robust_buffer_access_behavior */ + +#ifndef GL_ARB_robustness +#define GL_ARB_robustness 1 +#define GL_CONTEXT_FLAG_ROBUST_ACCESS_BIT_ARB 0x00000004 +#define GL_LOSE_CONTEXT_ON_RESET_ARB 0x8252 +#define GL_GUILTY_CONTEXT_RESET_ARB 0x8253 +#define GL_INNOCENT_CONTEXT_RESET_ARB 0x8254 +#define GL_UNKNOWN_CONTEXT_RESET_ARB 0x8255 +#define GL_RESET_NOTIFICATION_STRATEGY_ARB 0x8256 +#define GL_NO_RESET_NOTIFICATION_ARB 0x8261 +typedef GLenum (APIENTRYP PFNGLGETGRAPHICSRESETSTATUSARBPROC) (void); +typedef void (APIENTRYP PFNGLGETNTEXIMAGEARBPROC) (GLenum target, GLint level, GLenum format, GLenum type, GLsizei bufSize, void *img); +typedef void (APIENTRYP PFNGLREADNPIXELSARBPROC) (GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLsizei bufSize, void *data); +typedef void (APIENTRYP PFNGLGETNCOMPRESSEDTEXIMAGEARBPROC) (GLenum target, GLint lod, GLsizei bufSize, void *img); +typedef void (APIENTRYP PFNGLGETNUNIFORMFVARBPROC) (GLuint program, GLint location, GLsizei bufSize, GLfloat *params); +typedef void (APIENTRYP PFNGLGETNUNIFORMIVARBPROC) (GLuint program, GLint location, GLsizei bufSize, GLint *params); +typedef void (APIENTRYP PFNGLGETNUNIFORMUIVARBPROC) (GLuint program, GLint location, GLsizei bufSize, GLuint *params); +typedef void (APIENTRYP PFNGLGETNUNIFORMDVARBPROC) (GLuint program, GLint location, GLsizei bufSize, GLdouble *params); +typedef void (APIENTRYP PFNGLGETNMAPDVARBPROC) (GLenum target, GLenum query, GLsizei bufSize, GLdouble *v); +typedef void (APIENTRYP PFNGLGETNMAPFVARBPROC) (GLenum target, GLenum query, GLsizei bufSize, GLfloat *v); +typedef void (APIENTRYP PFNGLGETNMAPIVARBPROC) (GLenum target, GLenum query, GLsizei bufSize, GLint *v); +typedef void (APIENTRYP PFNGLGETNPIXELMAPFVARBPROC) (GLenum map, GLsizei bufSize, GLfloat *values); +typedef void (APIENTRYP PFNGLGETNPIXELMAPUIVARBPROC) (GLenum map, GLsizei bufSize, GLuint *values); +typedef void (APIENTRYP PFNGLGETNPIXELMAPUSVARBPROC) (GLenum map, GLsizei bufSize, GLushort *values); +typedef void (APIENTRYP PFNGLGETNPOLYGONSTIPPLEARBPROC) (GLsizei bufSize, GLubyte *pattern); +typedef void (APIENTRYP PFNGLGETNCOLORTABLEARBPROC) (GLenum target, GLenum format, GLenum type, GLsizei bufSize, void *table); +typedef void (APIENTRYP PFNGLGETNCONVOLUTIONFILTERARBPROC) (GLenum target, GLenum format, GLenum type, GLsizei bufSize, void *image); +typedef void (APIENTRYP PFNGLGETNSEPARABLEFILTERARBPROC) (GLenum target, GLenum format, GLenum type, GLsizei rowBufSize, void *row, GLsizei columnBufSize, void *column, void *span); +typedef void (APIENTRYP PFNGLGETNHISTOGRAMARBPROC) (GLenum target, GLboolean reset, GLenum format, GLenum type, GLsizei bufSize, void *values); +typedef void (APIENTRYP PFNGLGETNMINMAXARBPROC) (GLenum target, GLboolean reset, GLenum format, GLenum type, GLsizei bufSize, void *values); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI GLenum APIENTRY glGetGraphicsResetStatusARB (void); +GLAPI void APIENTRY glGetnTexImageARB (GLenum target, GLint level, GLenum format, GLenum type, GLsizei bufSize, void *img); +GLAPI void APIENTRY glReadnPixelsARB (GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLsizei bufSize, void *data); +GLAPI void APIENTRY glGetnCompressedTexImageARB (GLenum target, GLint lod, GLsizei bufSize, void *img); +GLAPI void APIENTRY glGetnUniformfvARB (GLuint program, GLint location, GLsizei bufSize, GLfloat *params); +GLAPI void APIENTRY glGetnUniformivARB (GLuint program, GLint location, GLsizei bufSize, GLint *params); +GLAPI void APIENTRY glGetnUniformuivARB (GLuint program, GLint location, GLsizei bufSize, GLuint *params); +GLAPI void APIENTRY glGetnUniformdvARB (GLuint program, GLint location, GLsizei bufSize, GLdouble *params); +GLAPI void APIENTRY glGetnMapdvARB (GLenum target, GLenum query, GLsizei bufSize, GLdouble *v); +GLAPI void APIENTRY glGetnMapfvARB (GLenum target, GLenum query, GLsizei bufSize, GLfloat *v); +GLAPI void APIENTRY glGetnMapivARB (GLenum target, GLenum query, GLsizei bufSize, GLint *v); +GLAPI void APIENTRY glGetnPixelMapfvARB (GLenum map, GLsizei bufSize, GLfloat *values); +GLAPI void APIENTRY glGetnPixelMapuivARB (GLenum map, GLsizei bufSize, GLuint *values); +GLAPI void APIENTRY glGetnPixelMapusvARB (GLenum map, GLsizei bufSize, GLushort *values); +GLAPI void APIENTRY glGetnPolygonStippleARB (GLsizei bufSize, GLubyte *pattern); +GLAPI void APIENTRY glGetnColorTableARB (GLenum target, GLenum format, GLenum type, GLsizei bufSize, void *table); +GLAPI void APIENTRY glGetnConvolutionFilterARB (GLenum target, GLenum format, GLenum type, GLsizei bufSize, void *image); +GLAPI void APIENTRY glGetnSeparableFilterARB (GLenum target, GLenum format, GLenum type, GLsizei rowBufSize, void *row, GLsizei columnBufSize, void *column, void *span); +GLAPI void APIENTRY glGetnHistogramARB (GLenum target, GLboolean reset, GLenum format, GLenum type, GLsizei bufSize, void *values); +GLAPI void APIENTRY glGetnMinmaxARB (GLenum target, GLboolean reset, GLenum format, GLenum type, GLsizei bufSize, void *values); +#endif +#endif /* GL_ARB_robustness */ + +#ifndef GL_ARB_robustness_isolation +#define GL_ARB_robustness_isolation 1 +#endif /* GL_ARB_robustness_isolation */ + +#ifndef GL_ARB_sample_shading +#define GL_ARB_sample_shading 1 +#define GL_SAMPLE_SHADING_ARB 0x8C36 +#define GL_MIN_SAMPLE_SHADING_VALUE_ARB 0x8C37 +typedef void (APIENTRYP PFNGLMINSAMPLESHADINGARBPROC) (GLfloat value); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glMinSampleShadingARB (GLfloat value); +#endif +#endif /* GL_ARB_sample_shading */ + +#ifndef GL_ARB_sampler_objects +#define GL_ARB_sampler_objects 1 +#endif /* GL_ARB_sampler_objects */ + +#ifndef GL_ARB_seamless_cube_map +#define GL_ARB_seamless_cube_map 1 +#endif /* GL_ARB_seamless_cube_map */ + +#ifndef GL_ARB_seamless_cubemap_per_texture +#define GL_ARB_seamless_cubemap_per_texture 1 +#endif /* GL_ARB_seamless_cubemap_per_texture */ + +#ifndef GL_ARB_separate_shader_objects +#define GL_ARB_separate_shader_objects 1 +#endif /* GL_ARB_separate_shader_objects */ + +#ifndef GL_ARB_shader_atomic_counters +#define GL_ARB_shader_atomic_counters 1 +#endif /* GL_ARB_shader_atomic_counters */ + +#ifndef GL_ARB_shader_bit_encoding +#define GL_ARB_shader_bit_encoding 1 +#endif /* GL_ARB_shader_bit_encoding */ + +#ifndef GL_ARB_shader_draw_parameters +#define GL_ARB_shader_draw_parameters 1 +#endif /* GL_ARB_shader_draw_parameters */ + +#ifndef GL_ARB_shader_group_vote +#define GL_ARB_shader_group_vote 1 +#endif /* GL_ARB_shader_group_vote */ + +#ifndef GL_ARB_shader_image_load_store +#define GL_ARB_shader_image_load_store 1 +#endif /* GL_ARB_shader_image_load_store */ + +#ifndef GL_ARB_shader_image_size +#define GL_ARB_shader_image_size 1 +#endif /* GL_ARB_shader_image_size */ + +#ifndef GL_ARB_shader_objects +#define GL_ARB_shader_objects 1 +#ifdef __APPLE__ +typedef void *GLhandleARB; +#else +typedef unsigned int GLhandleARB; +#endif +typedef char GLcharARB; +#define GL_PROGRAM_OBJECT_ARB 0x8B40 +#define GL_SHADER_OBJECT_ARB 0x8B48 +#define GL_OBJECT_TYPE_ARB 0x8B4E +#define GL_OBJECT_SUBTYPE_ARB 0x8B4F +#define GL_FLOAT_VEC2_ARB 0x8B50 +#define GL_FLOAT_VEC3_ARB 0x8B51 +#define GL_FLOAT_VEC4_ARB 0x8B52 +#define GL_INT_VEC2_ARB 0x8B53 +#define GL_INT_VEC3_ARB 0x8B54 +#define GL_INT_VEC4_ARB 0x8B55 +#define GL_BOOL_ARB 0x8B56 +#define GL_BOOL_VEC2_ARB 0x8B57 +#define GL_BOOL_VEC3_ARB 0x8B58 +#define GL_BOOL_VEC4_ARB 0x8B59 +#define GL_FLOAT_MAT2_ARB 0x8B5A +#define GL_FLOAT_MAT3_ARB 0x8B5B +#define GL_FLOAT_MAT4_ARB 0x8B5C +#define GL_SAMPLER_1D_ARB 0x8B5D +#define GL_SAMPLER_2D_ARB 0x8B5E +#define GL_SAMPLER_3D_ARB 0x8B5F +#define GL_SAMPLER_CUBE_ARB 0x8B60 +#define GL_SAMPLER_1D_SHADOW_ARB 0x8B61 +#define GL_SAMPLER_2D_SHADOW_ARB 0x8B62 +#define GL_SAMPLER_2D_RECT_ARB 0x8B63 +#define GL_SAMPLER_2D_RECT_SHADOW_ARB 0x8B64 +#define GL_OBJECT_DELETE_STATUS_ARB 0x8B80 +#define GL_OBJECT_COMPILE_STATUS_ARB 0x8B81 +#define GL_OBJECT_LINK_STATUS_ARB 0x8B82 +#define GL_OBJECT_VALIDATE_STATUS_ARB 0x8B83 +#define GL_OBJECT_INFO_LOG_LENGTH_ARB 0x8B84 +#define GL_OBJECT_ATTACHED_OBJECTS_ARB 0x8B85 +#define GL_OBJECT_ACTIVE_UNIFORMS_ARB 0x8B86 +#define GL_OBJECT_ACTIVE_UNIFORM_MAX_LENGTH_ARB 0x8B87 +#define GL_OBJECT_SHADER_SOURCE_LENGTH_ARB 0x8B88 +typedef void (APIENTRYP PFNGLDELETEOBJECTARBPROC) (GLhandleARB obj); +typedef GLhandleARB (APIENTRYP PFNGLGETHANDLEARBPROC) (GLenum pname); +typedef void (APIENTRYP PFNGLDETACHOBJECTARBPROC) (GLhandleARB containerObj, GLhandleARB attachedObj); +typedef GLhandleARB (APIENTRYP PFNGLCREATESHADEROBJECTARBPROC) (GLenum shaderType); +typedef void (APIENTRYP PFNGLSHADERSOURCEARBPROC) (GLhandleARB shaderObj, GLsizei count, const GLcharARB **string, const GLint *length); +typedef void (APIENTRYP PFNGLCOMPILESHADERARBPROC) (GLhandleARB shaderObj); +typedef GLhandleARB (APIENTRYP PFNGLCREATEPROGRAMOBJECTARBPROC) (void); +typedef void (APIENTRYP PFNGLATTACHOBJECTARBPROC) (GLhandleARB containerObj, GLhandleARB obj); +typedef void (APIENTRYP PFNGLLINKPROGRAMARBPROC) (GLhandleARB programObj); +typedef void (APIENTRYP PFNGLUSEPROGRAMOBJECTARBPROC) (GLhandleARB programObj); +typedef void (APIENTRYP PFNGLVALIDATEPROGRAMARBPROC) (GLhandleARB programObj); +typedef void (APIENTRYP PFNGLUNIFORM1FARBPROC) (GLint location, GLfloat v0); +typedef void (APIENTRYP PFNGLUNIFORM2FARBPROC) (GLint location, GLfloat v0, GLfloat v1); +typedef void (APIENTRYP PFNGLUNIFORM3FARBPROC) (GLint location, GLfloat v0, GLfloat v1, GLfloat v2); +typedef void (APIENTRYP PFNGLUNIFORM4FARBPROC) (GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); +typedef void (APIENTRYP PFNGLUNIFORM1IARBPROC) (GLint location, GLint v0); +typedef void (APIENTRYP PFNGLUNIFORM2IARBPROC) (GLint location, GLint v0, GLint v1); +typedef void (APIENTRYP PFNGLUNIFORM3IARBPROC) (GLint location, GLint v0, GLint v1, GLint v2); +typedef void (APIENTRYP PFNGLUNIFORM4IARBPROC) (GLint location, GLint v0, GLint v1, GLint v2, GLint v3); +typedef void (APIENTRYP PFNGLUNIFORM1FVARBPROC) (GLint location, GLsizei count, const GLfloat *value); +typedef void (APIENTRYP PFNGLUNIFORM2FVARBPROC) (GLint location, GLsizei count, const GLfloat *value); +typedef void (APIENTRYP PFNGLUNIFORM3FVARBPROC) (GLint location, GLsizei count, const GLfloat *value); +typedef void (APIENTRYP PFNGLUNIFORM4FVARBPROC) (GLint location, GLsizei count, const GLfloat *value); +typedef void (APIENTRYP PFNGLUNIFORM1IVARBPROC) (GLint location, GLsizei count, const GLint *value); +typedef void (APIENTRYP PFNGLUNIFORM2IVARBPROC) (GLint location, GLsizei count, const GLint *value); +typedef void (APIENTRYP PFNGLUNIFORM3IVARBPROC) (GLint location, GLsizei count, const GLint *value); +typedef void (APIENTRYP PFNGLUNIFORM4IVARBPROC) (GLint location, GLsizei count, const GLint *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX2FVARBPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX3FVARBPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX4FVARBPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLGETOBJECTPARAMETERFVARBPROC) (GLhandleARB obj, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETOBJECTPARAMETERIVARBPROC) (GLhandleARB obj, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETINFOLOGARBPROC) (GLhandleARB obj, GLsizei maxLength, GLsizei *length, GLcharARB *infoLog); +typedef void (APIENTRYP PFNGLGETATTACHEDOBJECTSARBPROC) (GLhandleARB containerObj, GLsizei maxCount, GLsizei *count, GLhandleARB *obj); +typedef GLint (APIENTRYP PFNGLGETUNIFORMLOCATIONARBPROC) (GLhandleARB programObj, const GLcharARB *name); +typedef void (APIENTRYP PFNGLGETACTIVEUNIFORMARBPROC) (GLhandleARB programObj, GLuint index, GLsizei maxLength, GLsizei *length, GLint *size, GLenum *type, GLcharARB *name); +typedef void (APIENTRYP PFNGLGETUNIFORMFVARBPROC) (GLhandleARB programObj, GLint location, GLfloat *params); +typedef void (APIENTRYP PFNGLGETUNIFORMIVARBPROC) (GLhandleARB programObj, GLint location, GLint *params); +typedef void (APIENTRYP PFNGLGETSHADERSOURCEARBPROC) (GLhandleARB obj, GLsizei maxLength, GLsizei *length, GLcharARB *source); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glDeleteObjectARB (GLhandleARB obj); +GLAPI GLhandleARB APIENTRY glGetHandleARB (GLenum pname); +GLAPI void APIENTRY glDetachObjectARB (GLhandleARB containerObj, GLhandleARB attachedObj); +GLAPI GLhandleARB APIENTRY glCreateShaderObjectARB (GLenum shaderType); +GLAPI void APIENTRY glShaderSourceARB (GLhandleARB shaderObj, GLsizei count, const GLcharARB **string, const GLint *length); +GLAPI void APIENTRY glCompileShaderARB (GLhandleARB shaderObj); +GLAPI GLhandleARB APIENTRY glCreateProgramObjectARB (void); +GLAPI void APIENTRY glAttachObjectARB (GLhandleARB containerObj, GLhandleARB obj); +GLAPI void APIENTRY glLinkProgramARB (GLhandleARB programObj); +GLAPI void APIENTRY glUseProgramObjectARB (GLhandleARB programObj); +GLAPI void APIENTRY glValidateProgramARB (GLhandleARB programObj); +GLAPI void APIENTRY glUniform1fARB (GLint location, GLfloat v0); +GLAPI void APIENTRY glUniform2fARB (GLint location, GLfloat v0, GLfloat v1); +GLAPI void APIENTRY glUniform3fARB (GLint location, GLfloat v0, GLfloat v1, GLfloat v2); +GLAPI void APIENTRY glUniform4fARB (GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); +GLAPI void APIENTRY glUniform1iARB (GLint location, GLint v0); +GLAPI void APIENTRY glUniform2iARB (GLint location, GLint v0, GLint v1); +GLAPI void APIENTRY glUniform3iARB (GLint location, GLint v0, GLint v1, GLint v2); +GLAPI void APIENTRY glUniform4iARB (GLint location, GLint v0, GLint v1, GLint v2, GLint v3); +GLAPI void APIENTRY glUniform1fvARB (GLint location, GLsizei count, const GLfloat *value); +GLAPI void APIENTRY glUniform2fvARB (GLint location, GLsizei count, const GLfloat *value); +GLAPI void APIENTRY glUniform3fvARB (GLint location, GLsizei count, const GLfloat *value); +GLAPI void APIENTRY glUniform4fvARB (GLint location, GLsizei count, const GLfloat *value); +GLAPI void APIENTRY glUniform1ivARB (GLint location, GLsizei count, const GLint *value); +GLAPI void APIENTRY glUniform2ivARB (GLint location, GLsizei count, const GLint *value); +GLAPI void APIENTRY glUniform3ivARB (GLint location, GLsizei count, const GLint *value); +GLAPI void APIENTRY glUniform4ivARB (GLint location, GLsizei count, const GLint *value); +GLAPI void APIENTRY glUniformMatrix2fvARB (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glUniformMatrix3fvARB (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glUniformMatrix4fvARB (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glGetObjectParameterfvARB (GLhandleARB obj, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetObjectParameterivARB (GLhandleARB obj, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetInfoLogARB (GLhandleARB obj, GLsizei maxLength, GLsizei *length, GLcharARB *infoLog); +GLAPI void APIENTRY glGetAttachedObjectsARB (GLhandleARB containerObj, GLsizei maxCount, GLsizei *count, GLhandleARB *obj); +GLAPI GLint APIENTRY glGetUniformLocationARB (GLhandleARB programObj, const GLcharARB *name); +GLAPI void APIENTRY glGetActiveUniformARB (GLhandleARB programObj, GLuint index, GLsizei maxLength, GLsizei *length, GLint *size, GLenum *type, GLcharARB *name); +GLAPI void APIENTRY glGetUniformfvARB (GLhandleARB programObj, GLint location, GLfloat *params); +GLAPI void APIENTRY glGetUniformivARB (GLhandleARB programObj, GLint location, GLint *params); +GLAPI void APIENTRY glGetShaderSourceARB (GLhandleARB obj, GLsizei maxLength, GLsizei *length, GLcharARB *source); +#endif +#endif /* GL_ARB_shader_objects */ + +#ifndef GL_ARB_shader_precision +#define GL_ARB_shader_precision 1 +#endif /* GL_ARB_shader_precision */ + +#ifndef GL_ARB_shader_stencil_export +#define GL_ARB_shader_stencil_export 1 +#endif /* GL_ARB_shader_stencil_export */ + +#ifndef GL_ARB_shader_storage_buffer_object +#define GL_ARB_shader_storage_buffer_object 1 +#endif /* GL_ARB_shader_storage_buffer_object */ + +#ifndef GL_ARB_shader_subroutine +#define GL_ARB_shader_subroutine 1 +#endif /* GL_ARB_shader_subroutine */ + +#ifndef GL_ARB_shader_texture_image_samples +#define GL_ARB_shader_texture_image_samples 1 +#endif /* GL_ARB_shader_texture_image_samples */ + +#ifndef GL_ARB_shader_texture_lod +#define GL_ARB_shader_texture_lod 1 +#endif /* GL_ARB_shader_texture_lod */ + +#ifndef GL_ARB_shading_language_100 +#define GL_ARB_shading_language_100 1 +#define GL_SHADING_LANGUAGE_VERSION_ARB 0x8B8C +#endif /* GL_ARB_shading_language_100 */ + +#ifndef GL_ARB_shading_language_420pack +#define GL_ARB_shading_language_420pack 1 +#endif /* GL_ARB_shading_language_420pack */ + +#ifndef GL_ARB_shading_language_include +#define GL_ARB_shading_language_include 1 +#define GL_SHADER_INCLUDE_ARB 0x8DAE +#define GL_NAMED_STRING_LENGTH_ARB 0x8DE9 +#define GL_NAMED_STRING_TYPE_ARB 0x8DEA +typedef void (APIENTRYP PFNGLNAMEDSTRINGARBPROC) (GLenum type, GLint namelen, const GLchar *name, GLint stringlen, const GLchar *string); +typedef void (APIENTRYP PFNGLDELETENAMEDSTRINGARBPROC) (GLint namelen, const GLchar *name); +typedef void (APIENTRYP PFNGLCOMPILESHADERINCLUDEARBPROC) (GLuint shader, GLsizei count, const GLchar *const*path, const GLint *length); +typedef GLboolean (APIENTRYP PFNGLISNAMEDSTRINGARBPROC) (GLint namelen, const GLchar *name); +typedef void (APIENTRYP PFNGLGETNAMEDSTRINGARBPROC) (GLint namelen, const GLchar *name, GLsizei bufSize, GLint *stringlen, GLchar *string); +typedef void (APIENTRYP PFNGLGETNAMEDSTRINGIVARBPROC) (GLint namelen, const GLchar *name, GLenum pname, GLint *params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glNamedStringARB (GLenum type, GLint namelen, const GLchar *name, GLint stringlen, const GLchar *string); +GLAPI void APIENTRY glDeleteNamedStringARB (GLint namelen, const GLchar *name); +GLAPI void APIENTRY glCompileShaderIncludeARB (GLuint shader, GLsizei count, const GLchar *const*path, const GLint *length); +GLAPI GLboolean APIENTRY glIsNamedStringARB (GLint namelen, const GLchar *name); +GLAPI void APIENTRY glGetNamedStringARB (GLint namelen, const GLchar *name, GLsizei bufSize, GLint *stringlen, GLchar *string); +GLAPI void APIENTRY glGetNamedStringivARB (GLint namelen, const GLchar *name, GLenum pname, GLint *params); +#endif +#endif /* GL_ARB_shading_language_include */ + +#ifndef GL_ARB_shading_language_packing +#define GL_ARB_shading_language_packing 1 +#endif /* GL_ARB_shading_language_packing */ + +#ifndef GL_ARB_shadow +#define GL_ARB_shadow 1 +#define GL_TEXTURE_COMPARE_MODE_ARB 0x884C +#define GL_TEXTURE_COMPARE_FUNC_ARB 0x884D +#define GL_COMPARE_R_TO_TEXTURE_ARB 0x884E +#endif /* GL_ARB_shadow */ + +#ifndef GL_ARB_shadow_ambient +#define GL_ARB_shadow_ambient 1 +#define GL_TEXTURE_COMPARE_FAIL_VALUE_ARB 0x80BF +#endif /* GL_ARB_shadow_ambient */ + +#ifndef GL_ARB_sparse_buffer +#define GL_ARB_sparse_buffer 1 +#define GL_SPARSE_STORAGE_BIT_ARB 0x0400 +#define GL_SPARSE_BUFFER_PAGE_SIZE_ARB 0x82F8 +typedef void (APIENTRYP PFNGLBUFFERPAGECOMMITMENTARBPROC) (GLenum target, GLintptr offset, GLsizeiptr size, GLboolean commit); +typedef void (APIENTRYP PFNGLNAMEDBUFFERPAGECOMMITMENTEXTPROC) (GLuint buffer, GLintptr offset, GLsizeiptr size, GLboolean commit); +typedef void (APIENTRYP PFNGLNAMEDBUFFERPAGECOMMITMENTARBPROC) (GLuint buffer, GLintptr offset, GLsizeiptr size, GLboolean commit); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBufferPageCommitmentARB (GLenum target, GLintptr offset, GLsizeiptr size, GLboolean commit); +GLAPI void APIENTRY glNamedBufferPageCommitmentEXT (GLuint buffer, GLintptr offset, GLsizeiptr size, GLboolean commit); +GLAPI void APIENTRY glNamedBufferPageCommitmentARB (GLuint buffer, GLintptr offset, GLsizeiptr size, GLboolean commit); +#endif +#endif /* GL_ARB_sparse_buffer */ + +#ifndef GL_ARB_sparse_texture +#define GL_ARB_sparse_texture 1 +#define GL_TEXTURE_SPARSE_ARB 0x91A6 +#define GL_VIRTUAL_PAGE_SIZE_INDEX_ARB 0x91A7 +#define GL_NUM_SPARSE_LEVELS_ARB 0x91AA +#define GL_NUM_VIRTUAL_PAGE_SIZES_ARB 0x91A8 +#define GL_VIRTUAL_PAGE_SIZE_X_ARB 0x9195 +#define GL_VIRTUAL_PAGE_SIZE_Y_ARB 0x9196 +#define GL_VIRTUAL_PAGE_SIZE_Z_ARB 0x9197 +#define GL_MAX_SPARSE_TEXTURE_SIZE_ARB 0x9198 +#define GL_MAX_SPARSE_3D_TEXTURE_SIZE_ARB 0x9199 +#define GL_MAX_SPARSE_ARRAY_TEXTURE_LAYERS_ARB 0x919A +#define GL_SPARSE_TEXTURE_FULL_ARRAY_CUBE_MIPMAPS_ARB 0x91A9 +typedef void (APIENTRYP PFNGLTEXPAGECOMMITMENTARBPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLboolean commit); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glTexPageCommitmentARB (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLboolean commit); +#endif +#endif /* GL_ARB_sparse_texture */ + +#ifndef GL_ARB_stencil_texturing +#define GL_ARB_stencil_texturing 1 +#endif /* GL_ARB_stencil_texturing */ + +#ifndef GL_ARB_sync +#define GL_ARB_sync 1 +#endif /* GL_ARB_sync */ + +#ifndef GL_ARB_tessellation_shader +#define GL_ARB_tessellation_shader 1 +#endif /* GL_ARB_tessellation_shader */ + +#ifndef GL_ARB_texture_barrier +#define GL_ARB_texture_barrier 1 +#endif /* GL_ARB_texture_barrier */ + +#ifndef GL_ARB_texture_border_clamp +#define GL_ARB_texture_border_clamp 1 +#define GL_CLAMP_TO_BORDER_ARB 0x812D +#endif /* GL_ARB_texture_border_clamp */ + +#ifndef GL_ARB_texture_buffer_object +#define GL_ARB_texture_buffer_object 1 +#define GL_TEXTURE_BUFFER_ARB 0x8C2A +#define GL_MAX_TEXTURE_BUFFER_SIZE_ARB 0x8C2B +#define GL_TEXTURE_BINDING_BUFFER_ARB 0x8C2C +#define GL_TEXTURE_BUFFER_DATA_STORE_BINDING_ARB 0x8C2D +#define GL_TEXTURE_BUFFER_FORMAT_ARB 0x8C2E +typedef void (APIENTRYP PFNGLTEXBUFFERARBPROC) (GLenum target, GLenum internalformat, GLuint buffer); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glTexBufferARB (GLenum target, GLenum internalformat, GLuint buffer); +#endif +#endif /* GL_ARB_texture_buffer_object */ + +#ifndef GL_ARB_texture_buffer_object_rgb32 +#define GL_ARB_texture_buffer_object_rgb32 1 +#endif /* GL_ARB_texture_buffer_object_rgb32 */ + +#ifndef GL_ARB_texture_buffer_range +#define GL_ARB_texture_buffer_range 1 +#endif /* GL_ARB_texture_buffer_range */ + +#ifndef GL_ARB_texture_compression +#define GL_ARB_texture_compression 1 +#define GL_COMPRESSED_ALPHA_ARB 0x84E9 +#define GL_COMPRESSED_LUMINANCE_ARB 0x84EA +#define GL_COMPRESSED_LUMINANCE_ALPHA_ARB 0x84EB +#define GL_COMPRESSED_INTENSITY_ARB 0x84EC +#define GL_COMPRESSED_RGB_ARB 0x84ED +#define GL_COMPRESSED_RGBA_ARB 0x84EE +#define GL_TEXTURE_COMPRESSION_HINT_ARB 0x84EF +#define GL_TEXTURE_COMPRESSED_IMAGE_SIZE_ARB 0x86A0 +#define GL_TEXTURE_COMPRESSED_ARB 0x86A1 +#define GL_NUM_COMPRESSED_TEXTURE_FORMATS_ARB 0x86A2 +#define GL_COMPRESSED_TEXTURE_FORMATS_ARB 0x86A3 +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXIMAGE3DARBPROC) (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const void *data); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXIMAGE2DARBPROC) (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void *data); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXIMAGE1DARBPROC) (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLint border, GLsizei imageSize, const void *data); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXSUBIMAGE3DARBPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *data); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXSUBIMAGE2DARBPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *data); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXSUBIMAGE1DARBPROC) (GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const void *data); +typedef void (APIENTRYP PFNGLGETCOMPRESSEDTEXIMAGEARBPROC) (GLenum target, GLint level, void *img); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glCompressedTexImage3DARB (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const void *data); +GLAPI void APIENTRY glCompressedTexImage2DARB (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void *data); +GLAPI void APIENTRY glCompressedTexImage1DARB (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLint border, GLsizei imageSize, const void *data); +GLAPI void APIENTRY glCompressedTexSubImage3DARB (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *data); +GLAPI void APIENTRY glCompressedTexSubImage2DARB (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *data); +GLAPI void APIENTRY glCompressedTexSubImage1DARB (GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const void *data); +GLAPI void APIENTRY glGetCompressedTexImageARB (GLenum target, GLint level, void *img); +#endif +#endif /* GL_ARB_texture_compression */ + +#ifndef GL_ARB_texture_compression_bptc +#define GL_ARB_texture_compression_bptc 1 +#define GL_COMPRESSED_RGBA_BPTC_UNORM_ARB 0x8E8C +#define GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM_ARB 0x8E8D +#define GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT_ARB 0x8E8E +#define GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_ARB 0x8E8F +#endif /* GL_ARB_texture_compression_bptc */ + +#ifndef GL_ARB_texture_compression_rgtc +#define GL_ARB_texture_compression_rgtc 1 +#endif /* GL_ARB_texture_compression_rgtc */ + +#ifndef GL_ARB_texture_cube_map +#define GL_ARB_texture_cube_map 1 +#define GL_NORMAL_MAP_ARB 0x8511 +#define GL_REFLECTION_MAP_ARB 0x8512 +#define GL_TEXTURE_CUBE_MAP_ARB 0x8513 +#define GL_TEXTURE_BINDING_CUBE_MAP_ARB 0x8514 +#define GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB 0x8515 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_X_ARB 0x8516 +#define GL_TEXTURE_CUBE_MAP_POSITIVE_Y_ARB 0x8517 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_ARB 0x8518 +#define GL_TEXTURE_CUBE_MAP_POSITIVE_Z_ARB 0x8519 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB 0x851A +#define GL_PROXY_TEXTURE_CUBE_MAP_ARB 0x851B +#define GL_MAX_CUBE_MAP_TEXTURE_SIZE_ARB 0x851C +#endif /* GL_ARB_texture_cube_map */ + +#ifndef GL_ARB_texture_cube_map_array +#define GL_ARB_texture_cube_map_array 1 +#define GL_TEXTURE_CUBE_MAP_ARRAY_ARB 0x9009 +#define GL_TEXTURE_BINDING_CUBE_MAP_ARRAY_ARB 0x900A +#define GL_PROXY_TEXTURE_CUBE_MAP_ARRAY_ARB 0x900B +#define GL_SAMPLER_CUBE_MAP_ARRAY_ARB 0x900C +#define GL_SAMPLER_CUBE_MAP_ARRAY_SHADOW_ARB 0x900D +#define GL_INT_SAMPLER_CUBE_MAP_ARRAY_ARB 0x900E +#define GL_UNSIGNED_INT_SAMPLER_CUBE_MAP_ARRAY_ARB 0x900F +#endif /* GL_ARB_texture_cube_map_array */ + +#ifndef GL_ARB_texture_env_add +#define GL_ARB_texture_env_add 1 +#endif /* GL_ARB_texture_env_add */ + +#ifndef GL_ARB_texture_env_combine +#define GL_ARB_texture_env_combine 1 +#define GL_COMBINE_ARB 0x8570 +#define GL_COMBINE_RGB_ARB 0x8571 +#define GL_COMBINE_ALPHA_ARB 0x8572 +#define GL_SOURCE0_RGB_ARB 0x8580 +#define GL_SOURCE1_RGB_ARB 0x8581 +#define GL_SOURCE2_RGB_ARB 0x8582 +#define GL_SOURCE0_ALPHA_ARB 0x8588 +#define GL_SOURCE1_ALPHA_ARB 0x8589 +#define GL_SOURCE2_ALPHA_ARB 0x858A +#define GL_OPERAND0_RGB_ARB 0x8590 +#define GL_OPERAND1_RGB_ARB 0x8591 +#define GL_OPERAND2_RGB_ARB 0x8592 +#define GL_OPERAND0_ALPHA_ARB 0x8598 +#define GL_OPERAND1_ALPHA_ARB 0x8599 +#define GL_OPERAND2_ALPHA_ARB 0x859A +#define GL_RGB_SCALE_ARB 0x8573 +#define GL_ADD_SIGNED_ARB 0x8574 +#define GL_INTERPOLATE_ARB 0x8575 +#define GL_SUBTRACT_ARB 0x84E7 +#define GL_CONSTANT_ARB 0x8576 +#define GL_PRIMARY_COLOR_ARB 0x8577 +#define GL_PREVIOUS_ARB 0x8578 +#endif /* GL_ARB_texture_env_combine */ + +#ifndef GL_ARB_texture_env_crossbar +#define GL_ARB_texture_env_crossbar 1 +#endif /* GL_ARB_texture_env_crossbar */ + +#ifndef GL_ARB_texture_env_dot3 +#define GL_ARB_texture_env_dot3 1 +#define GL_DOT3_RGB_ARB 0x86AE +#define GL_DOT3_RGBA_ARB 0x86AF +#endif /* GL_ARB_texture_env_dot3 */ + +#ifndef GL_ARB_texture_float +#define GL_ARB_texture_float 1 +#define GL_TEXTURE_RED_TYPE_ARB 0x8C10 +#define GL_TEXTURE_GREEN_TYPE_ARB 0x8C11 +#define GL_TEXTURE_BLUE_TYPE_ARB 0x8C12 +#define GL_TEXTURE_ALPHA_TYPE_ARB 0x8C13 +#define GL_TEXTURE_LUMINANCE_TYPE_ARB 0x8C14 +#define GL_TEXTURE_INTENSITY_TYPE_ARB 0x8C15 +#define GL_TEXTURE_DEPTH_TYPE_ARB 0x8C16 +#define GL_UNSIGNED_NORMALIZED_ARB 0x8C17 +#define GL_RGBA32F_ARB 0x8814 +#define GL_RGB32F_ARB 0x8815 +#define GL_ALPHA32F_ARB 0x8816 +#define GL_INTENSITY32F_ARB 0x8817 +#define GL_LUMINANCE32F_ARB 0x8818 +#define GL_LUMINANCE_ALPHA32F_ARB 0x8819 +#define GL_RGBA16F_ARB 0x881A +#define GL_RGB16F_ARB 0x881B +#define GL_ALPHA16F_ARB 0x881C +#define GL_INTENSITY16F_ARB 0x881D +#define GL_LUMINANCE16F_ARB 0x881E +#define GL_LUMINANCE_ALPHA16F_ARB 0x881F +#endif /* GL_ARB_texture_float */ + +#ifndef GL_ARB_texture_gather +#define GL_ARB_texture_gather 1 +#define GL_MIN_PROGRAM_TEXTURE_GATHER_OFFSET_ARB 0x8E5E +#define GL_MAX_PROGRAM_TEXTURE_GATHER_OFFSET_ARB 0x8E5F +#define GL_MAX_PROGRAM_TEXTURE_GATHER_COMPONENTS_ARB 0x8F9F +#endif /* GL_ARB_texture_gather */ + +#ifndef GL_ARB_texture_mirror_clamp_to_edge +#define GL_ARB_texture_mirror_clamp_to_edge 1 +#endif /* GL_ARB_texture_mirror_clamp_to_edge */ + +#ifndef GL_ARB_texture_mirrored_repeat +#define GL_ARB_texture_mirrored_repeat 1 +#define GL_MIRRORED_REPEAT_ARB 0x8370 +#endif /* GL_ARB_texture_mirrored_repeat */ + +#ifndef GL_ARB_texture_multisample +#define GL_ARB_texture_multisample 1 +#endif /* GL_ARB_texture_multisample */ + +#ifndef GL_ARB_texture_non_power_of_two +#define GL_ARB_texture_non_power_of_two 1 +#endif /* GL_ARB_texture_non_power_of_two */ + +#ifndef GL_ARB_texture_query_levels +#define GL_ARB_texture_query_levels 1 +#endif /* GL_ARB_texture_query_levels */ + +#ifndef GL_ARB_texture_query_lod +#define GL_ARB_texture_query_lod 1 +#endif /* GL_ARB_texture_query_lod */ + +#ifndef GL_ARB_texture_rectangle +#define GL_ARB_texture_rectangle 1 +#define GL_TEXTURE_RECTANGLE_ARB 0x84F5 +#define GL_TEXTURE_BINDING_RECTANGLE_ARB 0x84F6 +#define GL_PROXY_TEXTURE_RECTANGLE_ARB 0x84F7 +#define GL_MAX_RECTANGLE_TEXTURE_SIZE_ARB 0x84F8 +#endif /* GL_ARB_texture_rectangle */ + +#ifndef GL_ARB_texture_rg +#define GL_ARB_texture_rg 1 +#endif /* GL_ARB_texture_rg */ + +#ifndef GL_ARB_texture_rgb10_a2ui +#define GL_ARB_texture_rgb10_a2ui 1 +#endif /* GL_ARB_texture_rgb10_a2ui */ + +#ifndef GL_ARB_texture_stencil8 +#define GL_ARB_texture_stencil8 1 +#endif /* GL_ARB_texture_stencil8 */ + +#ifndef GL_ARB_texture_storage +#define GL_ARB_texture_storage 1 +#endif /* GL_ARB_texture_storage */ + +#ifndef GL_ARB_texture_storage_multisample +#define GL_ARB_texture_storage_multisample 1 +#endif /* GL_ARB_texture_storage_multisample */ + +#ifndef GL_ARB_texture_swizzle +#define GL_ARB_texture_swizzle 1 +#endif /* GL_ARB_texture_swizzle */ + +#ifndef GL_ARB_texture_view +#define GL_ARB_texture_view 1 +#endif /* GL_ARB_texture_view */ + +#ifndef GL_ARB_timer_query +#define GL_ARB_timer_query 1 +#endif /* GL_ARB_timer_query */ + +#ifndef GL_ARB_transform_feedback2 +#define GL_ARB_transform_feedback2 1 +#endif /* GL_ARB_transform_feedback2 */ + +#ifndef GL_ARB_transform_feedback3 +#define GL_ARB_transform_feedback3 1 +#endif /* GL_ARB_transform_feedback3 */ + +#ifndef GL_ARB_transform_feedback_instanced +#define GL_ARB_transform_feedback_instanced 1 +#endif /* GL_ARB_transform_feedback_instanced */ + +#ifndef GL_ARB_transform_feedback_overflow_query +#define GL_ARB_transform_feedback_overflow_query 1 +#define GL_TRANSFORM_FEEDBACK_OVERFLOW_ARB 0x82EC +#define GL_TRANSFORM_FEEDBACK_STREAM_OVERFLOW_ARB 0x82ED +#endif /* GL_ARB_transform_feedback_overflow_query */ + +#ifndef GL_ARB_transpose_matrix +#define GL_ARB_transpose_matrix 1 +#define GL_TRANSPOSE_MODELVIEW_MATRIX_ARB 0x84E3 +#define GL_TRANSPOSE_PROJECTION_MATRIX_ARB 0x84E4 +#define GL_TRANSPOSE_TEXTURE_MATRIX_ARB 0x84E5 +#define GL_TRANSPOSE_COLOR_MATRIX_ARB 0x84E6 +typedef void (APIENTRYP PFNGLLOADTRANSPOSEMATRIXFARBPROC) (const GLfloat *m); +typedef void (APIENTRYP PFNGLLOADTRANSPOSEMATRIXDARBPROC) (const GLdouble *m); +typedef void (APIENTRYP PFNGLMULTTRANSPOSEMATRIXFARBPROC) (const GLfloat *m); +typedef void (APIENTRYP PFNGLMULTTRANSPOSEMATRIXDARBPROC) (const GLdouble *m); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glLoadTransposeMatrixfARB (const GLfloat *m); +GLAPI void APIENTRY glLoadTransposeMatrixdARB (const GLdouble *m); +GLAPI void APIENTRY glMultTransposeMatrixfARB (const GLfloat *m); +GLAPI void APIENTRY glMultTransposeMatrixdARB (const GLdouble *m); +#endif +#endif /* GL_ARB_transpose_matrix */ + +#ifndef GL_ARB_uniform_buffer_object +#define GL_ARB_uniform_buffer_object 1 +#endif /* GL_ARB_uniform_buffer_object */ + +#ifndef GL_ARB_vertex_array_bgra +#define GL_ARB_vertex_array_bgra 1 +#endif /* GL_ARB_vertex_array_bgra */ + +#ifndef GL_ARB_vertex_array_object +#define GL_ARB_vertex_array_object 1 +#endif /* GL_ARB_vertex_array_object */ + +#ifndef GL_ARB_vertex_attrib_64bit +#define GL_ARB_vertex_attrib_64bit 1 +#endif /* GL_ARB_vertex_attrib_64bit */ + +#ifndef GL_ARB_vertex_attrib_binding +#define GL_ARB_vertex_attrib_binding 1 +#endif /* GL_ARB_vertex_attrib_binding */ + +#ifndef GL_ARB_vertex_blend +#define GL_ARB_vertex_blend 1 +#define GL_MAX_VERTEX_UNITS_ARB 0x86A4 +#define GL_ACTIVE_VERTEX_UNITS_ARB 0x86A5 +#define GL_WEIGHT_SUM_UNITY_ARB 0x86A6 +#define GL_VERTEX_BLEND_ARB 0x86A7 +#define GL_CURRENT_WEIGHT_ARB 0x86A8 +#define GL_WEIGHT_ARRAY_TYPE_ARB 0x86A9 +#define GL_WEIGHT_ARRAY_STRIDE_ARB 0x86AA +#define GL_WEIGHT_ARRAY_SIZE_ARB 0x86AB +#define GL_WEIGHT_ARRAY_POINTER_ARB 0x86AC +#define GL_WEIGHT_ARRAY_ARB 0x86AD +#define GL_MODELVIEW0_ARB 0x1700 +#define GL_MODELVIEW1_ARB 0x850A +#define GL_MODELVIEW2_ARB 0x8722 +#define GL_MODELVIEW3_ARB 0x8723 +#define GL_MODELVIEW4_ARB 0x8724 +#define GL_MODELVIEW5_ARB 0x8725 +#define GL_MODELVIEW6_ARB 0x8726 +#define GL_MODELVIEW7_ARB 0x8727 +#define GL_MODELVIEW8_ARB 0x8728 +#define GL_MODELVIEW9_ARB 0x8729 +#define GL_MODELVIEW10_ARB 0x872A +#define GL_MODELVIEW11_ARB 0x872B +#define GL_MODELVIEW12_ARB 0x872C +#define GL_MODELVIEW13_ARB 0x872D +#define GL_MODELVIEW14_ARB 0x872E +#define GL_MODELVIEW15_ARB 0x872F +#define GL_MODELVIEW16_ARB 0x8730 +#define GL_MODELVIEW17_ARB 0x8731 +#define GL_MODELVIEW18_ARB 0x8732 +#define GL_MODELVIEW19_ARB 0x8733 +#define GL_MODELVIEW20_ARB 0x8734 +#define GL_MODELVIEW21_ARB 0x8735 +#define GL_MODELVIEW22_ARB 0x8736 +#define GL_MODELVIEW23_ARB 0x8737 +#define GL_MODELVIEW24_ARB 0x8738 +#define GL_MODELVIEW25_ARB 0x8739 +#define GL_MODELVIEW26_ARB 0x873A +#define GL_MODELVIEW27_ARB 0x873B +#define GL_MODELVIEW28_ARB 0x873C +#define GL_MODELVIEW29_ARB 0x873D +#define GL_MODELVIEW30_ARB 0x873E +#define GL_MODELVIEW31_ARB 0x873F +typedef void (APIENTRYP PFNGLWEIGHTBVARBPROC) (GLint size, const GLbyte *weights); +typedef void (APIENTRYP PFNGLWEIGHTSVARBPROC) (GLint size, const GLshort *weights); +typedef void (APIENTRYP PFNGLWEIGHTIVARBPROC) (GLint size, const GLint *weights); +typedef void (APIENTRYP PFNGLWEIGHTFVARBPROC) (GLint size, const GLfloat *weights); +typedef void (APIENTRYP PFNGLWEIGHTDVARBPROC) (GLint size, const GLdouble *weights); +typedef void (APIENTRYP PFNGLWEIGHTUBVARBPROC) (GLint size, const GLubyte *weights); +typedef void (APIENTRYP PFNGLWEIGHTUSVARBPROC) (GLint size, const GLushort *weights); +typedef void (APIENTRYP PFNGLWEIGHTUIVARBPROC) (GLint size, const GLuint *weights); +typedef void (APIENTRYP PFNGLWEIGHTPOINTERARBPROC) (GLint size, GLenum type, GLsizei stride, const void *pointer); +typedef void (APIENTRYP PFNGLVERTEXBLENDARBPROC) (GLint count); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glWeightbvARB (GLint size, const GLbyte *weights); +GLAPI void APIENTRY glWeightsvARB (GLint size, const GLshort *weights); +GLAPI void APIENTRY glWeightivARB (GLint size, const GLint *weights); +GLAPI void APIENTRY glWeightfvARB (GLint size, const GLfloat *weights); +GLAPI void APIENTRY glWeightdvARB (GLint size, const GLdouble *weights); +GLAPI void APIENTRY glWeightubvARB (GLint size, const GLubyte *weights); +GLAPI void APIENTRY glWeightusvARB (GLint size, const GLushort *weights); +GLAPI void APIENTRY glWeightuivARB (GLint size, const GLuint *weights); +GLAPI void APIENTRY glWeightPointerARB (GLint size, GLenum type, GLsizei stride, const void *pointer); +GLAPI void APIENTRY glVertexBlendARB (GLint count); +#endif +#endif /* GL_ARB_vertex_blend */ + +#ifndef GL_ARB_vertex_buffer_object +#define GL_ARB_vertex_buffer_object 1 +typedef ptrdiff_t GLsizeiptrARB; +typedef ptrdiff_t GLintptrARB; +#define GL_BUFFER_SIZE_ARB 0x8764 +#define GL_BUFFER_USAGE_ARB 0x8765 +#define GL_ARRAY_BUFFER_ARB 0x8892 +#define GL_ELEMENT_ARRAY_BUFFER_ARB 0x8893 +#define GL_ARRAY_BUFFER_BINDING_ARB 0x8894 +#define GL_ELEMENT_ARRAY_BUFFER_BINDING_ARB 0x8895 +#define GL_VERTEX_ARRAY_BUFFER_BINDING_ARB 0x8896 +#define GL_NORMAL_ARRAY_BUFFER_BINDING_ARB 0x8897 +#define GL_COLOR_ARRAY_BUFFER_BINDING_ARB 0x8898 +#define GL_INDEX_ARRAY_BUFFER_BINDING_ARB 0x8899 +#define GL_TEXTURE_COORD_ARRAY_BUFFER_BINDING_ARB 0x889A +#define GL_EDGE_FLAG_ARRAY_BUFFER_BINDING_ARB 0x889B +#define GL_SECONDARY_COLOR_ARRAY_BUFFER_BINDING_ARB 0x889C +#define GL_FOG_COORDINATE_ARRAY_BUFFER_BINDING_ARB 0x889D +#define GL_WEIGHT_ARRAY_BUFFER_BINDING_ARB 0x889E +#define GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING_ARB 0x889F +#define GL_READ_ONLY_ARB 0x88B8 +#define GL_WRITE_ONLY_ARB 0x88B9 +#define GL_READ_WRITE_ARB 0x88BA +#define GL_BUFFER_ACCESS_ARB 0x88BB +#define GL_BUFFER_MAPPED_ARB 0x88BC +#define GL_BUFFER_MAP_POINTER_ARB 0x88BD +#define GL_STREAM_DRAW_ARB 0x88E0 +#define GL_STREAM_READ_ARB 0x88E1 +#define GL_STREAM_COPY_ARB 0x88E2 +#define GL_STATIC_DRAW_ARB 0x88E4 +#define GL_STATIC_READ_ARB 0x88E5 +#define GL_STATIC_COPY_ARB 0x88E6 +#define GL_DYNAMIC_DRAW_ARB 0x88E8 +#define GL_DYNAMIC_READ_ARB 0x88E9 +#define GL_DYNAMIC_COPY_ARB 0x88EA +typedef void (APIENTRYP PFNGLBINDBUFFERARBPROC) (GLenum target, GLuint buffer); +typedef void (APIENTRYP PFNGLDELETEBUFFERSARBPROC) (GLsizei n, const GLuint *buffers); +typedef void (APIENTRYP PFNGLGENBUFFERSARBPROC) (GLsizei n, GLuint *buffers); +typedef GLboolean (APIENTRYP PFNGLISBUFFERARBPROC) (GLuint buffer); +typedef void (APIENTRYP PFNGLBUFFERDATAARBPROC) (GLenum target, GLsizeiptrARB size, const void *data, GLenum usage); +typedef void (APIENTRYP PFNGLBUFFERSUBDATAARBPROC) (GLenum target, GLintptrARB offset, GLsizeiptrARB size, const void *data); +typedef void (APIENTRYP PFNGLGETBUFFERSUBDATAARBPROC) (GLenum target, GLintptrARB offset, GLsizeiptrARB size, void *data); +typedef void *(APIENTRYP PFNGLMAPBUFFERARBPROC) (GLenum target, GLenum access); +typedef GLboolean (APIENTRYP PFNGLUNMAPBUFFERARBPROC) (GLenum target); +typedef void (APIENTRYP PFNGLGETBUFFERPARAMETERIVARBPROC) (GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETBUFFERPOINTERVARBPROC) (GLenum target, GLenum pname, void **params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBindBufferARB (GLenum target, GLuint buffer); +GLAPI void APIENTRY glDeleteBuffersARB (GLsizei n, const GLuint *buffers); +GLAPI void APIENTRY glGenBuffersARB (GLsizei n, GLuint *buffers); +GLAPI GLboolean APIENTRY glIsBufferARB (GLuint buffer); +GLAPI void APIENTRY glBufferDataARB (GLenum target, GLsizeiptrARB size, const void *data, GLenum usage); +GLAPI void APIENTRY glBufferSubDataARB (GLenum target, GLintptrARB offset, GLsizeiptrARB size, const void *data); +GLAPI void APIENTRY glGetBufferSubDataARB (GLenum target, GLintptrARB offset, GLsizeiptrARB size, void *data); +GLAPI void *APIENTRY glMapBufferARB (GLenum target, GLenum access); +GLAPI GLboolean APIENTRY glUnmapBufferARB (GLenum target); +GLAPI void APIENTRY glGetBufferParameterivARB (GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetBufferPointervARB (GLenum target, GLenum pname, void **params); +#endif +#endif /* GL_ARB_vertex_buffer_object */ + +#ifndef GL_ARB_vertex_program +#define GL_ARB_vertex_program 1 +#define GL_COLOR_SUM_ARB 0x8458 +#define GL_VERTEX_PROGRAM_ARB 0x8620 +#define GL_VERTEX_ATTRIB_ARRAY_ENABLED_ARB 0x8622 +#define GL_VERTEX_ATTRIB_ARRAY_SIZE_ARB 0x8623 +#define GL_VERTEX_ATTRIB_ARRAY_STRIDE_ARB 0x8624 +#define GL_VERTEX_ATTRIB_ARRAY_TYPE_ARB 0x8625 +#define GL_CURRENT_VERTEX_ATTRIB_ARB 0x8626 +#define GL_VERTEX_PROGRAM_POINT_SIZE_ARB 0x8642 +#define GL_VERTEX_PROGRAM_TWO_SIDE_ARB 0x8643 +#define GL_VERTEX_ATTRIB_ARRAY_POINTER_ARB 0x8645 +#define GL_MAX_VERTEX_ATTRIBS_ARB 0x8869 +#define GL_VERTEX_ATTRIB_ARRAY_NORMALIZED_ARB 0x886A +#define GL_PROGRAM_ADDRESS_REGISTERS_ARB 0x88B0 +#define GL_MAX_PROGRAM_ADDRESS_REGISTERS_ARB 0x88B1 +#define GL_PROGRAM_NATIVE_ADDRESS_REGISTERS_ARB 0x88B2 +#define GL_MAX_PROGRAM_NATIVE_ADDRESS_REGISTERS_ARB 0x88B3 +typedef void (APIENTRYP PFNGLVERTEXATTRIB1DARBPROC) (GLuint index, GLdouble x); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1DVARBPROC) (GLuint index, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1FARBPROC) (GLuint index, GLfloat x); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1FVARBPROC) (GLuint index, const GLfloat *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1SARBPROC) (GLuint index, GLshort x); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1SVARBPROC) (GLuint index, const GLshort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2DARBPROC) (GLuint index, GLdouble x, GLdouble y); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2DVARBPROC) (GLuint index, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2FARBPROC) (GLuint index, GLfloat x, GLfloat y); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2FVARBPROC) (GLuint index, const GLfloat *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2SARBPROC) (GLuint index, GLshort x, GLshort y); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2SVARBPROC) (GLuint index, const GLshort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3DARBPROC) (GLuint index, GLdouble x, GLdouble y, GLdouble z); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3DVARBPROC) (GLuint index, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3FARBPROC) (GLuint index, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3FVARBPROC) (GLuint index, const GLfloat *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3SARBPROC) (GLuint index, GLshort x, GLshort y, GLshort z); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3SVARBPROC) (GLuint index, const GLshort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4NBVARBPROC) (GLuint index, const GLbyte *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4NIVARBPROC) (GLuint index, const GLint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4NSVARBPROC) (GLuint index, const GLshort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4NUBARBPROC) (GLuint index, GLubyte x, GLubyte y, GLubyte z, GLubyte w); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4NUBVARBPROC) (GLuint index, const GLubyte *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4NUIVARBPROC) (GLuint index, const GLuint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4NUSVARBPROC) (GLuint index, const GLushort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4BVARBPROC) (GLuint index, const GLbyte *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4DARBPROC) (GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4DVARBPROC) (GLuint index, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4FARBPROC) (GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4FVARBPROC) (GLuint index, const GLfloat *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4IVARBPROC) (GLuint index, const GLint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4SARBPROC) (GLuint index, GLshort x, GLshort y, GLshort z, GLshort w); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4SVARBPROC) (GLuint index, const GLshort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4UBVARBPROC) (GLuint index, const GLubyte *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4UIVARBPROC) (GLuint index, const GLuint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4USVARBPROC) (GLuint index, const GLushort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBPOINTERARBPROC) (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void *pointer); +typedef void (APIENTRYP PFNGLENABLEVERTEXATTRIBARRAYARBPROC) (GLuint index); +typedef void (APIENTRYP PFNGLDISABLEVERTEXATTRIBARRAYARBPROC) (GLuint index); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBDVARBPROC) (GLuint index, GLenum pname, GLdouble *params); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBFVARBPROC) (GLuint index, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBIVARBPROC) (GLuint index, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBPOINTERVARBPROC) (GLuint index, GLenum pname, void **pointer); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glVertexAttrib1dARB (GLuint index, GLdouble x); +GLAPI void APIENTRY glVertexAttrib1dvARB (GLuint index, const GLdouble *v); +GLAPI void APIENTRY glVertexAttrib1fARB (GLuint index, GLfloat x); +GLAPI void APIENTRY glVertexAttrib1fvARB (GLuint index, const GLfloat *v); +GLAPI void APIENTRY glVertexAttrib1sARB (GLuint index, GLshort x); +GLAPI void APIENTRY glVertexAttrib1svARB (GLuint index, const GLshort *v); +GLAPI void APIENTRY glVertexAttrib2dARB (GLuint index, GLdouble x, GLdouble y); +GLAPI void APIENTRY glVertexAttrib2dvARB (GLuint index, const GLdouble *v); +GLAPI void APIENTRY glVertexAttrib2fARB (GLuint index, GLfloat x, GLfloat y); +GLAPI void APIENTRY glVertexAttrib2fvARB (GLuint index, const GLfloat *v); +GLAPI void APIENTRY glVertexAttrib2sARB (GLuint index, GLshort x, GLshort y); +GLAPI void APIENTRY glVertexAttrib2svARB (GLuint index, const GLshort *v); +GLAPI void APIENTRY glVertexAttrib3dARB (GLuint index, GLdouble x, GLdouble y, GLdouble z); +GLAPI void APIENTRY glVertexAttrib3dvARB (GLuint index, const GLdouble *v); +GLAPI void APIENTRY glVertexAttrib3fARB (GLuint index, GLfloat x, GLfloat y, GLfloat z); +GLAPI void APIENTRY glVertexAttrib3fvARB (GLuint index, const GLfloat *v); +GLAPI void APIENTRY glVertexAttrib3sARB (GLuint index, GLshort x, GLshort y, GLshort z); +GLAPI void APIENTRY glVertexAttrib3svARB (GLuint index, const GLshort *v); +GLAPI void APIENTRY glVertexAttrib4NbvARB (GLuint index, const GLbyte *v); +GLAPI void APIENTRY glVertexAttrib4NivARB (GLuint index, const GLint *v); +GLAPI void APIENTRY glVertexAttrib4NsvARB (GLuint index, const GLshort *v); +GLAPI void APIENTRY glVertexAttrib4NubARB (GLuint index, GLubyte x, GLubyte y, GLubyte z, GLubyte w); +GLAPI void APIENTRY glVertexAttrib4NubvARB (GLuint index, const GLubyte *v); +GLAPI void APIENTRY glVertexAttrib4NuivARB (GLuint index, const GLuint *v); +GLAPI void APIENTRY glVertexAttrib4NusvARB (GLuint index, const GLushort *v); +GLAPI void APIENTRY glVertexAttrib4bvARB (GLuint index, const GLbyte *v); +GLAPI void APIENTRY glVertexAttrib4dARB (GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +GLAPI void APIENTRY glVertexAttrib4dvARB (GLuint index, const GLdouble *v); +GLAPI void APIENTRY glVertexAttrib4fARB (GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +GLAPI void APIENTRY glVertexAttrib4fvARB (GLuint index, const GLfloat *v); +GLAPI void APIENTRY glVertexAttrib4ivARB (GLuint index, const GLint *v); +GLAPI void APIENTRY glVertexAttrib4sARB (GLuint index, GLshort x, GLshort y, GLshort z, GLshort w); +GLAPI void APIENTRY glVertexAttrib4svARB (GLuint index, const GLshort *v); +GLAPI void APIENTRY glVertexAttrib4ubvARB (GLuint index, const GLubyte *v); +GLAPI void APIENTRY glVertexAttrib4uivARB (GLuint index, const GLuint *v); +GLAPI void APIENTRY glVertexAttrib4usvARB (GLuint index, const GLushort *v); +GLAPI void APIENTRY glVertexAttribPointerARB (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void *pointer); +GLAPI void APIENTRY glEnableVertexAttribArrayARB (GLuint index); +GLAPI void APIENTRY glDisableVertexAttribArrayARB (GLuint index); +GLAPI void APIENTRY glGetVertexAttribdvARB (GLuint index, GLenum pname, GLdouble *params); +GLAPI void APIENTRY glGetVertexAttribfvARB (GLuint index, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetVertexAttribivARB (GLuint index, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetVertexAttribPointervARB (GLuint index, GLenum pname, void **pointer); +#endif +#endif /* GL_ARB_vertex_program */ + +#ifndef GL_ARB_vertex_shader +#define GL_ARB_vertex_shader 1 +#define GL_VERTEX_SHADER_ARB 0x8B31 +#define GL_MAX_VERTEX_UNIFORM_COMPONENTS_ARB 0x8B4A +#define GL_MAX_VARYING_FLOATS_ARB 0x8B4B +#define GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS_ARB 0x8B4C +#define GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS_ARB 0x8B4D +#define GL_OBJECT_ACTIVE_ATTRIBUTES_ARB 0x8B89 +#define GL_OBJECT_ACTIVE_ATTRIBUTE_MAX_LENGTH_ARB 0x8B8A +typedef void (APIENTRYP PFNGLBINDATTRIBLOCATIONARBPROC) (GLhandleARB programObj, GLuint index, const GLcharARB *name); +typedef void (APIENTRYP PFNGLGETACTIVEATTRIBARBPROC) (GLhandleARB programObj, GLuint index, GLsizei maxLength, GLsizei *length, GLint *size, GLenum *type, GLcharARB *name); +typedef GLint (APIENTRYP PFNGLGETATTRIBLOCATIONARBPROC) (GLhandleARB programObj, const GLcharARB *name); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBindAttribLocationARB (GLhandleARB programObj, GLuint index, const GLcharARB *name); +GLAPI void APIENTRY glGetActiveAttribARB (GLhandleARB programObj, GLuint index, GLsizei maxLength, GLsizei *length, GLint *size, GLenum *type, GLcharARB *name); +GLAPI GLint APIENTRY glGetAttribLocationARB (GLhandleARB programObj, const GLcharARB *name); +#endif +#endif /* GL_ARB_vertex_shader */ + +#ifndef GL_ARB_vertex_type_10f_11f_11f_rev +#define GL_ARB_vertex_type_10f_11f_11f_rev 1 +#endif /* GL_ARB_vertex_type_10f_11f_11f_rev */ + +#ifndef GL_ARB_vertex_type_2_10_10_10_rev +#define GL_ARB_vertex_type_2_10_10_10_rev 1 +#endif /* GL_ARB_vertex_type_2_10_10_10_rev */ + +#ifndef GL_ARB_viewport_array +#define GL_ARB_viewport_array 1 +#endif /* GL_ARB_viewport_array */ + +#ifndef GL_ARB_window_pos +#define GL_ARB_window_pos 1 +typedef void (APIENTRYP PFNGLWINDOWPOS2DARBPROC) (GLdouble x, GLdouble y); +typedef void (APIENTRYP PFNGLWINDOWPOS2DVARBPROC) (const GLdouble *v); +typedef void (APIENTRYP PFNGLWINDOWPOS2FARBPROC) (GLfloat x, GLfloat y); +typedef void (APIENTRYP PFNGLWINDOWPOS2FVARBPROC) (const GLfloat *v); +typedef void (APIENTRYP PFNGLWINDOWPOS2IARBPROC) (GLint x, GLint y); +typedef void (APIENTRYP PFNGLWINDOWPOS2IVARBPROC) (const GLint *v); +typedef void (APIENTRYP PFNGLWINDOWPOS2SARBPROC) (GLshort x, GLshort y); +typedef void (APIENTRYP PFNGLWINDOWPOS2SVARBPROC) (const GLshort *v); +typedef void (APIENTRYP PFNGLWINDOWPOS3DARBPROC) (GLdouble x, GLdouble y, GLdouble z); +typedef void (APIENTRYP PFNGLWINDOWPOS3DVARBPROC) (const GLdouble *v); +typedef void (APIENTRYP PFNGLWINDOWPOS3FARBPROC) (GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLWINDOWPOS3FVARBPROC) (const GLfloat *v); +typedef void (APIENTRYP PFNGLWINDOWPOS3IARBPROC) (GLint x, GLint y, GLint z); +typedef void (APIENTRYP PFNGLWINDOWPOS3IVARBPROC) (const GLint *v); +typedef void (APIENTRYP PFNGLWINDOWPOS3SARBPROC) (GLshort x, GLshort y, GLshort z); +typedef void (APIENTRYP PFNGLWINDOWPOS3SVARBPROC) (const GLshort *v); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glWindowPos2dARB (GLdouble x, GLdouble y); +GLAPI void APIENTRY glWindowPos2dvARB (const GLdouble *v); +GLAPI void APIENTRY glWindowPos2fARB (GLfloat x, GLfloat y); +GLAPI void APIENTRY glWindowPos2fvARB (const GLfloat *v); +GLAPI void APIENTRY glWindowPos2iARB (GLint x, GLint y); +GLAPI void APIENTRY glWindowPos2ivARB (const GLint *v); +GLAPI void APIENTRY glWindowPos2sARB (GLshort x, GLshort y); +GLAPI void APIENTRY glWindowPos2svARB (const GLshort *v); +GLAPI void APIENTRY glWindowPos3dARB (GLdouble x, GLdouble y, GLdouble z); +GLAPI void APIENTRY glWindowPos3dvARB (const GLdouble *v); +GLAPI void APIENTRY glWindowPos3fARB (GLfloat x, GLfloat y, GLfloat z); +GLAPI void APIENTRY glWindowPos3fvARB (const GLfloat *v); +GLAPI void APIENTRY glWindowPos3iARB (GLint x, GLint y, GLint z); +GLAPI void APIENTRY glWindowPos3ivARB (const GLint *v); +GLAPI void APIENTRY glWindowPos3sARB (GLshort x, GLshort y, GLshort z); +GLAPI void APIENTRY glWindowPos3svARB (const GLshort *v); +#endif +#endif /* GL_ARB_window_pos */ + +#ifndef GL_KHR_blend_equation_advanced +#define GL_KHR_blend_equation_advanced 1 +#define GL_MULTIPLY_KHR 0x9294 +#define GL_SCREEN_KHR 0x9295 +#define GL_OVERLAY_KHR 0x9296 +#define GL_DARKEN_KHR 0x9297 +#define GL_LIGHTEN_KHR 0x9298 +#define GL_COLORDODGE_KHR 0x9299 +#define GL_COLORBURN_KHR 0x929A +#define GL_HARDLIGHT_KHR 0x929B +#define GL_SOFTLIGHT_KHR 0x929C +#define GL_DIFFERENCE_KHR 0x929E +#define GL_EXCLUSION_KHR 0x92A0 +#define GL_HSL_HUE_KHR 0x92AD +#define GL_HSL_SATURATION_KHR 0x92AE +#define GL_HSL_COLOR_KHR 0x92AF +#define GL_HSL_LUMINOSITY_KHR 0x92B0 +typedef void (APIENTRYP PFNGLBLENDBARRIERKHRPROC) (void); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBlendBarrierKHR (void); +#endif +#endif /* GL_KHR_blend_equation_advanced */ + +#ifndef GL_KHR_blend_equation_advanced_coherent +#define GL_KHR_blend_equation_advanced_coherent 1 +#define GL_BLEND_ADVANCED_COHERENT_KHR 0x9285 +#endif /* GL_KHR_blend_equation_advanced_coherent */ + +#ifndef GL_KHR_context_flush_control +#define GL_KHR_context_flush_control 1 +#endif /* GL_KHR_context_flush_control */ + +#ifndef GL_KHR_debug +#define GL_KHR_debug 1 +#endif /* GL_KHR_debug */ + +#ifndef GL_KHR_no_error +#define GL_KHR_no_error 1 +#define GL_CONTEXT_FLAG_NO_ERROR_BIT_KHR 0x00000008 +#endif /* GL_KHR_no_error */ + +#ifndef GL_KHR_robust_buffer_access_behavior +#define GL_KHR_robust_buffer_access_behavior 1 +#endif /* GL_KHR_robust_buffer_access_behavior */ + +#ifndef GL_KHR_robustness +#define GL_KHR_robustness 1 +#define GL_CONTEXT_ROBUST_ACCESS 0x90F3 +#endif /* GL_KHR_robustness */ + +#ifndef GL_KHR_texture_compression_astc_hdr +#define GL_KHR_texture_compression_astc_hdr 1 +#define GL_COMPRESSED_RGBA_ASTC_4x4_KHR 0x93B0 +#define GL_COMPRESSED_RGBA_ASTC_5x4_KHR 0x93B1 +#define GL_COMPRESSED_RGBA_ASTC_5x5_KHR 0x93B2 +#define GL_COMPRESSED_RGBA_ASTC_6x5_KHR 0x93B3 +#define GL_COMPRESSED_RGBA_ASTC_6x6_KHR 0x93B4 +#define GL_COMPRESSED_RGBA_ASTC_8x5_KHR 0x93B5 +#define GL_COMPRESSED_RGBA_ASTC_8x6_KHR 0x93B6 +#define GL_COMPRESSED_RGBA_ASTC_8x8_KHR 0x93B7 +#define GL_COMPRESSED_RGBA_ASTC_10x5_KHR 0x93B8 +#define GL_COMPRESSED_RGBA_ASTC_10x6_KHR 0x93B9 +#define GL_COMPRESSED_RGBA_ASTC_10x8_KHR 0x93BA +#define GL_COMPRESSED_RGBA_ASTC_10x10_KHR 0x93BB +#define GL_COMPRESSED_RGBA_ASTC_12x10_KHR 0x93BC +#define GL_COMPRESSED_RGBA_ASTC_12x12_KHR 0x93BD +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR 0x93D0 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR 0x93D1 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR 0x93D2 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR 0x93D3 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR 0x93D4 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR 0x93D5 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR 0x93D6 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR 0x93D7 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR 0x93D8 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR 0x93D9 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR 0x93DA +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR 0x93DB +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR 0x93DC +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR 0x93DD +#endif /* GL_KHR_texture_compression_astc_hdr */ + +#ifndef GL_KHR_texture_compression_astc_ldr +#define GL_KHR_texture_compression_astc_ldr 1 +#endif /* GL_KHR_texture_compression_astc_ldr */ + +#ifndef GL_OES_byte_coordinates +#define GL_OES_byte_coordinates 1 +typedef void (APIENTRYP PFNGLMULTITEXCOORD1BOESPROC) (GLenum texture, GLbyte s); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1BVOESPROC) (GLenum texture, const GLbyte *coords); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2BOESPROC) (GLenum texture, GLbyte s, GLbyte t); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2BVOESPROC) (GLenum texture, const GLbyte *coords); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3BOESPROC) (GLenum texture, GLbyte s, GLbyte t, GLbyte r); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3BVOESPROC) (GLenum texture, const GLbyte *coords); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4BOESPROC) (GLenum texture, GLbyte s, GLbyte t, GLbyte r, GLbyte q); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4BVOESPROC) (GLenum texture, const GLbyte *coords); +typedef void (APIENTRYP PFNGLTEXCOORD1BOESPROC) (GLbyte s); +typedef void (APIENTRYP PFNGLTEXCOORD1BVOESPROC) (const GLbyte *coords); +typedef void (APIENTRYP PFNGLTEXCOORD2BOESPROC) (GLbyte s, GLbyte t); +typedef void (APIENTRYP PFNGLTEXCOORD2BVOESPROC) (const GLbyte *coords); +typedef void (APIENTRYP PFNGLTEXCOORD3BOESPROC) (GLbyte s, GLbyte t, GLbyte r); +typedef void (APIENTRYP PFNGLTEXCOORD3BVOESPROC) (const GLbyte *coords); +typedef void (APIENTRYP PFNGLTEXCOORD4BOESPROC) (GLbyte s, GLbyte t, GLbyte r, GLbyte q); +typedef void (APIENTRYP PFNGLTEXCOORD4BVOESPROC) (const GLbyte *coords); +typedef void (APIENTRYP PFNGLVERTEX2BOESPROC) (GLbyte x, GLbyte y); +typedef void (APIENTRYP PFNGLVERTEX2BVOESPROC) (const GLbyte *coords); +typedef void (APIENTRYP PFNGLVERTEX3BOESPROC) (GLbyte x, GLbyte y, GLbyte z); +typedef void (APIENTRYP PFNGLVERTEX3BVOESPROC) (const GLbyte *coords); +typedef void (APIENTRYP PFNGLVERTEX4BOESPROC) (GLbyte x, GLbyte y, GLbyte z, GLbyte w); +typedef void (APIENTRYP PFNGLVERTEX4BVOESPROC) (const GLbyte *coords); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glMultiTexCoord1bOES (GLenum texture, GLbyte s); +GLAPI void APIENTRY glMultiTexCoord1bvOES (GLenum texture, const GLbyte *coords); +GLAPI void APIENTRY glMultiTexCoord2bOES (GLenum texture, GLbyte s, GLbyte t); +GLAPI void APIENTRY glMultiTexCoord2bvOES (GLenum texture, const GLbyte *coords); +GLAPI void APIENTRY glMultiTexCoord3bOES (GLenum texture, GLbyte s, GLbyte t, GLbyte r); +GLAPI void APIENTRY glMultiTexCoord3bvOES (GLenum texture, const GLbyte *coords); +GLAPI void APIENTRY glMultiTexCoord4bOES (GLenum texture, GLbyte s, GLbyte t, GLbyte r, GLbyte q); +GLAPI void APIENTRY glMultiTexCoord4bvOES (GLenum texture, const GLbyte *coords); +GLAPI void APIENTRY glTexCoord1bOES (GLbyte s); +GLAPI void APIENTRY glTexCoord1bvOES (const GLbyte *coords); +GLAPI void APIENTRY glTexCoord2bOES (GLbyte s, GLbyte t); +GLAPI void APIENTRY glTexCoord2bvOES (const GLbyte *coords); +GLAPI void APIENTRY glTexCoord3bOES (GLbyte s, GLbyte t, GLbyte r); +GLAPI void APIENTRY glTexCoord3bvOES (const GLbyte *coords); +GLAPI void APIENTRY glTexCoord4bOES (GLbyte s, GLbyte t, GLbyte r, GLbyte q); +GLAPI void APIENTRY glTexCoord4bvOES (const GLbyte *coords); +GLAPI void APIENTRY glVertex2bOES (GLbyte x, GLbyte y); +GLAPI void APIENTRY glVertex2bvOES (const GLbyte *coords); +GLAPI void APIENTRY glVertex3bOES (GLbyte x, GLbyte y, GLbyte z); +GLAPI void APIENTRY glVertex3bvOES (const GLbyte *coords); +GLAPI void APIENTRY glVertex4bOES (GLbyte x, GLbyte y, GLbyte z, GLbyte w); +GLAPI void APIENTRY glVertex4bvOES (const GLbyte *coords); +#endif +#endif /* GL_OES_byte_coordinates */ + +#ifndef GL_OES_compressed_paletted_texture +#define GL_OES_compressed_paletted_texture 1 +#define GL_PALETTE4_RGB8_OES 0x8B90 +#define GL_PALETTE4_RGBA8_OES 0x8B91 +#define GL_PALETTE4_R5_G6_B5_OES 0x8B92 +#define GL_PALETTE4_RGBA4_OES 0x8B93 +#define GL_PALETTE4_RGB5_A1_OES 0x8B94 +#define GL_PALETTE8_RGB8_OES 0x8B95 +#define GL_PALETTE8_RGBA8_OES 0x8B96 +#define GL_PALETTE8_R5_G6_B5_OES 0x8B97 +#define GL_PALETTE8_RGBA4_OES 0x8B98 +#define GL_PALETTE8_RGB5_A1_OES 0x8B99 +#endif /* GL_OES_compressed_paletted_texture */ + +#ifndef GL_OES_fixed_point +#define GL_OES_fixed_point 1 +typedef GLint GLfixed; +#define GL_FIXED_OES 0x140C +typedef void (APIENTRYP PFNGLALPHAFUNCXOESPROC) (GLenum func, GLfixed ref); +typedef void (APIENTRYP PFNGLCLEARCOLORXOESPROC) (GLfixed red, GLfixed green, GLfixed blue, GLfixed alpha); +typedef void (APIENTRYP PFNGLCLEARDEPTHXOESPROC) (GLfixed depth); +typedef void (APIENTRYP PFNGLCLIPPLANEXOESPROC) (GLenum plane, const GLfixed *equation); +typedef void (APIENTRYP PFNGLCOLOR4XOESPROC) (GLfixed red, GLfixed green, GLfixed blue, GLfixed alpha); +typedef void (APIENTRYP PFNGLDEPTHRANGEXOESPROC) (GLfixed n, GLfixed f); +typedef void (APIENTRYP PFNGLFOGXOESPROC) (GLenum pname, GLfixed param); +typedef void (APIENTRYP PFNGLFOGXVOESPROC) (GLenum pname, const GLfixed *param); +typedef void (APIENTRYP PFNGLFRUSTUMXOESPROC) (GLfixed l, GLfixed r, GLfixed b, GLfixed t, GLfixed n, GLfixed f); +typedef void (APIENTRYP PFNGLGETCLIPPLANEXOESPROC) (GLenum plane, GLfixed *equation); +typedef void (APIENTRYP PFNGLGETFIXEDVOESPROC) (GLenum pname, GLfixed *params); +typedef void (APIENTRYP PFNGLGETTEXENVXVOESPROC) (GLenum target, GLenum pname, GLfixed *params); +typedef void (APIENTRYP PFNGLGETTEXPARAMETERXVOESPROC) (GLenum target, GLenum pname, GLfixed *params); +typedef void (APIENTRYP PFNGLLIGHTMODELXOESPROC) (GLenum pname, GLfixed param); +typedef void (APIENTRYP PFNGLLIGHTMODELXVOESPROC) (GLenum pname, const GLfixed *param); +typedef void (APIENTRYP PFNGLLIGHTXOESPROC) (GLenum light, GLenum pname, GLfixed param); +typedef void (APIENTRYP PFNGLLIGHTXVOESPROC) (GLenum light, GLenum pname, const GLfixed *params); +typedef void (APIENTRYP PFNGLLINEWIDTHXOESPROC) (GLfixed width); +typedef void (APIENTRYP PFNGLLOADMATRIXXOESPROC) (const GLfixed *m); +typedef void (APIENTRYP PFNGLMATERIALXOESPROC) (GLenum face, GLenum pname, GLfixed param); +typedef void (APIENTRYP PFNGLMATERIALXVOESPROC) (GLenum face, GLenum pname, const GLfixed *param); +typedef void (APIENTRYP PFNGLMULTMATRIXXOESPROC) (const GLfixed *m); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4XOESPROC) (GLenum texture, GLfixed s, GLfixed t, GLfixed r, GLfixed q); +typedef void (APIENTRYP PFNGLNORMAL3XOESPROC) (GLfixed nx, GLfixed ny, GLfixed nz); +typedef void (APIENTRYP PFNGLORTHOXOESPROC) (GLfixed l, GLfixed r, GLfixed b, GLfixed t, GLfixed n, GLfixed f); +typedef void (APIENTRYP PFNGLPOINTPARAMETERXVOESPROC) (GLenum pname, const GLfixed *params); +typedef void (APIENTRYP PFNGLPOINTSIZEXOESPROC) (GLfixed size); +typedef void (APIENTRYP PFNGLPOLYGONOFFSETXOESPROC) (GLfixed factor, GLfixed units); +typedef void (APIENTRYP PFNGLROTATEXOESPROC) (GLfixed angle, GLfixed x, GLfixed y, GLfixed z); +typedef void (APIENTRYP PFNGLSCALEXOESPROC) (GLfixed x, GLfixed y, GLfixed z); +typedef void (APIENTRYP PFNGLTEXENVXOESPROC) (GLenum target, GLenum pname, GLfixed param); +typedef void (APIENTRYP PFNGLTEXENVXVOESPROC) (GLenum target, GLenum pname, const GLfixed *params); +typedef void (APIENTRYP PFNGLTEXPARAMETERXOESPROC) (GLenum target, GLenum pname, GLfixed param); +typedef void (APIENTRYP PFNGLTEXPARAMETERXVOESPROC) (GLenum target, GLenum pname, const GLfixed *params); +typedef void (APIENTRYP PFNGLTRANSLATEXOESPROC) (GLfixed x, GLfixed y, GLfixed z); +typedef void (APIENTRYP PFNGLACCUMXOESPROC) (GLenum op, GLfixed value); +typedef void (APIENTRYP PFNGLBITMAPXOESPROC) (GLsizei width, GLsizei height, GLfixed xorig, GLfixed yorig, GLfixed xmove, GLfixed ymove, const GLubyte *bitmap); +typedef void (APIENTRYP PFNGLBLENDCOLORXOESPROC) (GLfixed red, GLfixed green, GLfixed blue, GLfixed alpha); +typedef void (APIENTRYP PFNGLCLEARACCUMXOESPROC) (GLfixed red, GLfixed green, GLfixed blue, GLfixed alpha); +typedef void (APIENTRYP PFNGLCOLOR3XOESPROC) (GLfixed red, GLfixed green, GLfixed blue); +typedef void (APIENTRYP PFNGLCOLOR3XVOESPROC) (const GLfixed *components); +typedef void (APIENTRYP PFNGLCOLOR4XVOESPROC) (const GLfixed *components); +typedef void (APIENTRYP PFNGLCONVOLUTIONPARAMETERXOESPROC) (GLenum target, GLenum pname, GLfixed param); +typedef void (APIENTRYP PFNGLCONVOLUTIONPARAMETERXVOESPROC) (GLenum target, GLenum pname, const GLfixed *params); +typedef void (APIENTRYP PFNGLEVALCOORD1XOESPROC) (GLfixed u); +typedef void (APIENTRYP PFNGLEVALCOORD1XVOESPROC) (const GLfixed *coords); +typedef void (APIENTRYP PFNGLEVALCOORD2XOESPROC) (GLfixed u, GLfixed v); +typedef void (APIENTRYP PFNGLEVALCOORD2XVOESPROC) (const GLfixed *coords); +typedef void (APIENTRYP PFNGLFEEDBACKBUFFERXOESPROC) (GLsizei n, GLenum type, const GLfixed *buffer); +typedef void (APIENTRYP PFNGLGETCONVOLUTIONPARAMETERXVOESPROC) (GLenum target, GLenum pname, GLfixed *params); +typedef void (APIENTRYP PFNGLGETHISTOGRAMPARAMETERXVOESPROC) (GLenum target, GLenum pname, GLfixed *params); +typedef void (APIENTRYP PFNGLGETLIGHTXOESPROC) (GLenum light, GLenum pname, GLfixed *params); +typedef void (APIENTRYP PFNGLGETMAPXVOESPROC) (GLenum target, GLenum query, GLfixed *v); +typedef void (APIENTRYP PFNGLGETMATERIALXOESPROC) (GLenum face, GLenum pname, GLfixed param); +typedef void (APIENTRYP PFNGLGETPIXELMAPXVPROC) (GLenum map, GLint size, GLfixed *values); +typedef void (APIENTRYP PFNGLGETTEXGENXVOESPROC) (GLenum coord, GLenum pname, GLfixed *params); +typedef void (APIENTRYP PFNGLGETTEXLEVELPARAMETERXVOESPROC) (GLenum target, GLint level, GLenum pname, GLfixed *params); +typedef void (APIENTRYP PFNGLINDEXXOESPROC) (GLfixed component); +typedef void (APIENTRYP PFNGLINDEXXVOESPROC) (const GLfixed *component); +typedef void (APIENTRYP PFNGLLOADTRANSPOSEMATRIXXOESPROC) (const GLfixed *m); +typedef void (APIENTRYP PFNGLMAP1XOESPROC) (GLenum target, GLfixed u1, GLfixed u2, GLint stride, GLint order, GLfixed points); +typedef void (APIENTRYP PFNGLMAP2XOESPROC) (GLenum target, GLfixed u1, GLfixed u2, GLint ustride, GLint uorder, GLfixed v1, GLfixed v2, GLint vstride, GLint vorder, GLfixed points); +typedef void (APIENTRYP PFNGLMAPGRID1XOESPROC) (GLint n, GLfixed u1, GLfixed u2); +typedef void (APIENTRYP PFNGLMAPGRID2XOESPROC) (GLint n, GLfixed u1, GLfixed u2, GLfixed v1, GLfixed v2); +typedef void (APIENTRYP PFNGLMULTTRANSPOSEMATRIXXOESPROC) (const GLfixed *m); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1XOESPROC) (GLenum texture, GLfixed s); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1XVOESPROC) (GLenum texture, const GLfixed *coords); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2XOESPROC) (GLenum texture, GLfixed s, GLfixed t); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2XVOESPROC) (GLenum texture, const GLfixed *coords); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3XOESPROC) (GLenum texture, GLfixed s, GLfixed t, GLfixed r); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3XVOESPROC) (GLenum texture, const GLfixed *coords); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4XVOESPROC) (GLenum texture, const GLfixed *coords); +typedef void (APIENTRYP PFNGLNORMAL3XVOESPROC) (const GLfixed *coords); +typedef void (APIENTRYP PFNGLPASSTHROUGHXOESPROC) (GLfixed token); +typedef void (APIENTRYP PFNGLPIXELMAPXPROC) (GLenum map, GLint size, const GLfixed *values); +typedef void (APIENTRYP PFNGLPIXELSTOREXPROC) (GLenum pname, GLfixed param); +typedef void (APIENTRYP PFNGLPIXELTRANSFERXOESPROC) (GLenum pname, GLfixed param); +typedef void (APIENTRYP PFNGLPIXELZOOMXOESPROC) (GLfixed xfactor, GLfixed yfactor); +typedef void (APIENTRYP PFNGLPRIORITIZETEXTURESXOESPROC) (GLsizei n, const GLuint *textures, const GLfixed *priorities); +typedef void (APIENTRYP PFNGLRASTERPOS2XOESPROC) (GLfixed x, GLfixed y); +typedef void (APIENTRYP PFNGLRASTERPOS2XVOESPROC) (const GLfixed *coords); +typedef void (APIENTRYP PFNGLRASTERPOS3XOESPROC) (GLfixed x, GLfixed y, GLfixed z); +typedef void (APIENTRYP PFNGLRASTERPOS3XVOESPROC) (const GLfixed *coords); +typedef void (APIENTRYP PFNGLRASTERPOS4XOESPROC) (GLfixed x, GLfixed y, GLfixed z, GLfixed w); +typedef void (APIENTRYP PFNGLRASTERPOS4XVOESPROC) (const GLfixed *coords); +typedef void (APIENTRYP PFNGLRECTXOESPROC) (GLfixed x1, GLfixed y1, GLfixed x2, GLfixed y2); +typedef void (APIENTRYP PFNGLRECTXVOESPROC) (const GLfixed *v1, const GLfixed *v2); +typedef void (APIENTRYP PFNGLTEXCOORD1XOESPROC) (GLfixed s); +typedef void (APIENTRYP PFNGLTEXCOORD1XVOESPROC) (const GLfixed *coords); +typedef void (APIENTRYP PFNGLTEXCOORD2XOESPROC) (GLfixed s, GLfixed t); +typedef void (APIENTRYP PFNGLTEXCOORD2XVOESPROC) (const GLfixed *coords); +typedef void (APIENTRYP PFNGLTEXCOORD3XOESPROC) (GLfixed s, GLfixed t, GLfixed r); +typedef void (APIENTRYP PFNGLTEXCOORD3XVOESPROC) (const GLfixed *coords); +typedef void (APIENTRYP PFNGLTEXCOORD4XOESPROC) (GLfixed s, GLfixed t, GLfixed r, GLfixed q); +typedef void (APIENTRYP PFNGLTEXCOORD4XVOESPROC) (const GLfixed *coords); +typedef void (APIENTRYP PFNGLTEXGENXOESPROC) (GLenum coord, GLenum pname, GLfixed param); +typedef void (APIENTRYP PFNGLTEXGENXVOESPROC) (GLenum coord, GLenum pname, const GLfixed *params); +typedef void (APIENTRYP PFNGLVERTEX2XOESPROC) (GLfixed x); +typedef void (APIENTRYP PFNGLVERTEX2XVOESPROC) (const GLfixed *coords); +typedef void (APIENTRYP PFNGLVERTEX3XOESPROC) (GLfixed x, GLfixed y); +typedef void (APIENTRYP PFNGLVERTEX3XVOESPROC) (const GLfixed *coords); +typedef void (APIENTRYP PFNGLVERTEX4XOESPROC) (GLfixed x, GLfixed y, GLfixed z); +typedef void (APIENTRYP PFNGLVERTEX4XVOESPROC) (const GLfixed *coords); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glAlphaFuncxOES (GLenum func, GLfixed ref); +GLAPI void APIENTRY glClearColorxOES (GLfixed red, GLfixed green, GLfixed blue, GLfixed alpha); +GLAPI void APIENTRY glClearDepthxOES (GLfixed depth); +GLAPI void APIENTRY glClipPlanexOES (GLenum plane, const GLfixed *equation); +GLAPI void APIENTRY glColor4xOES (GLfixed red, GLfixed green, GLfixed blue, GLfixed alpha); +GLAPI void APIENTRY glDepthRangexOES (GLfixed n, GLfixed f); +GLAPI void APIENTRY glFogxOES (GLenum pname, GLfixed param); +GLAPI void APIENTRY glFogxvOES (GLenum pname, const GLfixed *param); +GLAPI void APIENTRY glFrustumxOES (GLfixed l, GLfixed r, GLfixed b, GLfixed t, GLfixed n, GLfixed f); +GLAPI void APIENTRY glGetClipPlanexOES (GLenum plane, GLfixed *equation); +GLAPI void APIENTRY glGetFixedvOES (GLenum pname, GLfixed *params); +GLAPI void APIENTRY glGetTexEnvxvOES (GLenum target, GLenum pname, GLfixed *params); +GLAPI void APIENTRY glGetTexParameterxvOES (GLenum target, GLenum pname, GLfixed *params); +GLAPI void APIENTRY glLightModelxOES (GLenum pname, GLfixed param); +GLAPI void APIENTRY glLightModelxvOES (GLenum pname, const GLfixed *param); +GLAPI void APIENTRY glLightxOES (GLenum light, GLenum pname, GLfixed param); +GLAPI void APIENTRY glLightxvOES (GLenum light, GLenum pname, const GLfixed *params); +GLAPI void APIENTRY glLineWidthxOES (GLfixed width); +GLAPI void APIENTRY glLoadMatrixxOES (const GLfixed *m); +GLAPI void APIENTRY glMaterialxOES (GLenum face, GLenum pname, GLfixed param); +GLAPI void APIENTRY glMaterialxvOES (GLenum face, GLenum pname, const GLfixed *param); +GLAPI void APIENTRY glMultMatrixxOES (const GLfixed *m); +GLAPI void APIENTRY glMultiTexCoord4xOES (GLenum texture, GLfixed s, GLfixed t, GLfixed r, GLfixed q); +GLAPI void APIENTRY glNormal3xOES (GLfixed nx, GLfixed ny, GLfixed nz); +GLAPI void APIENTRY glOrthoxOES (GLfixed l, GLfixed r, GLfixed b, GLfixed t, GLfixed n, GLfixed f); +GLAPI void APIENTRY glPointParameterxvOES (GLenum pname, const GLfixed *params); +GLAPI void APIENTRY glPointSizexOES (GLfixed size); +GLAPI void APIENTRY glPolygonOffsetxOES (GLfixed factor, GLfixed units); +GLAPI void APIENTRY glRotatexOES (GLfixed angle, GLfixed x, GLfixed y, GLfixed z); +GLAPI void APIENTRY glScalexOES (GLfixed x, GLfixed y, GLfixed z); +GLAPI void APIENTRY glTexEnvxOES (GLenum target, GLenum pname, GLfixed param); +GLAPI void APIENTRY glTexEnvxvOES (GLenum target, GLenum pname, const GLfixed *params); +GLAPI void APIENTRY glTexParameterxOES (GLenum target, GLenum pname, GLfixed param); +GLAPI void APIENTRY glTexParameterxvOES (GLenum target, GLenum pname, const GLfixed *params); +GLAPI void APIENTRY glTranslatexOES (GLfixed x, GLfixed y, GLfixed z); +GLAPI void APIENTRY glAccumxOES (GLenum op, GLfixed value); +GLAPI void APIENTRY glBitmapxOES (GLsizei width, GLsizei height, GLfixed xorig, GLfixed yorig, GLfixed xmove, GLfixed ymove, const GLubyte *bitmap); +GLAPI void APIENTRY glBlendColorxOES (GLfixed red, GLfixed green, GLfixed blue, GLfixed alpha); +GLAPI void APIENTRY glClearAccumxOES (GLfixed red, GLfixed green, GLfixed blue, GLfixed alpha); +GLAPI void APIENTRY glColor3xOES (GLfixed red, GLfixed green, GLfixed blue); +GLAPI void APIENTRY glColor3xvOES (const GLfixed *components); +GLAPI void APIENTRY glColor4xvOES (const GLfixed *components); +GLAPI void APIENTRY glConvolutionParameterxOES (GLenum target, GLenum pname, GLfixed param); +GLAPI void APIENTRY glConvolutionParameterxvOES (GLenum target, GLenum pname, const GLfixed *params); +GLAPI void APIENTRY glEvalCoord1xOES (GLfixed u); +GLAPI void APIENTRY glEvalCoord1xvOES (const GLfixed *coords); +GLAPI void APIENTRY glEvalCoord2xOES (GLfixed u, GLfixed v); +GLAPI void APIENTRY glEvalCoord2xvOES (const GLfixed *coords); +GLAPI void APIENTRY glFeedbackBufferxOES (GLsizei n, GLenum type, const GLfixed *buffer); +GLAPI void APIENTRY glGetConvolutionParameterxvOES (GLenum target, GLenum pname, GLfixed *params); +GLAPI void APIENTRY glGetHistogramParameterxvOES (GLenum target, GLenum pname, GLfixed *params); +GLAPI void APIENTRY glGetLightxOES (GLenum light, GLenum pname, GLfixed *params); +GLAPI void APIENTRY glGetMapxvOES (GLenum target, GLenum query, GLfixed *v); +GLAPI void APIENTRY glGetMaterialxOES (GLenum face, GLenum pname, GLfixed param); +GLAPI void APIENTRY glGetPixelMapxv (GLenum map, GLint size, GLfixed *values); +GLAPI void APIENTRY glGetTexGenxvOES (GLenum coord, GLenum pname, GLfixed *params); +GLAPI void APIENTRY glGetTexLevelParameterxvOES (GLenum target, GLint level, GLenum pname, GLfixed *params); +GLAPI void APIENTRY glIndexxOES (GLfixed component); +GLAPI void APIENTRY glIndexxvOES (const GLfixed *component); +GLAPI void APIENTRY glLoadTransposeMatrixxOES (const GLfixed *m); +GLAPI void APIENTRY glMap1xOES (GLenum target, GLfixed u1, GLfixed u2, GLint stride, GLint order, GLfixed points); +GLAPI void APIENTRY glMap2xOES (GLenum target, GLfixed u1, GLfixed u2, GLint ustride, GLint uorder, GLfixed v1, GLfixed v2, GLint vstride, GLint vorder, GLfixed points); +GLAPI void APIENTRY glMapGrid1xOES (GLint n, GLfixed u1, GLfixed u2); +GLAPI void APIENTRY glMapGrid2xOES (GLint n, GLfixed u1, GLfixed u2, GLfixed v1, GLfixed v2); +GLAPI void APIENTRY glMultTransposeMatrixxOES (const GLfixed *m); +GLAPI void APIENTRY glMultiTexCoord1xOES (GLenum texture, GLfixed s); +GLAPI void APIENTRY glMultiTexCoord1xvOES (GLenum texture, const GLfixed *coords); +GLAPI void APIENTRY glMultiTexCoord2xOES (GLenum texture, GLfixed s, GLfixed t); +GLAPI void APIENTRY glMultiTexCoord2xvOES (GLenum texture, const GLfixed *coords); +GLAPI void APIENTRY glMultiTexCoord3xOES (GLenum texture, GLfixed s, GLfixed t, GLfixed r); +GLAPI void APIENTRY glMultiTexCoord3xvOES (GLenum texture, const GLfixed *coords); +GLAPI void APIENTRY glMultiTexCoord4xvOES (GLenum texture, const GLfixed *coords); +GLAPI void APIENTRY glNormal3xvOES (const GLfixed *coords); +GLAPI void APIENTRY glPassThroughxOES (GLfixed token); +GLAPI void APIENTRY glPixelMapx (GLenum map, GLint size, const GLfixed *values); +GLAPI void APIENTRY glPixelStorex (GLenum pname, GLfixed param); +GLAPI void APIENTRY glPixelTransferxOES (GLenum pname, GLfixed param); +GLAPI void APIENTRY glPixelZoomxOES (GLfixed xfactor, GLfixed yfactor); +GLAPI void APIENTRY glPrioritizeTexturesxOES (GLsizei n, const GLuint *textures, const GLfixed *priorities); +GLAPI void APIENTRY glRasterPos2xOES (GLfixed x, GLfixed y); +GLAPI void APIENTRY glRasterPos2xvOES (const GLfixed *coords); +GLAPI void APIENTRY glRasterPos3xOES (GLfixed x, GLfixed y, GLfixed z); +GLAPI void APIENTRY glRasterPos3xvOES (const GLfixed *coords); +GLAPI void APIENTRY glRasterPos4xOES (GLfixed x, GLfixed y, GLfixed z, GLfixed w); +GLAPI void APIENTRY glRasterPos4xvOES (const GLfixed *coords); +GLAPI void APIENTRY glRectxOES (GLfixed x1, GLfixed y1, GLfixed x2, GLfixed y2); +GLAPI void APIENTRY glRectxvOES (const GLfixed *v1, const GLfixed *v2); +GLAPI void APIENTRY glTexCoord1xOES (GLfixed s); +GLAPI void APIENTRY glTexCoord1xvOES (const GLfixed *coords); +GLAPI void APIENTRY glTexCoord2xOES (GLfixed s, GLfixed t); +GLAPI void APIENTRY glTexCoord2xvOES (const GLfixed *coords); +GLAPI void APIENTRY glTexCoord3xOES (GLfixed s, GLfixed t, GLfixed r); +GLAPI void APIENTRY glTexCoord3xvOES (const GLfixed *coords); +GLAPI void APIENTRY glTexCoord4xOES (GLfixed s, GLfixed t, GLfixed r, GLfixed q); +GLAPI void APIENTRY glTexCoord4xvOES (const GLfixed *coords); +GLAPI void APIENTRY glTexGenxOES (GLenum coord, GLenum pname, GLfixed param); +GLAPI void APIENTRY glTexGenxvOES (GLenum coord, GLenum pname, const GLfixed *params); +GLAPI void APIENTRY glVertex2xOES (GLfixed x); +GLAPI void APIENTRY glVertex2xvOES (const GLfixed *coords); +GLAPI void APIENTRY glVertex3xOES (GLfixed x, GLfixed y); +GLAPI void APIENTRY glVertex3xvOES (const GLfixed *coords); +GLAPI void APIENTRY glVertex4xOES (GLfixed x, GLfixed y, GLfixed z); +GLAPI void APIENTRY glVertex4xvOES (const GLfixed *coords); +#endif +#endif /* GL_OES_fixed_point */ + +#ifndef GL_OES_query_matrix +#define GL_OES_query_matrix 1 +typedef GLbitfield (APIENTRYP PFNGLQUERYMATRIXXOESPROC) (GLfixed *mantissa, GLint *exponent); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI GLbitfield APIENTRY glQueryMatrixxOES (GLfixed *mantissa, GLint *exponent); +#endif +#endif /* GL_OES_query_matrix */ + +#ifndef GL_OES_read_format +#define GL_OES_read_format 1 +#define GL_IMPLEMENTATION_COLOR_READ_TYPE_OES 0x8B9A +#define GL_IMPLEMENTATION_COLOR_READ_FORMAT_OES 0x8B9B +#endif /* GL_OES_read_format */ + +#ifndef GL_OES_single_precision +#define GL_OES_single_precision 1 +typedef void (APIENTRYP PFNGLCLEARDEPTHFOESPROC) (GLclampf depth); +typedef void (APIENTRYP PFNGLCLIPPLANEFOESPROC) (GLenum plane, const GLfloat *equation); +typedef void (APIENTRYP PFNGLDEPTHRANGEFOESPROC) (GLclampf n, GLclampf f); +typedef void (APIENTRYP PFNGLFRUSTUMFOESPROC) (GLfloat l, GLfloat r, GLfloat b, GLfloat t, GLfloat n, GLfloat f); +typedef void (APIENTRYP PFNGLGETCLIPPLANEFOESPROC) (GLenum plane, GLfloat *equation); +typedef void (APIENTRYP PFNGLORTHOFOESPROC) (GLfloat l, GLfloat r, GLfloat b, GLfloat t, GLfloat n, GLfloat f); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glClearDepthfOES (GLclampf depth); +GLAPI void APIENTRY glClipPlanefOES (GLenum plane, const GLfloat *equation); +GLAPI void APIENTRY glDepthRangefOES (GLclampf n, GLclampf f); +GLAPI void APIENTRY glFrustumfOES (GLfloat l, GLfloat r, GLfloat b, GLfloat t, GLfloat n, GLfloat f); +GLAPI void APIENTRY glGetClipPlanefOES (GLenum plane, GLfloat *equation); +GLAPI void APIENTRY glOrthofOES (GLfloat l, GLfloat r, GLfloat b, GLfloat t, GLfloat n, GLfloat f); +#endif +#endif /* GL_OES_single_precision */ + +#ifndef GL_3DFX_multisample +#define GL_3DFX_multisample 1 +#define GL_MULTISAMPLE_3DFX 0x86B2 +#define GL_SAMPLE_BUFFERS_3DFX 0x86B3 +#define GL_SAMPLES_3DFX 0x86B4 +#define GL_MULTISAMPLE_BIT_3DFX 0x20000000 +#endif /* GL_3DFX_multisample */ + +#ifndef GL_3DFX_tbuffer +#define GL_3DFX_tbuffer 1 +typedef void (APIENTRYP PFNGLTBUFFERMASK3DFXPROC) (GLuint mask); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glTbufferMask3DFX (GLuint mask); +#endif +#endif /* GL_3DFX_tbuffer */ + +#ifndef GL_3DFX_texture_compression_FXT1 +#define GL_3DFX_texture_compression_FXT1 1 +#define GL_COMPRESSED_RGB_FXT1_3DFX 0x86B0 +#define GL_COMPRESSED_RGBA_FXT1_3DFX 0x86B1 +#endif /* GL_3DFX_texture_compression_FXT1 */ + +#ifndef GL_AMD_blend_minmax_factor +#define GL_AMD_blend_minmax_factor 1 +#define GL_FACTOR_MIN_AMD 0x901C +#define GL_FACTOR_MAX_AMD 0x901D +#endif /* GL_AMD_blend_minmax_factor */ + +#ifndef GL_AMD_conservative_depth +#define GL_AMD_conservative_depth 1 +#endif /* GL_AMD_conservative_depth */ + +#ifndef GL_AMD_debug_output +#define GL_AMD_debug_output 1 +typedef void (APIENTRY *GLDEBUGPROCAMD)(GLuint id,GLenum category,GLenum severity,GLsizei length,const GLchar *message,void *userParam); +#define GL_MAX_DEBUG_MESSAGE_LENGTH_AMD 0x9143 +#define GL_MAX_DEBUG_LOGGED_MESSAGES_AMD 0x9144 +#define GL_DEBUG_LOGGED_MESSAGES_AMD 0x9145 +#define GL_DEBUG_SEVERITY_HIGH_AMD 0x9146 +#define GL_DEBUG_SEVERITY_MEDIUM_AMD 0x9147 +#define GL_DEBUG_SEVERITY_LOW_AMD 0x9148 +#define GL_DEBUG_CATEGORY_API_ERROR_AMD 0x9149 +#define GL_DEBUG_CATEGORY_WINDOW_SYSTEM_AMD 0x914A +#define GL_DEBUG_CATEGORY_DEPRECATION_AMD 0x914B +#define GL_DEBUG_CATEGORY_UNDEFINED_BEHAVIOR_AMD 0x914C +#define GL_DEBUG_CATEGORY_PERFORMANCE_AMD 0x914D +#define GL_DEBUG_CATEGORY_SHADER_COMPILER_AMD 0x914E +#define GL_DEBUG_CATEGORY_APPLICATION_AMD 0x914F +#define GL_DEBUG_CATEGORY_OTHER_AMD 0x9150 +typedef void (APIENTRYP PFNGLDEBUGMESSAGEENABLEAMDPROC) (GLenum category, GLenum severity, GLsizei count, const GLuint *ids, GLboolean enabled); +typedef void (APIENTRYP PFNGLDEBUGMESSAGEINSERTAMDPROC) (GLenum category, GLenum severity, GLuint id, GLsizei length, const GLchar *buf); +typedef void (APIENTRYP PFNGLDEBUGMESSAGECALLBACKAMDPROC) (GLDEBUGPROCAMD callback, void *userParam); +typedef GLuint (APIENTRYP PFNGLGETDEBUGMESSAGELOGAMDPROC) (GLuint count, GLsizei bufsize, GLenum *categories, GLuint *severities, GLuint *ids, GLsizei *lengths, GLchar *message); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glDebugMessageEnableAMD (GLenum category, GLenum severity, GLsizei count, const GLuint *ids, GLboolean enabled); +GLAPI void APIENTRY glDebugMessageInsertAMD (GLenum category, GLenum severity, GLuint id, GLsizei length, const GLchar *buf); +GLAPI void APIENTRY glDebugMessageCallbackAMD (GLDEBUGPROCAMD callback, void *userParam); +GLAPI GLuint APIENTRY glGetDebugMessageLogAMD (GLuint count, GLsizei bufsize, GLenum *categories, GLuint *severities, GLuint *ids, GLsizei *lengths, GLchar *message); +#endif +#endif /* GL_AMD_debug_output */ + +#ifndef GL_AMD_depth_clamp_separate +#define GL_AMD_depth_clamp_separate 1 +#define GL_DEPTH_CLAMP_NEAR_AMD 0x901E +#define GL_DEPTH_CLAMP_FAR_AMD 0x901F +#endif /* GL_AMD_depth_clamp_separate */ + +#ifndef GL_AMD_draw_buffers_blend +#define GL_AMD_draw_buffers_blend 1 +typedef void (APIENTRYP PFNGLBLENDFUNCINDEXEDAMDPROC) (GLuint buf, GLenum src, GLenum dst); +typedef void (APIENTRYP PFNGLBLENDFUNCSEPARATEINDEXEDAMDPROC) (GLuint buf, GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha); +typedef void (APIENTRYP PFNGLBLENDEQUATIONINDEXEDAMDPROC) (GLuint buf, GLenum mode); +typedef void (APIENTRYP PFNGLBLENDEQUATIONSEPARATEINDEXEDAMDPROC) (GLuint buf, GLenum modeRGB, GLenum modeAlpha); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBlendFuncIndexedAMD (GLuint buf, GLenum src, GLenum dst); +GLAPI void APIENTRY glBlendFuncSeparateIndexedAMD (GLuint buf, GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha); +GLAPI void APIENTRY glBlendEquationIndexedAMD (GLuint buf, GLenum mode); +GLAPI void APIENTRY glBlendEquationSeparateIndexedAMD (GLuint buf, GLenum modeRGB, GLenum modeAlpha); +#endif +#endif /* GL_AMD_draw_buffers_blend */ + +#ifndef GL_AMD_gcn_shader +#define GL_AMD_gcn_shader 1 +#endif /* GL_AMD_gcn_shader */ + +#ifndef GL_AMD_gpu_shader_int64 +#define GL_AMD_gpu_shader_int64 1 +typedef int64_t GLint64EXT; +#define GL_INT64_NV 0x140E +#define GL_UNSIGNED_INT64_NV 0x140F +#define GL_INT8_NV 0x8FE0 +#define GL_INT8_VEC2_NV 0x8FE1 +#define GL_INT8_VEC3_NV 0x8FE2 +#define GL_INT8_VEC4_NV 0x8FE3 +#define GL_INT16_NV 0x8FE4 +#define GL_INT16_VEC2_NV 0x8FE5 +#define GL_INT16_VEC3_NV 0x8FE6 +#define GL_INT16_VEC4_NV 0x8FE7 +#define GL_INT64_VEC2_NV 0x8FE9 +#define GL_INT64_VEC3_NV 0x8FEA +#define GL_INT64_VEC4_NV 0x8FEB +#define GL_UNSIGNED_INT8_NV 0x8FEC +#define GL_UNSIGNED_INT8_VEC2_NV 0x8FED +#define GL_UNSIGNED_INT8_VEC3_NV 0x8FEE +#define GL_UNSIGNED_INT8_VEC4_NV 0x8FEF +#define GL_UNSIGNED_INT16_NV 0x8FF0 +#define GL_UNSIGNED_INT16_VEC2_NV 0x8FF1 +#define GL_UNSIGNED_INT16_VEC3_NV 0x8FF2 +#define GL_UNSIGNED_INT16_VEC4_NV 0x8FF3 +#define GL_UNSIGNED_INT64_VEC2_NV 0x8FF5 +#define GL_UNSIGNED_INT64_VEC3_NV 0x8FF6 +#define GL_UNSIGNED_INT64_VEC4_NV 0x8FF7 +#define GL_FLOAT16_NV 0x8FF8 +#define GL_FLOAT16_VEC2_NV 0x8FF9 +#define GL_FLOAT16_VEC3_NV 0x8FFA +#define GL_FLOAT16_VEC4_NV 0x8FFB +typedef void (APIENTRYP PFNGLUNIFORM1I64NVPROC) (GLint location, GLint64EXT x); +typedef void (APIENTRYP PFNGLUNIFORM2I64NVPROC) (GLint location, GLint64EXT x, GLint64EXT y); +typedef void (APIENTRYP PFNGLUNIFORM3I64NVPROC) (GLint location, GLint64EXT x, GLint64EXT y, GLint64EXT z); +typedef void (APIENTRYP PFNGLUNIFORM4I64NVPROC) (GLint location, GLint64EXT x, GLint64EXT y, GLint64EXT z, GLint64EXT w); +typedef void (APIENTRYP PFNGLUNIFORM1I64VNVPROC) (GLint location, GLsizei count, const GLint64EXT *value); +typedef void (APIENTRYP PFNGLUNIFORM2I64VNVPROC) (GLint location, GLsizei count, const GLint64EXT *value); +typedef void (APIENTRYP PFNGLUNIFORM3I64VNVPROC) (GLint location, GLsizei count, const GLint64EXT *value); +typedef void (APIENTRYP PFNGLUNIFORM4I64VNVPROC) (GLint location, GLsizei count, const GLint64EXT *value); +typedef void (APIENTRYP PFNGLUNIFORM1UI64NVPROC) (GLint location, GLuint64EXT x); +typedef void (APIENTRYP PFNGLUNIFORM2UI64NVPROC) (GLint location, GLuint64EXT x, GLuint64EXT y); +typedef void (APIENTRYP PFNGLUNIFORM3UI64NVPROC) (GLint location, GLuint64EXT x, GLuint64EXT y, GLuint64EXT z); +typedef void (APIENTRYP PFNGLUNIFORM4UI64NVPROC) (GLint location, GLuint64EXT x, GLuint64EXT y, GLuint64EXT z, GLuint64EXT w); +typedef void (APIENTRYP PFNGLUNIFORM1UI64VNVPROC) (GLint location, GLsizei count, const GLuint64EXT *value); +typedef void (APIENTRYP PFNGLUNIFORM2UI64VNVPROC) (GLint location, GLsizei count, const GLuint64EXT *value); +typedef void (APIENTRYP PFNGLUNIFORM3UI64VNVPROC) (GLint location, GLsizei count, const GLuint64EXT *value); +typedef void (APIENTRYP PFNGLUNIFORM4UI64VNVPROC) (GLint location, GLsizei count, const GLuint64EXT *value); +typedef void (APIENTRYP PFNGLGETUNIFORMI64VNVPROC) (GLuint program, GLint location, GLint64EXT *params); +typedef void (APIENTRYP PFNGLGETUNIFORMUI64VNVPROC) (GLuint program, GLint location, GLuint64EXT *params); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1I64NVPROC) (GLuint program, GLint location, GLint64EXT x); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2I64NVPROC) (GLuint program, GLint location, GLint64EXT x, GLint64EXT y); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3I64NVPROC) (GLuint program, GLint location, GLint64EXT x, GLint64EXT y, GLint64EXT z); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4I64NVPROC) (GLuint program, GLint location, GLint64EXT x, GLint64EXT y, GLint64EXT z, GLint64EXT w); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1I64VNVPROC) (GLuint program, GLint location, GLsizei count, const GLint64EXT *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2I64VNVPROC) (GLuint program, GLint location, GLsizei count, const GLint64EXT *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3I64VNVPROC) (GLuint program, GLint location, GLsizei count, const GLint64EXT *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4I64VNVPROC) (GLuint program, GLint location, GLsizei count, const GLint64EXT *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1UI64NVPROC) (GLuint program, GLint location, GLuint64EXT x); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2UI64NVPROC) (GLuint program, GLint location, GLuint64EXT x, GLuint64EXT y); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3UI64NVPROC) (GLuint program, GLint location, GLuint64EXT x, GLuint64EXT y, GLuint64EXT z); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4UI64NVPROC) (GLuint program, GLint location, GLuint64EXT x, GLuint64EXT y, GLuint64EXT z, GLuint64EXT w); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1UI64VNVPROC) (GLuint program, GLint location, GLsizei count, const GLuint64EXT *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2UI64VNVPROC) (GLuint program, GLint location, GLsizei count, const GLuint64EXT *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3UI64VNVPROC) (GLuint program, GLint location, GLsizei count, const GLuint64EXT *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4UI64VNVPROC) (GLuint program, GLint location, GLsizei count, const GLuint64EXT *value); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glUniform1i64NV (GLint location, GLint64EXT x); +GLAPI void APIENTRY glUniform2i64NV (GLint location, GLint64EXT x, GLint64EXT y); +GLAPI void APIENTRY glUniform3i64NV (GLint location, GLint64EXT x, GLint64EXT y, GLint64EXT z); +GLAPI void APIENTRY glUniform4i64NV (GLint location, GLint64EXT x, GLint64EXT y, GLint64EXT z, GLint64EXT w); +GLAPI void APIENTRY glUniform1i64vNV (GLint location, GLsizei count, const GLint64EXT *value); +GLAPI void APIENTRY glUniform2i64vNV (GLint location, GLsizei count, const GLint64EXT *value); +GLAPI void APIENTRY glUniform3i64vNV (GLint location, GLsizei count, const GLint64EXT *value); +GLAPI void APIENTRY glUniform4i64vNV (GLint location, GLsizei count, const GLint64EXT *value); +GLAPI void APIENTRY glUniform1ui64NV (GLint location, GLuint64EXT x); +GLAPI void APIENTRY glUniform2ui64NV (GLint location, GLuint64EXT x, GLuint64EXT y); +GLAPI void APIENTRY glUniform3ui64NV (GLint location, GLuint64EXT x, GLuint64EXT y, GLuint64EXT z); +GLAPI void APIENTRY glUniform4ui64NV (GLint location, GLuint64EXT x, GLuint64EXT y, GLuint64EXT z, GLuint64EXT w); +GLAPI void APIENTRY glUniform1ui64vNV (GLint location, GLsizei count, const GLuint64EXT *value); +GLAPI void APIENTRY glUniform2ui64vNV (GLint location, GLsizei count, const GLuint64EXT *value); +GLAPI void APIENTRY glUniform3ui64vNV (GLint location, GLsizei count, const GLuint64EXT *value); +GLAPI void APIENTRY glUniform4ui64vNV (GLint location, GLsizei count, const GLuint64EXT *value); +GLAPI void APIENTRY glGetUniformi64vNV (GLuint program, GLint location, GLint64EXT *params); +GLAPI void APIENTRY glGetUniformui64vNV (GLuint program, GLint location, GLuint64EXT *params); +GLAPI void APIENTRY glProgramUniform1i64NV (GLuint program, GLint location, GLint64EXT x); +GLAPI void APIENTRY glProgramUniform2i64NV (GLuint program, GLint location, GLint64EXT x, GLint64EXT y); +GLAPI void APIENTRY glProgramUniform3i64NV (GLuint program, GLint location, GLint64EXT x, GLint64EXT y, GLint64EXT z); +GLAPI void APIENTRY glProgramUniform4i64NV (GLuint program, GLint location, GLint64EXT x, GLint64EXT y, GLint64EXT z, GLint64EXT w); +GLAPI void APIENTRY glProgramUniform1i64vNV (GLuint program, GLint location, GLsizei count, const GLint64EXT *value); +GLAPI void APIENTRY glProgramUniform2i64vNV (GLuint program, GLint location, GLsizei count, const GLint64EXT *value); +GLAPI void APIENTRY glProgramUniform3i64vNV (GLuint program, GLint location, GLsizei count, const GLint64EXT *value); +GLAPI void APIENTRY glProgramUniform4i64vNV (GLuint program, GLint location, GLsizei count, const GLint64EXT *value); +GLAPI void APIENTRY glProgramUniform1ui64NV (GLuint program, GLint location, GLuint64EXT x); +GLAPI void APIENTRY glProgramUniform2ui64NV (GLuint program, GLint location, GLuint64EXT x, GLuint64EXT y); +GLAPI void APIENTRY glProgramUniform3ui64NV (GLuint program, GLint location, GLuint64EXT x, GLuint64EXT y, GLuint64EXT z); +GLAPI void APIENTRY glProgramUniform4ui64NV (GLuint program, GLint location, GLuint64EXT x, GLuint64EXT y, GLuint64EXT z, GLuint64EXT w); +GLAPI void APIENTRY glProgramUniform1ui64vNV (GLuint program, GLint location, GLsizei count, const GLuint64EXT *value); +GLAPI void APIENTRY glProgramUniform2ui64vNV (GLuint program, GLint location, GLsizei count, const GLuint64EXT *value); +GLAPI void APIENTRY glProgramUniform3ui64vNV (GLuint program, GLint location, GLsizei count, const GLuint64EXT *value); +GLAPI void APIENTRY glProgramUniform4ui64vNV (GLuint program, GLint location, GLsizei count, const GLuint64EXT *value); +#endif +#endif /* GL_AMD_gpu_shader_int64 */ + +#ifndef GL_AMD_interleaved_elements +#define GL_AMD_interleaved_elements 1 +#define GL_VERTEX_ELEMENT_SWIZZLE_AMD 0x91A4 +#define GL_VERTEX_ID_SWIZZLE_AMD 0x91A5 +typedef void (APIENTRYP PFNGLVERTEXATTRIBPARAMETERIAMDPROC) (GLuint index, GLenum pname, GLint param); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glVertexAttribParameteriAMD (GLuint index, GLenum pname, GLint param); +#endif +#endif /* GL_AMD_interleaved_elements */ + +#ifndef GL_AMD_multi_draw_indirect +#define GL_AMD_multi_draw_indirect 1 +typedef void (APIENTRYP PFNGLMULTIDRAWARRAYSINDIRECTAMDPROC) (GLenum mode, const void *indirect, GLsizei primcount, GLsizei stride); +typedef void (APIENTRYP PFNGLMULTIDRAWELEMENTSINDIRECTAMDPROC) (GLenum mode, GLenum type, const void *indirect, GLsizei primcount, GLsizei stride); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glMultiDrawArraysIndirectAMD (GLenum mode, const void *indirect, GLsizei primcount, GLsizei stride); +GLAPI void APIENTRY glMultiDrawElementsIndirectAMD (GLenum mode, GLenum type, const void *indirect, GLsizei primcount, GLsizei stride); +#endif +#endif /* GL_AMD_multi_draw_indirect */ + +#ifndef GL_AMD_name_gen_delete +#define GL_AMD_name_gen_delete 1 +#define GL_DATA_BUFFER_AMD 0x9151 +#define GL_PERFORMANCE_MONITOR_AMD 0x9152 +#define GL_QUERY_OBJECT_AMD 0x9153 +#define GL_VERTEX_ARRAY_OBJECT_AMD 0x9154 +#define GL_SAMPLER_OBJECT_AMD 0x9155 +typedef void (APIENTRYP PFNGLGENNAMESAMDPROC) (GLenum identifier, GLuint num, GLuint *names); +typedef void (APIENTRYP PFNGLDELETENAMESAMDPROC) (GLenum identifier, GLuint num, const GLuint *names); +typedef GLboolean (APIENTRYP PFNGLISNAMEAMDPROC) (GLenum identifier, GLuint name); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glGenNamesAMD (GLenum identifier, GLuint num, GLuint *names); +GLAPI void APIENTRY glDeleteNamesAMD (GLenum identifier, GLuint num, const GLuint *names); +GLAPI GLboolean APIENTRY glIsNameAMD (GLenum identifier, GLuint name); +#endif +#endif /* GL_AMD_name_gen_delete */ + +#ifndef GL_AMD_occlusion_query_event +#define GL_AMD_occlusion_query_event 1 +#define GL_OCCLUSION_QUERY_EVENT_MASK_AMD 0x874F +#define GL_QUERY_DEPTH_PASS_EVENT_BIT_AMD 0x00000001 +#define GL_QUERY_DEPTH_FAIL_EVENT_BIT_AMD 0x00000002 +#define GL_QUERY_STENCIL_FAIL_EVENT_BIT_AMD 0x00000004 +#define GL_QUERY_DEPTH_BOUNDS_FAIL_EVENT_BIT_AMD 0x00000008 +#define GL_QUERY_ALL_EVENT_BITS_AMD 0xFFFFFFFF +typedef void (APIENTRYP PFNGLQUERYOBJECTPARAMETERUIAMDPROC) (GLenum target, GLuint id, GLenum pname, GLuint param); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glQueryObjectParameteruiAMD (GLenum target, GLuint id, GLenum pname, GLuint param); +#endif +#endif /* GL_AMD_occlusion_query_event */ + +#ifndef GL_AMD_performance_monitor +#define GL_AMD_performance_monitor 1 +#define GL_COUNTER_TYPE_AMD 0x8BC0 +#define GL_COUNTER_RANGE_AMD 0x8BC1 +#define GL_UNSIGNED_INT64_AMD 0x8BC2 +#define GL_PERCENTAGE_AMD 0x8BC3 +#define GL_PERFMON_RESULT_AVAILABLE_AMD 0x8BC4 +#define GL_PERFMON_RESULT_SIZE_AMD 0x8BC5 +#define GL_PERFMON_RESULT_AMD 0x8BC6 +typedef void (APIENTRYP PFNGLGETPERFMONITORGROUPSAMDPROC) (GLint *numGroups, GLsizei groupsSize, GLuint *groups); +typedef void (APIENTRYP PFNGLGETPERFMONITORCOUNTERSAMDPROC) (GLuint group, GLint *numCounters, GLint *maxActiveCounters, GLsizei counterSize, GLuint *counters); +typedef void (APIENTRYP PFNGLGETPERFMONITORGROUPSTRINGAMDPROC) (GLuint group, GLsizei bufSize, GLsizei *length, GLchar *groupString); +typedef void (APIENTRYP PFNGLGETPERFMONITORCOUNTERSTRINGAMDPROC) (GLuint group, GLuint counter, GLsizei bufSize, GLsizei *length, GLchar *counterString); +typedef void (APIENTRYP PFNGLGETPERFMONITORCOUNTERINFOAMDPROC) (GLuint group, GLuint counter, GLenum pname, void *data); +typedef void (APIENTRYP PFNGLGENPERFMONITORSAMDPROC) (GLsizei n, GLuint *monitors); +typedef void (APIENTRYP PFNGLDELETEPERFMONITORSAMDPROC) (GLsizei n, GLuint *monitors); +typedef void (APIENTRYP PFNGLSELECTPERFMONITORCOUNTERSAMDPROC) (GLuint monitor, GLboolean enable, GLuint group, GLint numCounters, GLuint *counterList); +typedef void (APIENTRYP PFNGLBEGINPERFMONITORAMDPROC) (GLuint monitor); +typedef void (APIENTRYP PFNGLENDPERFMONITORAMDPROC) (GLuint monitor); +typedef void (APIENTRYP PFNGLGETPERFMONITORCOUNTERDATAAMDPROC) (GLuint monitor, GLenum pname, GLsizei dataSize, GLuint *data, GLint *bytesWritten); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glGetPerfMonitorGroupsAMD (GLint *numGroups, GLsizei groupsSize, GLuint *groups); +GLAPI void APIENTRY glGetPerfMonitorCountersAMD (GLuint group, GLint *numCounters, GLint *maxActiveCounters, GLsizei counterSize, GLuint *counters); +GLAPI void APIENTRY glGetPerfMonitorGroupStringAMD (GLuint group, GLsizei bufSize, GLsizei *length, GLchar *groupString); +GLAPI void APIENTRY glGetPerfMonitorCounterStringAMD (GLuint group, GLuint counter, GLsizei bufSize, GLsizei *length, GLchar *counterString); +GLAPI void APIENTRY glGetPerfMonitorCounterInfoAMD (GLuint group, GLuint counter, GLenum pname, void *data); +GLAPI void APIENTRY glGenPerfMonitorsAMD (GLsizei n, GLuint *monitors); +GLAPI void APIENTRY glDeletePerfMonitorsAMD (GLsizei n, GLuint *monitors); +GLAPI void APIENTRY glSelectPerfMonitorCountersAMD (GLuint monitor, GLboolean enable, GLuint group, GLint numCounters, GLuint *counterList); +GLAPI void APIENTRY glBeginPerfMonitorAMD (GLuint monitor); +GLAPI void APIENTRY glEndPerfMonitorAMD (GLuint monitor); +GLAPI void APIENTRY glGetPerfMonitorCounterDataAMD (GLuint monitor, GLenum pname, GLsizei dataSize, GLuint *data, GLint *bytesWritten); +#endif +#endif /* GL_AMD_performance_monitor */ + +#ifndef GL_AMD_pinned_memory +#define GL_AMD_pinned_memory 1 +#define GL_EXTERNAL_VIRTUAL_MEMORY_BUFFER_AMD 0x9160 +#endif /* GL_AMD_pinned_memory */ + +#ifndef GL_AMD_query_buffer_object +#define GL_AMD_query_buffer_object 1 +#define GL_QUERY_BUFFER_AMD 0x9192 +#define GL_QUERY_BUFFER_BINDING_AMD 0x9193 +#define GL_QUERY_RESULT_NO_WAIT_AMD 0x9194 +#endif /* GL_AMD_query_buffer_object */ + +#ifndef GL_AMD_sample_positions +#define GL_AMD_sample_positions 1 +#define GL_SUBSAMPLE_DISTANCE_AMD 0x883F +typedef void (APIENTRYP PFNGLSETMULTISAMPLEFVAMDPROC) (GLenum pname, GLuint index, const GLfloat *val); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glSetMultisamplefvAMD (GLenum pname, GLuint index, const GLfloat *val); +#endif +#endif /* GL_AMD_sample_positions */ + +#ifndef GL_AMD_seamless_cubemap_per_texture +#define GL_AMD_seamless_cubemap_per_texture 1 +#endif /* GL_AMD_seamless_cubemap_per_texture */ + +#ifndef GL_AMD_shader_atomic_counter_ops +#define GL_AMD_shader_atomic_counter_ops 1 +#endif /* GL_AMD_shader_atomic_counter_ops */ + +#ifndef GL_AMD_shader_stencil_export +#define GL_AMD_shader_stencil_export 1 +#endif /* GL_AMD_shader_stencil_export */ + +#ifndef GL_AMD_shader_trinary_minmax +#define GL_AMD_shader_trinary_minmax 1 +#endif /* GL_AMD_shader_trinary_minmax */ + +#ifndef GL_AMD_sparse_texture +#define GL_AMD_sparse_texture 1 +#define GL_VIRTUAL_PAGE_SIZE_X_AMD 0x9195 +#define GL_VIRTUAL_PAGE_SIZE_Y_AMD 0x9196 +#define GL_VIRTUAL_PAGE_SIZE_Z_AMD 0x9197 +#define GL_MAX_SPARSE_TEXTURE_SIZE_AMD 0x9198 +#define GL_MAX_SPARSE_3D_TEXTURE_SIZE_AMD 0x9199 +#define GL_MAX_SPARSE_ARRAY_TEXTURE_LAYERS 0x919A +#define GL_MIN_SPARSE_LEVEL_AMD 0x919B +#define GL_MIN_LOD_WARNING_AMD 0x919C +#define GL_TEXTURE_STORAGE_SPARSE_BIT_AMD 0x00000001 +typedef void (APIENTRYP PFNGLTEXSTORAGESPARSEAMDPROC) (GLenum target, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLsizei layers, GLbitfield flags); +typedef void (APIENTRYP PFNGLTEXTURESTORAGESPARSEAMDPROC) (GLuint texture, GLenum target, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLsizei layers, GLbitfield flags); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glTexStorageSparseAMD (GLenum target, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLsizei layers, GLbitfield flags); +GLAPI void APIENTRY glTextureStorageSparseAMD (GLuint texture, GLenum target, GLenum internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLsizei layers, GLbitfield flags); +#endif +#endif /* GL_AMD_sparse_texture */ + +#ifndef GL_AMD_stencil_operation_extended +#define GL_AMD_stencil_operation_extended 1 +#define GL_SET_AMD 0x874A +#define GL_REPLACE_VALUE_AMD 0x874B +#define GL_STENCIL_OP_VALUE_AMD 0x874C +#define GL_STENCIL_BACK_OP_VALUE_AMD 0x874D +typedef void (APIENTRYP PFNGLSTENCILOPVALUEAMDPROC) (GLenum face, GLuint value); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glStencilOpValueAMD (GLenum face, GLuint value); +#endif +#endif /* GL_AMD_stencil_operation_extended */ + +#ifndef GL_AMD_texture_texture4 +#define GL_AMD_texture_texture4 1 +#endif /* GL_AMD_texture_texture4 */ + +#ifndef GL_AMD_transform_feedback3_lines_triangles +#define GL_AMD_transform_feedback3_lines_triangles 1 +#endif /* GL_AMD_transform_feedback3_lines_triangles */ + +#ifndef GL_AMD_transform_feedback4 +#define GL_AMD_transform_feedback4 1 +#define GL_STREAM_RASTERIZATION_AMD 0x91A0 +#endif /* GL_AMD_transform_feedback4 */ + +#ifndef GL_AMD_vertex_shader_layer +#define GL_AMD_vertex_shader_layer 1 +#endif /* GL_AMD_vertex_shader_layer */ + +#ifndef GL_AMD_vertex_shader_tessellator +#define GL_AMD_vertex_shader_tessellator 1 +#define GL_SAMPLER_BUFFER_AMD 0x9001 +#define GL_INT_SAMPLER_BUFFER_AMD 0x9002 +#define GL_UNSIGNED_INT_SAMPLER_BUFFER_AMD 0x9003 +#define GL_TESSELLATION_MODE_AMD 0x9004 +#define GL_TESSELLATION_FACTOR_AMD 0x9005 +#define GL_DISCRETE_AMD 0x9006 +#define GL_CONTINUOUS_AMD 0x9007 +typedef void (APIENTRYP PFNGLTESSELLATIONFACTORAMDPROC) (GLfloat factor); +typedef void (APIENTRYP PFNGLTESSELLATIONMODEAMDPROC) (GLenum mode); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glTessellationFactorAMD (GLfloat factor); +GLAPI void APIENTRY glTessellationModeAMD (GLenum mode); +#endif +#endif /* GL_AMD_vertex_shader_tessellator */ + +#ifndef GL_AMD_vertex_shader_viewport_index +#define GL_AMD_vertex_shader_viewport_index 1 +#endif /* GL_AMD_vertex_shader_viewport_index */ + +#ifndef GL_APPLE_aux_depth_stencil +#define GL_APPLE_aux_depth_stencil 1 +#define GL_AUX_DEPTH_STENCIL_APPLE 0x8A14 +#endif /* GL_APPLE_aux_depth_stencil */ + +#ifndef GL_APPLE_client_storage +#define GL_APPLE_client_storage 1 +#define GL_UNPACK_CLIENT_STORAGE_APPLE 0x85B2 +#endif /* GL_APPLE_client_storage */ + +#ifndef GL_APPLE_element_array +#define GL_APPLE_element_array 1 +#define GL_ELEMENT_ARRAY_APPLE 0x8A0C +#define GL_ELEMENT_ARRAY_TYPE_APPLE 0x8A0D +#define GL_ELEMENT_ARRAY_POINTER_APPLE 0x8A0E +typedef void (APIENTRYP PFNGLELEMENTPOINTERAPPLEPROC) (GLenum type, const void *pointer); +typedef void (APIENTRYP PFNGLDRAWELEMENTARRAYAPPLEPROC) (GLenum mode, GLint first, GLsizei count); +typedef void (APIENTRYP PFNGLDRAWRANGEELEMENTARRAYAPPLEPROC) (GLenum mode, GLuint start, GLuint end, GLint first, GLsizei count); +typedef void (APIENTRYP PFNGLMULTIDRAWELEMENTARRAYAPPLEPROC) (GLenum mode, const GLint *first, const GLsizei *count, GLsizei primcount); +typedef void (APIENTRYP PFNGLMULTIDRAWRANGEELEMENTARRAYAPPLEPROC) (GLenum mode, GLuint start, GLuint end, const GLint *first, const GLsizei *count, GLsizei primcount); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glElementPointerAPPLE (GLenum type, const void *pointer); +GLAPI void APIENTRY glDrawElementArrayAPPLE (GLenum mode, GLint first, GLsizei count); +GLAPI void APIENTRY glDrawRangeElementArrayAPPLE (GLenum mode, GLuint start, GLuint end, GLint first, GLsizei count); +GLAPI void APIENTRY glMultiDrawElementArrayAPPLE (GLenum mode, const GLint *first, const GLsizei *count, GLsizei primcount); +GLAPI void APIENTRY glMultiDrawRangeElementArrayAPPLE (GLenum mode, GLuint start, GLuint end, const GLint *first, const GLsizei *count, GLsizei primcount); +#endif +#endif /* GL_APPLE_element_array */ + +#ifndef GL_APPLE_fence +#define GL_APPLE_fence 1 +#define GL_DRAW_PIXELS_APPLE 0x8A0A +#define GL_FENCE_APPLE 0x8A0B +typedef void (APIENTRYP PFNGLGENFENCESAPPLEPROC) (GLsizei n, GLuint *fences); +typedef void (APIENTRYP PFNGLDELETEFENCESAPPLEPROC) (GLsizei n, const GLuint *fences); +typedef void (APIENTRYP PFNGLSETFENCEAPPLEPROC) (GLuint fence); +typedef GLboolean (APIENTRYP PFNGLISFENCEAPPLEPROC) (GLuint fence); +typedef GLboolean (APIENTRYP PFNGLTESTFENCEAPPLEPROC) (GLuint fence); +typedef void (APIENTRYP PFNGLFINISHFENCEAPPLEPROC) (GLuint fence); +typedef GLboolean (APIENTRYP PFNGLTESTOBJECTAPPLEPROC) (GLenum object, GLuint name); +typedef void (APIENTRYP PFNGLFINISHOBJECTAPPLEPROC) (GLenum object, GLint name); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glGenFencesAPPLE (GLsizei n, GLuint *fences); +GLAPI void APIENTRY glDeleteFencesAPPLE (GLsizei n, const GLuint *fences); +GLAPI void APIENTRY glSetFenceAPPLE (GLuint fence); +GLAPI GLboolean APIENTRY glIsFenceAPPLE (GLuint fence); +GLAPI GLboolean APIENTRY glTestFenceAPPLE (GLuint fence); +GLAPI void APIENTRY glFinishFenceAPPLE (GLuint fence); +GLAPI GLboolean APIENTRY glTestObjectAPPLE (GLenum object, GLuint name); +GLAPI void APIENTRY glFinishObjectAPPLE (GLenum object, GLint name); +#endif +#endif /* GL_APPLE_fence */ + +#ifndef GL_APPLE_float_pixels +#define GL_APPLE_float_pixels 1 +#define GL_HALF_APPLE 0x140B +#define GL_RGBA_FLOAT32_APPLE 0x8814 +#define GL_RGB_FLOAT32_APPLE 0x8815 +#define GL_ALPHA_FLOAT32_APPLE 0x8816 +#define GL_INTENSITY_FLOAT32_APPLE 0x8817 +#define GL_LUMINANCE_FLOAT32_APPLE 0x8818 +#define GL_LUMINANCE_ALPHA_FLOAT32_APPLE 0x8819 +#define GL_RGBA_FLOAT16_APPLE 0x881A +#define GL_RGB_FLOAT16_APPLE 0x881B +#define GL_ALPHA_FLOAT16_APPLE 0x881C +#define GL_INTENSITY_FLOAT16_APPLE 0x881D +#define GL_LUMINANCE_FLOAT16_APPLE 0x881E +#define GL_LUMINANCE_ALPHA_FLOAT16_APPLE 0x881F +#define GL_COLOR_FLOAT_APPLE 0x8A0F +#endif /* GL_APPLE_float_pixels */ + +#ifndef GL_APPLE_flush_buffer_range +#define GL_APPLE_flush_buffer_range 1 +#define GL_BUFFER_SERIALIZED_MODIFY_APPLE 0x8A12 +#define GL_BUFFER_FLUSHING_UNMAP_APPLE 0x8A13 +typedef void (APIENTRYP PFNGLBUFFERPARAMETERIAPPLEPROC) (GLenum target, GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLFLUSHMAPPEDBUFFERRANGEAPPLEPROC) (GLenum target, GLintptr offset, GLsizeiptr size); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBufferParameteriAPPLE (GLenum target, GLenum pname, GLint param); +GLAPI void APIENTRY glFlushMappedBufferRangeAPPLE (GLenum target, GLintptr offset, GLsizeiptr size); +#endif +#endif /* GL_APPLE_flush_buffer_range */ + +#ifndef GL_APPLE_object_purgeable +#define GL_APPLE_object_purgeable 1 +#define GL_BUFFER_OBJECT_APPLE 0x85B3 +#define GL_RELEASED_APPLE 0x8A19 +#define GL_VOLATILE_APPLE 0x8A1A +#define GL_RETAINED_APPLE 0x8A1B +#define GL_UNDEFINED_APPLE 0x8A1C +#define GL_PURGEABLE_APPLE 0x8A1D +typedef GLenum (APIENTRYP PFNGLOBJECTPURGEABLEAPPLEPROC) (GLenum objectType, GLuint name, GLenum option); +typedef GLenum (APIENTRYP PFNGLOBJECTUNPURGEABLEAPPLEPROC) (GLenum objectType, GLuint name, GLenum option); +typedef void (APIENTRYP PFNGLGETOBJECTPARAMETERIVAPPLEPROC) (GLenum objectType, GLuint name, GLenum pname, GLint *params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI GLenum APIENTRY glObjectPurgeableAPPLE (GLenum objectType, GLuint name, GLenum option); +GLAPI GLenum APIENTRY glObjectUnpurgeableAPPLE (GLenum objectType, GLuint name, GLenum option); +GLAPI void APIENTRY glGetObjectParameterivAPPLE (GLenum objectType, GLuint name, GLenum pname, GLint *params); +#endif +#endif /* GL_APPLE_object_purgeable */ + +#ifndef GL_APPLE_rgb_422 +#define GL_APPLE_rgb_422 1 +#define GL_RGB_422_APPLE 0x8A1F +#define GL_UNSIGNED_SHORT_8_8_APPLE 0x85BA +#define GL_UNSIGNED_SHORT_8_8_REV_APPLE 0x85BB +#define GL_RGB_RAW_422_APPLE 0x8A51 +#endif /* GL_APPLE_rgb_422 */ + +#ifndef GL_APPLE_row_bytes +#define GL_APPLE_row_bytes 1 +#define GL_PACK_ROW_BYTES_APPLE 0x8A15 +#define GL_UNPACK_ROW_BYTES_APPLE 0x8A16 +#endif /* GL_APPLE_row_bytes */ + +#ifndef GL_APPLE_specular_vector +#define GL_APPLE_specular_vector 1 +#define GL_LIGHT_MODEL_SPECULAR_VECTOR_APPLE 0x85B0 +#endif /* GL_APPLE_specular_vector */ + +#ifndef GL_APPLE_texture_range +#define GL_APPLE_texture_range 1 +#define GL_TEXTURE_RANGE_LENGTH_APPLE 0x85B7 +#define GL_TEXTURE_RANGE_POINTER_APPLE 0x85B8 +#define GL_TEXTURE_STORAGE_HINT_APPLE 0x85BC +#define GL_STORAGE_PRIVATE_APPLE 0x85BD +#define GL_STORAGE_CACHED_APPLE 0x85BE +#define GL_STORAGE_SHARED_APPLE 0x85BF +typedef void (APIENTRYP PFNGLTEXTURERANGEAPPLEPROC) (GLenum target, GLsizei length, const void *pointer); +typedef void (APIENTRYP PFNGLGETTEXPARAMETERPOINTERVAPPLEPROC) (GLenum target, GLenum pname, void **params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glTextureRangeAPPLE (GLenum target, GLsizei length, const void *pointer); +GLAPI void APIENTRY glGetTexParameterPointervAPPLE (GLenum target, GLenum pname, void **params); +#endif +#endif /* GL_APPLE_texture_range */ + +#ifndef GL_APPLE_transform_hint +#define GL_APPLE_transform_hint 1 +#define GL_TRANSFORM_HINT_APPLE 0x85B1 +#endif /* GL_APPLE_transform_hint */ + +#ifndef GL_APPLE_vertex_array_object +#define GL_APPLE_vertex_array_object 1 +#define GL_VERTEX_ARRAY_BINDING_APPLE 0x85B5 +typedef void (APIENTRYP PFNGLBINDVERTEXARRAYAPPLEPROC) (GLuint array); +typedef void (APIENTRYP PFNGLDELETEVERTEXARRAYSAPPLEPROC) (GLsizei n, const GLuint *arrays); +typedef void (APIENTRYP PFNGLGENVERTEXARRAYSAPPLEPROC) (GLsizei n, GLuint *arrays); +typedef GLboolean (APIENTRYP PFNGLISVERTEXARRAYAPPLEPROC) (GLuint array); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBindVertexArrayAPPLE (GLuint array); +GLAPI void APIENTRY glDeleteVertexArraysAPPLE (GLsizei n, const GLuint *arrays); +GLAPI void APIENTRY glGenVertexArraysAPPLE (GLsizei n, GLuint *arrays); +GLAPI GLboolean APIENTRY glIsVertexArrayAPPLE (GLuint array); +#endif +#endif /* GL_APPLE_vertex_array_object */ + +#ifndef GL_APPLE_vertex_array_range +#define GL_APPLE_vertex_array_range 1 +#define GL_VERTEX_ARRAY_RANGE_APPLE 0x851D +#define GL_VERTEX_ARRAY_RANGE_LENGTH_APPLE 0x851E +#define GL_VERTEX_ARRAY_STORAGE_HINT_APPLE 0x851F +#define GL_VERTEX_ARRAY_RANGE_POINTER_APPLE 0x8521 +#define GL_STORAGE_CLIENT_APPLE 0x85B4 +typedef void (APIENTRYP PFNGLVERTEXARRAYRANGEAPPLEPROC) (GLsizei length, void *pointer); +typedef void (APIENTRYP PFNGLFLUSHVERTEXARRAYRANGEAPPLEPROC) (GLsizei length, void *pointer); +typedef void (APIENTRYP PFNGLVERTEXARRAYPARAMETERIAPPLEPROC) (GLenum pname, GLint param); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glVertexArrayRangeAPPLE (GLsizei length, void *pointer); +GLAPI void APIENTRY glFlushVertexArrayRangeAPPLE (GLsizei length, void *pointer); +GLAPI void APIENTRY glVertexArrayParameteriAPPLE (GLenum pname, GLint param); +#endif +#endif /* GL_APPLE_vertex_array_range */ + +#ifndef GL_APPLE_vertex_program_evaluators +#define GL_APPLE_vertex_program_evaluators 1 +#define GL_VERTEX_ATTRIB_MAP1_APPLE 0x8A00 +#define GL_VERTEX_ATTRIB_MAP2_APPLE 0x8A01 +#define GL_VERTEX_ATTRIB_MAP1_SIZE_APPLE 0x8A02 +#define GL_VERTEX_ATTRIB_MAP1_COEFF_APPLE 0x8A03 +#define GL_VERTEX_ATTRIB_MAP1_ORDER_APPLE 0x8A04 +#define GL_VERTEX_ATTRIB_MAP1_DOMAIN_APPLE 0x8A05 +#define GL_VERTEX_ATTRIB_MAP2_SIZE_APPLE 0x8A06 +#define GL_VERTEX_ATTRIB_MAP2_COEFF_APPLE 0x8A07 +#define GL_VERTEX_ATTRIB_MAP2_ORDER_APPLE 0x8A08 +#define GL_VERTEX_ATTRIB_MAP2_DOMAIN_APPLE 0x8A09 +typedef void (APIENTRYP PFNGLENABLEVERTEXATTRIBAPPLEPROC) (GLuint index, GLenum pname); +typedef void (APIENTRYP PFNGLDISABLEVERTEXATTRIBAPPLEPROC) (GLuint index, GLenum pname); +typedef GLboolean (APIENTRYP PFNGLISVERTEXATTRIBENABLEDAPPLEPROC) (GLuint index, GLenum pname); +typedef void (APIENTRYP PFNGLMAPVERTEXATTRIB1DAPPLEPROC) (GLuint index, GLuint size, GLdouble u1, GLdouble u2, GLint stride, GLint order, const GLdouble *points); +typedef void (APIENTRYP PFNGLMAPVERTEXATTRIB1FAPPLEPROC) (GLuint index, GLuint size, GLfloat u1, GLfloat u2, GLint stride, GLint order, const GLfloat *points); +typedef void (APIENTRYP PFNGLMAPVERTEXATTRIB2DAPPLEPROC) (GLuint index, GLuint size, GLdouble u1, GLdouble u2, GLint ustride, GLint uorder, GLdouble v1, GLdouble v2, GLint vstride, GLint vorder, const GLdouble *points); +typedef void (APIENTRYP PFNGLMAPVERTEXATTRIB2FAPPLEPROC) (GLuint index, GLuint size, GLfloat u1, GLfloat u2, GLint ustride, GLint uorder, GLfloat v1, GLfloat v2, GLint vstride, GLint vorder, const GLfloat *points); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glEnableVertexAttribAPPLE (GLuint index, GLenum pname); +GLAPI void APIENTRY glDisableVertexAttribAPPLE (GLuint index, GLenum pname); +GLAPI GLboolean APIENTRY glIsVertexAttribEnabledAPPLE (GLuint index, GLenum pname); +GLAPI void APIENTRY glMapVertexAttrib1dAPPLE (GLuint index, GLuint size, GLdouble u1, GLdouble u2, GLint stride, GLint order, const GLdouble *points); +GLAPI void APIENTRY glMapVertexAttrib1fAPPLE (GLuint index, GLuint size, GLfloat u1, GLfloat u2, GLint stride, GLint order, const GLfloat *points); +GLAPI void APIENTRY glMapVertexAttrib2dAPPLE (GLuint index, GLuint size, GLdouble u1, GLdouble u2, GLint ustride, GLint uorder, GLdouble v1, GLdouble v2, GLint vstride, GLint vorder, const GLdouble *points); +GLAPI void APIENTRY glMapVertexAttrib2fAPPLE (GLuint index, GLuint size, GLfloat u1, GLfloat u2, GLint ustride, GLint uorder, GLfloat v1, GLfloat v2, GLint vstride, GLint vorder, const GLfloat *points); +#endif +#endif /* GL_APPLE_vertex_program_evaluators */ + +#ifndef GL_APPLE_ycbcr_422 +#define GL_APPLE_ycbcr_422 1 +#define GL_YCBCR_422_APPLE 0x85B9 +#endif /* GL_APPLE_ycbcr_422 */ + +#ifndef GL_ATI_draw_buffers +#define GL_ATI_draw_buffers 1 +#define GL_MAX_DRAW_BUFFERS_ATI 0x8824 +#define GL_DRAW_BUFFER0_ATI 0x8825 +#define GL_DRAW_BUFFER1_ATI 0x8826 +#define GL_DRAW_BUFFER2_ATI 0x8827 +#define GL_DRAW_BUFFER3_ATI 0x8828 +#define GL_DRAW_BUFFER4_ATI 0x8829 +#define GL_DRAW_BUFFER5_ATI 0x882A +#define GL_DRAW_BUFFER6_ATI 0x882B +#define GL_DRAW_BUFFER7_ATI 0x882C +#define GL_DRAW_BUFFER8_ATI 0x882D +#define GL_DRAW_BUFFER9_ATI 0x882E +#define GL_DRAW_BUFFER10_ATI 0x882F +#define GL_DRAW_BUFFER11_ATI 0x8830 +#define GL_DRAW_BUFFER12_ATI 0x8831 +#define GL_DRAW_BUFFER13_ATI 0x8832 +#define GL_DRAW_BUFFER14_ATI 0x8833 +#define GL_DRAW_BUFFER15_ATI 0x8834 +typedef void (APIENTRYP PFNGLDRAWBUFFERSATIPROC) (GLsizei n, const GLenum *bufs); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glDrawBuffersATI (GLsizei n, const GLenum *bufs); +#endif +#endif /* GL_ATI_draw_buffers */ + +#ifndef GL_ATI_element_array +#define GL_ATI_element_array 1 +#define GL_ELEMENT_ARRAY_ATI 0x8768 +#define GL_ELEMENT_ARRAY_TYPE_ATI 0x8769 +#define GL_ELEMENT_ARRAY_POINTER_ATI 0x876A +typedef void (APIENTRYP PFNGLELEMENTPOINTERATIPROC) (GLenum type, const void *pointer); +typedef void (APIENTRYP PFNGLDRAWELEMENTARRAYATIPROC) (GLenum mode, GLsizei count); +typedef void (APIENTRYP PFNGLDRAWRANGEELEMENTARRAYATIPROC) (GLenum mode, GLuint start, GLuint end, GLsizei count); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glElementPointerATI (GLenum type, const void *pointer); +GLAPI void APIENTRY glDrawElementArrayATI (GLenum mode, GLsizei count); +GLAPI void APIENTRY glDrawRangeElementArrayATI (GLenum mode, GLuint start, GLuint end, GLsizei count); +#endif +#endif /* GL_ATI_element_array */ + +#ifndef GL_ATI_envmap_bumpmap +#define GL_ATI_envmap_bumpmap 1 +#define GL_BUMP_ROT_MATRIX_ATI 0x8775 +#define GL_BUMP_ROT_MATRIX_SIZE_ATI 0x8776 +#define GL_BUMP_NUM_TEX_UNITS_ATI 0x8777 +#define GL_BUMP_TEX_UNITS_ATI 0x8778 +#define GL_DUDV_ATI 0x8779 +#define GL_DU8DV8_ATI 0x877A +#define GL_BUMP_ENVMAP_ATI 0x877B +#define GL_BUMP_TARGET_ATI 0x877C +typedef void (APIENTRYP PFNGLTEXBUMPPARAMETERIVATIPROC) (GLenum pname, const GLint *param); +typedef void (APIENTRYP PFNGLTEXBUMPPARAMETERFVATIPROC) (GLenum pname, const GLfloat *param); +typedef void (APIENTRYP PFNGLGETTEXBUMPPARAMETERIVATIPROC) (GLenum pname, GLint *param); +typedef void (APIENTRYP PFNGLGETTEXBUMPPARAMETERFVATIPROC) (GLenum pname, GLfloat *param); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glTexBumpParameterivATI (GLenum pname, const GLint *param); +GLAPI void APIENTRY glTexBumpParameterfvATI (GLenum pname, const GLfloat *param); +GLAPI void APIENTRY glGetTexBumpParameterivATI (GLenum pname, GLint *param); +GLAPI void APIENTRY glGetTexBumpParameterfvATI (GLenum pname, GLfloat *param); +#endif +#endif /* GL_ATI_envmap_bumpmap */ + +#ifndef GL_ATI_fragment_shader +#define GL_ATI_fragment_shader 1 +#define GL_FRAGMENT_SHADER_ATI 0x8920 +#define GL_REG_0_ATI 0x8921 +#define GL_REG_1_ATI 0x8922 +#define GL_REG_2_ATI 0x8923 +#define GL_REG_3_ATI 0x8924 +#define GL_REG_4_ATI 0x8925 +#define GL_REG_5_ATI 0x8926 +#define GL_REG_6_ATI 0x8927 +#define GL_REG_7_ATI 0x8928 +#define GL_REG_8_ATI 0x8929 +#define GL_REG_9_ATI 0x892A +#define GL_REG_10_ATI 0x892B +#define GL_REG_11_ATI 0x892C +#define GL_REG_12_ATI 0x892D +#define GL_REG_13_ATI 0x892E +#define GL_REG_14_ATI 0x892F +#define GL_REG_15_ATI 0x8930 +#define GL_REG_16_ATI 0x8931 +#define GL_REG_17_ATI 0x8932 +#define GL_REG_18_ATI 0x8933 +#define GL_REG_19_ATI 0x8934 +#define GL_REG_20_ATI 0x8935 +#define GL_REG_21_ATI 0x8936 +#define GL_REG_22_ATI 0x8937 +#define GL_REG_23_ATI 0x8938 +#define GL_REG_24_ATI 0x8939 +#define GL_REG_25_ATI 0x893A +#define GL_REG_26_ATI 0x893B +#define GL_REG_27_ATI 0x893C +#define GL_REG_28_ATI 0x893D +#define GL_REG_29_ATI 0x893E +#define GL_REG_30_ATI 0x893F +#define GL_REG_31_ATI 0x8940 +#define GL_CON_0_ATI 0x8941 +#define GL_CON_1_ATI 0x8942 +#define GL_CON_2_ATI 0x8943 +#define GL_CON_3_ATI 0x8944 +#define GL_CON_4_ATI 0x8945 +#define GL_CON_5_ATI 0x8946 +#define GL_CON_6_ATI 0x8947 +#define GL_CON_7_ATI 0x8948 +#define GL_CON_8_ATI 0x8949 +#define GL_CON_9_ATI 0x894A +#define GL_CON_10_ATI 0x894B +#define GL_CON_11_ATI 0x894C +#define GL_CON_12_ATI 0x894D +#define GL_CON_13_ATI 0x894E +#define GL_CON_14_ATI 0x894F +#define GL_CON_15_ATI 0x8950 +#define GL_CON_16_ATI 0x8951 +#define GL_CON_17_ATI 0x8952 +#define GL_CON_18_ATI 0x8953 +#define GL_CON_19_ATI 0x8954 +#define GL_CON_20_ATI 0x8955 +#define GL_CON_21_ATI 0x8956 +#define GL_CON_22_ATI 0x8957 +#define GL_CON_23_ATI 0x8958 +#define GL_CON_24_ATI 0x8959 +#define GL_CON_25_ATI 0x895A +#define GL_CON_26_ATI 0x895B +#define GL_CON_27_ATI 0x895C +#define GL_CON_28_ATI 0x895D +#define GL_CON_29_ATI 0x895E +#define GL_CON_30_ATI 0x895F +#define GL_CON_31_ATI 0x8960 +#define GL_MOV_ATI 0x8961 +#define GL_ADD_ATI 0x8963 +#define GL_MUL_ATI 0x8964 +#define GL_SUB_ATI 0x8965 +#define GL_DOT3_ATI 0x8966 +#define GL_DOT4_ATI 0x8967 +#define GL_MAD_ATI 0x8968 +#define GL_LERP_ATI 0x8969 +#define GL_CND_ATI 0x896A +#define GL_CND0_ATI 0x896B +#define GL_DOT2_ADD_ATI 0x896C +#define GL_SECONDARY_INTERPOLATOR_ATI 0x896D +#define GL_NUM_FRAGMENT_REGISTERS_ATI 0x896E +#define GL_NUM_FRAGMENT_CONSTANTS_ATI 0x896F +#define GL_NUM_PASSES_ATI 0x8970 +#define GL_NUM_INSTRUCTIONS_PER_PASS_ATI 0x8971 +#define GL_NUM_INSTRUCTIONS_TOTAL_ATI 0x8972 +#define GL_NUM_INPUT_INTERPOLATOR_COMPONENTS_ATI 0x8973 +#define GL_NUM_LOOPBACK_COMPONENTS_ATI 0x8974 +#define GL_COLOR_ALPHA_PAIRING_ATI 0x8975 +#define GL_SWIZZLE_STR_ATI 0x8976 +#define GL_SWIZZLE_STQ_ATI 0x8977 +#define GL_SWIZZLE_STR_DR_ATI 0x8978 +#define GL_SWIZZLE_STQ_DQ_ATI 0x8979 +#define GL_SWIZZLE_STRQ_ATI 0x897A +#define GL_SWIZZLE_STRQ_DQ_ATI 0x897B +#define GL_RED_BIT_ATI 0x00000001 +#define GL_GREEN_BIT_ATI 0x00000002 +#define GL_BLUE_BIT_ATI 0x00000004 +#define GL_2X_BIT_ATI 0x00000001 +#define GL_4X_BIT_ATI 0x00000002 +#define GL_8X_BIT_ATI 0x00000004 +#define GL_HALF_BIT_ATI 0x00000008 +#define GL_QUARTER_BIT_ATI 0x00000010 +#define GL_EIGHTH_BIT_ATI 0x00000020 +#define GL_SATURATE_BIT_ATI 0x00000040 +#define GL_COMP_BIT_ATI 0x00000002 +#define GL_NEGATE_BIT_ATI 0x00000004 +#define GL_BIAS_BIT_ATI 0x00000008 +typedef GLuint (APIENTRYP PFNGLGENFRAGMENTSHADERSATIPROC) (GLuint range); +typedef void (APIENTRYP PFNGLBINDFRAGMENTSHADERATIPROC) (GLuint id); +typedef void (APIENTRYP PFNGLDELETEFRAGMENTSHADERATIPROC) (GLuint id); +typedef void (APIENTRYP PFNGLBEGINFRAGMENTSHADERATIPROC) (void); +typedef void (APIENTRYP PFNGLENDFRAGMENTSHADERATIPROC) (void); +typedef void (APIENTRYP PFNGLPASSTEXCOORDATIPROC) (GLuint dst, GLuint coord, GLenum swizzle); +typedef void (APIENTRYP PFNGLSAMPLEMAPATIPROC) (GLuint dst, GLuint interp, GLenum swizzle); +typedef void (APIENTRYP PFNGLCOLORFRAGMENTOP1ATIPROC) (GLenum op, GLuint dst, GLuint dstMask, GLuint dstMod, GLuint arg1, GLuint arg1Rep, GLuint arg1Mod); +typedef void (APIENTRYP PFNGLCOLORFRAGMENTOP2ATIPROC) (GLenum op, GLuint dst, GLuint dstMask, GLuint dstMod, GLuint arg1, GLuint arg1Rep, GLuint arg1Mod, GLuint arg2, GLuint arg2Rep, GLuint arg2Mod); +typedef void (APIENTRYP PFNGLCOLORFRAGMENTOP3ATIPROC) (GLenum op, GLuint dst, GLuint dstMask, GLuint dstMod, GLuint arg1, GLuint arg1Rep, GLuint arg1Mod, GLuint arg2, GLuint arg2Rep, GLuint arg2Mod, GLuint arg3, GLuint arg3Rep, GLuint arg3Mod); +typedef void (APIENTRYP PFNGLALPHAFRAGMENTOP1ATIPROC) (GLenum op, GLuint dst, GLuint dstMod, GLuint arg1, GLuint arg1Rep, GLuint arg1Mod); +typedef void (APIENTRYP PFNGLALPHAFRAGMENTOP2ATIPROC) (GLenum op, GLuint dst, GLuint dstMod, GLuint arg1, GLuint arg1Rep, GLuint arg1Mod, GLuint arg2, GLuint arg2Rep, GLuint arg2Mod); +typedef void (APIENTRYP PFNGLALPHAFRAGMENTOP3ATIPROC) (GLenum op, GLuint dst, GLuint dstMod, GLuint arg1, GLuint arg1Rep, GLuint arg1Mod, GLuint arg2, GLuint arg2Rep, GLuint arg2Mod, GLuint arg3, GLuint arg3Rep, GLuint arg3Mod); +typedef void (APIENTRYP PFNGLSETFRAGMENTSHADERCONSTANTATIPROC) (GLuint dst, const GLfloat *value); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI GLuint APIENTRY glGenFragmentShadersATI (GLuint range); +GLAPI void APIENTRY glBindFragmentShaderATI (GLuint id); +GLAPI void APIENTRY glDeleteFragmentShaderATI (GLuint id); +GLAPI void APIENTRY glBeginFragmentShaderATI (void); +GLAPI void APIENTRY glEndFragmentShaderATI (void); +GLAPI void APIENTRY glPassTexCoordATI (GLuint dst, GLuint coord, GLenum swizzle); +GLAPI void APIENTRY glSampleMapATI (GLuint dst, GLuint interp, GLenum swizzle); +GLAPI void APIENTRY glColorFragmentOp1ATI (GLenum op, GLuint dst, GLuint dstMask, GLuint dstMod, GLuint arg1, GLuint arg1Rep, GLuint arg1Mod); +GLAPI void APIENTRY glColorFragmentOp2ATI (GLenum op, GLuint dst, GLuint dstMask, GLuint dstMod, GLuint arg1, GLuint arg1Rep, GLuint arg1Mod, GLuint arg2, GLuint arg2Rep, GLuint arg2Mod); +GLAPI void APIENTRY glColorFragmentOp3ATI (GLenum op, GLuint dst, GLuint dstMask, GLuint dstMod, GLuint arg1, GLuint arg1Rep, GLuint arg1Mod, GLuint arg2, GLuint arg2Rep, GLuint arg2Mod, GLuint arg3, GLuint arg3Rep, GLuint arg3Mod); +GLAPI void APIENTRY glAlphaFragmentOp1ATI (GLenum op, GLuint dst, GLuint dstMod, GLuint arg1, GLuint arg1Rep, GLuint arg1Mod); +GLAPI void APIENTRY glAlphaFragmentOp2ATI (GLenum op, GLuint dst, GLuint dstMod, GLuint arg1, GLuint arg1Rep, GLuint arg1Mod, GLuint arg2, GLuint arg2Rep, GLuint arg2Mod); +GLAPI void APIENTRY glAlphaFragmentOp3ATI (GLenum op, GLuint dst, GLuint dstMod, GLuint arg1, GLuint arg1Rep, GLuint arg1Mod, GLuint arg2, GLuint arg2Rep, GLuint arg2Mod, GLuint arg3, GLuint arg3Rep, GLuint arg3Mod); +GLAPI void APIENTRY glSetFragmentShaderConstantATI (GLuint dst, const GLfloat *value); +#endif +#endif /* GL_ATI_fragment_shader */ + +#ifndef GL_ATI_map_object_buffer +#define GL_ATI_map_object_buffer 1 +typedef void *(APIENTRYP PFNGLMAPOBJECTBUFFERATIPROC) (GLuint buffer); +typedef void (APIENTRYP PFNGLUNMAPOBJECTBUFFERATIPROC) (GLuint buffer); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void *APIENTRY glMapObjectBufferATI (GLuint buffer); +GLAPI void APIENTRY glUnmapObjectBufferATI (GLuint buffer); +#endif +#endif /* GL_ATI_map_object_buffer */ + +#ifndef GL_ATI_meminfo +#define GL_ATI_meminfo 1 +#define GL_VBO_FREE_MEMORY_ATI 0x87FB +#define GL_TEXTURE_FREE_MEMORY_ATI 0x87FC +#define GL_RENDERBUFFER_FREE_MEMORY_ATI 0x87FD +#endif /* GL_ATI_meminfo */ + +#ifndef GL_ATI_pixel_format_float +#define GL_ATI_pixel_format_float 1 +#define GL_RGBA_FLOAT_MODE_ATI 0x8820 +#define GL_COLOR_CLEAR_UNCLAMPED_VALUE_ATI 0x8835 +#endif /* GL_ATI_pixel_format_float */ + +#ifndef GL_ATI_pn_triangles +#define GL_ATI_pn_triangles 1 +#define GL_PN_TRIANGLES_ATI 0x87F0 +#define GL_MAX_PN_TRIANGLES_TESSELATION_LEVEL_ATI 0x87F1 +#define GL_PN_TRIANGLES_POINT_MODE_ATI 0x87F2 +#define GL_PN_TRIANGLES_NORMAL_MODE_ATI 0x87F3 +#define GL_PN_TRIANGLES_TESSELATION_LEVEL_ATI 0x87F4 +#define GL_PN_TRIANGLES_POINT_MODE_LINEAR_ATI 0x87F5 +#define GL_PN_TRIANGLES_POINT_MODE_CUBIC_ATI 0x87F6 +#define GL_PN_TRIANGLES_NORMAL_MODE_LINEAR_ATI 0x87F7 +#define GL_PN_TRIANGLES_NORMAL_MODE_QUADRATIC_ATI 0x87F8 +typedef void (APIENTRYP PFNGLPNTRIANGLESIATIPROC) (GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLPNTRIANGLESFATIPROC) (GLenum pname, GLfloat param); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glPNTrianglesiATI (GLenum pname, GLint param); +GLAPI void APIENTRY glPNTrianglesfATI (GLenum pname, GLfloat param); +#endif +#endif /* GL_ATI_pn_triangles */ + +#ifndef GL_ATI_separate_stencil +#define GL_ATI_separate_stencil 1 +#define GL_STENCIL_BACK_FUNC_ATI 0x8800 +#define GL_STENCIL_BACK_FAIL_ATI 0x8801 +#define GL_STENCIL_BACK_PASS_DEPTH_FAIL_ATI 0x8802 +#define GL_STENCIL_BACK_PASS_DEPTH_PASS_ATI 0x8803 +typedef void (APIENTRYP PFNGLSTENCILOPSEPARATEATIPROC) (GLenum face, GLenum sfail, GLenum dpfail, GLenum dppass); +typedef void (APIENTRYP PFNGLSTENCILFUNCSEPARATEATIPROC) (GLenum frontfunc, GLenum backfunc, GLint ref, GLuint mask); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glStencilOpSeparateATI (GLenum face, GLenum sfail, GLenum dpfail, GLenum dppass); +GLAPI void APIENTRY glStencilFuncSeparateATI (GLenum frontfunc, GLenum backfunc, GLint ref, GLuint mask); +#endif +#endif /* GL_ATI_separate_stencil */ + +#ifndef GL_ATI_text_fragment_shader +#define GL_ATI_text_fragment_shader 1 +#define GL_TEXT_FRAGMENT_SHADER_ATI 0x8200 +#endif /* GL_ATI_text_fragment_shader */ + +#ifndef GL_ATI_texture_env_combine3 +#define GL_ATI_texture_env_combine3 1 +#define GL_MODULATE_ADD_ATI 0x8744 +#define GL_MODULATE_SIGNED_ADD_ATI 0x8745 +#define GL_MODULATE_SUBTRACT_ATI 0x8746 +#endif /* GL_ATI_texture_env_combine3 */ + +#ifndef GL_ATI_texture_float +#define GL_ATI_texture_float 1 +#define GL_RGBA_FLOAT32_ATI 0x8814 +#define GL_RGB_FLOAT32_ATI 0x8815 +#define GL_ALPHA_FLOAT32_ATI 0x8816 +#define GL_INTENSITY_FLOAT32_ATI 0x8817 +#define GL_LUMINANCE_FLOAT32_ATI 0x8818 +#define GL_LUMINANCE_ALPHA_FLOAT32_ATI 0x8819 +#define GL_RGBA_FLOAT16_ATI 0x881A +#define GL_RGB_FLOAT16_ATI 0x881B +#define GL_ALPHA_FLOAT16_ATI 0x881C +#define GL_INTENSITY_FLOAT16_ATI 0x881D +#define GL_LUMINANCE_FLOAT16_ATI 0x881E +#define GL_LUMINANCE_ALPHA_FLOAT16_ATI 0x881F +#endif /* GL_ATI_texture_float */ + +#ifndef GL_ATI_texture_mirror_once +#define GL_ATI_texture_mirror_once 1 +#define GL_MIRROR_CLAMP_ATI 0x8742 +#define GL_MIRROR_CLAMP_TO_EDGE_ATI 0x8743 +#endif /* GL_ATI_texture_mirror_once */ + +#ifndef GL_ATI_vertex_array_object +#define GL_ATI_vertex_array_object 1 +#define GL_STATIC_ATI 0x8760 +#define GL_DYNAMIC_ATI 0x8761 +#define GL_PRESERVE_ATI 0x8762 +#define GL_DISCARD_ATI 0x8763 +#define GL_OBJECT_BUFFER_SIZE_ATI 0x8764 +#define GL_OBJECT_BUFFER_USAGE_ATI 0x8765 +#define GL_ARRAY_OBJECT_BUFFER_ATI 0x8766 +#define GL_ARRAY_OBJECT_OFFSET_ATI 0x8767 +typedef GLuint (APIENTRYP PFNGLNEWOBJECTBUFFERATIPROC) (GLsizei size, const void *pointer, GLenum usage); +typedef GLboolean (APIENTRYP PFNGLISOBJECTBUFFERATIPROC) (GLuint buffer); +typedef void (APIENTRYP PFNGLUPDATEOBJECTBUFFERATIPROC) (GLuint buffer, GLuint offset, GLsizei size, const void *pointer, GLenum preserve); +typedef void (APIENTRYP PFNGLGETOBJECTBUFFERFVATIPROC) (GLuint buffer, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETOBJECTBUFFERIVATIPROC) (GLuint buffer, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLFREEOBJECTBUFFERATIPROC) (GLuint buffer); +typedef void (APIENTRYP PFNGLARRAYOBJECTATIPROC) (GLenum array, GLint size, GLenum type, GLsizei stride, GLuint buffer, GLuint offset); +typedef void (APIENTRYP PFNGLGETARRAYOBJECTFVATIPROC) (GLenum array, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETARRAYOBJECTIVATIPROC) (GLenum array, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLVARIANTARRAYOBJECTATIPROC) (GLuint id, GLenum type, GLsizei stride, GLuint buffer, GLuint offset); +typedef void (APIENTRYP PFNGLGETVARIANTARRAYOBJECTFVATIPROC) (GLuint id, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETVARIANTARRAYOBJECTIVATIPROC) (GLuint id, GLenum pname, GLint *params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI GLuint APIENTRY glNewObjectBufferATI (GLsizei size, const void *pointer, GLenum usage); +GLAPI GLboolean APIENTRY glIsObjectBufferATI (GLuint buffer); +GLAPI void APIENTRY glUpdateObjectBufferATI (GLuint buffer, GLuint offset, GLsizei size, const void *pointer, GLenum preserve); +GLAPI void APIENTRY glGetObjectBufferfvATI (GLuint buffer, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetObjectBufferivATI (GLuint buffer, GLenum pname, GLint *params); +GLAPI void APIENTRY glFreeObjectBufferATI (GLuint buffer); +GLAPI void APIENTRY glArrayObjectATI (GLenum array, GLint size, GLenum type, GLsizei stride, GLuint buffer, GLuint offset); +GLAPI void APIENTRY glGetArrayObjectfvATI (GLenum array, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetArrayObjectivATI (GLenum array, GLenum pname, GLint *params); +GLAPI void APIENTRY glVariantArrayObjectATI (GLuint id, GLenum type, GLsizei stride, GLuint buffer, GLuint offset); +GLAPI void APIENTRY glGetVariantArrayObjectfvATI (GLuint id, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetVariantArrayObjectivATI (GLuint id, GLenum pname, GLint *params); +#endif +#endif /* GL_ATI_vertex_array_object */ + +#ifndef GL_ATI_vertex_attrib_array_object +#define GL_ATI_vertex_attrib_array_object 1 +typedef void (APIENTRYP PFNGLVERTEXATTRIBARRAYOBJECTATIPROC) (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, GLuint buffer, GLuint offset); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBARRAYOBJECTFVATIPROC) (GLuint index, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBARRAYOBJECTIVATIPROC) (GLuint index, GLenum pname, GLint *params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glVertexAttribArrayObjectATI (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, GLuint buffer, GLuint offset); +GLAPI void APIENTRY glGetVertexAttribArrayObjectfvATI (GLuint index, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetVertexAttribArrayObjectivATI (GLuint index, GLenum pname, GLint *params); +#endif +#endif /* GL_ATI_vertex_attrib_array_object */ + +#ifndef GL_ATI_vertex_streams +#define GL_ATI_vertex_streams 1 +#define GL_MAX_VERTEX_STREAMS_ATI 0x876B +#define GL_VERTEX_STREAM0_ATI 0x876C +#define GL_VERTEX_STREAM1_ATI 0x876D +#define GL_VERTEX_STREAM2_ATI 0x876E +#define GL_VERTEX_STREAM3_ATI 0x876F +#define GL_VERTEX_STREAM4_ATI 0x8770 +#define GL_VERTEX_STREAM5_ATI 0x8771 +#define GL_VERTEX_STREAM6_ATI 0x8772 +#define GL_VERTEX_STREAM7_ATI 0x8773 +#define GL_VERTEX_SOURCE_ATI 0x8774 +typedef void (APIENTRYP PFNGLVERTEXSTREAM1SATIPROC) (GLenum stream, GLshort x); +typedef void (APIENTRYP PFNGLVERTEXSTREAM1SVATIPROC) (GLenum stream, const GLshort *coords); +typedef void (APIENTRYP PFNGLVERTEXSTREAM1IATIPROC) (GLenum stream, GLint x); +typedef void (APIENTRYP PFNGLVERTEXSTREAM1IVATIPROC) (GLenum stream, const GLint *coords); +typedef void (APIENTRYP PFNGLVERTEXSTREAM1FATIPROC) (GLenum stream, GLfloat x); +typedef void (APIENTRYP PFNGLVERTEXSTREAM1FVATIPROC) (GLenum stream, const GLfloat *coords); +typedef void (APIENTRYP PFNGLVERTEXSTREAM1DATIPROC) (GLenum stream, GLdouble x); +typedef void (APIENTRYP PFNGLVERTEXSTREAM1DVATIPROC) (GLenum stream, const GLdouble *coords); +typedef void (APIENTRYP PFNGLVERTEXSTREAM2SATIPROC) (GLenum stream, GLshort x, GLshort y); +typedef void (APIENTRYP PFNGLVERTEXSTREAM2SVATIPROC) (GLenum stream, const GLshort *coords); +typedef void (APIENTRYP PFNGLVERTEXSTREAM2IATIPROC) (GLenum stream, GLint x, GLint y); +typedef void (APIENTRYP PFNGLVERTEXSTREAM2IVATIPROC) (GLenum stream, const GLint *coords); +typedef void (APIENTRYP PFNGLVERTEXSTREAM2FATIPROC) (GLenum stream, GLfloat x, GLfloat y); +typedef void (APIENTRYP PFNGLVERTEXSTREAM2FVATIPROC) (GLenum stream, const GLfloat *coords); +typedef void (APIENTRYP PFNGLVERTEXSTREAM2DATIPROC) (GLenum stream, GLdouble x, GLdouble y); +typedef void (APIENTRYP PFNGLVERTEXSTREAM2DVATIPROC) (GLenum stream, const GLdouble *coords); +typedef void (APIENTRYP PFNGLVERTEXSTREAM3SATIPROC) (GLenum stream, GLshort x, GLshort y, GLshort z); +typedef void (APIENTRYP PFNGLVERTEXSTREAM3SVATIPROC) (GLenum stream, const GLshort *coords); +typedef void (APIENTRYP PFNGLVERTEXSTREAM3IATIPROC) (GLenum stream, GLint x, GLint y, GLint z); +typedef void (APIENTRYP PFNGLVERTEXSTREAM3IVATIPROC) (GLenum stream, const GLint *coords); +typedef void (APIENTRYP PFNGLVERTEXSTREAM3FATIPROC) (GLenum stream, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLVERTEXSTREAM3FVATIPROC) (GLenum stream, const GLfloat *coords); +typedef void (APIENTRYP PFNGLVERTEXSTREAM3DATIPROC) (GLenum stream, GLdouble x, GLdouble y, GLdouble z); +typedef void (APIENTRYP PFNGLVERTEXSTREAM3DVATIPROC) (GLenum stream, const GLdouble *coords); +typedef void (APIENTRYP PFNGLVERTEXSTREAM4SATIPROC) (GLenum stream, GLshort x, GLshort y, GLshort z, GLshort w); +typedef void (APIENTRYP PFNGLVERTEXSTREAM4SVATIPROC) (GLenum stream, const GLshort *coords); +typedef void (APIENTRYP PFNGLVERTEXSTREAM4IATIPROC) (GLenum stream, GLint x, GLint y, GLint z, GLint w); +typedef void (APIENTRYP PFNGLVERTEXSTREAM4IVATIPROC) (GLenum stream, const GLint *coords); +typedef void (APIENTRYP PFNGLVERTEXSTREAM4FATIPROC) (GLenum stream, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +typedef void (APIENTRYP PFNGLVERTEXSTREAM4FVATIPROC) (GLenum stream, const GLfloat *coords); +typedef void (APIENTRYP PFNGLVERTEXSTREAM4DATIPROC) (GLenum stream, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +typedef void (APIENTRYP PFNGLVERTEXSTREAM4DVATIPROC) (GLenum stream, const GLdouble *coords); +typedef void (APIENTRYP PFNGLNORMALSTREAM3BATIPROC) (GLenum stream, GLbyte nx, GLbyte ny, GLbyte nz); +typedef void (APIENTRYP PFNGLNORMALSTREAM3BVATIPROC) (GLenum stream, const GLbyte *coords); +typedef void (APIENTRYP PFNGLNORMALSTREAM3SATIPROC) (GLenum stream, GLshort nx, GLshort ny, GLshort nz); +typedef void (APIENTRYP PFNGLNORMALSTREAM3SVATIPROC) (GLenum stream, const GLshort *coords); +typedef void (APIENTRYP PFNGLNORMALSTREAM3IATIPROC) (GLenum stream, GLint nx, GLint ny, GLint nz); +typedef void (APIENTRYP PFNGLNORMALSTREAM3IVATIPROC) (GLenum stream, const GLint *coords); +typedef void (APIENTRYP PFNGLNORMALSTREAM3FATIPROC) (GLenum stream, GLfloat nx, GLfloat ny, GLfloat nz); +typedef void (APIENTRYP PFNGLNORMALSTREAM3FVATIPROC) (GLenum stream, const GLfloat *coords); +typedef void (APIENTRYP PFNGLNORMALSTREAM3DATIPROC) (GLenum stream, GLdouble nx, GLdouble ny, GLdouble nz); +typedef void (APIENTRYP PFNGLNORMALSTREAM3DVATIPROC) (GLenum stream, const GLdouble *coords); +typedef void (APIENTRYP PFNGLCLIENTACTIVEVERTEXSTREAMATIPROC) (GLenum stream); +typedef void (APIENTRYP PFNGLVERTEXBLENDENVIATIPROC) (GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLVERTEXBLENDENVFATIPROC) (GLenum pname, GLfloat param); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glVertexStream1sATI (GLenum stream, GLshort x); +GLAPI void APIENTRY glVertexStream1svATI (GLenum stream, const GLshort *coords); +GLAPI void APIENTRY glVertexStream1iATI (GLenum stream, GLint x); +GLAPI void APIENTRY glVertexStream1ivATI (GLenum stream, const GLint *coords); +GLAPI void APIENTRY glVertexStream1fATI (GLenum stream, GLfloat x); +GLAPI void APIENTRY glVertexStream1fvATI (GLenum stream, const GLfloat *coords); +GLAPI void APIENTRY glVertexStream1dATI (GLenum stream, GLdouble x); +GLAPI void APIENTRY glVertexStream1dvATI (GLenum stream, const GLdouble *coords); +GLAPI void APIENTRY glVertexStream2sATI (GLenum stream, GLshort x, GLshort y); +GLAPI void APIENTRY glVertexStream2svATI (GLenum stream, const GLshort *coords); +GLAPI void APIENTRY glVertexStream2iATI (GLenum stream, GLint x, GLint y); +GLAPI void APIENTRY glVertexStream2ivATI (GLenum stream, const GLint *coords); +GLAPI void APIENTRY glVertexStream2fATI (GLenum stream, GLfloat x, GLfloat y); +GLAPI void APIENTRY glVertexStream2fvATI (GLenum stream, const GLfloat *coords); +GLAPI void APIENTRY glVertexStream2dATI (GLenum stream, GLdouble x, GLdouble y); +GLAPI void APIENTRY glVertexStream2dvATI (GLenum stream, const GLdouble *coords); +GLAPI void APIENTRY glVertexStream3sATI (GLenum stream, GLshort x, GLshort y, GLshort z); +GLAPI void APIENTRY glVertexStream3svATI (GLenum stream, const GLshort *coords); +GLAPI void APIENTRY glVertexStream3iATI (GLenum stream, GLint x, GLint y, GLint z); +GLAPI void APIENTRY glVertexStream3ivATI (GLenum stream, const GLint *coords); +GLAPI void APIENTRY glVertexStream3fATI (GLenum stream, GLfloat x, GLfloat y, GLfloat z); +GLAPI void APIENTRY glVertexStream3fvATI (GLenum stream, const GLfloat *coords); +GLAPI void APIENTRY glVertexStream3dATI (GLenum stream, GLdouble x, GLdouble y, GLdouble z); +GLAPI void APIENTRY glVertexStream3dvATI (GLenum stream, const GLdouble *coords); +GLAPI void APIENTRY glVertexStream4sATI (GLenum stream, GLshort x, GLshort y, GLshort z, GLshort w); +GLAPI void APIENTRY glVertexStream4svATI (GLenum stream, const GLshort *coords); +GLAPI void APIENTRY glVertexStream4iATI (GLenum stream, GLint x, GLint y, GLint z, GLint w); +GLAPI void APIENTRY glVertexStream4ivATI (GLenum stream, const GLint *coords); +GLAPI void APIENTRY glVertexStream4fATI (GLenum stream, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +GLAPI void APIENTRY glVertexStream4fvATI (GLenum stream, const GLfloat *coords); +GLAPI void APIENTRY glVertexStream4dATI (GLenum stream, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +GLAPI void APIENTRY glVertexStream4dvATI (GLenum stream, const GLdouble *coords); +GLAPI void APIENTRY glNormalStream3bATI (GLenum stream, GLbyte nx, GLbyte ny, GLbyte nz); +GLAPI void APIENTRY glNormalStream3bvATI (GLenum stream, const GLbyte *coords); +GLAPI void APIENTRY glNormalStream3sATI (GLenum stream, GLshort nx, GLshort ny, GLshort nz); +GLAPI void APIENTRY glNormalStream3svATI (GLenum stream, const GLshort *coords); +GLAPI void APIENTRY glNormalStream3iATI (GLenum stream, GLint nx, GLint ny, GLint nz); +GLAPI void APIENTRY glNormalStream3ivATI (GLenum stream, const GLint *coords); +GLAPI void APIENTRY glNormalStream3fATI (GLenum stream, GLfloat nx, GLfloat ny, GLfloat nz); +GLAPI void APIENTRY glNormalStream3fvATI (GLenum stream, const GLfloat *coords); +GLAPI void APIENTRY glNormalStream3dATI (GLenum stream, GLdouble nx, GLdouble ny, GLdouble nz); +GLAPI void APIENTRY glNormalStream3dvATI (GLenum stream, const GLdouble *coords); +GLAPI void APIENTRY glClientActiveVertexStreamATI (GLenum stream); +GLAPI void APIENTRY glVertexBlendEnviATI (GLenum pname, GLint param); +GLAPI void APIENTRY glVertexBlendEnvfATI (GLenum pname, GLfloat param); +#endif +#endif /* GL_ATI_vertex_streams */ + +#ifndef GL_EXT_422_pixels +#define GL_EXT_422_pixels 1 +#define GL_422_EXT 0x80CC +#define GL_422_REV_EXT 0x80CD +#define GL_422_AVERAGE_EXT 0x80CE +#define GL_422_REV_AVERAGE_EXT 0x80CF +#endif /* GL_EXT_422_pixels */ + +#ifndef GL_EXT_abgr +#define GL_EXT_abgr 1 +#define GL_ABGR_EXT 0x8000 +#endif /* GL_EXT_abgr */ + +#ifndef GL_EXT_bgra +#define GL_EXT_bgra 1 +#define GL_BGR_EXT 0x80E0 +#define GL_BGRA_EXT 0x80E1 +#endif /* GL_EXT_bgra */ + +#ifndef GL_EXT_bindable_uniform +#define GL_EXT_bindable_uniform 1 +#define GL_MAX_VERTEX_BINDABLE_UNIFORMS_EXT 0x8DE2 +#define GL_MAX_FRAGMENT_BINDABLE_UNIFORMS_EXT 0x8DE3 +#define GL_MAX_GEOMETRY_BINDABLE_UNIFORMS_EXT 0x8DE4 +#define GL_MAX_BINDABLE_UNIFORM_SIZE_EXT 0x8DED +#define GL_UNIFORM_BUFFER_EXT 0x8DEE +#define GL_UNIFORM_BUFFER_BINDING_EXT 0x8DEF +typedef void (APIENTRYP PFNGLUNIFORMBUFFEREXTPROC) (GLuint program, GLint location, GLuint buffer); +typedef GLint (APIENTRYP PFNGLGETUNIFORMBUFFERSIZEEXTPROC) (GLuint program, GLint location); +typedef GLintptr (APIENTRYP PFNGLGETUNIFORMOFFSETEXTPROC) (GLuint program, GLint location); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glUniformBufferEXT (GLuint program, GLint location, GLuint buffer); +GLAPI GLint APIENTRY glGetUniformBufferSizeEXT (GLuint program, GLint location); +GLAPI GLintptr APIENTRY glGetUniformOffsetEXT (GLuint program, GLint location); +#endif +#endif /* GL_EXT_bindable_uniform */ + +#ifndef GL_EXT_blend_color +#define GL_EXT_blend_color 1 +#define GL_CONSTANT_COLOR_EXT 0x8001 +#define GL_ONE_MINUS_CONSTANT_COLOR_EXT 0x8002 +#define GL_CONSTANT_ALPHA_EXT 0x8003 +#define GL_ONE_MINUS_CONSTANT_ALPHA_EXT 0x8004 +#define GL_BLEND_COLOR_EXT 0x8005 +typedef void (APIENTRYP PFNGLBLENDCOLOREXTPROC) (GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBlendColorEXT (GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); +#endif +#endif /* GL_EXT_blend_color */ + +#ifndef GL_EXT_blend_equation_separate +#define GL_EXT_blend_equation_separate 1 +#define GL_BLEND_EQUATION_RGB_EXT 0x8009 +#define GL_BLEND_EQUATION_ALPHA_EXT 0x883D +typedef void (APIENTRYP PFNGLBLENDEQUATIONSEPARATEEXTPROC) (GLenum modeRGB, GLenum modeAlpha); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBlendEquationSeparateEXT (GLenum modeRGB, GLenum modeAlpha); +#endif +#endif /* GL_EXT_blend_equation_separate */ + +#ifndef GL_EXT_blend_func_separate +#define GL_EXT_blend_func_separate 1 +#define GL_BLEND_DST_RGB_EXT 0x80C8 +#define GL_BLEND_SRC_RGB_EXT 0x80C9 +#define GL_BLEND_DST_ALPHA_EXT 0x80CA +#define GL_BLEND_SRC_ALPHA_EXT 0x80CB +typedef void (APIENTRYP PFNGLBLENDFUNCSEPARATEEXTPROC) (GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBlendFuncSeparateEXT (GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha); +#endif +#endif /* GL_EXT_blend_func_separate */ + +#ifndef GL_EXT_blend_logic_op +#define GL_EXT_blend_logic_op 1 +#endif /* GL_EXT_blend_logic_op */ + +#ifndef GL_EXT_blend_minmax +#define GL_EXT_blend_minmax 1 +#define GL_MIN_EXT 0x8007 +#define GL_MAX_EXT 0x8008 +#define GL_FUNC_ADD_EXT 0x8006 +#define GL_BLEND_EQUATION_EXT 0x8009 +typedef void (APIENTRYP PFNGLBLENDEQUATIONEXTPROC) (GLenum mode); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBlendEquationEXT (GLenum mode); +#endif +#endif /* GL_EXT_blend_minmax */ + +#ifndef GL_EXT_blend_subtract +#define GL_EXT_blend_subtract 1 +#define GL_FUNC_SUBTRACT_EXT 0x800A +#define GL_FUNC_REVERSE_SUBTRACT_EXT 0x800B +#endif /* GL_EXT_blend_subtract */ + +#ifndef GL_EXT_clip_volume_hint +#define GL_EXT_clip_volume_hint 1 +#define GL_CLIP_VOLUME_CLIPPING_HINT_EXT 0x80F0 +#endif /* GL_EXT_clip_volume_hint */ + +#ifndef GL_EXT_cmyka +#define GL_EXT_cmyka 1 +#define GL_CMYK_EXT 0x800C +#define GL_CMYKA_EXT 0x800D +#define GL_PACK_CMYK_HINT_EXT 0x800E +#define GL_UNPACK_CMYK_HINT_EXT 0x800F +#endif /* GL_EXT_cmyka */ + +#ifndef GL_EXT_color_subtable +#define GL_EXT_color_subtable 1 +typedef void (APIENTRYP PFNGLCOLORSUBTABLEEXTPROC) (GLenum target, GLsizei start, GLsizei count, GLenum format, GLenum type, const void *data); +typedef void (APIENTRYP PFNGLCOPYCOLORSUBTABLEEXTPROC) (GLenum target, GLsizei start, GLint x, GLint y, GLsizei width); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glColorSubTableEXT (GLenum target, GLsizei start, GLsizei count, GLenum format, GLenum type, const void *data); +GLAPI void APIENTRY glCopyColorSubTableEXT (GLenum target, GLsizei start, GLint x, GLint y, GLsizei width); +#endif +#endif /* GL_EXT_color_subtable */ + +#ifndef GL_EXT_compiled_vertex_array +#define GL_EXT_compiled_vertex_array 1 +#define GL_ARRAY_ELEMENT_LOCK_FIRST_EXT 0x81A8 +#define GL_ARRAY_ELEMENT_LOCK_COUNT_EXT 0x81A9 +typedef void (APIENTRYP PFNGLLOCKARRAYSEXTPROC) (GLint first, GLsizei count); +typedef void (APIENTRYP PFNGLUNLOCKARRAYSEXTPROC) (void); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glLockArraysEXT (GLint first, GLsizei count); +GLAPI void APIENTRY glUnlockArraysEXT (void); +#endif +#endif /* GL_EXT_compiled_vertex_array */ + +#ifndef GL_EXT_convolution +#define GL_EXT_convolution 1 +#define GL_CONVOLUTION_1D_EXT 0x8010 +#define GL_CONVOLUTION_2D_EXT 0x8011 +#define GL_SEPARABLE_2D_EXT 0x8012 +#define GL_CONVOLUTION_BORDER_MODE_EXT 0x8013 +#define GL_CONVOLUTION_FILTER_SCALE_EXT 0x8014 +#define GL_CONVOLUTION_FILTER_BIAS_EXT 0x8015 +#define GL_REDUCE_EXT 0x8016 +#define GL_CONVOLUTION_FORMAT_EXT 0x8017 +#define GL_CONVOLUTION_WIDTH_EXT 0x8018 +#define GL_CONVOLUTION_HEIGHT_EXT 0x8019 +#define GL_MAX_CONVOLUTION_WIDTH_EXT 0x801A +#define GL_MAX_CONVOLUTION_HEIGHT_EXT 0x801B +#define GL_POST_CONVOLUTION_RED_SCALE_EXT 0x801C +#define GL_POST_CONVOLUTION_GREEN_SCALE_EXT 0x801D +#define GL_POST_CONVOLUTION_BLUE_SCALE_EXT 0x801E +#define GL_POST_CONVOLUTION_ALPHA_SCALE_EXT 0x801F +#define GL_POST_CONVOLUTION_RED_BIAS_EXT 0x8020 +#define GL_POST_CONVOLUTION_GREEN_BIAS_EXT 0x8021 +#define GL_POST_CONVOLUTION_BLUE_BIAS_EXT 0x8022 +#define GL_POST_CONVOLUTION_ALPHA_BIAS_EXT 0x8023 +typedef void (APIENTRYP PFNGLCONVOLUTIONFILTER1DEXTPROC) (GLenum target, GLenum internalformat, GLsizei width, GLenum format, GLenum type, const void *image); +typedef void (APIENTRYP PFNGLCONVOLUTIONFILTER2DEXTPROC) (GLenum target, GLenum internalformat, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *image); +typedef void (APIENTRYP PFNGLCONVOLUTIONPARAMETERFEXTPROC) (GLenum target, GLenum pname, GLfloat params); +typedef void (APIENTRYP PFNGLCONVOLUTIONPARAMETERFVEXTPROC) (GLenum target, GLenum pname, const GLfloat *params); +typedef void (APIENTRYP PFNGLCONVOLUTIONPARAMETERIEXTPROC) (GLenum target, GLenum pname, GLint params); +typedef void (APIENTRYP PFNGLCONVOLUTIONPARAMETERIVEXTPROC) (GLenum target, GLenum pname, const GLint *params); +typedef void (APIENTRYP PFNGLCOPYCONVOLUTIONFILTER1DEXTPROC) (GLenum target, GLenum internalformat, GLint x, GLint y, GLsizei width); +typedef void (APIENTRYP PFNGLCOPYCONVOLUTIONFILTER2DEXTPROC) (GLenum target, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLGETCONVOLUTIONFILTEREXTPROC) (GLenum target, GLenum format, GLenum type, void *image); +typedef void (APIENTRYP PFNGLGETCONVOLUTIONPARAMETERFVEXTPROC) (GLenum target, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETCONVOLUTIONPARAMETERIVEXTPROC) (GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETSEPARABLEFILTEREXTPROC) (GLenum target, GLenum format, GLenum type, void *row, void *column, void *span); +typedef void (APIENTRYP PFNGLSEPARABLEFILTER2DEXTPROC) (GLenum target, GLenum internalformat, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *row, const void *column); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glConvolutionFilter1DEXT (GLenum target, GLenum internalformat, GLsizei width, GLenum format, GLenum type, const void *image); +GLAPI void APIENTRY glConvolutionFilter2DEXT (GLenum target, GLenum internalformat, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *image); +GLAPI void APIENTRY glConvolutionParameterfEXT (GLenum target, GLenum pname, GLfloat params); +GLAPI void APIENTRY glConvolutionParameterfvEXT (GLenum target, GLenum pname, const GLfloat *params); +GLAPI void APIENTRY glConvolutionParameteriEXT (GLenum target, GLenum pname, GLint params); +GLAPI void APIENTRY glConvolutionParameterivEXT (GLenum target, GLenum pname, const GLint *params); +GLAPI void APIENTRY glCopyConvolutionFilter1DEXT (GLenum target, GLenum internalformat, GLint x, GLint y, GLsizei width); +GLAPI void APIENTRY glCopyConvolutionFilter2DEXT (GLenum target, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height); +GLAPI void APIENTRY glGetConvolutionFilterEXT (GLenum target, GLenum format, GLenum type, void *image); +GLAPI void APIENTRY glGetConvolutionParameterfvEXT (GLenum target, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetConvolutionParameterivEXT (GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetSeparableFilterEXT (GLenum target, GLenum format, GLenum type, void *row, void *column, void *span); +GLAPI void APIENTRY glSeparableFilter2DEXT (GLenum target, GLenum internalformat, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *row, const void *column); +#endif +#endif /* GL_EXT_convolution */ + +#ifndef GL_EXT_coordinate_frame +#define GL_EXT_coordinate_frame 1 +#define GL_TANGENT_ARRAY_EXT 0x8439 +#define GL_BINORMAL_ARRAY_EXT 0x843A +#define GL_CURRENT_TANGENT_EXT 0x843B +#define GL_CURRENT_BINORMAL_EXT 0x843C +#define GL_TANGENT_ARRAY_TYPE_EXT 0x843E +#define GL_TANGENT_ARRAY_STRIDE_EXT 0x843F +#define GL_BINORMAL_ARRAY_TYPE_EXT 0x8440 +#define GL_BINORMAL_ARRAY_STRIDE_EXT 0x8441 +#define GL_TANGENT_ARRAY_POINTER_EXT 0x8442 +#define GL_BINORMAL_ARRAY_POINTER_EXT 0x8443 +#define GL_MAP1_TANGENT_EXT 0x8444 +#define GL_MAP2_TANGENT_EXT 0x8445 +#define GL_MAP1_BINORMAL_EXT 0x8446 +#define GL_MAP2_BINORMAL_EXT 0x8447 +typedef void (APIENTRYP PFNGLTANGENT3BEXTPROC) (GLbyte tx, GLbyte ty, GLbyte tz); +typedef void (APIENTRYP PFNGLTANGENT3BVEXTPROC) (const GLbyte *v); +typedef void (APIENTRYP PFNGLTANGENT3DEXTPROC) (GLdouble tx, GLdouble ty, GLdouble tz); +typedef void (APIENTRYP PFNGLTANGENT3DVEXTPROC) (const GLdouble *v); +typedef void (APIENTRYP PFNGLTANGENT3FEXTPROC) (GLfloat tx, GLfloat ty, GLfloat tz); +typedef void (APIENTRYP PFNGLTANGENT3FVEXTPROC) (const GLfloat *v); +typedef void (APIENTRYP PFNGLTANGENT3IEXTPROC) (GLint tx, GLint ty, GLint tz); +typedef void (APIENTRYP PFNGLTANGENT3IVEXTPROC) (const GLint *v); +typedef void (APIENTRYP PFNGLTANGENT3SEXTPROC) (GLshort tx, GLshort ty, GLshort tz); +typedef void (APIENTRYP PFNGLTANGENT3SVEXTPROC) (const GLshort *v); +typedef void (APIENTRYP PFNGLBINORMAL3BEXTPROC) (GLbyte bx, GLbyte by, GLbyte bz); +typedef void (APIENTRYP PFNGLBINORMAL3BVEXTPROC) (const GLbyte *v); +typedef void (APIENTRYP PFNGLBINORMAL3DEXTPROC) (GLdouble bx, GLdouble by, GLdouble bz); +typedef void (APIENTRYP PFNGLBINORMAL3DVEXTPROC) (const GLdouble *v); +typedef void (APIENTRYP PFNGLBINORMAL3FEXTPROC) (GLfloat bx, GLfloat by, GLfloat bz); +typedef void (APIENTRYP PFNGLBINORMAL3FVEXTPROC) (const GLfloat *v); +typedef void (APIENTRYP PFNGLBINORMAL3IEXTPROC) (GLint bx, GLint by, GLint bz); +typedef void (APIENTRYP PFNGLBINORMAL3IVEXTPROC) (const GLint *v); +typedef void (APIENTRYP PFNGLBINORMAL3SEXTPROC) (GLshort bx, GLshort by, GLshort bz); +typedef void (APIENTRYP PFNGLBINORMAL3SVEXTPROC) (const GLshort *v); +typedef void (APIENTRYP PFNGLTANGENTPOINTEREXTPROC) (GLenum type, GLsizei stride, const void *pointer); +typedef void (APIENTRYP PFNGLBINORMALPOINTEREXTPROC) (GLenum type, GLsizei stride, const void *pointer); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glTangent3bEXT (GLbyte tx, GLbyte ty, GLbyte tz); +GLAPI void APIENTRY glTangent3bvEXT (const GLbyte *v); +GLAPI void APIENTRY glTangent3dEXT (GLdouble tx, GLdouble ty, GLdouble tz); +GLAPI void APIENTRY glTangent3dvEXT (const GLdouble *v); +GLAPI void APIENTRY glTangent3fEXT (GLfloat tx, GLfloat ty, GLfloat tz); +GLAPI void APIENTRY glTangent3fvEXT (const GLfloat *v); +GLAPI void APIENTRY glTangent3iEXT (GLint tx, GLint ty, GLint tz); +GLAPI void APIENTRY glTangent3ivEXT (const GLint *v); +GLAPI void APIENTRY glTangent3sEXT (GLshort tx, GLshort ty, GLshort tz); +GLAPI void APIENTRY glTangent3svEXT (const GLshort *v); +GLAPI void APIENTRY glBinormal3bEXT (GLbyte bx, GLbyte by, GLbyte bz); +GLAPI void APIENTRY glBinormal3bvEXT (const GLbyte *v); +GLAPI void APIENTRY glBinormal3dEXT (GLdouble bx, GLdouble by, GLdouble bz); +GLAPI void APIENTRY glBinormal3dvEXT (const GLdouble *v); +GLAPI void APIENTRY glBinormal3fEXT (GLfloat bx, GLfloat by, GLfloat bz); +GLAPI void APIENTRY glBinormal3fvEXT (const GLfloat *v); +GLAPI void APIENTRY glBinormal3iEXT (GLint bx, GLint by, GLint bz); +GLAPI void APIENTRY glBinormal3ivEXT (const GLint *v); +GLAPI void APIENTRY glBinormal3sEXT (GLshort bx, GLshort by, GLshort bz); +GLAPI void APIENTRY glBinormal3svEXT (const GLshort *v); +GLAPI void APIENTRY glTangentPointerEXT (GLenum type, GLsizei stride, const void *pointer); +GLAPI void APIENTRY glBinormalPointerEXT (GLenum type, GLsizei stride, const void *pointer); +#endif +#endif /* GL_EXT_coordinate_frame */ + +#ifndef GL_EXT_copy_texture +#define GL_EXT_copy_texture 1 +typedef void (APIENTRYP PFNGLCOPYTEXIMAGE1DEXTPROC) (GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLint border); +typedef void (APIENTRYP PFNGLCOPYTEXIMAGE2DEXTPROC) (GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border); +typedef void (APIENTRYP PFNGLCOPYTEXSUBIMAGE1DEXTPROC) (GLenum target, GLint level, GLint xoffset, GLint x, GLint y, GLsizei width); +typedef void (APIENTRYP PFNGLCOPYTEXSUBIMAGE2DEXTPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLCOPYTEXSUBIMAGE3DEXTPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glCopyTexImage1DEXT (GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLint border); +GLAPI void APIENTRY glCopyTexImage2DEXT (GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border); +GLAPI void APIENTRY glCopyTexSubImage1DEXT (GLenum target, GLint level, GLint xoffset, GLint x, GLint y, GLsizei width); +GLAPI void APIENTRY glCopyTexSubImage2DEXT (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height); +GLAPI void APIENTRY glCopyTexSubImage3DEXT (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height); +#endif +#endif /* GL_EXT_copy_texture */ + +#ifndef GL_EXT_cull_vertex +#define GL_EXT_cull_vertex 1 +#define GL_CULL_VERTEX_EXT 0x81AA +#define GL_CULL_VERTEX_EYE_POSITION_EXT 0x81AB +#define GL_CULL_VERTEX_OBJECT_POSITION_EXT 0x81AC +typedef void (APIENTRYP PFNGLCULLPARAMETERDVEXTPROC) (GLenum pname, GLdouble *params); +typedef void (APIENTRYP PFNGLCULLPARAMETERFVEXTPROC) (GLenum pname, GLfloat *params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glCullParameterdvEXT (GLenum pname, GLdouble *params); +GLAPI void APIENTRY glCullParameterfvEXT (GLenum pname, GLfloat *params); +#endif +#endif /* GL_EXT_cull_vertex */ + +#ifndef GL_EXT_debug_label +#define GL_EXT_debug_label 1 +#define GL_PROGRAM_PIPELINE_OBJECT_EXT 0x8A4F +#define GL_PROGRAM_OBJECT_EXT 0x8B40 +#define GL_SHADER_OBJECT_EXT 0x8B48 +#define GL_BUFFER_OBJECT_EXT 0x9151 +#define GL_QUERY_OBJECT_EXT 0x9153 +#define GL_VERTEX_ARRAY_OBJECT_EXT 0x9154 +typedef void (APIENTRYP PFNGLLABELOBJECTEXTPROC) (GLenum type, GLuint object, GLsizei length, const GLchar *label); +typedef void (APIENTRYP PFNGLGETOBJECTLABELEXTPROC) (GLenum type, GLuint object, GLsizei bufSize, GLsizei *length, GLchar *label); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glLabelObjectEXT (GLenum type, GLuint object, GLsizei length, const GLchar *label); +GLAPI void APIENTRY glGetObjectLabelEXT (GLenum type, GLuint object, GLsizei bufSize, GLsizei *length, GLchar *label); +#endif +#endif /* GL_EXT_debug_label */ + +#ifndef GL_EXT_debug_marker +#define GL_EXT_debug_marker 1 +typedef void (APIENTRYP PFNGLINSERTEVENTMARKEREXTPROC) (GLsizei length, const GLchar *marker); +typedef void (APIENTRYP PFNGLPUSHGROUPMARKEREXTPROC) (GLsizei length, const GLchar *marker); +typedef void (APIENTRYP PFNGLPOPGROUPMARKEREXTPROC) (void); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glInsertEventMarkerEXT (GLsizei length, const GLchar *marker); +GLAPI void APIENTRY glPushGroupMarkerEXT (GLsizei length, const GLchar *marker); +GLAPI void APIENTRY glPopGroupMarkerEXT (void); +#endif +#endif /* GL_EXT_debug_marker */ + +#ifndef GL_EXT_depth_bounds_test +#define GL_EXT_depth_bounds_test 1 +#define GL_DEPTH_BOUNDS_TEST_EXT 0x8890 +#define GL_DEPTH_BOUNDS_EXT 0x8891 +typedef void (APIENTRYP PFNGLDEPTHBOUNDSEXTPROC) (GLclampd zmin, GLclampd zmax); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glDepthBoundsEXT (GLclampd zmin, GLclampd zmax); +#endif +#endif /* GL_EXT_depth_bounds_test */ + +#ifndef GL_EXT_direct_state_access +#define GL_EXT_direct_state_access 1 +#define GL_PROGRAM_MATRIX_EXT 0x8E2D +#define GL_TRANSPOSE_PROGRAM_MATRIX_EXT 0x8E2E +#define GL_PROGRAM_MATRIX_STACK_DEPTH_EXT 0x8E2F +typedef void (APIENTRYP PFNGLMATRIXLOADFEXTPROC) (GLenum mode, const GLfloat *m); +typedef void (APIENTRYP PFNGLMATRIXLOADDEXTPROC) (GLenum mode, const GLdouble *m); +typedef void (APIENTRYP PFNGLMATRIXMULTFEXTPROC) (GLenum mode, const GLfloat *m); +typedef void (APIENTRYP PFNGLMATRIXMULTDEXTPROC) (GLenum mode, const GLdouble *m); +typedef void (APIENTRYP PFNGLMATRIXLOADIDENTITYEXTPROC) (GLenum mode); +typedef void (APIENTRYP PFNGLMATRIXROTATEFEXTPROC) (GLenum mode, GLfloat angle, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLMATRIXROTATEDEXTPROC) (GLenum mode, GLdouble angle, GLdouble x, GLdouble y, GLdouble z); +typedef void (APIENTRYP PFNGLMATRIXSCALEFEXTPROC) (GLenum mode, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLMATRIXSCALEDEXTPROC) (GLenum mode, GLdouble x, GLdouble y, GLdouble z); +typedef void (APIENTRYP PFNGLMATRIXTRANSLATEFEXTPROC) (GLenum mode, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLMATRIXTRANSLATEDEXTPROC) (GLenum mode, GLdouble x, GLdouble y, GLdouble z); +typedef void (APIENTRYP PFNGLMATRIXFRUSTUMEXTPROC) (GLenum mode, GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble zNear, GLdouble zFar); +typedef void (APIENTRYP PFNGLMATRIXORTHOEXTPROC) (GLenum mode, GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble zNear, GLdouble zFar); +typedef void (APIENTRYP PFNGLMATRIXPOPEXTPROC) (GLenum mode); +typedef void (APIENTRYP PFNGLMATRIXPUSHEXTPROC) (GLenum mode); +typedef void (APIENTRYP PFNGLCLIENTATTRIBDEFAULTEXTPROC) (GLbitfield mask); +typedef void (APIENTRYP PFNGLPUSHCLIENTATTRIBDEFAULTEXTPROC) (GLbitfield mask); +typedef void (APIENTRYP PFNGLTEXTUREPARAMETERFEXTPROC) (GLuint texture, GLenum target, GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLTEXTUREPARAMETERFVEXTPROC) (GLuint texture, GLenum target, GLenum pname, const GLfloat *params); +typedef void (APIENTRYP PFNGLTEXTUREPARAMETERIEXTPROC) (GLuint texture, GLenum target, GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLTEXTUREPARAMETERIVEXTPROC) (GLuint texture, GLenum target, GLenum pname, const GLint *params); +typedef void (APIENTRYP PFNGLTEXTUREIMAGE1DEXTPROC) (GLuint texture, GLenum target, GLint level, GLint internalformat, GLsizei width, GLint border, GLenum format, GLenum type, const void *pixels); +typedef void (APIENTRYP PFNGLTEXTUREIMAGE2DEXTPROC) (GLuint texture, GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void *pixels); +typedef void (APIENTRYP PFNGLTEXTURESUBIMAGE1DEXTPROC) (GLuint texture, GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const void *pixels); +typedef void (APIENTRYP PFNGLTEXTURESUBIMAGE2DEXTPROC) (GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels); +typedef void (APIENTRYP PFNGLCOPYTEXTUREIMAGE1DEXTPROC) (GLuint texture, GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLint border); +typedef void (APIENTRYP PFNGLCOPYTEXTUREIMAGE2DEXTPROC) (GLuint texture, GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border); +typedef void (APIENTRYP PFNGLCOPYTEXTURESUBIMAGE1DEXTPROC) (GLuint texture, GLenum target, GLint level, GLint xoffset, GLint x, GLint y, GLsizei width); +typedef void (APIENTRYP PFNGLCOPYTEXTURESUBIMAGE2DEXTPROC) (GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLGETTEXTUREIMAGEEXTPROC) (GLuint texture, GLenum target, GLint level, GLenum format, GLenum type, void *pixels); +typedef void (APIENTRYP PFNGLGETTEXTUREPARAMETERFVEXTPROC) (GLuint texture, GLenum target, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETTEXTUREPARAMETERIVEXTPROC) (GLuint texture, GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETTEXTURELEVELPARAMETERFVEXTPROC) (GLuint texture, GLenum target, GLint level, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETTEXTURELEVELPARAMETERIVEXTPROC) (GLuint texture, GLenum target, GLint level, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLTEXTUREIMAGE3DEXTPROC) (GLuint texture, GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void *pixels); +typedef void (APIENTRYP PFNGLTEXTURESUBIMAGE3DEXTPROC) (GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *pixels); +typedef void (APIENTRYP PFNGLCOPYTEXTURESUBIMAGE3DEXTPROC) (GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLBINDMULTITEXTUREEXTPROC) (GLenum texunit, GLenum target, GLuint texture); +typedef void (APIENTRYP PFNGLMULTITEXCOORDPOINTEREXTPROC) (GLenum texunit, GLint size, GLenum type, GLsizei stride, const void *pointer); +typedef void (APIENTRYP PFNGLMULTITEXENVFEXTPROC) (GLenum texunit, GLenum target, GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLMULTITEXENVFVEXTPROC) (GLenum texunit, GLenum target, GLenum pname, const GLfloat *params); +typedef void (APIENTRYP PFNGLMULTITEXENVIEXTPROC) (GLenum texunit, GLenum target, GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLMULTITEXENVIVEXTPROC) (GLenum texunit, GLenum target, GLenum pname, const GLint *params); +typedef void (APIENTRYP PFNGLMULTITEXGENDEXTPROC) (GLenum texunit, GLenum coord, GLenum pname, GLdouble param); +typedef void (APIENTRYP PFNGLMULTITEXGENDVEXTPROC) (GLenum texunit, GLenum coord, GLenum pname, const GLdouble *params); +typedef void (APIENTRYP PFNGLMULTITEXGENFEXTPROC) (GLenum texunit, GLenum coord, GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLMULTITEXGENFVEXTPROC) (GLenum texunit, GLenum coord, GLenum pname, const GLfloat *params); +typedef void (APIENTRYP PFNGLMULTITEXGENIEXTPROC) (GLenum texunit, GLenum coord, GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLMULTITEXGENIVEXTPROC) (GLenum texunit, GLenum coord, GLenum pname, const GLint *params); +typedef void (APIENTRYP PFNGLGETMULTITEXENVFVEXTPROC) (GLenum texunit, GLenum target, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETMULTITEXENVIVEXTPROC) (GLenum texunit, GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETMULTITEXGENDVEXTPROC) (GLenum texunit, GLenum coord, GLenum pname, GLdouble *params); +typedef void (APIENTRYP PFNGLGETMULTITEXGENFVEXTPROC) (GLenum texunit, GLenum coord, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETMULTITEXGENIVEXTPROC) (GLenum texunit, GLenum coord, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLMULTITEXPARAMETERIEXTPROC) (GLenum texunit, GLenum target, GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLMULTITEXPARAMETERIVEXTPROC) (GLenum texunit, GLenum target, GLenum pname, const GLint *params); +typedef void (APIENTRYP PFNGLMULTITEXPARAMETERFEXTPROC) (GLenum texunit, GLenum target, GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLMULTITEXPARAMETERFVEXTPROC) (GLenum texunit, GLenum target, GLenum pname, const GLfloat *params); +typedef void (APIENTRYP PFNGLMULTITEXIMAGE1DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLint internalformat, GLsizei width, GLint border, GLenum format, GLenum type, const void *pixels); +typedef void (APIENTRYP PFNGLMULTITEXIMAGE2DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void *pixels); +typedef void (APIENTRYP PFNGLMULTITEXSUBIMAGE1DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const void *pixels); +typedef void (APIENTRYP PFNGLMULTITEXSUBIMAGE2DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels); +typedef void (APIENTRYP PFNGLCOPYMULTITEXIMAGE1DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLint border); +typedef void (APIENTRYP PFNGLCOPYMULTITEXIMAGE2DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border); +typedef void (APIENTRYP PFNGLCOPYMULTITEXSUBIMAGE1DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint x, GLint y, GLsizei width); +typedef void (APIENTRYP PFNGLCOPYMULTITEXSUBIMAGE2DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLGETMULTITEXIMAGEEXTPROC) (GLenum texunit, GLenum target, GLint level, GLenum format, GLenum type, void *pixels); +typedef void (APIENTRYP PFNGLGETMULTITEXPARAMETERFVEXTPROC) (GLenum texunit, GLenum target, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETMULTITEXPARAMETERIVEXTPROC) (GLenum texunit, GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETMULTITEXLEVELPARAMETERFVEXTPROC) (GLenum texunit, GLenum target, GLint level, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETMULTITEXLEVELPARAMETERIVEXTPROC) (GLenum texunit, GLenum target, GLint level, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLMULTITEXIMAGE3DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void *pixels); +typedef void (APIENTRYP PFNGLMULTITEXSUBIMAGE3DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *pixels); +typedef void (APIENTRYP PFNGLCOPYMULTITEXSUBIMAGE3DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLENABLECLIENTSTATEINDEXEDEXTPROC) (GLenum array, GLuint index); +typedef void (APIENTRYP PFNGLDISABLECLIENTSTATEINDEXEDEXTPROC) (GLenum array, GLuint index); +typedef void (APIENTRYP PFNGLGETFLOATINDEXEDVEXTPROC) (GLenum target, GLuint index, GLfloat *data); +typedef void (APIENTRYP PFNGLGETDOUBLEINDEXEDVEXTPROC) (GLenum target, GLuint index, GLdouble *data); +typedef void (APIENTRYP PFNGLGETPOINTERINDEXEDVEXTPROC) (GLenum target, GLuint index, void **data); +typedef void (APIENTRYP PFNGLENABLEINDEXEDEXTPROC) (GLenum target, GLuint index); +typedef void (APIENTRYP PFNGLDISABLEINDEXEDEXTPROC) (GLenum target, GLuint index); +typedef GLboolean (APIENTRYP PFNGLISENABLEDINDEXEDEXTPROC) (GLenum target, GLuint index); +typedef void (APIENTRYP PFNGLGETINTEGERINDEXEDVEXTPROC) (GLenum target, GLuint index, GLint *data); +typedef void (APIENTRYP PFNGLGETBOOLEANINDEXEDVEXTPROC) (GLenum target, GLuint index, GLboolean *data); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXTUREIMAGE3DEXTPROC) (GLuint texture, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const void *bits); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXTUREIMAGE2DEXTPROC) (GLuint texture, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void *bits); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXTUREIMAGE1DEXTPROC) (GLuint texture, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLint border, GLsizei imageSize, const void *bits); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXTURESUBIMAGE3DEXTPROC) (GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *bits); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXTURESUBIMAGE2DEXTPROC) (GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *bits); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXTURESUBIMAGE1DEXTPROC) (GLuint texture, GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const void *bits); +typedef void (APIENTRYP PFNGLGETCOMPRESSEDTEXTUREIMAGEEXTPROC) (GLuint texture, GLenum target, GLint lod, void *img); +typedef void (APIENTRYP PFNGLCOMPRESSEDMULTITEXIMAGE3DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const void *bits); +typedef void (APIENTRYP PFNGLCOMPRESSEDMULTITEXIMAGE2DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void *bits); +typedef void (APIENTRYP PFNGLCOMPRESSEDMULTITEXIMAGE1DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLint border, GLsizei imageSize, const void *bits); +typedef void (APIENTRYP PFNGLCOMPRESSEDMULTITEXSUBIMAGE3DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *bits); +typedef void (APIENTRYP PFNGLCOMPRESSEDMULTITEXSUBIMAGE2DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *bits); +typedef void (APIENTRYP PFNGLCOMPRESSEDMULTITEXSUBIMAGE1DEXTPROC) (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const void *bits); +typedef void (APIENTRYP PFNGLGETCOMPRESSEDMULTITEXIMAGEEXTPROC) (GLenum texunit, GLenum target, GLint lod, void *img); +typedef void (APIENTRYP PFNGLMATRIXLOADTRANSPOSEFEXTPROC) (GLenum mode, const GLfloat *m); +typedef void (APIENTRYP PFNGLMATRIXLOADTRANSPOSEDEXTPROC) (GLenum mode, const GLdouble *m); +typedef void (APIENTRYP PFNGLMATRIXMULTTRANSPOSEFEXTPROC) (GLenum mode, const GLfloat *m); +typedef void (APIENTRYP PFNGLMATRIXMULTTRANSPOSEDEXTPROC) (GLenum mode, const GLdouble *m); +typedef void (APIENTRYP PFNGLNAMEDBUFFERDATAEXTPROC) (GLuint buffer, GLsizeiptr size, const void *data, GLenum usage); +typedef void (APIENTRYP PFNGLNAMEDBUFFERSUBDATAEXTPROC) (GLuint buffer, GLintptr offset, GLsizeiptr size, const void *data); +typedef void *(APIENTRYP PFNGLMAPNAMEDBUFFEREXTPROC) (GLuint buffer, GLenum access); +typedef GLboolean (APIENTRYP PFNGLUNMAPNAMEDBUFFEREXTPROC) (GLuint buffer); +typedef void (APIENTRYP PFNGLGETNAMEDBUFFERPARAMETERIVEXTPROC) (GLuint buffer, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETNAMEDBUFFERPOINTERVEXTPROC) (GLuint buffer, GLenum pname, void **params); +typedef void (APIENTRYP PFNGLGETNAMEDBUFFERSUBDATAEXTPROC) (GLuint buffer, GLintptr offset, GLsizeiptr size, void *data); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1FEXTPROC) (GLuint program, GLint location, GLfloat v0); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2FEXTPROC) (GLuint program, GLint location, GLfloat v0, GLfloat v1); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3FEXTPROC) (GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4FEXTPROC) (GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1IEXTPROC) (GLuint program, GLint location, GLint v0); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2IEXTPROC) (GLuint program, GLint location, GLint v0, GLint v1); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3IEXTPROC) (GLuint program, GLint location, GLint v0, GLint v1, GLint v2); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4IEXTPROC) (GLuint program, GLint location, GLint v0, GLint v1, GLint v2, GLint v3); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1FVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2FVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3FVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4FVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1IVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLint *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2IVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLint *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3IVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLint *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4IVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLint *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2FVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3FVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4FVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2X3FVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3X2FVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2X4FVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4X2FVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3X4FVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4X3FVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLTEXTUREBUFFEREXTPROC) (GLuint texture, GLenum target, GLenum internalformat, GLuint buffer); +typedef void (APIENTRYP PFNGLMULTITEXBUFFEREXTPROC) (GLenum texunit, GLenum target, GLenum internalformat, GLuint buffer); +typedef void (APIENTRYP PFNGLTEXTUREPARAMETERIIVEXTPROC) (GLuint texture, GLenum target, GLenum pname, const GLint *params); +typedef void (APIENTRYP PFNGLTEXTUREPARAMETERIUIVEXTPROC) (GLuint texture, GLenum target, GLenum pname, const GLuint *params); +typedef void (APIENTRYP PFNGLGETTEXTUREPARAMETERIIVEXTPROC) (GLuint texture, GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETTEXTUREPARAMETERIUIVEXTPROC) (GLuint texture, GLenum target, GLenum pname, GLuint *params); +typedef void (APIENTRYP PFNGLMULTITEXPARAMETERIIVEXTPROC) (GLenum texunit, GLenum target, GLenum pname, const GLint *params); +typedef void (APIENTRYP PFNGLMULTITEXPARAMETERIUIVEXTPROC) (GLenum texunit, GLenum target, GLenum pname, const GLuint *params); +typedef void (APIENTRYP PFNGLGETMULTITEXPARAMETERIIVEXTPROC) (GLenum texunit, GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETMULTITEXPARAMETERIUIVEXTPROC) (GLenum texunit, GLenum target, GLenum pname, GLuint *params); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1UIEXTPROC) (GLuint program, GLint location, GLuint v0); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2UIEXTPROC) (GLuint program, GLint location, GLuint v0, GLuint v1); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3UIEXTPROC) (GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4UIEXTPROC) (GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1UIVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLuint *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2UIVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLuint *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3UIVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLuint *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4UIVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLuint *value); +typedef void (APIENTRYP PFNGLNAMEDPROGRAMLOCALPARAMETERS4FVEXTPROC) (GLuint program, GLenum target, GLuint index, GLsizei count, const GLfloat *params); +typedef void (APIENTRYP PFNGLNAMEDPROGRAMLOCALPARAMETERI4IEXTPROC) (GLuint program, GLenum target, GLuint index, GLint x, GLint y, GLint z, GLint w); +typedef void (APIENTRYP PFNGLNAMEDPROGRAMLOCALPARAMETERI4IVEXTPROC) (GLuint program, GLenum target, GLuint index, const GLint *params); +typedef void (APIENTRYP PFNGLNAMEDPROGRAMLOCALPARAMETERSI4IVEXTPROC) (GLuint program, GLenum target, GLuint index, GLsizei count, const GLint *params); +typedef void (APIENTRYP PFNGLNAMEDPROGRAMLOCALPARAMETERI4UIEXTPROC) (GLuint program, GLenum target, GLuint index, GLuint x, GLuint y, GLuint z, GLuint w); +typedef void (APIENTRYP PFNGLNAMEDPROGRAMLOCALPARAMETERI4UIVEXTPROC) (GLuint program, GLenum target, GLuint index, const GLuint *params); +typedef void (APIENTRYP PFNGLNAMEDPROGRAMLOCALPARAMETERSI4UIVEXTPROC) (GLuint program, GLenum target, GLuint index, GLsizei count, const GLuint *params); +typedef void (APIENTRYP PFNGLGETNAMEDPROGRAMLOCALPARAMETERIIVEXTPROC) (GLuint program, GLenum target, GLuint index, GLint *params); +typedef void (APIENTRYP PFNGLGETNAMEDPROGRAMLOCALPARAMETERIUIVEXTPROC) (GLuint program, GLenum target, GLuint index, GLuint *params); +typedef void (APIENTRYP PFNGLENABLECLIENTSTATEIEXTPROC) (GLenum array, GLuint index); +typedef void (APIENTRYP PFNGLDISABLECLIENTSTATEIEXTPROC) (GLenum array, GLuint index); +typedef void (APIENTRYP PFNGLGETFLOATI_VEXTPROC) (GLenum pname, GLuint index, GLfloat *params); +typedef void (APIENTRYP PFNGLGETDOUBLEI_VEXTPROC) (GLenum pname, GLuint index, GLdouble *params); +typedef void (APIENTRYP PFNGLGETPOINTERI_VEXTPROC) (GLenum pname, GLuint index, void **params); +typedef void (APIENTRYP PFNGLNAMEDPROGRAMSTRINGEXTPROC) (GLuint program, GLenum target, GLenum format, GLsizei len, const void *string); +typedef void (APIENTRYP PFNGLNAMEDPROGRAMLOCALPARAMETER4DEXTPROC) (GLuint program, GLenum target, GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +typedef void (APIENTRYP PFNGLNAMEDPROGRAMLOCALPARAMETER4DVEXTPROC) (GLuint program, GLenum target, GLuint index, const GLdouble *params); +typedef void (APIENTRYP PFNGLNAMEDPROGRAMLOCALPARAMETER4FEXTPROC) (GLuint program, GLenum target, GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +typedef void (APIENTRYP PFNGLNAMEDPROGRAMLOCALPARAMETER4FVEXTPROC) (GLuint program, GLenum target, GLuint index, const GLfloat *params); +typedef void (APIENTRYP PFNGLGETNAMEDPROGRAMLOCALPARAMETERDVEXTPROC) (GLuint program, GLenum target, GLuint index, GLdouble *params); +typedef void (APIENTRYP PFNGLGETNAMEDPROGRAMLOCALPARAMETERFVEXTPROC) (GLuint program, GLenum target, GLuint index, GLfloat *params); +typedef void (APIENTRYP PFNGLGETNAMEDPROGRAMIVEXTPROC) (GLuint program, GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETNAMEDPROGRAMSTRINGEXTPROC) (GLuint program, GLenum target, GLenum pname, void *string); +typedef void (APIENTRYP PFNGLNAMEDRENDERBUFFERSTORAGEEXTPROC) (GLuint renderbuffer, GLenum internalformat, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLGETNAMEDRENDERBUFFERPARAMETERIVEXTPROC) (GLuint renderbuffer, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLNAMEDRENDERBUFFERSTORAGEMULTISAMPLEEXTPROC) (GLuint renderbuffer, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLNAMEDRENDERBUFFERSTORAGEMULTISAMPLECOVERAGEEXTPROC) (GLuint renderbuffer, GLsizei coverageSamples, GLsizei colorSamples, GLenum internalformat, GLsizei width, GLsizei height); +typedef GLenum (APIENTRYP PFNGLCHECKNAMEDFRAMEBUFFERSTATUSEXTPROC) (GLuint framebuffer, GLenum target); +typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERTEXTURE1DEXTPROC) (GLuint framebuffer, GLenum attachment, GLenum textarget, GLuint texture, GLint level); +typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERTEXTURE2DEXTPROC) (GLuint framebuffer, GLenum attachment, GLenum textarget, GLuint texture, GLint level); +typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERTEXTURE3DEXTPROC) (GLuint framebuffer, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset); +typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERRENDERBUFFEREXTPROC) (GLuint framebuffer, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); +typedef void (APIENTRYP PFNGLGETNAMEDFRAMEBUFFERATTACHMENTPARAMETERIVEXTPROC) (GLuint framebuffer, GLenum attachment, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGENERATETEXTUREMIPMAPEXTPROC) (GLuint texture, GLenum target); +typedef void (APIENTRYP PFNGLGENERATEMULTITEXMIPMAPEXTPROC) (GLenum texunit, GLenum target); +typedef void (APIENTRYP PFNGLFRAMEBUFFERDRAWBUFFEREXTPROC) (GLuint framebuffer, GLenum mode); +typedef void (APIENTRYP PFNGLFRAMEBUFFERDRAWBUFFERSEXTPROC) (GLuint framebuffer, GLsizei n, const GLenum *bufs); +typedef void (APIENTRYP PFNGLFRAMEBUFFERREADBUFFEREXTPROC) (GLuint framebuffer, GLenum mode); +typedef void (APIENTRYP PFNGLGETFRAMEBUFFERPARAMETERIVEXTPROC) (GLuint framebuffer, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLNAMEDCOPYBUFFERSUBDATAEXTPROC) (GLuint readBuffer, GLuint writeBuffer, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size); +typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERTEXTUREEXTPROC) (GLuint framebuffer, GLenum attachment, GLuint texture, GLint level); +typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERTEXTURELAYEREXTPROC) (GLuint framebuffer, GLenum attachment, GLuint texture, GLint level, GLint layer); +typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERTEXTUREFACEEXTPROC) (GLuint framebuffer, GLenum attachment, GLuint texture, GLint level, GLenum face); +typedef void (APIENTRYP PFNGLTEXTURERENDERBUFFEREXTPROC) (GLuint texture, GLenum target, GLuint renderbuffer); +typedef void (APIENTRYP PFNGLMULTITEXRENDERBUFFEREXTPROC) (GLenum texunit, GLenum target, GLuint renderbuffer); +typedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXOFFSETEXTPROC) (GLuint vaobj, GLuint buffer, GLint size, GLenum type, GLsizei stride, GLintptr offset); +typedef void (APIENTRYP PFNGLVERTEXARRAYCOLOROFFSETEXTPROC) (GLuint vaobj, GLuint buffer, GLint size, GLenum type, GLsizei stride, GLintptr offset); +typedef void (APIENTRYP PFNGLVERTEXARRAYEDGEFLAGOFFSETEXTPROC) (GLuint vaobj, GLuint buffer, GLsizei stride, GLintptr offset); +typedef void (APIENTRYP PFNGLVERTEXARRAYINDEXOFFSETEXTPROC) (GLuint vaobj, GLuint buffer, GLenum type, GLsizei stride, GLintptr offset); +typedef void (APIENTRYP PFNGLVERTEXARRAYNORMALOFFSETEXTPROC) (GLuint vaobj, GLuint buffer, GLenum type, GLsizei stride, GLintptr offset); +typedef void (APIENTRYP PFNGLVERTEXARRAYTEXCOORDOFFSETEXTPROC) (GLuint vaobj, GLuint buffer, GLint size, GLenum type, GLsizei stride, GLintptr offset); +typedef void (APIENTRYP PFNGLVERTEXARRAYMULTITEXCOORDOFFSETEXTPROC) (GLuint vaobj, GLuint buffer, GLenum texunit, GLint size, GLenum type, GLsizei stride, GLintptr offset); +typedef void (APIENTRYP PFNGLVERTEXARRAYFOGCOORDOFFSETEXTPROC) (GLuint vaobj, GLuint buffer, GLenum type, GLsizei stride, GLintptr offset); +typedef void (APIENTRYP PFNGLVERTEXARRAYSECONDARYCOLOROFFSETEXTPROC) (GLuint vaobj, GLuint buffer, GLint size, GLenum type, GLsizei stride, GLintptr offset); +typedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXATTRIBOFFSETEXTPROC) (GLuint vaobj, GLuint buffer, GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, GLintptr offset); +typedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXATTRIBIOFFSETEXTPROC) (GLuint vaobj, GLuint buffer, GLuint index, GLint size, GLenum type, GLsizei stride, GLintptr offset); +typedef void (APIENTRYP PFNGLENABLEVERTEXARRAYEXTPROC) (GLuint vaobj, GLenum array); +typedef void (APIENTRYP PFNGLDISABLEVERTEXARRAYEXTPROC) (GLuint vaobj, GLenum array); +typedef void (APIENTRYP PFNGLENABLEVERTEXARRAYATTRIBEXTPROC) (GLuint vaobj, GLuint index); +typedef void (APIENTRYP PFNGLDISABLEVERTEXARRAYATTRIBEXTPROC) (GLuint vaobj, GLuint index); +typedef void (APIENTRYP PFNGLGETVERTEXARRAYINTEGERVEXTPROC) (GLuint vaobj, GLenum pname, GLint *param); +typedef void (APIENTRYP PFNGLGETVERTEXARRAYPOINTERVEXTPROC) (GLuint vaobj, GLenum pname, void **param); +typedef void (APIENTRYP PFNGLGETVERTEXARRAYINTEGERI_VEXTPROC) (GLuint vaobj, GLuint index, GLenum pname, GLint *param); +typedef void (APIENTRYP PFNGLGETVERTEXARRAYPOINTERI_VEXTPROC) (GLuint vaobj, GLuint index, GLenum pname, void **param); +typedef void *(APIENTRYP PFNGLMAPNAMEDBUFFERRANGEEXTPROC) (GLuint buffer, GLintptr offset, GLsizeiptr length, GLbitfield access); +typedef void (APIENTRYP PFNGLFLUSHMAPPEDNAMEDBUFFERRANGEEXTPROC) (GLuint buffer, GLintptr offset, GLsizeiptr length); +typedef void (APIENTRYP PFNGLNAMEDBUFFERSTORAGEEXTPROC) (GLuint buffer, GLsizeiptr size, const void *data, GLbitfield flags); +typedef void (APIENTRYP PFNGLCLEARNAMEDBUFFERDATAEXTPROC) (GLuint buffer, GLenum internalformat, GLenum format, GLenum type, const void *data); +typedef void (APIENTRYP PFNGLCLEARNAMEDBUFFERSUBDATAEXTPROC) (GLuint buffer, GLenum internalformat, GLsizeiptr offset, GLsizeiptr size, GLenum format, GLenum type, const void *data); +typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERPARAMETERIEXTPROC) (GLuint framebuffer, GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLGETNAMEDFRAMEBUFFERPARAMETERIVEXTPROC) (GLuint framebuffer, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1DEXTPROC) (GLuint program, GLint location, GLdouble x); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2DEXTPROC) (GLuint program, GLint location, GLdouble x, GLdouble y); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3DEXTPROC) (GLuint program, GLint location, GLdouble x, GLdouble y, GLdouble z); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4DEXTPROC) (GLuint program, GLint location, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1DVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2DVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3DVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4DVEXTPROC) (GLuint program, GLint location, GLsizei count, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2DVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3DVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4DVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2X3DVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2X4DVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3X2DVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3X4DVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4X2DVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4X3DVEXTPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLTEXTUREBUFFERRANGEEXTPROC) (GLuint texture, GLenum target, GLenum internalformat, GLuint buffer, GLintptr offset, GLsizeiptr size); +typedef void (APIENTRYP PFNGLTEXTURESTORAGE1DEXTPROC) (GLuint texture, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width); +typedef void (APIENTRYP PFNGLTEXTURESTORAGE2DEXTPROC) (GLuint texture, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLTEXTURESTORAGE3DEXTPROC) (GLuint texture, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth); +typedef void (APIENTRYP PFNGLTEXTURESTORAGE2DMULTISAMPLEEXTPROC) (GLuint texture, GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations); +typedef void (APIENTRYP PFNGLTEXTURESTORAGE3DMULTISAMPLEEXTPROC) (GLuint texture, GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations); +typedef void (APIENTRYP PFNGLVERTEXARRAYBINDVERTEXBUFFEREXTPROC) (GLuint vaobj, GLuint bindingindex, GLuint buffer, GLintptr offset, GLsizei stride); +typedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXATTRIBFORMATEXTPROC) (GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLboolean normalized, GLuint relativeoffset); +typedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXATTRIBIFORMATEXTPROC) (GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset); +typedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXATTRIBLFORMATEXTPROC) (GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset); +typedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXATTRIBBINDINGEXTPROC) (GLuint vaobj, GLuint attribindex, GLuint bindingindex); +typedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXBINDINGDIVISOREXTPROC) (GLuint vaobj, GLuint bindingindex, GLuint divisor); +typedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXATTRIBLOFFSETEXTPROC) (GLuint vaobj, GLuint buffer, GLuint index, GLint size, GLenum type, GLsizei stride, GLintptr offset); +typedef void (APIENTRYP PFNGLTEXTUREPAGECOMMITMENTEXTPROC) (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLboolean commit); +typedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXATTRIBDIVISOREXTPROC) (GLuint vaobj, GLuint index, GLuint divisor); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glMatrixLoadfEXT (GLenum mode, const GLfloat *m); +GLAPI void APIENTRY glMatrixLoaddEXT (GLenum mode, const GLdouble *m); +GLAPI void APIENTRY glMatrixMultfEXT (GLenum mode, const GLfloat *m); +GLAPI void APIENTRY glMatrixMultdEXT (GLenum mode, const GLdouble *m); +GLAPI void APIENTRY glMatrixLoadIdentityEXT (GLenum mode); +GLAPI void APIENTRY glMatrixRotatefEXT (GLenum mode, GLfloat angle, GLfloat x, GLfloat y, GLfloat z); +GLAPI void APIENTRY glMatrixRotatedEXT (GLenum mode, GLdouble angle, GLdouble x, GLdouble y, GLdouble z); +GLAPI void APIENTRY glMatrixScalefEXT (GLenum mode, GLfloat x, GLfloat y, GLfloat z); +GLAPI void APIENTRY glMatrixScaledEXT (GLenum mode, GLdouble x, GLdouble y, GLdouble z); +GLAPI void APIENTRY glMatrixTranslatefEXT (GLenum mode, GLfloat x, GLfloat y, GLfloat z); +GLAPI void APIENTRY glMatrixTranslatedEXT (GLenum mode, GLdouble x, GLdouble y, GLdouble z); +GLAPI void APIENTRY glMatrixFrustumEXT (GLenum mode, GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble zNear, GLdouble zFar); +GLAPI void APIENTRY glMatrixOrthoEXT (GLenum mode, GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble zNear, GLdouble zFar); +GLAPI void APIENTRY glMatrixPopEXT (GLenum mode); +GLAPI void APIENTRY glMatrixPushEXT (GLenum mode); +GLAPI void APIENTRY glClientAttribDefaultEXT (GLbitfield mask); +GLAPI void APIENTRY glPushClientAttribDefaultEXT (GLbitfield mask); +GLAPI void APIENTRY glTextureParameterfEXT (GLuint texture, GLenum target, GLenum pname, GLfloat param); +GLAPI void APIENTRY glTextureParameterfvEXT (GLuint texture, GLenum target, GLenum pname, const GLfloat *params); +GLAPI void APIENTRY glTextureParameteriEXT (GLuint texture, GLenum target, GLenum pname, GLint param); +GLAPI void APIENTRY glTextureParameterivEXT (GLuint texture, GLenum target, GLenum pname, const GLint *params); +GLAPI void APIENTRY glTextureImage1DEXT (GLuint texture, GLenum target, GLint level, GLint internalformat, GLsizei width, GLint border, GLenum format, GLenum type, const void *pixels); +GLAPI void APIENTRY glTextureImage2DEXT (GLuint texture, GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void *pixels); +GLAPI void APIENTRY glTextureSubImage1DEXT (GLuint texture, GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const void *pixels); +GLAPI void APIENTRY glTextureSubImage2DEXT (GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels); +GLAPI void APIENTRY glCopyTextureImage1DEXT (GLuint texture, GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLint border); +GLAPI void APIENTRY glCopyTextureImage2DEXT (GLuint texture, GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border); +GLAPI void APIENTRY glCopyTextureSubImage1DEXT (GLuint texture, GLenum target, GLint level, GLint xoffset, GLint x, GLint y, GLsizei width); +GLAPI void APIENTRY glCopyTextureSubImage2DEXT (GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height); +GLAPI void APIENTRY glGetTextureImageEXT (GLuint texture, GLenum target, GLint level, GLenum format, GLenum type, void *pixels); +GLAPI void APIENTRY glGetTextureParameterfvEXT (GLuint texture, GLenum target, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetTextureParameterivEXT (GLuint texture, GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetTextureLevelParameterfvEXT (GLuint texture, GLenum target, GLint level, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetTextureLevelParameterivEXT (GLuint texture, GLenum target, GLint level, GLenum pname, GLint *params); +GLAPI void APIENTRY glTextureImage3DEXT (GLuint texture, GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void *pixels); +GLAPI void APIENTRY glTextureSubImage3DEXT (GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *pixels); +GLAPI void APIENTRY glCopyTextureSubImage3DEXT (GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height); +GLAPI void APIENTRY glBindMultiTextureEXT (GLenum texunit, GLenum target, GLuint texture); +GLAPI void APIENTRY glMultiTexCoordPointerEXT (GLenum texunit, GLint size, GLenum type, GLsizei stride, const void *pointer); +GLAPI void APIENTRY glMultiTexEnvfEXT (GLenum texunit, GLenum target, GLenum pname, GLfloat param); +GLAPI void APIENTRY glMultiTexEnvfvEXT (GLenum texunit, GLenum target, GLenum pname, const GLfloat *params); +GLAPI void APIENTRY glMultiTexEnviEXT (GLenum texunit, GLenum target, GLenum pname, GLint param); +GLAPI void APIENTRY glMultiTexEnvivEXT (GLenum texunit, GLenum target, GLenum pname, const GLint *params); +GLAPI void APIENTRY glMultiTexGendEXT (GLenum texunit, GLenum coord, GLenum pname, GLdouble param); +GLAPI void APIENTRY glMultiTexGendvEXT (GLenum texunit, GLenum coord, GLenum pname, const GLdouble *params); +GLAPI void APIENTRY glMultiTexGenfEXT (GLenum texunit, GLenum coord, GLenum pname, GLfloat param); +GLAPI void APIENTRY glMultiTexGenfvEXT (GLenum texunit, GLenum coord, GLenum pname, const GLfloat *params); +GLAPI void APIENTRY glMultiTexGeniEXT (GLenum texunit, GLenum coord, GLenum pname, GLint param); +GLAPI void APIENTRY glMultiTexGenivEXT (GLenum texunit, GLenum coord, GLenum pname, const GLint *params); +GLAPI void APIENTRY glGetMultiTexEnvfvEXT (GLenum texunit, GLenum target, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetMultiTexEnvivEXT (GLenum texunit, GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetMultiTexGendvEXT (GLenum texunit, GLenum coord, GLenum pname, GLdouble *params); +GLAPI void APIENTRY glGetMultiTexGenfvEXT (GLenum texunit, GLenum coord, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetMultiTexGenivEXT (GLenum texunit, GLenum coord, GLenum pname, GLint *params); +GLAPI void APIENTRY glMultiTexParameteriEXT (GLenum texunit, GLenum target, GLenum pname, GLint param); +GLAPI void APIENTRY glMultiTexParameterivEXT (GLenum texunit, GLenum target, GLenum pname, const GLint *params); +GLAPI void APIENTRY glMultiTexParameterfEXT (GLenum texunit, GLenum target, GLenum pname, GLfloat param); +GLAPI void APIENTRY glMultiTexParameterfvEXT (GLenum texunit, GLenum target, GLenum pname, const GLfloat *params); +GLAPI void APIENTRY glMultiTexImage1DEXT (GLenum texunit, GLenum target, GLint level, GLint internalformat, GLsizei width, GLint border, GLenum format, GLenum type, const void *pixels); +GLAPI void APIENTRY glMultiTexImage2DEXT (GLenum texunit, GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void *pixels); +GLAPI void APIENTRY glMultiTexSubImage1DEXT (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const void *pixels); +GLAPI void APIENTRY glMultiTexSubImage2DEXT (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels); +GLAPI void APIENTRY glCopyMultiTexImage1DEXT (GLenum texunit, GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLint border); +GLAPI void APIENTRY glCopyMultiTexImage2DEXT (GLenum texunit, GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border); +GLAPI void APIENTRY glCopyMultiTexSubImage1DEXT (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint x, GLint y, GLsizei width); +GLAPI void APIENTRY glCopyMultiTexSubImage2DEXT (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height); +GLAPI void APIENTRY glGetMultiTexImageEXT (GLenum texunit, GLenum target, GLint level, GLenum format, GLenum type, void *pixels); +GLAPI void APIENTRY glGetMultiTexParameterfvEXT (GLenum texunit, GLenum target, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetMultiTexParameterivEXT (GLenum texunit, GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetMultiTexLevelParameterfvEXT (GLenum texunit, GLenum target, GLint level, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetMultiTexLevelParameterivEXT (GLenum texunit, GLenum target, GLint level, GLenum pname, GLint *params); +GLAPI void APIENTRY glMultiTexImage3DEXT (GLenum texunit, GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void *pixels); +GLAPI void APIENTRY glMultiTexSubImage3DEXT (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *pixels); +GLAPI void APIENTRY glCopyMultiTexSubImage3DEXT (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height); +GLAPI void APIENTRY glEnableClientStateIndexedEXT (GLenum array, GLuint index); +GLAPI void APIENTRY glDisableClientStateIndexedEXT (GLenum array, GLuint index); +GLAPI void APIENTRY glGetFloatIndexedvEXT (GLenum target, GLuint index, GLfloat *data); +GLAPI void APIENTRY glGetDoubleIndexedvEXT (GLenum target, GLuint index, GLdouble *data); +GLAPI void APIENTRY glGetPointerIndexedvEXT (GLenum target, GLuint index, void **data); +GLAPI void APIENTRY glEnableIndexedEXT (GLenum target, GLuint index); +GLAPI void APIENTRY glDisableIndexedEXT (GLenum target, GLuint index); +GLAPI GLboolean APIENTRY glIsEnabledIndexedEXT (GLenum target, GLuint index); +GLAPI void APIENTRY glGetIntegerIndexedvEXT (GLenum target, GLuint index, GLint *data); +GLAPI void APIENTRY glGetBooleanIndexedvEXT (GLenum target, GLuint index, GLboolean *data); +GLAPI void APIENTRY glCompressedTextureImage3DEXT (GLuint texture, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const void *bits); +GLAPI void APIENTRY glCompressedTextureImage2DEXT (GLuint texture, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void *bits); +GLAPI void APIENTRY glCompressedTextureImage1DEXT (GLuint texture, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLint border, GLsizei imageSize, const void *bits); +GLAPI void APIENTRY glCompressedTextureSubImage3DEXT (GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *bits); +GLAPI void APIENTRY glCompressedTextureSubImage2DEXT (GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *bits); +GLAPI void APIENTRY glCompressedTextureSubImage1DEXT (GLuint texture, GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const void *bits); +GLAPI void APIENTRY glGetCompressedTextureImageEXT (GLuint texture, GLenum target, GLint lod, void *img); +GLAPI void APIENTRY glCompressedMultiTexImage3DEXT (GLenum texunit, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const void *bits); +GLAPI void APIENTRY glCompressedMultiTexImage2DEXT (GLenum texunit, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void *bits); +GLAPI void APIENTRY glCompressedMultiTexImage1DEXT (GLenum texunit, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLint border, GLsizei imageSize, const void *bits); +GLAPI void APIENTRY glCompressedMultiTexSubImage3DEXT (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *bits); +GLAPI void APIENTRY glCompressedMultiTexSubImage2DEXT (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *bits); +GLAPI void APIENTRY glCompressedMultiTexSubImage1DEXT (GLenum texunit, GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const void *bits); +GLAPI void APIENTRY glGetCompressedMultiTexImageEXT (GLenum texunit, GLenum target, GLint lod, void *img); +GLAPI void APIENTRY glMatrixLoadTransposefEXT (GLenum mode, const GLfloat *m); +GLAPI void APIENTRY glMatrixLoadTransposedEXT (GLenum mode, const GLdouble *m); +GLAPI void APIENTRY glMatrixMultTransposefEXT (GLenum mode, const GLfloat *m); +GLAPI void APIENTRY glMatrixMultTransposedEXT (GLenum mode, const GLdouble *m); +GLAPI void APIENTRY glNamedBufferDataEXT (GLuint buffer, GLsizeiptr size, const void *data, GLenum usage); +GLAPI void APIENTRY glNamedBufferSubDataEXT (GLuint buffer, GLintptr offset, GLsizeiptr size, const void *data); +GLAPI void *APIENTRY glMapNamedBufferEXT (GLuint buffer, GLenum access); +GLAPI GLboolean APIENTRY glUnmapNamedBufferEXT (GLuint buffer); +GLAPI void APIENTRY glGetNamedBufferParameterivEXT (GLuint buffer, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetNamedBufferPointervEXT (GLuint buffer, GLenum pname, void **params); +GLAPI void APIENTRY glGetNamedBufferSubDataEXT (GLuint buffer, GLintptr offset, GLsizeiptr size, void *data); +GLAPI void APIENTRY glProgramUniform1fEXT (GLuint program, GLint location, GLfloat v0); +GLAPI void APIENTRY glProgramUniform2fEXT (GLuint program, GLint location, GLfloat v0, GLfloat v1); +GLAPI void APIENTRY glProgramUniform3fEXT (GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2); +GLAPI void APIENTRY glProgramUniform4fEXT (GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); +GLAPI void APIENTRY glProgramUniform1iEXT (GLuint program, GLint location, GLint v0); +GLAPI void APIENTRY glProgramUniform2iEXT (GLuint program, GLint location, GLint v0, GLint v1); +GLAPI void APIENTRY glProgramUniform3iEXT (GLuint program, GLint location, GLint v0, GLint v1, GLint v2); +GLAPI void APIENTRY glProgramUniform4iEXT (GLuint program, GLint location, GLint v0, GLint v1, GLint v2, GLint v3); +GLAPI void APIENTRY glProgramUniform1fvEXT (GLuint program, GLint location, GLsizei count, const GLfloat *value); +GLAPI void APIENTRY glProgramUniform2fvEXT (GLuint program, GLint location, GLsizei count, const GLfloat *value); +GLAPI void APIENTRY glProgramUniform3fvEXT (GLuint program, GLint location, GLsizei count, const GLfloat *value); +GLAPI void APIENTRY glProgramUniform4fvEXT (GLuint program, GLint location, GLsizei count, const GLfloat *value); +GLAPI void APIENTRY glProgramUniform1ivEXT (GLuint program, GLint location, GLsizei count, const GLint *value); +GLAPI void APIENTRY glProgramUniform2ivEXT (GLuint program, GLint location, GLsizei count, const GLint *value); +GLAPI void APIENTRY glProgramUniform3ivEXT (GLuint program, GLint location, GLsizei count, const GLint *value); +GLAPI void APIENTRY glProgramUniform4ivEXT (GLuint program, GLint location, GLsizei count, const GLint *value); +GLAPI void APIENTRY glProgramUniformMatrix2fvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glProgramUniformMatrix3fvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glProgramUniformMatrix4fvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glProgramUniformMatrix2x3fvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glProgramUniformMatrix3x2fvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glProgramUniformMatrix2x4fvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glProgramUniformMatrix4x2fvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glProgramUniformMatrix3x4fvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glProgramUniformMatrix4x3fvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glTextureBufferEXT (GLuint texture, GLenum target, GLenum internalformat, GLuint buffer); +GLAPI void APIENTRY glMultiTexBufferEXT (GLenum texunit, GLenum target, GLenum internalformat, GLuint buffer); +GLAPI void APIENTRY glTextureParameterIivEXT (GLuint texture, GLenum target, GLenum pname, const GLint *params); +GLAPI void APIENTRY glTextureParameterIuivEXT (GLuint texture, GLenum target, GLenum pname, const GLuint *params); +GLAPI void APIENTRY glGetTextureParameterIivEXT (GLuint texture, GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetTextureParameterIuivEXT (GLuint texture, GLenum target, GLenum pname, GLuint *params); +GLAPI void APIENTRY glMultiTexParameterIivEXT (GLenum texunit, GLenum target, GLenum pname, const GLint *params); +GLAPI void APIENTRY glMultiTexParameterIuivEXT (GLenum texunit, GLenum target, GLenum pname, const GLuint *params); +GLAPI void APIENTRY glGetMultiTexParameterIivEXT (GLenum texunit, GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetMultiTexParameterIuivEXT (GLenum texunit, GLenum target, GLenum pname, GLuint *params); +GLAPI void APIENTRY glProgramUniform1uiEXT (GLuint program, GLint location, GLuint v0); +GLAPI void APIENTRY glProgramUniform2uiEXT (GLuint program, GLint location, GLuint v0, GLuint v1); +GLAPI void APIENTRY glProgramUniform3uiEXT (GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2); +GLAPI void APIENTRY glProgramUniform4uiEXT (GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3); +GLAPI void APIENTRY glProgramUniform1uivEXT (GLuint program, GLint location, GLsizei count, const GLuint *value); +GLAPI void APIENTRY glProgramUniform2uivEXT (GLuint program, GLint location, GLsizei count, const GLuint *value); +GLAPI void APIENTRY glProgramUniform3uivEXT (GLuint program, GLint location, GLsizei count, const GLuint *value); +GLAPI void APIENTRY glProgramUniform4uivEXT (GLuint program, GLint location, GLsizei count, const GLuint *value); +GLAPI void APIENTRY glNamedProgramLocalParameters4fvEXT (GLuint program, GLenum target, GLuint index, GLsizei count, const GLfloat *params); +GLAPI void APIENTRY glNamedProgramLocalParameterI4iEXT (GLuint program, GLenum target, GLuint index, GLint x, GLint y, GLint z, GLint w); +GLAPI void APIENTRY glNamedProgramLocalParameterI4ivEXT (GLuint program, GLenum target, GLuint index, const GLint *params); +GLAPI void APIENTRY glNamedProgramLocalParametersI4ivEXT (GLuint program, GLenum target, GLuint index, GLsizei count, const GLint *params); +GLAPI void APIENTRY glNamedProgramLocalParameterI4uiEXT (GLuint program, GLenum target, GLuint index, GLuint x, GLuint y, GLuint z, GLuint w); +GLAPI void APIENTRY glNamedProgramLocalParameterI4uivEXT (GLuint program, GLenum target, GLuint index, const GLuint *params); +GLAPI void APIENTRY glNamedProgramLocalParametersI4uivEXT (GLuint program, GLenum target, GLuint index, GLsizei count, const GLuint *params); +GLAPI void APIENTRY glGetNamedProgramLocalParameterIivEXT (GLuint program, GLenum target, GLuint index, GLint *params); +GLAPI void APIENTRY glGetNamedProgramLocalParameterIuivEXT (GLuint program, GLenum target, GLuint index, GLuint *params); +GLAPI void APIENTRY glEnableClientStateiEXT (GLenum array, GLuint index); +GLAPI void APIENTRY glDisableClientStateiEXT (GLenum array, GLuint index); +GLAPI void APIENTRY glGetFloati_vEXT (GLenum pname, GLuint index, GLfloat *params); +GLAPI void APIENTRY glGetDoublei_vEXT (GLenum pname, GLuint index, GLdouble *params); +GLAPI void APIENTRY glGetPointeri_vEXT (GLenum pname, GLuint index, void **params); +GLAPI void APIENTRY glNamedProgramStringEXT (GLuint program, GLenum target, GLenum format, GLsizei len, const void *string); +GLAPI void APIENTRY glNamedProgramLocalParameter4dEXT (GLuint program, GLenum target, GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +GLAPI void APIENTRY glNamedProgramLocalParameter4dvEXT (GLuint program, GLenum target, GLuint index, const GLdouble *params); +GLAPI void APIENTRY glNamedProgramLocalParameter4fEXT (GLuint program, GLenum target, GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +GLAPI void APIENTRY glNamedProgramLocalParameter4fvEXT (GLuint program, GLenum target, GLuint index, const GLfloat *params); +GLAPI void APIENTRY glGetNamedProgramLocalParameterdvEXT (GLuint program, GLenum target, GLuint index, GLdouble *params); +GLAPI void APIENTRY glGetNamedProgramLocalParameterfvEXT (GLuint program, GLenum target, GLuint index, GLfloat *params); +GLAPI void APIENTRY glGetNamedProgramivEXT (GLuint program, GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetNamedProgramStringEXT (GLuint program, GLenum target, GLenum pname, void *string); +GLAPI void APIENTRY glNamedRenderbufferStorageEXT (GLuint renderbuffer, GLenum internalformat, GLsizei width, GLsizei height); +GLAPI void APIENTRY glGetNamedRenderbufferParameterivEXT (GLuint renderbuffer, GLenum pname, GLint *params); +GLAPI void APIENTRY glNamedRenderbufferStorageMultisampleEXT (GLuint renderbuffer, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height); +GLAPI void APIENTRY glNamedRenderbufferStorageMultisampleCoverageEXT (GLuint renderbuffer, GLsizei coverageSamples, GLsizei colorSamples, GLenum internalformat, GLsizei width, GLsizei height); +GLAPI GLenum APIENTRY glCheckNamedFramebufferStatusEXT (GLuint framebuffer, GLenum target); +GLAPI void APIENTRY glNamedFramebufferTexture1DEXT (GLuint framebuffer, GLenum attachment, GLenum textarget, GLuint texture, GLint level); +GLAPI void APIENTRY glNamedFramebufferTexture2DEXT (GLuint framebuffer, GLenum attachment, GLenum textarget, GLuint texture, GLint level); +GLAPI void APIENTRY glNamedFramebufferTexture3DEXT (GLuint framebuffer, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset); +GLAPI void APIENTRY glNamedFramebufferRenderbufferEXT (GLuint framebuffer, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); +GLAPI void APIENTRY glGetNamedFramebufferAttachmentParameterivEXT (GLuint framebuffer, GLenum attachment, GLenum pname, GLint *params); +GLAPI void APIENTRY glGenerateTextureMipmapEXT (GLuint texture, GLenum target); +GLAPI void APIENTRY glGenerateMultiTexMipmapEXT (GLenum texunit, GLenum target); +GLAPI void APIENTRY glFramebufferDrawBufferEXT (GLuint framebuffer, GLenum mode); +GLAPI void APIENTRY glFramebufferDrawBuffersEXT (GLuint framebuffer, GLsizei n, const GLenum *bufs); +GLAPI void APIENTRY glFramebufferReadBufferEXT (GLuint framebuffer, GLenum mode); +GLAPI void APIENTRY glGetFramebufferParameterivEXT (GLuint framebuffer, GLenum pname, GLint *params); +GLAPI void APIENTRY glNamedCopyBufferSubDataEXT (GLuint readBuffer, GLuint writeBuffer, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size); +GLAPI void APIENTRY glNamedFramebufferTextureEXT (GLuint framebuffer, GLenum attachment, GLuint texture, GLint level); +GLAPI void APIENTRY glNamedFramebufferTextureLayerEXT (GLuint framebuffer, GLenum attachment, GLuint texture, GLint level, GLint layer); +GLAPI void APIENTRY glNamedFramebufferTextureFaceEXT (GLuint framebuffer, GLenum attachment, GLuint texture, GLint level, GLenum face); +GLAPI void APIENTRY glTextureRenderbufferEXT (GLuint texture, GLenum target, GLuint renderbuffer); +GLAPI void APIENTRY glMultiTexRenderbufferEXT (GLenum texunit, GLenum target, GLuint renderbuffer); +GLAPI void APIENTRY glVertexArrayVertexOffsetEXT (GLuint vaobj, GLuint buffer, GLint size, GLenum type, GLsizei stride, GLintptr offset); +GLAPI void APIENTRY glVertexArrayColorOffsetEXT (GLuint vaobj, GLuint buffer, GLint size, GLenum type, GLsizei stride, GLintptr offset); +GLAPI void APIENTRY glVertexArrayEdgeFlagOffsetEXT (GLuint vaobj, GLuint buffer, GLsizei stride, GLintptr offset); +GLAPI void APIENTRY glVertexArrayIndexOffsetEXT (GLuint vaobj, GLuint buffer, GLenum type, GLsizei stride, GLintptr offset); +GLAPI void APIENTRY glVertexArrayNormalOffsetEXT (GLuint vaobj, GLuint buffer, GLenum type, GLsizei stride, GLintptr offset); +GLAPI void APIENTRY glVertexArrayTexCoordOffsetEXT (GLuint vaobj, GLuint buffer, GLint size, GLenum type, GLsizei stride, GLintptr offset); +GLAPI void APIENTRY glVertexArrayMultiTexCoordOffsetEXT (GLuint vaobj, GLuint buffer, GLenum texunit, GLint size, GLenum type, GLsizei stride, GLintptr offset); +GLAPI void APIENTRY glVertexArrayFogCoordOffsetEXT (GLuint vaobj, GLuint buffer, GLenum type, GLsizei stride, GLintptr offset); +GLAPI void APIENTRY glVertexArraySecondaryColorOffsetEXT (GLuint vaobj, GLuint buffer, GLint size, GLenum type, GLsizei stride, GLintptr offset); +GLAPI void APIENTRY glVertexArrayVertexAttribOffsetEXT (GLuint vaobj, GLuint buffer, GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, GLintptr offset); +GLAPI void APIENTRY glVertexArrayVertexAttribIOffsetEXT (GLuint vaobj, GLuint buffer, GLuint index, GLint size, GLenum type, GLsizei stride, GLintptr offset); +GLAPI void APIENTRY glEnableVertexArrayEXT (GLuint vaobj, GLenum array); +GLAPI void APIENTRY glDisableVertexArrayEXT (GLuint vaobj, GLenum array); +GLAPI void APIENTRY glEnableVertexArrayAttribEXT (GLuint vaobj, GLuint index); +GLAPI void APIENTRY glDisableVertexArrayAttribEXT (GLuint vaobj, GLuint index); +GLAPI void APIENTRY glGetVertexArrayIntegervEXT (GLuint vaobj, GLenum pname, GLint *param); +GLAPI void APIENTRY glGetVertexArrayPointervEXT (GLuint vaobj, GLenum pname, void **param); +GLAPI void APIENTRY glGetVertexArrayIntegeri_vEXT (GLuint vaobj, GLuint index, GLenum pname, GLint *param); +GLAPI void APIENTRY glGetVertexArrayPointeri_vEXT (GLuint vaobj, GLuint index, GLenum pname, void **param); +GLAPI void *APIENTRY glMapNamedBufferRangeEXT (GLuint buffer, GLintptr offset, GLsizeiptr length, GLbitfield access); +GLAPI void APIENTRY glFlushMappedNamedBufferRangeEXT (GLuint buffer, GLintptr offset, GLsizeiptr length); +GLAPI void APIENTRY glNamedBufferStorageEXT (GLuint buffer, GLsizeiptr size, const void *data, GLbitfield flags); +GLAPI void APIENTRY glClearNamedBufferDataEXT (GLuint buffer, GLenum internalformat, GLenum format, GLenum type, const void *data); +GLAPI void APIENTRY glClearNamedBufferSubDataEXT (GLuint buffer, GLenum internalformat, GLsizeiptr offset, GLsizeiptr size, GLenum format, GLenum type, const void *data); +GLAPI void APIENTRY glNamedFramebufferParameteriEXT (GLuint framebuffer, GLenum pname, GLint param); +GLAPI void APIENTRY glGetNamedFramebufferParameterivEXT (GLuint framebuffer, GLenum pname, GLint *params); +GLAPI void APIENTRY glProgramUniform1dEXT (GLuint program, GLint location, GLdouble x); +GLAPI void APIENTRY glProgramUniform2dEXT (GLuint program, GLint location, GLdouble x, GLdouble y); +GLAPI void APIENTRY glProgramUniform3dEXT (GLuint program, GLint location, GLdouble x, GLdouble y, GLdouble z); +GLAPI void APIENTRY glProgramUniform4dEXT (GLuint program, GLint location, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +GLAPI void APIENTRY glProgramUniform1dvEXT (GLuint program, GLint location, GLsizei count, const GLdouble *value); +GLAPI void APIENTRY glProgramUniform2dvEXT (GLuint program, GLint location, GLsizei count, const GLdouble *value); +GLAPI void APIENTRY glProgramUniform3dvEXT (GLuint program, GLint location, GLsizei count, const GLdouble *value); +GLAPI void APIENTRY glProgramUniform4dvEXT (GLuint program, GLint location, GLsizei count, const GLdouble *value); +GLAPI void APIENTRY glProgramUniformMatrix2dvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glProgramUniformMatrix3dvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glProgramUniformMatrix4dvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glProgramUniformMatrix2x3dvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glProgramUniformMatrix2x4dvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glProgramUniformMatrix3x2dvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glProgramUniformMatrix3x4dvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glProgramUniformMatrix4x2dvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glProgramUniformMatrix4x3dvEXT (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glTextureBufferRangeEXT (GLuint texture, GLenum target, GLenum internalformat, GLuint buffer, GLintptr offset, GLsizeiptr size); +GLAPI void APIENTRY glTextureStorage1DEXT (GLuint texture, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width); +GLAPI void APIENTRY glTextureStorage2DEXT (GLuint texture, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height); +GLAPI void APIENTRY glTextureStorage3DEXT (GLuint texture, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth); +GLAPI void APIENTRY glTextureStorage2DMultisampleEXT (GLuint texture, GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations); +GLAPI void APIENTRY glTextureStorage3DMultisampleEXT (GLuint texture, GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations); +GLAPI void APIENTRY glVertexArrayBindVertexBufferEXT (GLuint vaobj, GLuint bindingindex, GLuint buffer, GLintptr offset, GLsizei stride); +GLAPI void APIENTRY glVertexArrayVertexAttribFormatEXT (GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLboolean normalized, GLuint relativeoffset); +GLAPI void APIENTRY glVertexArrayVertexAttribIFormatEXT (GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset); +GLAPI void APIENTRY glVertexArrayVertexAttribLFormatEXT (GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset); +GLAPI void APIENTRY glVertexArrayVertexAttribBindingEXT (GLuint vaobj, GLuint attribindex, GLuint bindingindex); +GLAPI void APIENTRY glVertexArrayVertexBindingDivisorEXT (GLuint vaobj, GLuint bindingindex, GLuint divisor); +GLAPI void APIENTRY glVertexArrayVertexAttribLOffsetEXT (GLuint vaobj, GLuint buffer, GLuint index, GLint size, GLenum type, GLsizei stride, GLintptr offset); +GLAPI void APIENTRY glTexturePageCommitmentEXT (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLboolean commit); +GLAPI void APIENTRY glVertexArrayVertexAttribDivisorEXT (GLuint vaobj, GLuint index, GLuint divisor); +#endif +#endif /* GL_EXT_direct_state_access */ + +#ifndef GL_EXT_draw_buffers2 +#define GL_EXT_draw_buffers2 1 +typedef void (APIENTRYP PFNGLCOLORMASKINDEXEDEXTPROC) (GLuint index, GLboolean r, GLboolean g, GLboolean b, GLboolean a); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glColorMaskIndexedEXT (GLuint index, GLboolean r, GLboolean g, GLboolean b, GLboolean a); +#endif +#endif /* GL_EXT_draw_buffers2 */ + +#ifndef GL_EXT_draw_instanced +#define GL_EXT_draw_instanced 1 +typedef void (APIENTRYP PFNGLDRAWARRAYSINSTANCEDEXTPROC) (GLenum mode, GLint start, GLsizei count, GLsizei primcount); +typedef void (APIENTRYP PFNGLDRAWELEMENTSINSTANCEDEXTPROC) (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei primcount); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glDrawArraysInstancedEXT (GLenum mode, GLint start, GLsizei count, GLsizei primcount); +GLAPI void APIENTRY glDrawElementsInstancedEXT (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei primcount); +#endif +#endif /* GL_EXT_draw_instanced */ + +#ifndef GL_EXT_draw_range_elements +#define GL_EXT_draw_range_elements 1 +#define GL_MAX_ELEMENTS_VERTICES_EXT 0x80E8 +#define GL_MAX_ELEMENTS_INDICES_EXT 0x80E9 +typedef void (APIENTRYP PFNGLDRAWRANGEELEMENTSEXTPROC) (GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void *indices); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glDrawRangeElementsEXT (GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void *indices); +#endif +#endif /* GL_EXT_draw_range_elements */ + +#ifndef GL_EXT_fog_coord +#define GL_EXT_fog_coord 1 +#define GL_FOG_COORDINATE_SOURCE_EXT 0x8450 +#define GL_FOG_COORDINATE_EXT 0x8451 +#define GL_FRAGMENT_DEPTH_EXT 0x8452 +#define GL_CURRENT_FOG_COORDINATE_EXT 0x8453 +#define GL_FOG_COORDINATE_ARRAY_TYPE_EXT 0x8454 +#define GL_FOG_COORDINATE_ARRAY_STRIDE_EXT 0x8455 +#define GL_FOG_COORDINATE_ARRAY_POINTER_EXT 0x8456 +#define GL_FOG_COORDINATE_ARRAY_EXT 0x8457 +typedef void (APIENTRYP PFNGLFOGCOORDFEXTPROC) (GLfloat coord); +typedef void (APIENTRYP PFNGLFOGCOORDFVEXTPROC) (const GLfloat *coord); +typedef void (APIENTRYP PFNGLFOGCOORDDEXTPROC) (GLdouble coord); +typedef void (APIENTRYP PFNGLFOGCOORDDVEXTPROC) (const GLdouble *coord); +typedef void (APIENTRYP PFNGLFOGCOORDPOINTEREXTPROC) (GLenum type, GLsizei stride, const void *pointer); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glFogCoordfEXT (GLfloat coord); +GLAPI void APIENTRY glFogCoordfvEXT (const GLfloat *coord); +GLAPI void APIENTRY glFogCoorddEXT (GLdouble coord); +GLAPI void APIENTRY glFogCoorddvEXT (const GLdouble *coord); +GLAPI void APIENTRY glFogCoordPointerEXT (GLenum type, GLsizei stride, const void *pointer); +#endif +#endif /* GL_EXT_fog_coord */ + +#ifndef GL_EXT_framebuffer_blit +#define GL_EXT_framebuffer_blit 1 +#define GL_READ_FRAMEBUFFER_EXT 0x8CA8 +#define GL_DRAW_FRAMEBUFFER_EXT 0x8CA9 +#define GL_DRAW_FRAMEBUFFER_BINDING_EXT 0x8CA6 +#define GL_READ_FRAMEBUFFER_BINDING_EXT 0x8CAA +typedef void (APIENTRYP PFNGLBLITFRAMEBUFFEREXTPROC) (GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBlitFramebufferEXT (GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter); +#endif +#endif /* GL_EXT_framebuffer_blit */ + +#ifndef GL_EXT_framebuffer_multisample +#define GL_EXT_framebuffer_multisample 1 +#define GL_RENDERBUFFER_SAMPLES_EXT 0x8CAB +#define GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE_EXT 0x8D56 +#define GL_MAX_SAMPLES_EXT 0x8D57 +typedef void (APIENTRYP PFNGLRENDERBUFFERSTORAGEMULTISAMPLEEXTPROC) (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glRenderbufferStorageMultisampleEXT (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height); +#endif +#endif /* GL_EXT_framebuffer_multisample */ + +#ifndef GL_EXT_framebuffer_multisample_blit_scaled +#define GL_EXT_framebuffer_multisample_blit_scaled 1 +#define GL_SCALED_RESOLVE_FASTEST_EXT 0x90BA +#define GL_SCALED_RESOLVE_NICEST_EXT 0x90BB +#endif /* GL_EXT_framebuffer_multisample_blit_scaled */ + +#ifndef GL_EXT_framebuffer_object +#define GL_EXT_framebuffer_object 1 +#define GL_INVALID_FRAMEBUFFER_OPERATION_EXT 0x0506 +#define GL_MAX_RENDERBUFFER_SIZE_EXT 0x84E8 +#define GL_FRAMEBUFFER_BINDING_EXT 0x8CA6 +#define GL_RENDERBUFFER_BINDING_EXT 0x8CA7 +#define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE_EXT 0x8CD0 +#define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME_EXT 0x8CD1 +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL_EXT 0x8CD2 +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE_EXT 0x8CD3 +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_3D_ZOFFSET_EXT 0x8CD4 +#define GL_FRAMEBUFFER_COMPLETE_EXT 0x8CD5 +#define GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT 0x8CD6 +#define GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT 0x8CD7 +#define GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT 0x8CD9 +#define GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT 0x8CDA +#define GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT 0x8CDB +#define GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT 0x8CDC +#define GL_FRAMEBUFFER_UNSUPPORTED_EXT 0x8CDD +#define GL_MAX_COLOR_ATTACHMENTS_EXT 0x8CDF +#define GL_COLOR_ATTACHMENT0_EXT 0x8CE0 +#define GL_COLOR_ATTACHMENT1_EXT 0x8CE1 +#define GL_COLOR_ATTACHMENT2_EXT 0x8CE2 +#define GL_COLOR_ATTACHMENT3_EXT 0x8CE3 +#define GL_COLOR_ATTACHMENT4_EXT 0x8CE4 +#define GL_COLOR_ATTACHMENT5_EXT 0x8CE5 +#define GL_COLOR_ATTACHMENT6_EXT 0x8CE6 +#define GL_COLOR_ATTACHMENT7_EXT 0x8CE7 +#define GL_COLOR_ATTACHMENT8_EXT 0x8CE8 +#define GL_COLOR_ATTACHMENT9_EXT 0x8CE9 +#define GL_COLOR_ATTACHMENT10_EXT 0x8CEA +#define GL_COLOR_ATTACHMENT11_EXT 0x8CEB +#define GL_COLOR_ATTACHMENT12_EXT 0x8CEC +#define GL_COLOR_ATTACHMENT13_EXT 0x8CED +#define GL_COLOR_ATTACHMENT14_EXT 0x8CEE +#define GL_COLOR_ATTACHMENT15_EXT 0x8CEF +#define GL_DEPTH_ATTACHMENT_EXT 0x8D00 +#define GL_STENCIL_ATTACHMENT_EXT 0x8D20 +#define GL_FRAMEBUFFER_EXT 0x8D40 +#define GL_RENDERBUFFER_EXT 0x8D41 +#define GL_RENDERBUFFER_WIDTH_EXT 0x8D42 +#define GL_RENDERBUFFER_HEIGHT_EXT 0x8D43 +#define GL_RENDERBUFFER_INTERNAL_FORMAT_EXT 0x8D44 +#define GL_STENCIL_INDEX1_EXT 0x8D46 +#define GL_STENCIL_INDEX4_EXT 0x8D47 +#define GL_STENCIL_INDEX8_EXT 0x8D48 +#define GL_STENCIL_INDEX16_EXT 0x8D49 +#define GL_RENDERBUFFER_RED_SIZE_EXT 0x8D50 +#define GL_RENDERBUFFER_GREEN_SIZE_EXT 0x8D51 +#define GL_RENDERBUFFER_BLUE_SIZE_EXT 0x8D52 +#define GL_RENDERBUFFER_ALPHA_SIZE_EXT 0x8D53 +#define GL_RENDERBUFFER_DEPTH_SIZE_EXT 0x8D54 +#define GL_RENDERBUFFER_STENCIL_SIZE_EXT 0x8D55 +typedef GLboolean (APIENTRYP PFNGLISRENDERBUFFEREXTPROC) (GLuint renderbuffer); +typedef void (APIENTRYP PFNGLBINDRENDERBUFFEREXTPROC) (GLenum target, GLuint renderbuffer); +typedef void (APIENTRYP PFNGLDELETERENDERBUFFERSEXTPROC) (GLsizei n, const GLuint *renderbuffers); +typedef void (APIENTRYP PFNGLGENRENDERBUFFERSEXTPROC) (GLsizei n, GLuint *renderbuffers); +typedef void (APIENTRYP PFNGLRENDERBUFFERSTORAGEEXTPROC) (GLenum target, GLenum internalformat, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLGETRENDERBUFFERPARAMETERIVEXTPROC) (GLenum target, GLenum pname, GLint *params); +typedef GLboolean (APIENTRYP PFNGLISFRAMEBUFFEREXTPROC) (GLuint framebuffer); +typedef void (APIENTRYP PFNGLBINDFRAMEBUFFEREXTPROC) (GLenum target, GLuint framebuffer); +typedef void (APIENTRYP PFNGLDELETEFRAMEBUFFERSEXTPROC) (GLsizei n, const GLuint *framebuffers); +typedef void (APIENTRYP PFNGLGENFRAMEBUFFERSEXTPROC) (GLsizei n, GLuint *framebuffers); +typedef GLenum (APIENTRYP PFNGLCHECKFRAMEBUFFERSTATUSEXTPROC) (GLenum target); +typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURE1DEXTPROC) (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); +typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURE2DEXTPROC) (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); +typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURE3DEXTPROC) (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset); +typedef void (APIENTRYP PFNGLFRAMEBUFFERRENDERBUFFEREXTPROC) (GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); +typedef void (APIENTRYP PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVEXTPROC) (GLenum target, GLenum attachment, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGENERATEMIPMAPEXTPROC) (GLenum target); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI GLboolean APIENTRY glIsRenderbufferEXT (GLuint renderbuffer); +GLAPI void APIENTRY glBindRenderbufferEXT (GLenum target, GLuint renderbuffer); +GLAPI void APIENTRY glDeleteRenderbuffersEXT (GLsizei n, const GLuint *renderbuffers); +GLAPI void APIENTRY glGenRenderbuffersEXT (GLsizei n, GLuint *renderbuffers); +GLAPI void APIENTRY glRenderbufferStorageEXT (GLenum target, GLenum internalformat, GLsizei width, GLsizei height); +GLAPI void APIENTRY glGetRenderbufferParameterivEXT (GLenum target, GLenum pname, GLint *params); +GLAPI GLboolean APIENTRY glIsFramebufferEXT (GLuint framebuffer); +GLAPI void APIENTRY glBindFramebufferEXT (GLenum target, GLuint framebuffer); +GLAPI void APIENTRY glDeleteFramebuffersEXT (GLsizei n, const GLuint *framebuffers); +GLAPI void APIENTRY glGenFramebuffersEXT (GLsizei n, GLuint *framebuffers); +GLAPI GLenum APIENTRY glCheckFramebufferStatusEXT (GLenum target); +GLAPI void APIENTRY glFramebufferTexture1DEXT (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); +GLAPI void APIENTRY glFramebufferTexture2DEXT (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); +GLAPI void APIENTRY glFramebufferTexture3DEXT (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset); +GLAPI void APIENTRY glFramebufferRenderbufferEXT (GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); +GLAPI void APIENTRY glGetFramebufferAttachmentParameterivEXT (GLenum target, GLenum attachment, GLenum pname, GLint *params); +GLAPI void APIENTRY glGenerateMipmapEXT (GLenum target); +#endif +#endif /* GL_EXT_framebuffer_object */ + +#ifndef GL_EXT_framebuffer_sRGB +#define GL_EXT_framebuffer_sRGB 1 +#define GL_FRAMEBUFFER_SRGB_EXT 0x8DB9 +#define GL_FRAMEBUFFER_SRGB_CAPABLE_EXT 0x8DBA +#endif /* GL_EXT_framebuffer_sRGB */ + +#ifndef GL_EXT_geometry_shader4 +#define GL_EXT_geometry_shader4 1 +#define GL_GEOMETRY_SHADER_EXT 0x8DD9 +#define GL_GEOMETRY_VERTICES_OUT_EXT 0x8DDA +#define GL_GEOMETRY_INPUT_TYPE_EXT 0x8DDB +#define GL_GEOMETRY_OUTPUT_TYPE_EXT 0x8DDC +#define GL_MAX_GEOMETRY_TEXTURE_IMAGE_UNITS_EXT 0x8C29 +#define GL_MAX_GEOMETRY_VARYING_COMPONENTS_EXT 0x8DDD +#define GL_MAX_VERTEX_VARYING_COMPONENTS_EXT 0x8DDE +#define GL_MAX_VARYING_COMPONENTS_EXT 0x8B4B +#define GL_MAX_GEOMETRY_UNIFORM_COMPONENTS_EXT 0x8DDF +#define GL_MAX_GEOMETRY_OUTPUT_VERTICES_EXT 0x8DE0 +#define GL_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS_EXT 0x8DE1 +#define GL_LINES_ADJACENCY_EXT 0x000A +#define GL_LINE_STRIP_ADJACENCY_EXT 0x000B +#define GL_TRIANGLES_ADJACENCY_EXT 0x000C +#define GL_TRIANGLE_STRIP_ADJACENCY_EXT 0x000D +#define GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS_EXT 0x8DA8 +#define GL_FRAMEBUFFER_INCOMPLETE_LAYER_COUNT_EXT 0x8DA9 +#define GL_FRAMEBUFFER_ATTACHMENT_LAYERED_EXT 0x8DA7 +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER_EXT 0x8CD4 +#define GL_PROGRAM_POINT_SIZE_EXT 0x8642 +typedef void (APIENTRYP PFNGLPROGRAMPARAMETERIEXTPROC) (GLuint program, GLenum pname, GLint value); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glProgramParameteriEXT (GLuint program, GLenum pname, GLint value); +#endif +#endif /* GL_EXT_geometry_shader4 */ + +#ifndef GL_EXT_gpu_program_parameters +#define GL_EXT_gpu_program_parameters 1 +typedef void (APIENTRYP PFNGLPROGRAMENVPARAMETERS4FVEXTPROC) (GLenum target, GLuint index, GLsizei count, const GLfloat *params); +typedef void (APIENTRYP PFNGLPROGRAMLOCALPARAMETERS4FVEXTPROC) (GLenum target, GLuint index, GLsizei count, const GLfloat *params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glProgramEnvParameters4fvEXT (GLenum target, GLuint index, GLsizei count, const GLfloat *params); +GLAPI void APIENTRY glProgramLocalParameters4fvEXT (GLenum target, GLuint index, GLsizei count, const GLfloat *params); +#endif +#endif /* GL_EXT_gpu_program_parameters */ + +#ifndef GL_EXT_gpu_shader4 +#define GL_EXT_gpu_shader4 1 +#define GL_VERTEX_ATTRIB_ARRAY_INTEGER_EXT 0x88FD +#define GL_SAMPLER_1D_ARRAY_EXT 0x8DC0 +#define GL_SAMPLER_2D_ARRAY_EXT 0x8DC1 +#define GL_SAMPLER_BUFFER_EXT 0x8DC2 +#define GL_SAMPLER_1D_ARRAY_SHADOW_EXT 0x8DC3 +#define GL_SAMPLER_2D_ARRAY_SHADOW_EXT 0x8DC4 +#define GL_SAMPLER_CUBE_SHADOW_EXT 0x8DC5 +#define GL_UNSIGNED_INT_VEC2_EXT 0x8DC6 +#define GL_UNSIGNED_INT_VEC3_EXT 0x8DC7 +#define GL_UNSIGNED_INT_VEC4_EXT 0x8DC8 +#define GL_INT_SAMPLER_1D_EXT 0x8DC9 +#define GL_INT_SAMPLER_2D_EXT 0x8DCA +#define GL_INT_SAMPLER_3D_EXT 0x8DCB +#define GL_INT_SAMPLER_CUBE_EXT 0x8DCC +#define GL_INT_SAMPLER_2D_RECT_EXT 0x8DCD +#define GL_INT_SAMPLER_1D_ARRAY_EXT 0x8DCE +#define GL_INT_SAMPLER_2D_ARRAY_EXT 0x8DCF +#define GL_INT_SAMPLER_BUFFER_EXT 0x8DD0 +#define GL_UNSIGNED_INT_SAMPLER_1D_EXT 0x8DD1 +#define GL_UNSIGNED_INT_SAMPLER_2D_EXT 0x8DD2 +#define GL_UNSIGNED_INT_SAMPLER_3D_EXT 0x8DD3 +#define GL_UNSIGNED_INT_SAMPLER_CUBE_EXT 0x8DD4 +#define GL_UNSIGNED_INT_SAMPLER_2D_RECT_EXT 0x8DD5 +#define GL_UNSIGNED_INT_SAMPLER_1D_ARRAY_EXT 0x8DD6 +#define GL_UNSIGNED_INT_SAMPLER_2D_ARRAY_EXT 0x8DD7 +#define GL_UNSIGNED_INT_SAMPLER_BUFFER_EXT 0x8DD8 +#define GL_MIN_PROGRAM_TEXEL_OFFSET_EXT 0x8904 +#define GL_MAX_PROGRAM_TEXEL_OFFSET_EXT 0x8905 +typedef void (APIENTRYP PFNGLGETUNIFORMUIVEXTPROC) (GLuint program, GLint location, GLuint *params); +typedef void (APIENTRYP PFNGLBINDFRAGDATALOCATIONEXTPROC) (GLuint program, GLuint color, const GLchar *name); +typedef GLint (APIENTRYP PFNGLGETFRAGDATALOCATIONEXTPROC) (GLuint program, const GLchar *name); +typedef void (APIENTRYP PFNGLUNIFORM1UIEXTPROC) (GLint location, GLuint v0); +typedef void (APIENTRYP PFNGLUNIFORM2UIEXTPROC) (GLint location, GLuint v0, GLuint v1); +typedef void (APIENTRYP PFNGLUNIFORM3UIEXTPROC) (GLint location, GLuint v0, GLuint v1, GLuint v2); +typedef void (APIENTRYP PFNGLUNIFORM4UIEXTPROC) (GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3); +typedef void (APIENTRYP PFNGLUNIFORM1UIVEXTPROC) (GLint location, GLsizei count, const GLuint *value); +typedef void (APIENTRYP PFNGLUNIFORM2UIVEXTPROC) (GLint location, GLsizei count, const GLuint *value); +typedef void (APIENTRYP PFNGLUNIFORM3UIVEXTPROC) (GLint location, GLsizei count, const GLuint *value); +typedef void (APIENTRYP PFNGLUNIFORM4UIVEXTPROC) (GLint location, GLsizei count, const GLuint *value); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glGetUniformuivEXT (GLuint program, GLint location, GLuint *params); +GLAPI void APIENTRY glBindFragDataLocationEXT (GLuint program, GLuint color, const GLchar *name); +GLAPI GLint APIENTRY glGetFragDataLocationEXT (GLuint program, const GLchar *name); +GLAPI void APIENTRY glUniform1uiEXT (GLint location, GLuint v0); +GLAPI void APIENTRY glUniform2uiEXT (GLint location, GLuint v0, GLuint v1); +GLAPI void APIENTRY glUniform3uiEXT (GLint location, GLuint v0, GLuint v1, GLuint v2); +GLAPI void APIENTRY glUniform4uiEXT (GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3); +GLAPI void APIENTRY glUniform1uivEXT (GLint location, GLsizei count, const GLuint *value); +GLAPI void APIENTRY glUniform2uivEXT (GLint location, GLsizei count, const GLuint *value); +GLAPI void APIENTRY glUniform3uivEXT (GLint location, GLsizei count, const GLuint *value); +GLAPI void APIENTRY glUniform4uivEXT (GLint location, GLsizei count, const GLuint *value); +#endif +#endif /* GL_EXT_gpu_shader4 */ + +#ifndef GL_EXT_histogram +#define GL_EXT_histogram 1 +#define GL_HISTOGRAM_EXT 0x8024 +#define GL_PROXY_HISTOGRAM_EXT 0x8025 +#define GL_HISTOGRAM_WIDTH_EXT 0x8026 +#define GL_HISTOGRAM_FORMAT_EXT 0x8027 +#define GL_HISTOGRAM_RED_SIZE_EXT 0x8028 +#define GL_HISTOGRAM_GREEN_SIZE_EXT 0x8029 +#define GL_HISTOGRAM_BLUE_SIZE_EXT 0x802A +#define GL_HISTOGRAM_ALPHA_SIZE_EXT 0x802B +#define GL_HISTOGRAM_LUMINANCE_SIZE_EXT 0x802C +#define GL_HISTOGRAM_SINK_EXT 0x802D +#define GL_MINMAX_EXT 0x802E +#define GL_MINMAX_FORMAT_EXT 0x802F +#define GL_MINMAX_SINK_EXT 0x8030 +#define GL_TABLE_TOO_LARGE_EXT 0x8031 +typedef void (APIENTRYP PFNGLGETHISTOGRAMEXTPROC) (GLenum target, GLboolean reset, GLenum format, GLenum type, void *values); +typedef void (APIENTRYP PFNGLGETHISTOGRAMPARAMETERFVEXTPROC) (GLenum target, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETHISTOGRAMPARAMETERIVEXTPROC) (GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETMINMAXEXTPROC) (GLenum target, GLboolean reset, GLenum format, GLenum type, void *values); +typedef void (APIENTRYP PFNGLGETMINMAXPARAMETERFVEXTPROC) (GLenum target, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETMINMAXPARAMETERIVEXTPROC) (GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLHISTOGRAMEXTPROC) (GLenum target, GLsizei width, GLenum internalformat, GLboolean sink); +typedef void (APIENTRYP PFNGLMINMAXEXTPROC) (GLenum target, GLenum internalformat, GLboolean sink); +typedef void (APIENTRYP PFNGLRESETHISTOGRAMEXTPROC) (GLenum target); +typedef void (APIENTRYP PFNGLRESETMINMAXEXTPROC) (GLenum target); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glGetHistogramEXT (GLenum target, GLboolean reset, GLenum format, GLenum type, void *values); +GLAPI void APIENTRY glGetHistogramParameterfvEXT (GLenum target, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetHistogramParameterivEXT (GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetMinmaxEXT (GLenum target, GLboolean reset, GLenum format, GLenum type, void *values); +GLAPI void APIENTRY glGetMinmaxParameterfvEXT (GLenum target, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetMinmaxParameterivEXT (GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glHistogramEXT (GLenum target, GLsizei width, GLenum internalformat, GLboolean sink); +GLAPI void APIENTRY glMinmaxEXT (GLenum target, GLenum internalformat, GLboolean sink); +GLAPI void APIENTRY glResetHistogramEXT (GLenum target); +GLAPI void APIENTRY glResetMinmaxEXT (GLenum target); +#endif +#endif /* GL_EXT_histogram */ + +#ifndef GL_EXT_index_array_formats +#define GL_EXT_index_array_formats 1 +#define GL_IUI_V2F_EXT 0x81AD +#define GL_IUI_V3F_EXT 0x81AE +#define GL_IUI_N3F_V2F_EXT 0x81AF +#define GL_IUI_N3F_V3F_EXT 0x81B0 +#define GL_T2F_IUI_V2F_EXT 0x81B1 +#define GL_T2F_IUI_V3F_EXT 0x81B2 +#define GL_T2F_IUI_N3F_V2F_EXT 0x81B3 +#define GL_T2F_IUI_N3F_V3F_EXT 0x81B4 +#endif /* GL_EXT_index_array_formats */ + +#ifndef GL_EXT_index_func +#define GL_EXT_index_func 1 +#define GL_INDEX_TEST_EXT 0x81B5 +#define GL_INDEX_TEST_FUNC_EXT 0x81B6 +#define GL_INDEX_TEST_REF_EXT 0x81B7 +typedef void (APIENTRYP PFNGLINDEXFUNCEXTPROC) (GLenum func, GLclampf ref); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glIndexFuncEXT (GLenum func, GLclampf ref); +#endif +#endif /* GL_EXT_index_func */ + +#ifndef GL_EXT_index_material +#define GL_EXT_index_material 1 +#define GL_INDEX_MATERIAL_EXT 0x81B8 +#define GL_INDEX_MATERIAL_PARAMETER_EXT 0x81B9 +#define GL_INDEX_MATERIAL_FACE_EXT 0x81BA +typedef void (APIENTRYP PFNGLINDEXMATERIALEXTPROC) (GLenum face, GLenum mode); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glIndexMaterialEXT (GLenum face, GLenum mode); +#endif +#endif /* GL_EXT_index_material */ + +#ifndef GL_EXT_index_texture +#define GL_EXT_index_texture 1 +#endif /* GL_EXT_index_texture */ + +#ifndef GL_EXT_light_texture +#define GL_EXT_light_texture 1 +#define GL_FRAGMENT_MATERIAL_EXT 0x8349 +#define GL_FRAGMENT_NORMAL_EXT 0x834A +#define GL_FRAGMENT_COLOR_EXT 0x834C +#define GL_ATTENUATION_EXT 0x834D +#define GL_SHADOW_ATTENUATION_EXT 0x834E +#define GL_TEXTURE_APPLICATION_MODE_EXT 0x834F +#define GL_TEXTURE_LIGHT_EXT 0x8350 +#define GL_TEXTURE_MATERIAL_FACE_EXT 0x8351 +#define GL_TEXTURE_MATERIAL_PARAMETER_EXT 0x8352 +typedef void (APIENTRYP PFNGLAPPLYTEXTUREEXTPROC) (GLenum mode); +typedef void (APIENTRYP PFNGLTEXTURELIGHTEXTPROC) (GLenum pname); +typedef void (APIENTRYP PFNGLTEXTUREMATERIALEXTPROC) (GLenum face, GLenum mode); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glApplyTextureEXT (GLenum mode); +GLAPI void APIENTRY glTextureLightEXT (GLenum pname); +GLAPI void APIENTRY glTextureMaterialEXT (GLenum face, GLenum mode); +#endif +#endif /* GL_EXT_light_texture */ + +#ifndef GL_EXT_misc_attribute +#define GL_EXT_misc_attribute 1 +#endif /* GL_EXT_misc_attribute */ + +#ifndef GL_EXT_multi_draw_arrays +#define GL_EXT_multi_draw_arrays 1 +typedef void (APIENTRYP PFNGLMULTIDRAWARRAYSEXTPROC) (GLenum mode, const GLint *first, const GLsizei *count, GLsizei primcount); +typedef void (APIENTRYP PFNGLMULTIDRAWELEMENTSEXTPROC) (GLenum mode, const GLsizei *count, GLenum type, const void *const*indices, GLsizei primcount); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glMultiDrawArraysEXT (GLenum mode, const GLint *first, const GLsizei *count, GLsizei primcount); +GLAPI void APIENTRY glMultiDrawElementsEXT (GLenum mode, const GLsizei *count, GLenum type, const void *const*indices, GLsizei primcount); +#endif +#endif /* GL_EXT_multi_draw_arrays */ + +#ifndef GL_EXT_multisample +#define GL_EXT_multisample 1 +#define GL_MULTISAMPLE_EXT 0x809D +#define GL_SAMPLE_ALPHA_TO_MASK_EXT 0x809E +#define GL_SAMPLE_ALPHA_TO_ONE_EXT 0x809F +#define GL_SAMPLE_MASK_EXT 0x80A0 +#define GL_1PASS_EXT 0x80A1 +#define GL_2PASS_0_EXT 0x80A2 +#define GL_2PASS_1_EXT 0x80A3 +#define GL_4PASS_0_EXT 0x80A4 +#define GL_4PASS_1_EXT 0x80A5 +#define GL_4PASS_2_EXT 0x80A6 +#define GL_4PASS_3_EXT 0x80A7 +#define GL_SAMPLE_BUFFERS_EXT 0x80A8 +#define GL_SAMPLES_EXT 0x80A9 +#define GL_SAMPLE_MASK_VALUE_EXT 0x80AA +#define GL_SAMPLE_MASK_INVERT_EXT 0x80AB +#define GL_SAMPLE_PATTERN_EXT 0x80AC +#define GL_MULTISAMPLE_BIT_EXT 0x20000000 +typedef void (APIENTRYP PFNGLSAMPLEMASKEXTPROC) (GLclampf value, GLboolean invert); +typedef void (APIENTRYP PFNGLSAMPLEPATTERNEXTPROC) (GLenum pattern); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glSampleMaskEXT (GLclampf value, GLboolean invert); +GLAPI void APIENTRY glSamplePatternEXT (GLenum pattern); +#endif +#endif /* GL_EXT_multisample */ + +#ifndef GL_EXT_packed_depth_stencil +#define GL_EXT_packed_depth_stencil 1 +#define GL_DEPTH_STENCIL_EXT 0x84F9 +#define GL_UNSIGNED_INT_24_8_EXT 0x84FA +#define GL_DEPTH24_STENCIL8_EXT 0x88F0 +#define GL_TEXTURE_STENCIL_SIZE_EXT 0x88F1 +#endif /* GL_EXT_packed_depth_stencil */ + +#ifndef GL_EXT_packed_float +#define GL_EXT_packed_float 1 +#define GL_R11F_G11F_B10F_EXT 0x8C3A +#define GL_UNSIGNED_INT_10F_11F_11F_REV_EXT 0x8C3B +#define GL_RGBA_SIGNED_COMPONENTS_EXT 0x8C3C +#endif /* GL_EXT_packed_float */ + +#ifndef GL_EXT_packed_pixels +#define GL_EXT_packed_pixels 1 +#define GL_UNSIGNED_BYTE_3_3_2_EXT 0x8032 +#define GL_UNSIGNED_SHORT_4_4_4_4_EXT 0x8033 +#define GL_UNSIGNED_SHORT_5_5_5_1_EXT 0x8034 +#define GL_UNSIGNED_INT_8_8_8_8_EXT 0x8035 +#define GL_UNSIGNED_INT_10_10_10_2_EXT 0x8036 +#endif /* GL_EXT_packed_pixels */ + +#ifndef GL_EXT_paletted_texture +#define GL_EXT_paletted_texture 1 +#define GL_COLOR_INDEX1_EXT 0x80E2 +#define GL_COLOR_INDEX2_EXT 0x80E3 +#define GL_COLOR_INDEX4_EXT 0x80E4 +#define GL_COLOR_INDEX8_EXT 0x80E5 +#define GL_COLOR_INDEX12_EXT 0x80E6 +#define GL_COLOR_INDEX16_EXT 0x80E7 +#define GL_TEXTURE_INDEX_SIZE_EXT 0x80ED +typedef void (APIENTRYP PFNGLCOLORTABLEEXTPROC) (GLenum target, GLenum internalFormat, GLsizei width, GLenum format, GLenum type, const void *table); +typedef void (APIENTRYP PFNGLGETCOLORTABLEEXTPROC) (GLenum target, GLenum format, GLenum type, void *data); +typedef void (APIENTRYP PFNGLGETCOLORTABLEPARAMETERIVEXTPROC) (GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETCOLORTABLEPARAMETERFVEXTPROC) (GLenum target, GLenum pname, GLfloat *params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glColorTableEXT (GLenum target, GLenum internalFormat, GLsizei width, GLenum format, GLenum type, const void *table); +GLAPI void APIENTRY glGetColorTableEXT (GLenum target, GLenum format, GLenum type, void *data); +GLAPI void APIENTRY glGetColorTableParameterivEXT (GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetColorTableParameterfvEXT (GLenum target, GLenum pname, GLfloat *params); +#endif +#endif /* GL_EXT_paletted_texture */ + +#ifndef GL_EXT_pixel_buffer_object +#define GL_EXT_pixel_buffer_object 1 +#define GL_PIXEL_PACK_BUFFER_EXT 0x88EB +#define GL_PIXEL_UNPACK_BUFFER_EXT 0x88EC +#define GL_PIXEL_PACK_BUFFER_BINDING_EXT 0x88ED +#define GL_PIXEL_UNPACK_BUFFER_BINDING_EXT 0x88EF +#endif /* GL_EXT_pixel_buffer_object */ + +#ifndef GL_EXT_pixel_transform +#define GL_EXT_pixel_transform 1 +#define GL_PIXEL_TRANSFORM_2D_EXT 0x8330 +#define GL_PIXEL_MAG_FILTER_EXT 0x8331 +#define GL_PIXEL_MIN_FILTER_EXT 0x8332 +#define GL_PIXEL_CUBIC_WEIGHT_EXT 0x8333 +#define GL_CUBIC_EXT 0x8334 +#define GL_AVERAGE_EXT 0x8335 +#define GL_PIXEL_TRANSFORM_2D_STACK_DEPTH_EXT 0x8336 +#define GL_MAX_PIXEL_TRANSFORM_2D_STACK_DEPTH_EXT 0x8337 +#define GL_PIXEL_TRANSFORM_2D_MATRIX_EXT 0x8338 +typedef void (APIENTRYP PFNGLPIXELTRANSFORMPARAMETERIEXTPROC) (GLenum target, GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLPIXELTRANSFORMPARAMETERFEXTPROC) (GLenum target, GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLPIXELTRANSFORMPARAMETERIVEXTPROC) (GLenum target, GLenum pname, const GLint *params); +typedef void (APIENTRYP PFNGLPIXELTRANSFORMPARAMETERFVEXTPROC) (GLenum target, GLenum pname, const GLfloat *params); +typedef void (APIENTRYP PFNGLGETPIXELTRANSFORMPARAMETERIVEXTPROC) (GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETPIXELTRANSFORMPARAMETERFVEXTPROC) (GLenum target, GLenum pname, GLfloat *params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glPixelTransformParameteriEXT (GLenum target, GLenum pname, GLint param); +GLAPI void APIENTRY glPixelTransformParameterfEXT (GLenum target, GLenum pname, GLfloat param); +GLAPI void APIENTRY glPixelTransformParameterivEXT (GLenum target, GLenum pname, const GLint *params); +GLAPI void APIENTRY glPixelTransformParameterfvEXT (GLenum target, GLenum pname, const GLfloat *params); +GLAPI void APIENTRY glGetPixelTransformParameterivEXT (GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetPixelTransformParameterfvEXT (GLenum target, GLenum pname, GLfloat *params); +#endif +#endif /* GL_EXT_pixel_transform */ + +#ifndef GL_EXT_pixel_transform_color_table +#define GL_EXT_pixel_transform_color_table 1 +#endif /* GL_EXT_pixel_transform_color_table */ + +#ifndef GL_EXT_point_parameters +#define GL_EXT_point_parameters 1 +#define GL_POINT_SIZE_MIN_EXT 0x8126 +#define GL_POINT_SIZE_MAX_EXT 0x8127 +#define GL_POINT_FADE_THRESHOLD_SIZE_EXT 0x8128 +#define GL_DISTANCE_ATTENUATION_EXT 0x8129 +typedef void (APIENTRYP PFNGLPOINTPARAMETERFEXTPROC) (GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLPOINTPARAMETERFVEXTPROC) (GLenum pname, const GLfloat *params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glPointParameterfEXT (GLenum pname, GLfloat param); +GLAPI void APIENTRY glPointParameterfvEXT (GLenum pname, const GLfloat *params); +#endif +#endif /* GL_EXT_point_parameters */ + +#ifndef GL_EXT_polygon_offset +#define GL_EXT_polygon_offset 1 +#define GL_POLYGON_OFFSET_EXT 0x8037 +#define GL_POLYGON_OFFSET_FACTOR_EXT 0x8038 +#define GL_POLYGON_OFFSET_BIAS_EXT 0x8039 +typedef void (APIENTRYP PFNGLPOLYGONOFFSETEXTPROC) (GLfloat factor, GLfloat bias); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glPolygonOffsetEXT (GLfloat factor, GLfloat bias); +#endif +#endif /* GL_EXT_polygon_offset */ + +#ifndef GL_EXT_polygon_offset_clamp +#define GL_EXT_polygon_offset_clamp 1 +#define GL_POLYGON_OFFSET_CLAMP_EXT 0x8E1B +typedef void (APIENTRYP PFNGLPOLYGONOFFSETCLAMPEXTPROC) (GLfloat factor, GLfloat units, GLfloat clamp); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glPolygonOffsetClampEXT (GLfloat factor, GLfloat units, GLfloat clamp); +#endif +#endif /* GL_EXT_polygon_offset_clamp */ + +#ifndef GL_EXT_post_depth_coverage +#define GL_EXT_post_depth_coverage 1 +#endif /* GL_EXT_post_depth_coverage */ + +#ifndef GL_EXT_provoking_vertex +#define GL_EXT_provoking_vertex 1 +#define GL_QUADS_FOLLOW_PROVOKING_VERTEX_CONVENTION_EXT 0x8E4C +#define GL_FIRST_VERTEX_CONVENTION_EXT 0x8E4D +#define GL_LAST_VERTEX_CONVENTION_EXT 0x8E4E +#define GL_PROVOKING_VERTEX_EXT 0x8E4F +typedef void (APIENTRYP PFNGLPROVOKINGVERTEXEXTPROC) (GLenum mode); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glProvokingVertexEXT (GLenum mode); +#endif +#endif /* GL_EXT_provoking_vertex */ + +#ifndef GL_EXT_raster_multisample +#define GL_EXT_raster_multisample 1 +#define GL_RASTER_MULTISAMPLE_EXT 0x9327 +#define GL_RASTER_SAMPLES_EXT 0x9328 +#define GL_MAX_RASTER_SAMPLES_EXT 0x9329 +#define GL_RASTER_FIXED_SAMPLE_LOCATIONS_EXT 0x932A +#define GL_MULTISAMPLE_RASTERIZATION_ALLOWED_EXT 0x932B +#define GL_EFFECTIVE_RASTER_SAMPLES_EXT 0x932C +typedef void (APIENTRYP PFNGLRASTERSAMPLESEXTPROC) (GLuint samples, GLboolean fixedsamplelocations); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glRasterSamplesEXT (GLuint samples, GLboolean fixedsamplelocations); +#endif +#endif /* GL_EXT_raster_multisample */ + +#ifndef GL_EXT_rescale_normal +#define GL_EXT_rescale_normal 1 +#define GL_RESCALE_NORMAL_EXT 0x803A +#endif /* GL_EXT_rescale_normal */ + +#ifndef GL_EXT_secondary_color +#define GL_EXT_secondary_color 1 +#define GL_COLOR_SUM_EXT 0x8458 +#define GL_CURRENT_SECONDARY_COLOR_EXT 0x8459 +#define GL_SECONDARY_COLOR_ARRAY_SIZE_EXT 0x845A +#define GL_SECONDARY_COLOR_ARRAY_TYPE_EXT 0x845B +#define GL_SECONDARY_COLOR_ARRAY_STRIDE_EXT 0x845C +#define GL_SECONDARY_COLOR_ARRAY_POINTER_EXT 0x845D +#define GL_SECONDARY_COLOR_ARRAY_EXT 0x845E +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3BEXTPROC) (GLbyte red, GLbyte green, GLbyte blue); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3BVEXTPROC) (const GLbyte *v); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3DEXTPROC) (GLdouble red, GLdouble green, GLdouble blue); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3DVEXTPROC) (const GLdouble *v); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3FEXTPROC) (GLfloat red, GLfloat green, GLfloat blue); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3FVEXTPROC) (const GLfloat *v); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3IEXTPROC) (GLint red, GLint green, GLint blue); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3IVEXTPROC) (const GLint *v); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3SEXTPROC) (GLshort red, GLshort green, GLshort blue); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3SVEXTPROC) (const GLshort *v); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3UBEXTPROC) (GLubyte red, GLubyte green, GLubyte blue); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3UBVEXTPROC) (const GLubyte *v); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3UIEXTPROC) (GLuint red, GLuint green, GLuint blue); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3UIVEXTPROC) (const GLuint *v); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3USEXTPROC) (GLushort red, GLushort green, GLushort blue); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3USVEXTPROC) (const GLushort *v); +typedef void (APIENTRYP PFNGLSECONDARYCOLORPOINTEREXTPROC) (GLint size, GLenum type, GLsizei stride, const void *pointer); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glSecondaryColor3bEXT (GLbyte red, GLbyte green, GLbyte blue); +GLAPI void APIENTRY glSecondaryColor3bvEXT (const GLbyte *v); +GLAPI void APIENTRY glSecondaryColor3dEXT (GLdouble red, GLdouble green, GLdouble blue); +GLAPI void APIENTRY glSecondaryColor3dvEXT (const GLdouble *v); +GLAPI void APIENTRY glSecondaryColor3fEXT (GLfloat red, GLfloat green, GLfloat blue); +GLAPI void APIENTRY glSecondaryColor3fvEXT (const GLfloat *v); +GLAPI void APIENTRY glSecondaryColor3iEXT (GLint red, GLint green, GLint blue); +GLAPI void APIENTRY glSecondaryColor3ivEXT (const GLint *v); +GLAPI void APIENTRY glSecondaryColor3sEXT (GLshort red, GLshort green, GLshort blue); +GLAPI void APIENTRY glSecondaryColor3svEXT (const GLshort *v); +GLAPI void APIENTRY glSecondaryColor3ubEXT (GLubyte red, GLubyte green, GLubyte blue); +GLAPI void APIENTRY glSecondaryColor3ubvEXT (const GLubyte *v); +GLAPI void APIENTRY glSecondaryColor3uiEXT (GLuint red, GLuint green, GLuint blue); +GLAPI void APIENTRY glSecondaryColor3uivEXT (const GLuint *v); +GLAPI void APIENTRY glSecondaryColor3usEXT (GLushort red, GLushort green, GLushort blue); +GLAPI void APIENTRY glSecondaryColor3usvEXT (const GLushort *v); +GLAPI void APIENTRY glSecondaryColorPointerEXT (GLint size, GLenum type, GLsizei stride, const void *pointer); +#endif +#endif /* GL_EXT_secondary_color */ + +#ifndef GL_EXT_separate_shader_objects +#define GL_EXT_separate_shader_objects 1 +#define GL_ACTIVE_PROGRAM_EXT 0x8B8D +typedef void (APIENTRYP PFNGLUSESHADERPROGRAMEXTPROC) (GLenum type, GLuint program); +typedef void (APIENTRYP PFNGLACTIVEPROGRAMEXTPROC) (GLuint program); +typedef GLuint (APIENTRYP PFNGLCREATESHADERPROGRAMEXTPROC) (GLenum type, const GLchar *string); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glUseShaderProgramEXT (GLenum type, GLuint program); +GLAPI void APIENTRY glActiveProgramEXT (GLuint program); +GLAPI GLuint APIENTRY glCreateShaderProgramEXT (GLenum type, const GLchar *string); +#endif +#endif /* GL_EXT_separate_shader_objects */ + +#ifndef GL_EXT_separate_specular_color +#define GL_EXT_separate_specular_color 1 +#define GL_LIGHT_MODEL_COLOR_CONTROL_EXT 0x81F8 +#define GL_SINGLE_COLOR_EXT 0x81F9 +#define GL_SEPARATE_SPECULAR_COLOR_EXT 0x81FA +#endif /* GL_EXT_separate_specular_color */ + +#ifndef GL_EXT_shader_image_load_formatted +#define GL_EXT_shader_image_load_formatted 1 +#endif /* GL_EXT_shader_image_load_formatted */ + +#ifndef GL_EXT_shader_image_load_store +#define GL_EXT_shader_image_load_store 1 +#define GL_MAX_IMAGE_UNITS_EXT 0x8F38 +#define GL_MAX_COMBINED_IMAGE_UNITS_AND_FRAGMENT_OUTPUTS_EXT 0x8F39 +#define GL_IMAGE_BINDING_NAME_EXT 0x8F3A +#define GL_IMAGE_BINDING_LEVEL_EXT 0x8F3B +#define GL_IMAGE_BINDING_LAYERED_EXT 0x8F3C +#define GL_IMAGE_BINDING_LAYER_EXT 0x8F3D +#define GL_IMAGE_BINDING_ACCESS_EXT 0x8F3E +#define GL_IMAGE_1D_EXT 0x904C +#define GL_IMAGE_2D_EXT 0x904D +#define GL_IMAGE_3D_EXT 0x904E +#define GL_IMAGE_2D_RECT_EXT 0x904F +#define GL_IMAGE_CUBE_EXT 0x9050 +#define GL_IMAGE_BUFFER_EXT 0x9051 +#define GL_IMAGE_1D_ARRAY_EXT 0x9052 +#define GL_IMAGE_2D_ARRAY_EXT 0x9053 +#define GL_IMAGE_CUBE_MAP_ARRAY_EXT 0x9054 +#define GL_IMAGE_2D_MULTISAMPLE_EXT 0x9055 +#define GL_IMAGE_2D_MULTISAMPLE_ARRAY_EXT 0x9056 +#define GL_INT_IMAGE_1D_EXT 0x9057 +#define GL_INT_IMAGE_2D_EXT 0x9058 +#define GL_INT_IMAGE_3D_EXT 0x9059 +#define GL_INT_IMAGE_2D_RECT_EXT 0x905A +#define GL_INT_IMAGE_CUBE_EXT 0x905B +#define GL_INT_IMAGE_BUFFER_EXT 0x905C +#define GL_INT_IMAGE_1D_ARRAY_EXT 0x905D +#define GL_INT_IMAGE_2D_ARRAY_EXT 0x905E +#define GL_INT_IMAGE_CUBE_MAP_ARRAY_EXT 0x905F +#define GL_INT_IMAGE_2D_MULTISAMPLE_EXT 0x9060 +#define GL_INT_IMAGE_2D_MULTISAMPLE_ARRAY_EXT 0x9061 +#define GL_UNSIGNED_INT_IMAGE_1D_EXT 0x9062 +#define GL_UNSIGNED_INT_IMAGE_2D_EXT 0x9063 +#define GL_UNSIGNED_INT_IMAGE_3D_EXT 0x9064 +#define GL_UNSIGNED_INT_IMAGE_2D_RECT_EXT 0x9065 +#define GL_UNSIGNED_INT_IMAGE_CUBE_EXT 0x9066 +#define GL_UNSIGNED_INT_IMAGE_BUFFER_EXT 0x9067 +#define GL_UNSIGNED_INT_IMAGE_1D_ARRAY_EXT 0x9068 +#define GL_UNSIGNED_INT_IMAGE_2D_ARRAY_EXT 0x9069 +#define GL_UNSIGNED_INT_IMAGE_CUBE_MAP_ARRAY_EXT 0x906A +#define GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE_EXT 0x906B +#define GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE_ARRAY_EXT 0x906C +#define GL_MAX_IMAGE_SAMPLES_EXT 0x906D +#define GL_IMAGE_BINDING_FORMAT_EXT 0x906E +#define GL_VERTEX_ATTRIB_ARRAY_BARRIER_BIT_EXT 0x00000001 +#define GL_ELEMENT_ARRAY_BARRIER_BIT_EXT 0x00000002 +#define GL_UNIFORM_BARRIER_BIT_EXT 0x00000004 +#define GL_TEXTURE_FETCH_BARRIER_BIT_EXT 0x00000008 +#define GL_SHADER_IMAGE_ACCESS_BARRIER_BIT_EXT 0x00000020 +#define GL_COMMAND_BARRIER_BIT_EXT 0x00000040 +#define GL_PIXEL_BUFFER_BARRIER_BIT_EXT 0x00000080 +#define GL_TEXTURE_UPDATE_BARRIER_BIT_EXT 0x00000100 +#define GL_BUFFER_UPDATE_BARRIER_BIT_EXT 0x00000200 +#define GL_FRAMEBUFFER_BARRIER_BIT_EXT 0x00000400 +#define GL_TRANSFORM_FEEDBACK_BARRIER_BIT_EXT 0x00000800 +#define GL_ATOMIC_COUNTER_BARRIER_BIT_EXT 0x00001000 +#define GL_ALL_BARRIER_BITS_EXT 0xFFFFFFFF +typedef void (APIENTRYP PFNGLBINDIMAGETEXTUREEXTPROC) (GLuint index, GLuint texture, GLint level, GLboolean layered, GLint layer, GLenum access, GLint format); +typedef void (APIENTRYP PFNGLMEMORYBARRIEREXTPROC) (GLbitfield barriers); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBindImageTextureEXT (GLuint index, GLuint texture, GLint level, GLboolean layered, GLint layer, GLenum access, GLint format); +GLAPI void APIENTRY glMemoryBarrierEXT (GLbitfield barriers); +#endif +#endif /* GL_EXT_shader_image_load_store */ + +#ifndef GL_EXT_shader_integer_mix +#define GL_EXT_shader_integer_mix 1 +#endif /* GL_EXT_shader_integer_mix */ + +#ifndef GL_EXT_shadow_funcs +#define GL_EXT_shadow_funcs 1 +#endif /* GL_EXT_shadow_funcs */ + +#ifndef GL_EXT_shared_texture_palette +#define GL_EXT_shared_texture_palette 1 +#define GL_SHARED_TEXTURE_PALETTE_EXT 0x81FB +#endif /* GL_EXT_shared_texture_palette */ + +#ifndef GL_EXT_sparse_texture2 +#define GL_EXT_sparse_texture2 1 +#endif /* GL_EXT_sparse_texture2 */ + +#ifndef GL_EXT_stencil_clear_tag +#define GL_EXT_stencil_clear_tag 1 +#define GL_STENCIL_TAG_BITS_EXT 0x88F2 +#define GL_STENCIL_CLEAR_TAG_VALUE_EXT 0x88F3 +typedef void (APIENTRYP PFNGLSTENCILCLEARTAGEXTPROC) (GLsizei stencilTagBits, GLuint stencilClearTag); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glStencilClearTagEXT (GLsizei stencilTagBits, GLuint stencilClearTag); +#endif +#endif /* GL_EXT_stencil_clear_tag */ + +#ifndef GL_EXT_stencil_two_side +#define GL_EXT_stencil_two_side 1 +#define GL_STENCIL_TEST_TWO_SIDE_EXT 0x8910 +#define GL_ACTIVE_STENCIL_FACE_EXT 0x8911 +typedef void (APIENTRYP PFNGLACTIVESTENCILFACEEXTPROC) (GLenum face); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glActiveStencilFaceEXT (GLenum face); +#endif +#endif /* GL_EXT_stencil_two_side */ + +#ifndef GL_EXT_stencil_wrap +#define GL_EXT_stencil_wrap 1 +#define GL_INCR_WRAP_EXT 0x8507 +#define GL_DECR_WRAP_EXT 0x8508 +#endif /* GL_EXT_stencil_wrap */ + +#ifndef GL_EXT_subtexture +#define GL_EXT_subtexture 1 +typedef void (APIENTRYP PFNGLTEXSUBIMAGE1DEXTPROC) (GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const void *pixels); +typedef void (APIENTRYP PFNGLTEXSUBIMAGE2DEXTPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glTexSubImage1DEXT (GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const void *pixels); +GLAPI void APIENTRY glTexSubImage2DEXT (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels); +#endif +#endif /* GL_EXT_subtexture */ + +#ifndef GL_EXT_texture +#define GL_EXT_texture 1 +#define GL_ALPHA4_EXT 0x803B +#define GL_ALPHA8_EXT 0x803C +#define GL_ALPHA12_EXT 0x803D +#define GL_ALPHA16_EXT 0x803E +#define GL_LUMINANCE4_EXT 0x803F +#define GL_LUMINANCE8_EXT 0x8040 +#define GL_LUMINANCE12_EXT 0x8041 +#define GL_LUMINANCE16_EXT 0x8042 +#define GL_LUMINANCE4_ALPHA4_EXT 0x8043 +#define GL_LUMINANCE6_ALPHA2_EXT 0x8044 +#define GL_LUMINANCE8_ALPHA8_EXT 0x8045 +#define GL_LUMINANCE12_ALPHA4_EXT 0x8046 +#define GL_LUMINANCE12_ALPHA12_EXT 0x8047 +#define GL_LUMINANCE16_ALPHA16_EXT 0x8048 +#define GL_INTENSITY_EXT 0x8049 +#define GL_INTENSITY4_EXT 0x804A +#define GL_INTENSITY8_EXT 0x804B +#define GL_INTENSITY12_EXT 0x804C +#define GL_INTENSITY16_EXT 0x804D +#define GL_RGB2_EXT 0x804E +#define GL_RGB4_EXT 0x804F +#define GL_RGB5_EXT 0x8050 +#define GL_RGB8_EXT 0x8051 +#define GL_RGB10_EXT 0x8052 +#define GL_RGB12_EXT 0x8053 +#define GL_RGB16_EXT 0x8054 +#define GL_RGBA2_EXT 0x8055 +#define GL_RGBA4_EXT 0x8056 +#define GL_RGB5_A1_EXT 0x8057 +#define GL_RGBA8_EXT 0x8058 +#define GL_RGB10_A2_EXT 0x8059 +#define GL_RGBA12_EXT 0x805A +#define GL_RGBA16_EXT 0x805B +#define GL_TEXTURE_RED_SIZE_EXT 0x805C +#define GL_TEXTURE_GREEN_SIZE_EXT 0x805D +#define GL_TEXTURE_BLUE_SIZE_EXT 0x805E +#define GL_TEXTURE_ALPHA_SIZE_EXT 0x805F +#define GL_TEXTURE_LUMINANCE_SIZE_EXT 0x8060 +#define GL_TEXTURE_INTENSITY_SIZE_EXT 0x8061 +#define GL_REPLACE_EXT 0x8062 +#define GL_PROXY_TEXTURE_1D_EXT 0x8063 +#define GL_PROXY_TEXTURE_2D_EXT 0x8064 +#define GL_TEXTURE_TOO_LARGE_EXT 0x8065 +#endif /* GL_EXT_texture */ + +#ifndef GL_EXT_texture3D +#define GL_EXT_texture3D 1 +#define GL_PACK_SKIP_IMAGES_EXT 0x806B +#define GL_PACK_IMAGE_HEIGHT_EXT 0x806C +#define GL_UNPACK_SKIP_IMAGES_EXT 0x806D +#define GL_UNPACK_IMAGE_HEIGHT_EXT 0x806E +#define GL_TEXTURE_3D_EXT 0x806F +#define GL_PROXY_TEXTURE_3D_EXT 0x8070 +#define GL_TEXTURE_DEPTH_EXT 0x8071 +#define GL_TEXTURE_WRAP_R_EXT 0x8072 +#define GL_MAX_3D_TEXTURE_SIZE_EXT 0x8073 +typedef void (APIENTRYP PFNGLTEXIMAGE3DEXTPROC) (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void *pixels); +typedef void (APIENTRYP PFNGLTEXSUBIMAGE3DEXTPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *pixels); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glTexImage3DEXT (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void *pixels); +GLAPI void APIENTRY glTexSubImage3DEXT (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *pixels); +#endif +#endif /* GL_EXT_texture3D */ + +#ifndef GL_EXT_texture_array +#define GL_EXT_texture_array 1 +#define GL_TEXTURE_1D_ARRAY_EXT 0x8C18 +#define GL_PROXY_TEXTURE_1D_ARRAY_EXT 0x8C19 +#define GL_TEXTURE_2D_ARRAY_EXT 0x8C1A +#define GL_PROXY_TEXTURE_2D_ARRAY_EXT 0x8C1B +#define GL_TEXTURE_BINDING_1D_ARRAY_EXT 0x8C1C +#define GL_TEXTURE_BINDING_2D_ARRAY_EXT 0x8C1D +#define GL_MAX_ARRAY_TEXTURE_LAYERS_EXT 0x88FF +#define GL_COMPARE_REF_DEPTH_TO_TEXTURE_EXT 0x884E +typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURELAYEREXTPROC) (GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glFramebufferTextureLayerEXT (GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer); +#endif +#endif /* GL_EXT_texture_array */ + +#ifndef GL_EXT_texture_buffer_object +#define GL_EXT_texture_buffer_object 1 +#define GL_TEXTURE_BUFFER_EXT 0x8C2A +#define GL_MAX_TEXTURE_BUFFER_SIZE_EXT 0x8C2B +#define GL_TEXTURE_BINDING_BUFFER_EXT 0x8C2C +#define GL_TEXTURE_BUFFER_DATA_STORE_BINDING_EXT 0x8C2D +#define GL_TEXTURE_BUFFER_FORMAT_EXT 0x8C2E +typedef void (APIENTRYP PFNGLTEXBUFFEREXTPROC) (GLenum target, GLenum internalformat, GLuint buffer); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glTexBufferEXT (GLenum target, GLenum internalformat, GLuint buffer); +#endif +#endif /* GL_EXT_texture_buffer_object */ + +#ifndef GL_EXT_texture_compression_latc +#define GL_EXT_texture_compression_latc 1 +#define GL_COMPRESSED_LUMINANCE_LATC1_EXT 0x8C70 +#define GL_COMPRESSED_SIGNED_LUMINANCE_LATC1_EXT 0x8C71 +#define GL_COMPRESSED_LUMINANCE_ALPHA_LATC2_EXT 0x8C72 +#define GL_COMPRESSED_SIGNED_LUMINANCE_ALPHA_LATC2_EXT 0x8C73 +#endif /* GL_EXT_texture_compression_latc */ + +#ifndef GL_EXT_texture_compression_rgtc +#define GL_EXT_texture_compression_rgtc 1 +#define GL_COMPRESSED_RED_RGTC1_EXT 0x8DBB +#define GL_COMPRESSED_SIGNED_RED_RGTC1_EXT 0x8DBC +#define GL_COMPRESSED_RED_GREEN_RGTC2_EXT 0x8DBD +#define GL_COMPRESSED_SIGNED_RED_GREEN_RGTC2_EXT 0x8DBE +#endif /* GL_EXT_texture_compression_rgtc */ + +#ifndef GL_EXT_texture_compression_s3tc +#define GL_EXT_texture_compression_s3tc 1 +#define GL_COMPRESSED_RGB_S3TC_DXT1_EXT 0x83F0 +#define GL_COMPRESSED_RGBA_S3TC_DXT1_EXT 0x83F1 +#define GL_COMPRESSED_RGBA_S3TC_DXT3_EXT 0x83F2 +#define GL_COMPRESSED_RGBA_S3TC_DXT5_EXT 0x83F3 +#endif /* GL_EXT_texture_compression_s3tc */ + +#ifndef GL_EXT_texture_cube_map +#define GL_EXT_texture_cube_map 1 +#define GL_NORMAL_MAP_EXT 0x8511 +#define GL_REFLECTION_MAP_EXT 0x8512 +#define GL_TEXTURE_CUBE_MAP_EXT 0x8513 +#define GL_TEXTURE_BINDING_CUBE_MAP_EXT 0x8514 +#define GL_TEXTURE_CUBE_MAP_POSITIVE_X_EXT 0x8515 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_X_EXT 0x8516 +#define GL_TEXTURE_CUBE_MAP_POSITIVE_Y_EXT 0x8517 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_EXT 0x8518 +#define GL_TEXTURE_CUBE_MAP_POSITIVE_Z_EXT 0x8519 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_EXT 0x851A +#define GL_PROXY_TEXTURE_CUBE_MAP_EXT 0x851B +#define GL_MAX_CUBE_MAP_TEXTURE_SIZE_EXT 0x851C +#endif /* GL_EXT_texture_cube_map */ + +#ifndef GL_EXT_texture_env_add +#define GL_EXT_texture_env_add 1 +#endif /* GL_EXT_texture_env_add */ + +#ifndef GL_EXT_texture_env_combine +#define GL_EXT_texture_env_combine 1 +#define GL_COMBINE_EXT 0x8570 +#define GL_COMBINE_RGB_EXT 0x8571 +#define GL_COMBINE_ALPHA_EXT 0x8572 +#define GL_RGB_SCALE_EXT 0x8573 +#define GL_ADD_SIGNED_EXT 0x8574 +#define GL_INTERPOLATE_EXT 0x8575 +#define GL_CONSTANT_EXT 0x8576 +#define GL_PRIMARY_COLOR_EXT 0x8577 +#define GL_PREVIOUS_EXT 0x8578 +#define GL_SOURCE0_RGB_EXT 0x8580 +#define GL_SOURCE1_RGB_EXT 0x8581 +#define GL_SOURCE2_RGB_EXT 0x8582 +#define GL_SOURCE0_ALPHA_EXT 0x8588 +#define GL_SOURCE1_ALPHA_EXT 0x8589 +#define GL_SOURCE2_ALPHA_EXT 0x858A +#define GL_OPERAND0_RGB_EXT 0x8590 +#define GL_OPERAND1_RGB_EXT 0x8591 +#define GL_OPERAND2_RGB_EXT 0x8592 +#define GL_OPERAND0_ALPHA_EXT 0x8598 +#define GL_OPERAND1_ALPHA_EXT 0x8599 +#define GL_OPERAND2_ALPHA_EXT 0x859A +#endif /* GL_EXT_texture_env_combine */ + +#ifndef GL_EXT_texture_env_dot3 +#define GL_EXT_texture_env_dot3 1 +#define GL_DOT3_RGB_EXT 0x8740 +#define GL_DOT3_RGBA_EXT 0x8741 +#endif /* GL_EXT_texture_env_dot3 */ + +#ifndef GL_EXT_texture_filter_anisotropic +#define GL_EXT_texture_filter_anisotropic 1 +#define GL_TEXTURE_MAX_ANISOTROPY_EXT 0x84FE +#define GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT 0x84FF +#endif /* GL_EXT_texture_filter_anisotropic */ + +#ifndef GL_EXT_texture_filter_minmax +#define GL_EXT_texture_filter_minmax 1 +#endif /* GL_EXT_texture_filter_minmax */ + +#ifndef GL_EXT_texture_integer +#define GL_EXT_texture_integer 1 +#define GL_RGBA32UI_EXT 0x8D70 +#define GL_RGB32UI_EXT 0x8D71 +#define GL_ALPHA32UI_EXT 0x8D72 +#define GL_INTENSITY32UI_EXT 0x8D73 +#define GL_LUMINANCE32UI_EXT 0x8D74 +#define GL_LUMINANCE_ALPHA32UI_EXT 0x8D75 +#define GL_RGBA16UI_EXT 0x8D76 +#define GL_RGB16UI_EXT 0x8D77 +#define GL_ALPHA16UI_EXT 0x8D78 +#define GL_INTENSITY16UI_EXT 0x8D79 +#define GL_LUMINANCE16UI_EXT 0x8D7A +#define GL_LUMINANCE_ALPHA16UI_EXT 0x8D7B +#define GL_RGBA8UI_EXT 0x8D7C +#define GL_RGB8UI_EXT 0x8D7D +#define GL_ALPHA8UI_EXT 0x8D7E +#define GL_INTENSITY8UI_EXT 0x8D7F +#define GL_LUMINANCE8UI_EXT 0x8D80 +#define GL_LUMINANCE_ALPHA8UI_EXT 0x8D81 +#define GL_RGBA32I_EXT 0x8D82 +#define GL_RGB32I_EXT 0x8D83 +#define GL_ALPHA32I_EXT 0x8D84 +#define GL_INTENSITY32I_EXT 0x8D85 +#define GL_LUMINANCE32I_EXT 0x8D86 +#define GL_LUMINANCE_ALPHA32I_EXT 0x8D87 +#define GL_RGBA16I_EXT 0x8D88 +#define GL_RGB16I_EXT 0x8D89 +#define GL_ALPHA16I_EXT 0x8D8A +#define GL_INTENSITY16I_EXT 0x8D8B +#define GL_LUMINANCE16I_EXT 0x8D8C +#define GL_LUMINANCE_ALPHA16I_EXT 0x8D8D +#define GL_RGBA8I_EXT 0x8D8E +#define GL_RGB8I_EXT 0x8D8F +#define GL_ALPHA8I_EXT 0x8D90 +#define GL_INTENSITY8I_EXT 0x8D91 +#define GL_LUMINANCE8I_EXT 0x8D92 +#define GL_LUMINANCE_ALPHA8I_EXT 0x8D93 +#define GL_RED_INTEGER_EXT 0x8D94 +#define GL_GREEN_INTEGER_EXT 0x8D95 +#define GL_BLUE_INTEGER_EXT 0x8D96 +#define GL_ALPHA_INTEGER_EXT 0x8D97 +#define GL_RGB_INTEGER_EXT 0x8D98 +#define GL_RGBA_INTEGER_EXT 0x8D99 +#define GL_BGR_INTEGER_EXT 0x8D9A +#define GL_BGRA_INTEGER_EXT 0x8D9B +#define GL_LUMINANCE_INTEGER_EXT 0x8D9C +#define GL_LUMINANCE_ALPHA_INTEGER_EXT 0x8D9D +#define GL_RGBA_INTEGER_MODE_EXT 0x8D9E +typedef void (APIENTRYP PFNGLTEXPARAMETERIIVEXTPROC) (GLenum target, GLenum pname, const GLint *params); +typedef void (APIENTRYP PFNGLTEXPARAMETERIUIVEXTPROC) (GLenum target, GLenum pname, const GLuint *params); +typedef void (APIENTRYP PFNGLGETTEXPARAMETERIIVEXTPROC) (GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETTEXPARAMETERIUIVEXTPROC) (GLenum target, GLenum pname, GLuint *params); +typedef void (APIENTRYP PFNGLCLEARCOLORIIEXTPROC) (GLint red, GLint green, GLint blue, GLint alpha); +typedef void (APIENTRYP PFNGLCLEARCOLORIUIEXTPROC) (GLuint red, GLuint green, GLuint blue, GLuint alpha); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glTexParameterIivEXT (GLenum target, GLenum pname, const GLint *params); +GLAPI void APIENTRY glTexParameterIuivEXT (GLenum target, GLenum pname, const GLuint *params); +GLAPI void APIENTRY glGetTexParameterIivEXT (GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetTexParameterIuivEXT (GLenum target, GLenum pname, GLuint *params); +GLAPI void APIENTRY glClearColorIiEXT (GLint red, GLint green, GLint blue, GLint alpha); +GLAPI void APIENTRY glClearColorIuiEXT (GLuint red, GLuint green, GLuint blue, GLuint alpha); +#endif +#endif /* GL_EXT_texture_integer */ + +#ifndef GL_EXT_texture_lod_bias +#define GL_EXT_texture_lod_bias 1 +#define GL_MAX_TEXTURE_LOD_BIAS_EXT 0x84FD +#define GL_TEXTURE_FILTER_CONTROL_EXT 0x8500 +#define GL_TEXTURE_LOD_BIAS_EXT 0x8501 +#endif /* GL_EXT_texture_lod_bias */ + +#ifndef GL_EXT_texture_mirror_clamp +#define GL_EXT_texture_mirror_clamp 1 +#define GL_MIRROR_CLAMP_EXT 0x8742 +#define GL_MIRROR_CLAMP_TO_EDGE_EXT 0x8743 +#define GL_MIRROR_CLAMP_TO_BORDER_EXT 0x8912 +#endif /* GL_EXT_texture_mirror_clamp */ + +#ifndef GL_EXT_texture_object +#define GL_EXT_texture_object 1 +#define GL_TEXTURE_PRIORITY_EXT 0x8066 +#define GL_TEXTURE_RESIDENT_EXT 0x8067 +#define GL_TEXTURE_1D_BINDING_EXT 0x8068 +#define GL_TEXTURE_2D_BINDING_EXT 0x8069 +#define GL_TEXTURE_3D_BINDING_EXT 0x806A +typedef GLboolean (APIENTRYP PFNGLARETEXTURESRESIDENTEXTPROC) (GLsizei n, const GLuint *textures, GLboolean *residences); +typedef void (APIENTRYP PFNGLBINDTEXTUREEXTPROC) (GLenum target, GLuint texture); +typedef void (APIENTRYP PFNGLDELETETEXTURESEXTPROC) (GLsizei n, const GLuint *textures); +typedef void (APIENTRYP PFNGLGENTEXTURESEXTPROC) (GLsizei n, GLuint *textures); +typedef GLboolean (APIENTRYP PFNGLISTEXTUREEXTPROC) (GLuint texture); +typedef void (APIENTRYP PFNGLPRIORITIZETEXTURESEXTPROC) (GLsizei n, const GLuint *textures, const GLclampf *priorities); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI GLboolean APIENTRY glAreTexturesResidentEXT (GLsizei n, const GLuint *textures, GLboolean *residences); +GLAPI void APIENTRY glBindTextureEXT (GLenum target, GLuint texture); +GLAPI void APIENTRY glDeleteTexturesEXT (GLsizei n, const GLuint *textures); +GLAPI void APIENTRY glGenTexturesEXT (GLsizei n, GLuint *textures); +GLAPI GLboolean APIENTRY glIsTextureEXT (GLuint texture); +GLAPI void APIENTRY glPrioritizeTexturesEXT (GLsizei n, const GLuint *textures, const GLclampf *priorities); +#endif +#endif /* GL_EXT_texture_object */ + +#ifndef GL_EXT_texture_perturb_normal +#define GL_EXT_texture_perturb_normal 1 +#define GL_PERTURB_EXT 0x85AE +#define GL_TEXTURE_NORMAL_EXT 0x85AF +typedef void (APIENTRYP PFNGLTEXTURENORMALEXTPROC) (GLenum mode); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glTextureNormalEXT (GLenum mode); +#endif +#endif /* GL_EXT_texture_perturb_normal */ + +#ifndef GL_EXT_texture_sRGB +#define GL_EXT_texture_sRGB 1 +#define GL_SRGB_EXT 0x8C40 +#define GL_SRGB8_EXT 0x8C41 +#define GL_SRGB_ALPHA_EXT 0x8C42 +#define GL_SRGB8_ALPHA8_EXT 0x8C43 +#define GL_SLUMINANCE_ALPHA_EXT 0x8C44 +#define GL_SLUMINANCE8_ALPHA8_EXT 0x8C45 +#define GL_SLUMINANCE_EXT 0x8C46 +#define GL_SLUMINANCE8_EXT 0x8C47 +#define GL_COMPRESSED_SRGB_EXT 0x8C48 +#define GL_COMPRESSED_SRGB_ALPHA_EXT 0x8C49 +#define GL_COMPRESSED_SLUMINANCE_EXT 0x8C4A +#define GL_COMPRESSED_SLUMINANCE_ALPHA_EXT 0x8C4B +#define GL_COMPRESSED_SRGB_S3TC_DXT1_EXT 0x8C4C +#define GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT 0x8C4D +#define GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT 0x8C4E +#define GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT 0x8C4F +#endif /* GL_EXT_texture_sRGB */ + +#ifndef GL_EXT_texture_sRGB_decode +#define GL_EXT_texture_sRGB_decode 1 +#define GL_TEXTURE_SRGB_DECODE_EXT 0x8A48 +#define GL_DECODE_EXT 0x8A49 +#define GL_SKIP_DECODE_EXT 0x8A4A +#endif /* GL_EXT_texture_sRGB_decode */ + +#ifndef GL_EXT_texture_shared_exponent +#define GL_EXT_texture_shared_exponent 1 +#define GL_RGB9_E5_EXT 0x8C3D +#define GL_UNSIGNED_INT_5_9_9_9_REV_EXT 0x8C3E +#define GL_TEXTURE_SHARED_SIZE_EXT 0x8C3F +#endif /* GL_EXT_texture_shared_exponent */ + +#ifndef GL_EXT_texture_snorm +#define GL_EXT_texture_snorm 1 +#define GL_ALPHA_SNORM 0x9010 +#define GL_LUMINANCE_SNORM 0x9011 +#define GL_LUMINANCE_ALPHA_SNORM 0x9012 +#define GL_INTENSITY_SNORM 0x9013 +#define GL_ALPHA8_SNORM 0x9014 +#define GL_LUMINANCE8_SNORM 0x9015 +#define GL_LUMINANCE8_ALPHA8_SNORM 0x9016 +#define GL_INTENSITY8_SNORM 0x9017 +#define GL_ALPHA16_SNORM 0x9018 +#define GL_LUMINANCE16_SNORM 0x9019 +#define GL_LUMINANCE16_ALPHA16_SNORM 0x901A +#define GL_INTENSITY16_SNORM 0x901B +#define GL_RED_SNORM 0x8F90 +#define GL_RG_SNORM 0x8F91 +#define GL_RGB_SNORM 0x8F92 +#define GL_RGBA_SNORM 0x8F93 +#endif /* GL_EXT_texture_snorm */ + +#ifndef GL_EXT_texture_swizzle +#define GL_EXT_texture_swizzle 1 +#define GL_TEXTURE_SWIZZLE_R_EXT 0x8E42 +#define GL_TEXTURE_SWIZZLE_G_EXT 0x8E43 +#define GL_TEXTURE_SWIZZLE_B_EXT 0x8E44 +#define GL_TEXTURE_SWIZZLE_A_EXT 0x8E45 +#define GL_TEXTURE_SWIZZLE_RGBA_EXT 0x8E46 +#endif /* GL_EXT_texture_swizzle */ + +#ifndef GL_EXT_timer_query +#define GL_EXT_timer_query 1 +#define GL_TIME_ELAPSED_EXT 0x88BF +typedef void (APIENTRYP PFNGLGETQUERYOBJECTI64VEXTPROC) (GLuint id, GLenum pname, GLint64 *params); +typedef void (APIENTRYP PFNGLGETQUERYOBJECTUI64VEXTPROC) (GLuint id, GLenum pname, GLuint64 *params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glGetQueryObjecti64vEXT (GLuint id, GLenum pname, GLint64 *params); +GLAPI void APIENTRY glGetQueryObjectui64vEXT (GLuint id, GLenum pname, GLuint64 *params); +#endif +#endif /* GL_EXT_timer_query */ + +#ifndef GL_EXT_transform_feedback +#define GL_EXT_transform_feedback 1 +#define GL_TRANSFORM_FEEDBACK_BUFFER_EXT 0x8C8E +#define GL_TRANSFORM_FEEDBACK_BUFFER_START_EXT 0x8C84 +#define GL_TRANSFORM_FEEDBACK_BUFFER_SIZE_EXT 0x8C85 +#define GL_TRANSFORM_FEEDBACK_BUFFER_BINDING_EXT 0x8C8F +#define GL_INTERLEAVED_ATTRIBS_EXT 0x8C8C +#define GL_SEPARATE_ATTRIBS_EXT 0x8C8D +#define GL_PRIMITIVES_GENERATED_EXT 0x8C87 +#define GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN_EXT 0x8C88 +#define GL_RASTERIZER_DISCARD_EXT 0x8C89 +#define GL_MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS_EXT 0x8C8A +#define GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS_EXT 0x8C8B +#define GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS_EXT 0x8C80 +#define GL_TRANSFORM_FEEDBACK_VARYINGS_EXT 0x8C83 +#define GL_TRANSFORM_FEEDBACK_BUFFER_MODE_EXT 0x8C7F +#define GL_TRANSFORM_FEEDBACK_VARYING_MAX_LENGTH_EXT 0x8C76 +typedef void (APIENTRYP PFNGLBEGINTRANSFORMFEEDBACKEXTPROC) (GLenum primitiveMode); +typedef void (APIENTRYP PFNGLENDTRANSFORMFEEDBACKEXTPROC) (void); +typedef void (APIENTRYP PFNGLBINDBUFFERRANGEEXTPROC) (GLenum target, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size); +typedef void (APIENTRYP PFNGLBINDBUFFEROFFSETEXTPROC) (GLenum target, GLuint index, GLuint buffer, GLintptr offset); +typedef void (APIENTRYP PFNGLBINDBUFFERBASEEXTPROC) (GLenum target, GLuint index, GLuint buffer); +typedef void (APIENTRYP PFNGLTRANSFORMFEEDBACKVARYINGSEXTPROC) (GLuint program, GLsizei count, const GLchar *const*varyings, GLenum bufferMode); +typedef void (APIENTRYP PFNGLGETTRANSFORMFEEDBACKVARYINGEXTPROC) (GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLsizei *size, GLenum *type, GLchar *name); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBeginTransformFeedbackEXT (GLenum primitiveMode); +GLAPI void APIENTRY glEndTransformFeedbackEXT (void); +GLAPI void APIENTRY glBindBufferRangeEXT (GLenum target, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size); +GLAPI void APIENTRY glBindBufferOffsetEXT (GLenum target, GLuint index, GLuint buffer, GLintptr offset); +GLAPI void APIENTRY glBindBufferBaseEXT (GLenum target, GLuint index, GLuint buffer); +GLAPI void APIENTRY glTransformFeedbackVaryingsEXT (GLuint program, GLsizei count, const GLchar *const*varyings, GLenum bufferMode); +GLAPI void APIENTRY glGetTransformFeedbackVaryingEXT (GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLsizei *size, GLenum *type, GLchar *name); +#endif +#endif /* GL_EXT_transform_feedback */ + +#ifndef GL_EXT_vertex_array +#define GL_EXT_vertex_array 1 +#define GL_VERTEX_ARRAY_EXT 0x8074 +#define GL_NORMAL_ARRAY_EXT 0x8075 +#define GL_COLOR_ARRAY_EXT 0x8076 +#define GL_INDEX_ARRAY_EXT 0x8077 +#define GL_TEXTURE_COORD_ARRAY_EXT 0x8078 +#define GL_EDGE_FLAG_ARRAY_EXT 0x8079 +#define GL_VERTEX_ARRAY_SIZE_EXT 0x807A +#define GL_VERTEX_ARRAY_TYPE_EXT 0x807B +#define GL_VERTEX_ARRAY_STRIDE_EXT 0x807C +#define GL_VERTEX_ARRAY_COUNT_EXT 0x807D +#define GL_NORMAL_ARRAY_TYPE_EXT 0x807E +#define GL_NORMAL_ARRAY_STRIDE_EXT 0x807F +#define GL_NORMAL_ARRAY_COUNT_EXT 0x8080 +#define GL_COLOR_ARRAY_SIZE_EXT 0x8081 +#define GL_COLOR_ARRAY_TYPE_EXT 0x8082 +#define GL_COLOR_ARRAY_STRIDE_EXT 0x8083 +#define GL_COLOR_ARRAY_COUNT_EXT 0x8084 +#define GL_INDEX_ARRAY_TYPE_EXT 0x8085 +#define GL_INDEX_ARRAY_STRIDE_EXT 0x8086 +#define GL_INDEX_ARRAY_COUNT_EXT 0x8087 +#define GL_TEXTURE_COORD_ARRAY_SIZE_EXT 0x8088 +#define GL_TEXTURE_COORD_ARRAY_TYPE_EXT 0x8089 +#define GL_TEXTURE_COORD_ARRAY_STRIDE_EXT 0x808A +#define GL_TEXTURE_COORD_ARRAY_COUNT_EXT 0x808B +#define GL_EDGE_FLAG_ARRAY_STRIDE_EXT 0x808C +#define GL_EDGE_FLAG_ARRAY_COUNT_EXT 0x808D +#define GL_VERTEX_ARRAY_POINTER_EXT 0x808E +#define GL_NORMAL_ARRAY_POINTER_EXT 0x808F +#define GL_COLOR_ARRAY_POINTER_EXT 0x8090 +#define GL_INDEX_ARRAY_POINTER_EXT 0x8091 +#define GL_TEXTURE_COORD_ARRAY_POINTER_EXT 0x8092 +#define GL_EDGE_FLAG_ARRAY_POINTER_EXT 0x8093 +typedef void (APIENTRYP PFNGLARRAYELEMENTEXTPROC) (GLint i); +typedef void (APIENTRYP PFNGLCOLORPOINTEREXTPROC) (GLint size, GLenum type, GLsizei stride, GLsizei count, const void *pointer); +typedef void (APIENTRYP PFNGLDRAWARRAYSEXTPROC) (GLenum mode, GLint first, GLsizei count); +typedef void (APIENTRYP PFNGLEDGEFLAGPOINTEREXTPROC) (GLsizei stride, GLsizei count, const GLboolean *pointer); +typedef void (APIENTRYP PFNGLGETPOINTERVEXTPROC) (GLenum pname, void **params); +typedef void (APIENTRYP PFNGLINDEXPOINTEREXTPROC) (GLenum type, GLsizei stride, GLsizei count, const void *pointer); +typedef void (APIENTRYP PFNGLNORMALPOINTEREXTPROC) (GLenum type, GLsizei stride, GLsizei count, const void *pointer); +typedef void (APIENTRYP PFNGLTEXCOORDPOINTEREXTPROC) (GLint size, GLenum type, GLsizei stride, GLsizei count, const void *pointer); +typedef void (APIENTRYP PFNGLVERTEXPOINTEREXTPROC) (GLint size, GLenum type, GLsizei stride, GLsizei count, const void *pointer); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glArrayElementEXT (GLint i); +GLAPI void APIENTRY glColorPointerEXT (GLint size, GLenum type, GLsizei stride, GLsizei count, const void *pointer); +GLAPI void APIENTRY glDrawArraysEXT (GLenum mode, GLint first, GLsizei count); +GLAPI void APIENTRY glEdgeFlagPointerEXT (GLsizei stride, GLsizei count, const GLboolean *pointer); +GLAPI void APIENTRY glGetPointervEXT (GLenum pname, void **params); +GLAPI void APIENTRY glIndexPointerEXT (GLenum type, GLsizei stride, GLsizei count, const void *pointer); +GLAPI void APIENTRY glNormalPointerEXT (GLenum type, GLsizei stride, GLsizei count, const void *pointer); +GLAPI void APIENTRY glTexCoordPointerEXT (GLint size, GLenum type, GLsizei stride, GLsizei count, const void *pointer); +GLAPI void APIENTRY glVertexPointerEXT (GLint size, GLenum type, GLsizei stride, GLsizei count, const void *pointer); +#endif +#endif /* GL_EXT_vertex_array */ + +#ifndef GL_EXT_vertex_array_bgra +#define GL_EXT_vertex_array_bgra 1 +#endif /* GL_EXT_vertex_array_bgra */ + +#ifndef GL_EXT_vertex_attrib_64bit +#define GL_EXT_vertex_attrib_64bit 1 +#define GL_DOUBLE_VEC2_EXT 0x8FFC +#define GL_DOUBLE_VEC3_EXT 0x8FFD +#define GL_DOUBLE_VEC4_EXT 0x8FFE +#define GL_DOUBLE_MAT2_EXT 0x8F46 +#define GL_DOUBLE_MAT3_EXT 0x8F47 +#define GL_DOUBLE_MAT4_EXT 0x8F48 +#define GL_DOUBLE_MAT2x3_EXT 0x8F49 +#define GL_DOUBLE_MAT2x4_EXT 0x8F4A +#define GL_DOUBLE_MAT3x2_EXT 0x8F4B +#define GL_DOUBLE_MAT3x4_EXT 0x8F4C +#define GL_DOUBLE_MAT4x2_EXT 0x8F4D +#define GL_DOUBLE_MAT4x3_EXT 0x8F4E +typedef void (APIENTRYP PFNGLVERTEXATTRIBL1DEXTPROC) (GLuint index, GLdouble x); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL2DEXTPROC) (GLuint index, GLdouble x, GLdouble y); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL3DEXTPROC) (GLuint index, GLdouble x, GLdouble y, GLdouble z); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL4DEXTPROC) (GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL1DVEXTPROC) (GLuint index, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL2DVEXTPROC) (GLuint index, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL3DVEXTPROC) (GLuint index, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL4DVEXTPROC) (GLuint index, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBLPOINTEREXTPROC) (GLuint index, GLint size, GLenum type, GLsizei stride, const void *pointer); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBLDVEXTPROC) (GLuint index, GLenum pname, GLdouble *params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glVertexAttribL1dEXT (GLuint index, GLdouble x); +GLAPI void APIENTRY glVertexAttribL2dEXT (GLuint index, GLdouble x, GLdouble y); +GLAPI void APIENTRY glVertexAttribL3dEXT (GLuint index, GLdouble x, GLdouble y, GLdouble z); +GLAPI void APIENTRY glVertexAttribL4dEXT (GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +GLAPI void APIENTRY glVertexAttribL1dvEXT (GLuint index, const GLdouble *v); +GLAPI void APIENTRY glVertexAttribL2dvEXT (GLuint index, const GLdouble *v); +GLAPI void APIENTRY glVertexAttribL3dvEXT (GLuint index, const GLdouble *v); +GLAPI void APIENTRY glVertexAttribL4dvEXT (GLuint index, const GLdouble *v); +GLAPI void APIENTRY glVertexAttribLPointerEXT (GLuint index, GLint size, GLenum type, GLsizei stride, const void *pointer); +GLAPI void APIENTRY glGetVertexAttribLdvEXT (GLuint index, GLenum pname, GLdouble *params); +#endif +#endif /* GL_EXT_vertex_attrib_64bit */ + +#ifndef GL_EXT_vertex_shader +#define GL_EXT_vertex_shader 1 +#define GL_VERTEX_SHADER_EXT 0x8780 +#define GL_VERTEX_SHADER_BINDING_EXT 0x8781 +#define GL_OP_INDEX_EXT 0x8782 +#define GL_OP_NEGATE_EXT 0x8783 +#define GL_OP_DOT3_EXT 0x8784 +#define GL_OP_DOT4_EXT 0x8785 +#define GL_OP_MUL_EXT 0x8786 +#define GL_OP_ADD_EXT 0x8787 +#define GL_OP_MADD_EXT 0x8788 +#define GL_OP_FRAC_EXT 0x8789 +#define GL_OP_MAX_EXT 0x878A +#define GL_OP_MIN_EXT 0x878B +#define GL_OP_SET_GE_EXT 0x878C +#define GL_OP_SET_LT_EXT 0x878D +#define GL_OP_CLAMP_EXT 0x878E +#define GL_OP_FLOOR_EXT 0x878F +#define GL_OP_ROUND_EXT 0x8790 +#define GL_OP_EXP_BASE_2_EXT 0x8791 +#define GL_OP_LOG_BASE_2_EXT 0x8792 +#define GL_OP_POWER_EXT 0x8793 +#define GL_OP_RECIP_EXT 0x8794 +#define GL_OP_RECIP_SQRT_EXT 0x8795 +#define GL_OP_SUB_EXT 0x8796 +#define GL_OP_CROSS_PRODUCT_EXT 0x8797 +#define GL_OP_MULTIPLY_MATRIX_EXT 0x8798 +#define GL_OP_MOV_EXT 0x8799 +#define GL_OUTPUT_VERTEX_EXT 0x879A +#define GL_OUTPUT_COLOR0_EXT 0x879B +#define GL_OUTPUT_COLOR1_EXT 0x879C +#define GL_OUTPUT_TEXTURE_COORD0_EXT 0x879D +#define GL_OUTPUT_TEXTURE_COORD1_EXT 0x879E +#define GL_OUTPUT_TEXTURE_COORD2_EXT 0x879F +#define GL_OUTPUT_TEXTURE_COORD3_EXT 0x87A0 +#define GL_OUTPUT_TEXTURE_COORD4_EXT 0x87A1 +#define GL_OUTPUT_TEXTURE_COORD5_EXT 0x87A2 +#define GL_OUTPUT_TEXTURE_COORD6_EXT 0x87A3 +#define GL_OUTPUT_TEXTURE_COORD7_EXT 0x87A4 +#define GL_OUTPUT_TEXTURE_COORD8_EXT 0x87A5 +#define GL_OUTPUT_TEXTURE_COORD9_EXT 0x87A6 +#define GL_OUTPUT_TEXTURE_COORD10_EXT 0x87A7 +#define GL_OUTPUT_TEXTURE_COORD11_EXT 0x87A8 +#define GL_OUTPUT_TEXTURE_COORD12_EXT 0x87A9 +#define GL_OUTPUT_TEXTURE_COORD13_EXT 0x87AA +#define GL_OUTPUT_TEXTURE_COORD14_EXT 0x87AB +#define GL_OUTPUT_TEXTURE_COORD15_EXT 0x87AC +#define GL_OUTPUT_TEXTURE_COORD16_EXT 0x87AD +#define GL_OUTPUT_TEXTURE_COORD17_EXT 0x87AE +#define GL_OUTPUT_TEXTURE_COORD18_EXT 0x87AF +#define GL_OUTPUT_TEXTURE_COORD19_EXT 0x87B0 +#define GL_OUTPUT_TEXTURE_COORD20_EXT 0x87B1 +#define GL_OUTPUT_TEXTURE_COORD21_EXT 0x87B2 +#define GL_OUTPUT_TEXTURE_COORD22_EXT 0x87B3 +#define GL_OUTPUT_TEXTURE_COORD23_EXT 0x87B4 +#define GL_OUTPUT_TEXTURE_COORD24_EXT 0x87B5 +#define GL_OUTPUT_TEXTURE_COORD25_EXT 0x87B6 +#define GL_OUTPUT_TEXTURE_COORD26_EXT 0x87B7 +#define GL_OUTPUT_TEXTURE_COORD27_EXT 0x87B8 +#define GL_OUTPUT_TEXTURE_COORD28_EXT 0x87B9 +#define GL_OUTPUT_TEXTURE_COORD29_EXT 0x87BA +#define GL_OUTPUT_TEXTURE_COORD30_EXT 0x87BB +#define GL_OUTPUT_TEXTURE_COORD31_EXT 0x87BC +#define GL_OUTPUT_FOG_EXT 0x87BD +#define GL_SCALAR_EXT 0x87BE +#define GL_VECTOR_EXT 0x87BF +#define GL_MATRIX_EXT 0x87C0 +#define GL_VARIANT_EXT 0x87C1 +#define GL_INVARIANT_EXT 0x87C2 +#define GL_LOCAL_CONSTANT_EXT 0x87C3 +#define GL_LOCAL_EXT 0x87C4 +#define GL_MAX_VERTEX_SHADER_INSTRUCTIONS_EXT 0x87C5 +#define GL_MAX_VERTEX_SHADER_VARIANTS_EXT 0x87C6 +#define GL_MAX_VERTEX_SHADER_INVARIANTS_EXT 0x87C7 +#define GL_MAX_VERTEX_SHADER_LOCAL_CONSTANTS_EXT 0x87C8 +#define GL_MAX_VERTEX_SHADER_LOCALS_EXT 0x87C9 +#define GL_MAX_OPTIMIZED_VERTEX_SHADER_INSTRUCTIONS_EXT 0x87CA +#define GL_MAX_OPTIMIZED_VERTEX_SHADER_VARIANTS_EXT 0x87CB +#define GL_MAX_OPTIMIZED_VERTEX_SHADER_LOCAL_CONSTANTS_EXT 0x87CC +#define GL_MAX_OPTIMIZED_VERTEX_SHADER_INVARIANTS_EXT 0x87CD +#define GL_MAX_OPTIMIZED_VERTEX_SHADER_LOCALS_EXT 0x87CE +#define GL_VERTEX_SHADER_INSTRUCTIONS_EXT 0x87CF +#define GL_VERTEX_SHADER_VARIANTS_EXT 0x87D0 +#define GL_VERTEX_SHADER_INVARIANTS_EXT 0x87D1 +#define GL_VERTEX_SHADER_LOCAL_CONSTANTS_EXT 0x87D2 +#define GL_VERTEX_SHADER_LOCALS_EXT 0x87D3 +#define GL_VERTEX_SHADER_OPTIMIZED_EXT 0x87D4 +#define GL_X_EXT 0x87D5 +#define GL_Y_EXT 0x87D6 +#define GL_Z_EXT 0x87D7 +#define GL_W_EXT 0x87D8 +#define GL_NEGATIVE_X_EXT 0x87D9 +#define GL_NEGATIVE_Y_EXT 0x87DA +#define GL_NEGATIVE_Z_EXT 0x87DB +#define GL_NEGATIVE_W_EXT 0x87DC +#define GL_ZERO_EXT 0x87DD +#define GL_ONE_EXT 0x87DE +#define GL_NEGATIVE_ONE_EXT 0x87DF +#define GL_NORMALIZED_RANGE_EXT 0x87E0 +#define GL_FULL_RANGE_EXT 0x87E1 +#define GL_CURRENT_VERTEX_EXT 0x87E2 +#define GL_MVP_MATRIX_EXT 0x87E3 +#define GL_VARIANT_VALUE_EXT 0x87E4 +#define GL_VARIANT_DATATYPE_EXT 0x87E5 +#define GL_VARIANT_ARRAY_STRIDE_EXT 0x87E6 +#define GL_VARIANT_ARRAY_TYPE_EXT 0x87E7 +#define GL_VARIANT_ARRAY_EXT 0x87E8 +#define GL_VARIANT_ARRAY_POINTER_EXT 0x87E9 +#define GL_INVARIANT_VALUE_EXT 0x87EA +#define GL_INVARIANT_DATATYPE_EXT 0x87EB +#define GL_LOCAL_CONSTANT_VALUE_EXT 0x87EC +#define GL_LOCAL_CONSTANT_DATATYPE_EXT 0x87ED +typedef void (APIENTRYP PFNGLBEGINVERTEXSHADEREXTPROC) (void); +typedef void (APIENTRYP PFNGLENDVERTEXSHADEREXTPROC) (void); +typedef void (APIENTRYP PFNGLBINDVERTEXSHADEREXTPROC) (GLuint id); +typedef GLuint (APIENTRYP PFNGLGENVERTEXSHADERSEXTPROC) (GLuint range); +typedef void (APIENTRYP PFNGLDELETEVERTEXSHADEREXTPROC) (GLuint id); +typedef void (APIENTRYP PFNGLSHADEROP1EXTPROC) (GLenum op, GLuint res, GLuint arg1); +typedef void (APIENTRYP PFNGLSHADEROP2EXTPROC) (GLenum op, GLuint res, GLuint arg1, GLuint arg2); +typedef void (APIENTRYP PFNGLSHADEROP3EXTPROC) (GLenum op, GLuint res, GLuint arg1, GLuint arg2, GLuint arg3); +typedef void (APIENTRYP PFNGLSWIZZLEEXTPROC) (GLuint res, GLuint in, GLenum outX, GLenum outY, GLenum outZ, GLenum outW); +typedef void (APIENTRYP PFNGLWRITEMASKEXTPROC) (GLuint res, GLuint in, GLenum outX, GLenum outY, GLenum outZ, GLenum outW); +typedef void (APIENTRYP PFNGLINSERTCOMPONENTEXTPROC) (GLuint res, GLuint src, GLuint num); +typedef void (APIENTRYP PFNGLEXTRACTCOMPONENTEXTPROC) (GLuint res, GLuint src, GLuint num); +typedef GLuint (APIENTRYP PFNGLGENSYMBOLSEXTPROC) (GLenum datatype, GLenum storagetype, GLenum range, GLuint components); +typedef void (APIENTRYP PFNGLSETINVARIANTEXTPROC) (GLuint id, GLenum type, const void *addr); +typedef void (APIENTRYP PFNGLSETLOCALCONSTANTEXTPROC) (GLuint id, GLenum type, const void *addr); +typedef void (APIENTRYP PFNGLVARIANTBVEXTPROC) (GLuint id, const GLbyte *addr); +typedef void (APIENTRYP PFNGLVARIANTSVEXTPROC) (GLuint id, const GLshort *addr); +typedef void (APIENTRYP PFNGLVARIANTIVEXTPROC) (GLuint id, const GLint *addr); +typedef void (APIENTRYP PFNGLVARIANTFVEXTPROC) (GLuint id, const GLfloat *addr); +typedef void (APIENTRYP PFNGLVARIANTDVEXTPROC) (GLuint id, const GLdouble *addr); +typedef void (APIENTRYP PFNGLVARIANTUBVEXTPROC) (GLuint id, const GLubyte *addr); +typedef void (APIENTRYP PFNGLVARIANTUSVEXTPROC) (GLuint id, const GLushort *addr); +typedef void (APIENTRYP PFNGLVARIANTUIVEXTPROC) (GLuint id, const GLuint *addr); +typedef void (APIENTRYP PFNGLVARIANTPOINTEREXTPROC) (GLuint id, GLenum type, GLuint stride, const void *addr); +typedef void (APIENTRYP PFNGLENABLEVARIANTCLIENTSTATEEXTPROC) (GLuint id); +typedef void (APIENTRYP PFNGLDISABLEVARIANTCLIENTSTATEEXTPROC) (GLuint id); +typedef GLuint (APIENTRYP PFNGLBINDLIGHTPARAMETEREXTPROC) (GLenum light, GLenum value); +typedef GLuint (APIENTRYP PFNGLBINDMATERIALPARAMETEREXTPROC) (GLenum face, GLenum value); +typedef GLuint (APIENTRYP PFNGLBINDTEXGENPARAMETEREXTPROC) (GLenum unit, GLenum coord, GLenum value); +typedef GLuint (APIENTRYP PFNGLBINDTEXTUREUNITPARAMETEREXTPROC) (GLenum unit, GLenum value); +typedef GLuint (APIENTRYP PFNGLBINDPARAMETEREXTPROC) (GLenum value); +typedef GLboolean (APIENTRYP PFNGLISVARIANTENABLEDEXTPROC) (GLuint id, GLenum cap); +typedef void (APIENTRYP PFNGLGETVARIANTBOOLEANVEXTPROC) (GLuint id, GLenum value, GLboolean *data); +typedef void (APIENTRYP PFNGLGETVARIANTINTEGERVEXTPROC) (GLuint id, GLenum value, GLint *data); +typedef void (APIENTRYP PFNGLGETVARIANTFLOATVEXTPROC) (GLuint id, GLenum value, GLfloat *data); +typedef void (APIENTRYP PFNGLGETVARIANTPOINTERVEXTPROC) (GLuint id, GLenum value, void **data); +typedef void (APIENTRYP PFNGLGETINVARIANTBOOLEANVEXTPROC) (GLuint id, GLenum value, GLboolean *data); +typedef void (APIENTRYP PFNGLGETINVARIANTINTEGERVEXTPROC) (GLuint id, GLenum value, GLint *data); +typedef void (APIENTRYP PFNGLGETINVARIANTFLOATVEXTPROC) (GLuint id, GLenum value, GLfloat *data); +typedef void (APIENTRYP PFNGLGETLOCALCONSTANTBOOLEANVEXTPROC) (GLuint id, GLenum value, GLboolean *data); +typedef void (APIENTRYP PFNGLGETLOCALCONSTANTINTEGERVEXTPROC) (GLuint id, GLenum value, GLint *data); +typedef void (APIENTRYP PFNGLGETLOCALCONSTANTFLOATVEXTPROC) (GLuint id, GLenum value, GLfloat *data); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBeginVertexShaderEXT (void); +GLAPI void APIENTRY glEndVertexShaderEXT (void); +GLAPI void APIENTRY glBindVertexShaderEXT (GLuint id); +GLAPI GLuint APIENTRY glGenVertexShadersEXT (GLuint range); +GLAPI void APIENTRY glDeleteVertexShaderEXT (GLuint id); +GLAPI void APIENTRY glShaderOp1EXT (GLenum op, GLuint res, GLuint arg1); +GLAPI void APIENTRY glShaderOp2EXT (GLenum op, GLuint res, GLuint arg1, GLuint arg2); +GLAPI void APIENTRY glShaderOp3EXT (GLenum op, GLuint res, GLuint arg1, GLuint arg2, GLuint arg3); +GLAPI void APIENTRY glSwizzleEXT (GLuint res, GLuint in, GLenum outX, GLenum outY, GLenum outZ, GLenum outW); +GLAPI void APIENTRY glWriteMaskEXT (GLuint res, GLuint in, GLenum outX, GLenum outY, GLenum outZ, GLenum outW); +GLAPI void APIENTRY glInsertComponentEXT (GLuint res, GLuint src, GLuint num); +GLAPI void APIENTRY glExtractComponentEXT (GLuint res, GLuint src, GLuint num); +GLAPI GLuint APIENTRY glGenSymbolsEXT (GLenum datatype, GLenum storagetype, GLenum range, GLuint components); +GLAPI void APIENTRY glSetInvariantEXT (GLuint id, GLenum type, const void *addr); +GLAPI void APIENTRY glSetLocalConstantEXT (GLuint id, GLenum type, const void *addr); +GLAPI void APIENTRY glVariantbvEXT (GLuint id, const GLbyte *addr); +GLAPI void APIENTRY glVariantsvEXT (GLuint id, const GLshort *addr); +GLAPI void APIENTRY glVariantivEXT (GLuint id, const GLint *addr); +GLAPI void APIENTRY glVariantfvEXT (GLuint id, const GLfloat *addr); +GLAPI void APIENTRY glVariantdvEXT (GLuint id, const GLdouble *addr); +GLAPI void APIENTRY glVariantubvEXT (GLuint id, const GLubyte *addr); +GLAPI void APIENTRY glVariantusvEXT (GLuint id, const GLushort *addr); +GLAPI void APIENTRY glVariantuivEXT (GLuint id, const GLuint *addr); +GLAPI void APIENTRY glVariantPointerEXT (GLuint id, GLenum type, GLuint stride, const void *addr); +GLAPI void APIENTRY glEnableVariantClientStateEXT (GLuint id); +GLAPI void APIENTRY glDisableVariantClientStateEXT (GLuint id); +GLAPI GLuint APIENTRY glBindLightParameterEXT (GLenum light, GLenum value); +GLAPI GLuint APIENTRY glBindMaterialParameterEXT (GLenum face, GLenum value); +GLAPI GLuint APIENTRY glBindTexGenParameterEXT (GLenum unit, GLenum coord, GLenum value); +GLAPI GLuint APIENTRY glBindTextureUnitParameterEXT (GLenum unit, GLenum value); +GLAPI GLuint APIENTRY glBindParameterEXT (GLenum value); +GLAPI GLboolean APIENTRY glIsVariantEnabledEXT (GLuint id, GLenum cap); +GLAPI void APIENTRY glGetVariantBooleanvEXT (GLuint id, GLenum value, GLboolean *data); +GLAPI void APIENTRY glGetVariantIntegervEXT (GLuint id, GLenum value, GLint *data); +GLAPI void APIENTRY glGetVariantFloatvEXT (GLuint id, GLenum value, GLfloat *data); +GLAPI void APIENTRY glGetVariantPointervEXT (GLuint id, GLenum value, void **data); +GLAPI void APIENTRY glGetInvariantBooleanvEXT (GLuint id, GLenum value, GLboolean *data); +GLAPI void APIENTRY glGetInvariantIntegervEXT (GLuint id, GLenum value, GLint *data); +GLAPI void APIENTRY glGetInvariantFloatvEXT (GLuint id, GLenum value, GLfloat *data); +GLAPI void APIENTRY glGetLocalConstantBooleanvEXT (GLuint id, GLenum value, GLboolean *data); +GLAPI void APIENTRY glGetLocalConstantIntegervEXT (GLuint id, GLenum value, GLint *data); +GLAPI void APIENTRY glGetLocalConstantFloatvEXT (GLuint id, GLenum value, GLfloat *data); +#endif +#endif /* GL_EXT_vertex_shader */ + +#ifndef GL_EXT_vertex_weighting +#define GL_EXT_vertex_weighting 1 +#define GL_MODELVIEW0_STACK_DEPTH_EXT 0x0BA3 +#define GL_MODELVIEW1_STACK_DEPTH_EXT 0x8502 +#define GL_MODELVIEW0_MATRIX_EXT 0x0BA6 +#define GL_MODELVIEW1_MATRIX_EXT 0x8506 +#define GL_VERTEX_WEIGHTING_EXT 0x8509 +#define GL_MODELVIEW0_EXT 0x1700 +#define GL_MODELVIEW1_EXT 0x850A +#define GL_CURRENT_VERTEX_WEIGHT_EXT 0x850B +#define GL_VERTEX_WEIGHT_ARRAY_EXT 0x850C +#define GL_VERTEX_WEIGHT_ARRAY_SIZE_EXT 0x850D +#define GL_VERTEX_WEIGHT_ARRAY_TYPE_EXT 0x850E +#define GL_VERTEX_WEIGHT_ARRAY_STRIDE_EXT 0x850F +#define GL_VERTEX_WEIGHT_ARRAY_POINTER_EXT 0x8510 +typedef void (APIENTRYP PFNGLVERTEXWEIGHTFEXTPROC) (GLfloat weight); +typedef void (APIENTRYP PFNGLVERTEXWEIGHTFVEXTPROC) (const GLfloat *weight); +typedef void (APIENTRYP PFNGLVERTEXWEIGHTPOINTEREXTPROC) (GLint size, GLenum type, GLsizei stride, const void *pointer); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glVertexWeightfEXT (GLfloat weight); +GLAPI void APIENTRY glVertexWeightfvEXT (const GLfloat *weight); +GLAPI void APIENTRY glVertexWeightPointerEXT (GLint size, GLenum type, GLsizei stride, const void *pointer); +#endif +#endif /* GL_EXT_vertex_weighting */ + +#ifndef GL_EXT_x11_sync_object +#define GL_EXT_x11_sync_object 1 +#define GL_SYNC_X11_FENCE_EXT 0x90E1 +typedef GLsync (APIENTRYP PFNGLIMPORTSYNCEXTPROC) (GLenum external_sync_type, GLintptr external_sync, GLbitfield flags); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI GLsync APIENTRY glImportSyncEXT (GLenum external_sync_type, GLintptr external_sync, GLbitfield flags); +#endif +#endif /* GL_EXT_x11_sync_object */ + +#ifndef GL_GREMEDY_frame_terminator +#define GL_GREMEDY_frame_terminator 1 +typedef void (APIENTRYP PFNGLFRAMETERMINATORGREMEDYPROC) (void); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glFrameTerminatorGREMEDY (void); +#endif +#endif /* GL_GREMEDY_frame_terminator */ + +#ifndef GL_GREMEDY_string_marker +#define GL_GREMEDY_string_marker 1 +typedef void (APIENTRYP PFNGLSTRINGMARKERGREMEDYPROC) (GLsizei len, const void *string); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glStringMarkerGREMEDY (GLsizei len, const void *string); +#endif +#endif /* GL_GREMEDY_string_marker */ + +#ifndef GL_HP_convolution_border_modes +#define GL_HP_convolution_border_modes 1 +#define GL_IGNORE_BORDER_HP 0x8150 +#define GL_CONSTANT_BORDER_HP 0x8151 +#define GL_REPLICATE_BORDER_HP 0x8153 +#define GL_CONVOLUTION_BORDER_COLOR_HP 0x8154 +#endif /* GL_HP_convolution_border_modes */ + +#ifndef GL_HP_image_transform +#define GL_HP_image_transform 1 +#define GL_IMAGE_SCALE_X_HP 0x8155 +#define GL_IMAGE_SCALE_Y_HP 0x8156 +#define GL_IMAGE_TRANSLATE_X_HP 0x8157 +#define GL_IMAGE_TRANSLATE_Y_HP 0x8158 +#define GL_IMAGE_ROTATE_ANGLE_HP 0x8159 +#define GL_IMAGE_ROTATE_ORIGIN_X_HP 0x815A +#define GL_IMAGE_ROTATE_ORIGIN_Y_HP 0x815B +#define GL_IMAGE_MAG_FILTER_HP 0x815C +#define GL_IMAGE_MIN_FILTER_HP 0x815D +#define GL_IMAGE_CUBIC_WEIGHT_HP 0x815E +#define GL_CUBIC_HP 0x815F +#define GL_AVERAGE_HP 0x8160 +#define GL_IMAGE_TRANSFORM_2D_HP 0x8161 +#define GL_POST_IMAGE_TRANSFORM_COLOR_TABLE_HP 0x8162 +#define GL_PROXY_POST_IMAGE_TRANSFORM_COLOR_TABLE_HP 0x8163 +typedef void (APIENTRYP PFNGLIMAGETRANSFORMPARAMETERIHPPROC) (GLenum target, GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLIMAGETRANSFORMPARAMETERFHPPROC) (GLenum target, GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLIMAGETRANSFORMPARAMETERIVHPPROC) (GLenum target, GLenum pname, const GLint *params); +typedef void (APIENTRYP PFNGLIMAGETRANSFORMPARAMETERFVHPPROC) (GLenum target, GLenum pname, const GLfloat *params); +typedef void (APIENTRYP PFNGLGETIMAGETRANSFORMPARAMETERIVHPPROC) (GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETIMAGETRANSFORMPARAMETERFVHPPROC) (GLenum target, GLenum pname, GLfloat *params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glImageTransformParameteriHP (GLenum target, GLenum pname, GLint param); +GLAPI void APIENTRY glImageTransformParameterfHP (GLenum target, GLenum pname, GLfloat param); +GLAPI void APIENTRY glImageTransformParameterivHP (GLenum target, GLenum pname, const GLint *params); +GLAPI void APIENTRY glImageTransformParameterfvHP (GLenum target, GLenum pname, const GLfloat *params); +GLAPI void APIENTRY glGetImageTransformParameterivHP (GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetImageTransformParameterfvHP (GLenum target, GLenum pname, GLfloat *params); +#endif +#endif /* GL_HP_image_transform */ + +#ifndef GL_HP_occlusion_test +#define GL_HP_occlusion_test 1 +#define GL_OCCLUSION_TEST_HP 0x8165 +#define GL_OCCLUSION_TEST_RESULT_HP 0x8166 +#endif /* GL_HP_occlusion_test */ + +#ifndef GL_HP_texture_lighting +#define GL_HP_texture_lighting 1 +#define GL_TEXTURE_LIGHTING_MODE_HP 0x8167 +#define GL_TEXTURE_POST_SPECULAR_HP 0x8168 +#define GL_TEXTURE_PRE_SPECULAR_HP 0x8169 +#endif /* GL_HP_texture_lighting */ + +#ifndef GL_IBM_cull_vertex +#define GL_IBM_cull_vertex 1 +#define GL_CULL_VERTEX_IBM 103050 +#endif /* GL_IBM_cull_vertex */ + +#ifndef GL_IBM_multimode_draw_arrays +#define GL_IBM_multimode_draw_arrays 1 +typedef void (APIENTRYP PFNGLMULTIMODEDRAWARRAYSIBMPROC) (const GLenum *mode, const GLint *first, const GLsizei *count, GLsizei primcount, GLint modestride); +typedef void (APIENTRYP PFNGLMULTIMODEDRAWELEMENTSIBMPROC) (const GLenum *mode, const GLsizei *count, GLenum type, const void *const*indices, GLsizei primcount, GLint modestride); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glMultiModeDrawArraysIBM (const GLenum *mode, const GLint *first, const GLsizei *count, GLsizei primcount, GLint modestride); +GLAPI void APIENTRY glMultiModeDrawElementsIBM (const GLenum *mode, const GLsizei *count, GLenum type, const void *const*indices, GLsizei primcount, GLint modestride); +#endif +#endif /* GL_IBM_multimode_draw_arrays */ + +#ifndef GL_IBM_rasterpos_clip +#define GL_IBM_rasterpos_clip 1 +#define GL_RASTER_POSITION_UNCLIPPED_IBM 0x19262 +#endif /* GL_IBM_rasterpos_clip */ + +#ifndef GL_IBM_static_data +#define GL_IBM_static_data 1 +#define GL_ALL_STATIC_DATA_IBM 103060 +#define GL_STATIC_VERTEX_ARRAY_IBM 103061 +typedef void (APIENTRYP PFNGLFLUSHSTATICDATAIBMPROC) (GLenum target); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glFlushStaticDataIBM (GLenum target); +#endif +#endif /* GL_IBM_static_data */ + +#ifndef GL_IBM_texture_mirrored_repeat +#define GL_IBM_texture_mirrored_repeat 1 +#define GL_MIRRORED_REPEAT_IBM 0x8370 +#endif /* GL_IBM_texture_mirrored_repeat */ + +#ifndef GL_IBM_vertex_array_lists +#define GL_IBM_vertex_array_lists 1 +#define GL_VERTEX_ARRAY_LIST_IBM 103070 +#define GL_NORMAL_ARRAY_LIST_IBM 103071 +#define GL_COLOR_ARRAY_LIST_IBM 103072 +#define GL_INDEX_ARRAY_LIST_IBM 103073 +#define GL_TEXTURE_COORD_ARRAY_LIST_IBM 103074 +#define GL_EDGE_FLAG_ARRAY_LIST_IBM 103075 +#define GL_FOG_COORDINATE_ARRAY_LIST_IBM 103076 +#define GL_SECONDARY_COLOR_ARRAY_LIST_IBM 103077 +#define GL_VERTEX_ARRAY_LIST_STRIDE_IBM 103080 +#define GL_NORMAL_ARRAY_LIST_STRIDE_IBM 103081 +#define GL_COLOR_ARRAY_LIST_STRIDE_IBM 103082 +#define GL_INDEX_ARRAY_LIST_STRIDE_IBM 103083 +#define GL_TEXTURE_COORD_ARRAY_LIST_STRIDE_IBM 103084 +#define GL_EDGE_FLAG_ARRAY_LIST_STRIDE_IBM 103085 +#define GL_FOG_COORDINATE_ARRAY_LIST_STRIDE_IBM 103086 +#define GL_SECONDARY_COLOR_ARRAY_LIST_STRIDE_IBM 103087 +typedef void (APIENTRYP PFNGLCOLORPOINTERLISTIBMPROC) (GLint size, GLenum type, GLint stride, const void **pointer, GLint ptrstride); +typedef void (APIENTRYP PFNGLSECONDARYCOLORPOINTERLISTIBMPROC) (GLint size, GLenum type, GLint stride, const void **pointer, GLint ptrstride); +typedef void (APIENTRYP PFNGLEDGEFLAGPOINTERLISTIBMPROC) (GLint stride, const GLboolean **pointer, GLint ptrstride); +typedef void (APIENTRYP PFNGLFOGCOORDPOINTERLISTIBMPROC) (GLenum type, GLint stride, const void **pointer, GLint ptrstride); +typedef void (APIENTRYP PFNGLINDEXPOINTERLISTIBMPROC) (GLenum type, GLint stride, const void **pointer, GLint ptrstride); +typedef void (APIENTRYP PFNGLNORMALPOINTERLISTIBMPROC) (GLenum type, GLint stride, const void **pointer, GLint ptrstride); +typedef void (APIENTRYP PFNGLTEXCOORDPOINTERLISTIBMPROC) (GLint size, GLenum type, GLint stride, const void **pointer, GLint ptrstride); +typedef void (APIENTRYP PFNGLVERTEXPOINTERLISTIBMPROC) (GLint size, GLenum type, GLint stride, const void **pointer, GLint ptrstride); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glColorPointerListIBM (GLint size, GLenum type, GLint stride, const void **pointer, GLint ptrstride); +GLAPI void APIENTRY glSecondaryColorPointerListIBM (GLint size, GLenum type, GLint stride, const void **pointer, GLint ptrstride); +GLAPI void APIENTRY glEdgeFlagPointerListIBM (GLint stride, const GLboolean **pointer, GLint ptrstride); +GLAPI void APIENTRY glFogCoordPointerListIBM (GLenum type, GLint stride, const void **pointer, GLint ptrstride); +GLAPI void APIENTRY glIndexPointerListIBM (GLenum type, GLint stride, const void **pointer, GLint ptrstride); +GLAPI void APIENTRY glNormalPointerListIBM (GLenum type, GLint stride, const void **pointer, GLint ptrstride); +GLAPI void APIENTRY glTexCoordPointerListIBM (GLint size, GLenum type, GLint stride, const void **pointer, GLint ptrstride); +GLAPI void APIENTRY glVertexPointerListIBM (GLint size, GLenum type, GLint stride, const void **pointer, GLint ptrstride); +#endif +#endif /* GL_IBM_vertex_array_lists */ + +#ifndef GL_INGR_blend_func_separate +#define GL_INGR_blend_func_separate 1 +typedef void (APIENTRYP PFNGLBLENDFUNCSEPARATEINGRPROC) (GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBlendFuncSeparateINGR (GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha); +#endif +#endif /* GL_INGR_blend_func_separate */ + +#ifndef GL_INGR_color_clamp +#define GL_INGR_color_clamp 1 +#define GL_RED_MIN_CLAMP_INGR 0x8560 +#define GL_GREEN_MIN_CLAMP_INGR 0x8561 +#define GL_BLUE_MIN_CLAMP_INGR 0x8562 +#define GL_ALPHA_MIN_CLAMP_INGR 0x8563 +#define GL_RED_MAX_CLAMP_INGR 0x8564 +#define GL_GREEN_MAX_CLAMP_INGR 0x8565 +#define GL_BLUE_MAX_CLAMP_INGR 0x8566 +#define GL_ALPHA_MAX_CLAMP_INGR 0x8567 +#endif /* GL_INGR_color_clamp */ + +#ifndef GL_INGR_interlace_read +#define GL_INGR_interlace_read 1 +#define GL_INTERLACE_READ_INGR 0x8568 +#endif /* GL_INGR_interlace_read */ + +#ifndef GL_INTEL_fragment_shader_ordering +#define GL_INTEL_fragment_shader_ordering 1 +#endif /* GL_INTEL_fragment_shader_ordering */ + +#ifndef GL_INTEL_map_texture +#define GL_INTEL_map_texture 1 +#define GL_TEXTURE_MEMORY_LAYOUT_INTEL 0x83FF +#define GL_LAYOUT_DEFAULT_INTEL 0 +#define GL_LAYOUT_LINEAR_INTEL 1 +#define GL_LAYOUT_LINEAR_CPU_CACHED_INTEL 2 +typedef void (APIENTRYP PFNGLSYNCTEXTUREINTELPROC) (GLuint texture); +typedef void (APIENTRYP PFNGLUNMAPTEXTURE2DINTELPROC) (GLuint texture, GLint level); +typedef void *(APIENTRYP PFNGLMAPTEXTURE2DINTELPROC) (GLuint texture, GLint level, GLbitfield access, GLint *stride, GLenum *layout); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glSyncTextureINTEL (GLuint texture); +GLAPI void APIENTRY glUnmapTexture2DINTEL (GLuint texture, GLint level); +GLAPI void *APIENTRY glMapTexture2DINTEL (GLuint texture, GLint level, GLbitfield access, GLint *stride, GLenum *layout); +#endif +#endif /* GL_INTEL_map_texture */ + +#ifndef GL_INTEL_parallel_arrays +#define GL_INTEL_parallel_arrays 1 +#define GL_PARALLEL_ARRAYS_INTEL 0x83F4 +#define GL_VERTEX_ARRAY_PARALLEL_POINTERS_INTEL 0x83F5 +#define GL_NORMAL_ARRAY_PARALLEL_POINTERS_INTEL 0x83F6 +#define GL_COLOR_ARRAY_PARALLEL_POINTERS_INTEL 0x83F7 +#define GL_TEXTURE_COORD_ARRAY_PARALLEL_POINTERS_INTEL 0x83F8 +typedef void (APIENTRYP PFNGLVERTEXPOINTERVINTELPROC) (GLint size, GLenum type, const void **pointer); +typedef void (APIENTRYP PFNGLNORMALPOINTERVINTELPROC) (GLenum type, const void **pointer); +typedef void (APIENTRYP PFNGLCOLORPOINTERVINTELPROC) (GLint size, GLenum type, const void **pointer); +typedef void (APIENTRYP PFNGLTEXCOORDPOINTERVINTELPROC) (GLint size, GLenum type, const void **pointer); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glVertexPointervINTEL (GLint size, GLenum type, const void **pointer); +GLAPI void APIENTRY glNormalPointervINTEL (GLenum type, const void **pointer); +GLAPI void APIENTRY glColorPointervINTEL (GLint size, GLenum type, const void **pointer); +GLAPI void APIENTRY glTexCoordPointervINTEL (GLint size, GLenum type, const void **pointer); +#endif +#endif /* GL_INTEL_parallel_arrays */ + +#ifndef GL_INTEL_performance_query +#define GL_INTEL_performance_query 1 +#define GL_PERFQUERY_SINGLE_CONTEXT_INTEL 0x00000000 +#define GL_PERFQUERY_GLOBAL_CONTEXT_INTEL 0x00000001 +#define GL_PERFQUERY_WAIT_INTEL 0x83FB +#define GL_PERFQUERY_FLUSH_INTEL 0x83FA +#define GL_PERFQUERY_DONOT_FLUSH_INTEL 0x83F9 +#define GL_PERFQUERY_COUNTER_EVENT_INTEL 0x94F0 +#define GL_PERFQUERY_COUNTER_DURATION_NORM_INTEL 0x94F1 +#define GL_PERFQUERY_COUNTER_DURATION_RAW_INTEL 0x94F2 +#define GL_PERFQUERY_COUNTER_THROUGHPUT_INTEL 0x94F3 +#define GL_PERFQUERY_COUNTER_RAW_INTEL 0x94F4 +#define GL_PERFQUERY_COUNTER_TIMESTAMP_INTEL 0x94F5 +#define GL_PERFQUERY_COUNTER_DATA_UINT32_INTEL 0x94F8 +#define GL_PERFQUERY_COUNTER_DATA_UINT64_INTEL 0x94F9 +#define GL_PERFQUERY_COUNTER_DATA_FLOAT_INTEL 0x94FA +#define GL_PERFQUERY_COUNTER_DATA_DOUBLE_INTEL 0x94FB +#define GL_PERFQUERY_COUNTER_DATA_BOOL32_INTEL 0x94FC +#define GL_PERFQUERY_QUERY_NAME_LENGTH_MAX_INTEL 0x94FD +#define GL_PERFQUERY_COUNTER_NAME_LENGTH_MAX_INTEL 0x94FE +#define GL_PERFQUERY_COUNTER_DESC_LENGTH_MAX_INTEL 0x94FF +#define GL_PERFQUERY_GPA_EXTENDED_COUNTERS_INTEL 0x9500 +typedef void (APIENTRYP PFNGLBEGINPERFQUERYINTELPROC) (GLuint queryHandle); +typedef void (APIENTRYP PFNGLCREATEPERFQUERYINTELPROC) (GLuint queryId, GLuint *queryHandle); +typedef void (APIENTRYP PFNGLDELETEPERFQUERYINTELPROC) (GLuint queryHandle); +typedef void (APIENTRYP PFNGLENDPERFQUERYINTELPROC) (GLuint queryHandle); +typedef void (APIENTRYP PFNGLGETFIRSTPERFQUERYIDINTELPROC) (GLuint *queryId); +typedef void (APIENTRYP PFNGLGETNEXTPERFQUERYIDINTELPROC) (GLuint queryId, GLuint *nextQueryId); +typedef void (APIENTRYP PFNGLGETPERFCOUNTERINFOINTELPROC) (GLuint queryId, GLuint counterId, GLuint counterNameLength, GLchar *counterName, GLuint counterDescLength, GLchar *counterDesc, GLuint *counterOffset, GLuint *counterDataSize, GLuint *counterTypeEnum, GLuint *counterDataTypeEnum, GLuint64 *rawCounterMaxValue); +typedef void (APIENTRYP PFNGLGETPERFQUERYDATAINTELPROC) (GLuint queryHandle, GLuint flags, GLsizei dataSize, GLvoid *data, GLuint *bytesWritten); +typedef void (APIENTRYP PFNGLGETPERFQUERYIDBYNAMEINTELPROC) (GLchar *queryName, GLuint *queryId); +typedef void (APIENTRYP PFNGLGETPERFQUERYINFOINTELPROC) (GLuint queryId, GLuint queryNameLength, GLchar *queryName, GLuint *dataSize, GLuint *noCounters, GLuint *noInstances, GLuint *capsMask); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBeginPerfQueryINTEL (GLuint queryHandle); +GLAPI void APIENTRY glCreatePerfQueryINTEL (GLuint queryId, GLuint *queryHandle); +GLAPI void APIENTRY glDeletePerfQueryINTEL (GLuint queryHandle); +GLAPI void APIENTRY glEndPerfQueryINTEL (GLuint queryHandle); +GLAPI void APIENTRY glGetFirstPerfQueryIdINTEL (GLuint *queryId); +GLAPI void APIENTRY glGetNextPerfQueryIdINTEL (GLuint queryId, GLuint *nextQueryId); +GLAPI void APIENTRY glGetPerfCounterInfoINTEL (GLuint queryId, GLuint counterId, GLuint counterNameLength, GLchar *counterName, GLuint counterDescLength, GLchar *counterDesc, GLuint *counterOffset, GLuint *counterDataSize, GLuint *counterTypeEnum, GLuint *counterDataTypeEnum, GLuint64 *rawCounterMaxValue); +GLAPI void APIENTRY glGetPerfQueryDataINTEL (GLuint queryHandle, GLuint flags, GLsizei dataSize, GLvoid *data, GLuint *bytesWritten); +GLAPI void APIENTRY glGetPerfQueryIdByNameINTEL (GLchar *queryName, GLuint *queryId); +GLAPI void APIENTRY glGetPerfQueryInfoINTEL (GLuint queryId, GLuint queryNameLength, GLchar *queryName, GLuint *dataSize, GLuint *noCounters, GLuint *noInstances, GLuint *capsMask); +#endif +#endif /* GL_INTEL_performance_query */ + +#ifndef GL_MESAX_texture_stack +#define GL_MESAX_texture_stack 1 +#define GL_TEXTURE_1D_STACK_MESAX 0x8759 +#define GL_TEXTURE_2D_STACK_MESAX 0x875A +#define GL_PROXY_TEXTURE_1D_STACK_MESAX 0x875B +#define GL_PROXY_TEXTURE_2D_STACK_MESAX 0x875C +#define GL_TEXTURE_1D_STACK_BINDING_MESAX 0x875D +#define GL_TEXTURE_2D_STACK_BINDING_MESAX 0x875E +#endif /* GL_MESAX_texture_stack */ + +#ifndef GL_MESA_pack_invert +#define GL_MESA_pack_invert 1 +#define GL_PACK_INVERT_MESA 0x8758 +#endif /* GL_MESA_pack_invert */ + +#ifndef GL_MESA_resize_buffers +#define GL_MESA_resize_buffers 1 +typedef void (APIENTRYP PFNGLRESIZEBUFFERSMESAPROC) (void); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glResizeBuffersMESA (void); +#endif +#endif /* GL_MESA_resize_buffers */ + +#ifndef GL_MESA_window_pos +#define GL_MESA_window_pos 1 +typedef void (APIENTRYP PFNGLWINDOWPOS2DMESAPROC) (GLdouble x, GLdouble y); +typedef void (APIENTRYP PFNGLWINDOWPOS2DVMESAPROC) (const GLdouble *v); +typedef void (APIENTRYP PFNGLWINDOWPOS2FMESAPROC) (GLfloat x, GLfloat y); +typedef void (APIENTRYP PFNGLWINDOWPOS2FVMESAPROC) (const GLfloat *v); +typedef void (APIENTRYP PFNGLWINDOWPOS2IMESAPROC) (GLint x, GLint y); +typedef void (APIENTRYP PFNGLWINDOWPOS2IVMESAPROC) (const GLint *v); +typedef void (APIENTRYP PFNGLWINDOWPOS2SMESAPROC) (GLshort x, GLshort y); +typedef void (APIENTRYP PFNGLWINDOWPOS2SVMESAPROC) (const GLshort *v); +typedef void (APIENTRYP PFNGLWINDOWPOS3DMESAPROC) (GLdouble x, GLdouble y, GLdouble z); +typedef void (APIENTRYP PFNGLWINDOWPOS3DVMESAPROC) (const GLdouble *v); +typedef void (APIENTRYP PFNGLWINDOWPOS3FMESAPROC) (GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLWINDOWPOS3FVMESAPROC) (const GLfloat *v); +typedef void (APIENTRYP PFNGLWINDOWPOS3IMESAPROC) (GLint x, GLint y, GLint z); +typedef void (APIENTRYP PFNGLWINDOWPOS3IVMESAPROC) (const GLint *v); +typedef void (APIENTRYP PFNGLWINDOWPOS3SMESAPROC) (GLshort x, GLshort y, GLshort z); +typedef void (APIENTRYP PFNGLWINDOWPOS3SVMESAPROC) (const GLshort *v); +typedef void (APIENTRYP PFNGLWINDOWPOS4DMESAPROC) (GLdouble x, GLdouble y, GLdouble z, GLdouble w); +typedef void (APIENTRYP PFNGLWINDOWPOS4DVMESAPROC) (const GLdouble *v); +typedef void (APIENTRYP PFNGLWINDOWPOS4FMESAPROC) (GLfloat x, GLfloat y, GLfloat z, GLfloat w); +typedef void (APIENTRYP PFNGLWINDOWPOS4FVMESAPROC) (const GLfloat *v); +typedef void (APIENTRYP PFNGLWINDOWPOS4IMESAPROC) (GLint x, GLint y, GLint z, GLint w); +typedef void (APIENTRYP PFNGLWINDOWPOS4IVMESAPROC) (const GLint *v); +typedef void (APIENTRYP PFNGLWINDOWPOS4SMESAPROC) (GLshort x, GLshort y, GLshort z, GLshort w); +typedef void (APIENTRYP PFNGLWINDOWPOS4SVMESAPROC) (const GLshort *v); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glWindowPos2dMESA (GLdouble x, GLdouble y); +GLAPI void APIENTRY glWindowPos2dvMESA (const GLdouble *v); +GLAPI void APIENTRY glWindowPos2fMESA (GLfloat x, GLfloat y); +GLAPI void APIENTRY glWindowPos2fvMESA (const GLfloat *v); +GLAPI void APIENTRY glWindowPos2iMESA (GLint x, GLint y); +GLAPI void APIENTRY glWindowPos2ivMESA (const GLint *v); +GLAPI void APIENTRY glWindowPos2sMESA (GLshort x, GLshort y); +GLAPI void APIENTRY glWindowPos2svMESA (const GLshort *v); +GLAPI void APIENTRY glWindowPos3dMESA (GLdouble x, GLdouble y, GLdouble z); +GLAPI void APIENTRY glWindowPos3dvMESA (const GLdouble *v); +GLAPI void APIENTRY glWindowPos3fMESA (GLfloat x, GLfloat y, GLfloat z); +GLAPI void APIENTRY glWindowPos3fvMESA (const GLfloat *v); +GLAPI void APIENTRY glWindowPos3iMESA (GLint x, GLint y, GLint z); +GLAPI void APIENTRY glWindowPos3ivMESA (const GLint *v); +GLAPI void APIENTRY glWindowPos3sMESA (GLshort x, GLshort y, GLshort z); +GLAPI void APIENTRY glWindowPos3svMESA (const GLshort *v); +GLAPI void APIENTRY glWindowPos4dMESA (GLdouble x, GLdouble y, GLdouble z, GLdouble w); +GLAPI void APIENTRY glWindowPos4dvMESA (const GLdouble *v); +GLAPI void APIENTRY glWindowPos4fMESA (GLfloat x, GLfloat y, GLfloat z, GLfloat w); +GLAPI void APIENTRY glWindowPos4fvMESA (const GLfloat *v); +GLAPI void APIENTRY glWindowPos4iMESA (GLint x, GLint y, GLint z, GLint w); +GLAPI void APIENTRY glWindowPos4ivMESA (const GLint *v); +GLAPI void APIENTRY glWindowPos4sMESA (GLshort x, GLshort y, GLshort z, GLshort w); +GLAPI void APIENTRY glWindowPos4svMESA (const GLshort *v); +#endif +#endif /* GL_MESA_window_pos */ + +#ifndef GL_MESA_ycbcr_texture +#define GL_MESA_ycbcr_texture 1 +#define GL_UNSIGNED_SHORT_8_8_MESA 0x85BA +#define GL_UNSIGNED_SHORT_8_8_REV_MESA 0x85BB +#define GL_YCBCR_MESA 0x8757 +#endif /* GL_MESA_ycbcr_texture */ + +#ifndef GL_NVX_conditional_render +#define GL_NVX_conditional_render 1 +typedef void (APIENTRYP PFNGLBEGINCONDITIONALRENDERNVXPROC) (GLuint id); +typedef void (APIENTRYP PFNGLENDCONDITIONALRENDERNVXPROC) (void); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBeginConditionalRenderNVX (GLuint id); +GLAPI void APIENTRY glEndConditionalRenderNVX (void); +#endif +#endif /* GL_NVX_conditional_render */ + +#ifndef GL_NVX_gpu_memory_info +#define GL_NVX_gpu_memory_info 1 +#define GL_GPU_MEMORY_INFO_DEDICATED_VIDMEM_NVX 0x9047 +#define GL_GPU_MEMORY_INFO_TOTAL_AVAILABLE_MEMORY_NVX 0x9048 +#define GL_GPU_MEMORY_INFO_CURRENT_AVAILABLE_VIDMEM_NVX 0x9049 +#define GL_GPU_MEMORY_INFO_EVICTION_COUNT_NVX 0x904A +#define GL_GPU_MEMORY_INFO_EVICTED_MEMORY_NVX 0x904B +#endif /* GL_NVX_gpu_memory_info */ + +#ifndef GL_NV_bindless_multi_draw_indirect +#define GL_NV_bindless_multi_draw_indirect 1 +typedef void (APIENTRYP PFNGLMULTIDRAWARRAYSINDIRECTBINDLESSNVPROC) (GLenum mode, const void *indirect, GLsizei drawCount, GLsizei stride, GLint vertexBufferCount); +typedef void (APIENTRYP PFNGLMULTIDRAWELEMENTSINDIRECTBINDLESSNVPROC) (GLenum mode, GLenum type, const void *indirect, GLsizei drawCount, GLsizei stride, GLint vertexBufferCount); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glMultiDrawArraysIndirectBindlessNV (GLenum mode, const void *indirect, GLsizei drawCount, GLsizei stride, GLint vertexBufferCount); +GLAPI void APIENTRY glMultiDrawElementsIndirectBindlessNV (GLenum mode, GLenum type, const void *indirect, GLsizei drawCount, GLsizei stride, GLint vertexBufferCount); +#endif +#endif /* GL_NV_bindless_multi_draw_indirect */ + +#ifndef GL_NV_bindless_multi_draw_indirect_count +#define GL_NV_bindless_multi_draw_indirect_count 1 +typedef void (APIENTRYP PFNGLMULTIDRAWARRAYSINDIRECTBINDLESSCOUNTNVPROC) (GLenum mode, const void *indirect, GLsizei drawCount, GLsizei maxDrawCount, GLsizei stride, GLint vertexBufferCount); +typedef void (APIENTRYP PFNGLMULTIDRAWELEMENTSINDIRECTBINDLESSCOUNTNVPROC) (GLenum mode, GLenum type, const void *indirect, GLsizei drawCount, GLsizei maxDrawCount, GLsizei stride, GLint vertexBufferCount); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glMultiDrawArraysIndirectBindlessCountNV (GLenum mode, const void *indirect, GLsizei drawCount, GLsizei maxDrawCount, GLsizei stride, GLint vertexBufferCount); +GLAPI void APIENTRY glMultiDrawElementsIndirectBindlessCountNV (GLenum mode, GLenum type, const void *indirect, GLsizei drawCount, GLsizei maxDrawCount, GLsizei stride, GLint vertexBufferCount); +#endif +#endif /* GL_NV_bindless_multi_draw_indirect_count */ + +#ifndef GL_NV_bindless_texture +#define GL_NV_bindless_texture 1 +typedef GLuint64 (APIENTRYP PFNGLGETTEXTUREHANDLENVPROC) (GLuint texture); +typedef GLuint64 (APIENTRYP PFNGLGETTEXTURESAMPLERHANDLENVPROC) (GLuint texture, GLuint sampler); +typedef void (APIENTRYP PFNGLMAKETEXTUREHANDLERESIDENTNVPROC) (GLuint64 handle); +typedef void (APIENTRYP PFNGLMAKETEXTUREHANDLENONRESIDENTNVPROC) (GLuint64 handle); +typedef GLuint64 (APIENTRYP PFNGLGETIMAGEHANDLENVPROC) (GLuint texture, GLint level, GLboolean layered, GLint layer, GLenum format); +typedef void (APIENTRYP PFNGLMAKEIMAGEHANDLERESIDENTNVPROC) (GLuint64 handle, GLenum access); +typedef void (APIENTRYP PFNGLMAKEIMAGEHANDLENONRESIDENTNVPROC) (GLuint64 handle); +typedef void (APIENTRYP PFNGLUNIFORMHANDLEUI64NVPROC) (GLint location, GLuint64 value); +typedef void (APIENTRYP PFNGLUNIFORMHANDLEUI64VNVPROC) (GLint location, GLsizei count, const GLuint64 *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMHANDLEUI64NVPROC) (GLuint program, GLint location, GLuint64 value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMHANDLEUI64VNVPROC) (GLuint program, GLint location, GLsizei count, const GLuint64 *values); +typedef GLboolean (APIENTRYP PFNGLISTEXTUREHANDLERESIDENTNVPROC) (GLuint64 handle); +typedef GLboolean (APIENTRYP PFNGLISIMAGEHANDLERESIDENTNVPROC) (GLuint64 handle); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI GLuint64 APIENTRY glGetTextureHandleNV (GLuint texture); +GLAPI GLuint64 APIENTRY glGetTextureSamplerHandleNV (GLuint texture, GLuint sampler); +GLAPI void APIENTRY glMakeTextureHandleResidentNV (GLuint64 handle); +GLAPI void APIENTRY glMakeTextureHandleNonResidentNV (GLuint64 handle); +GLAPI GLuint64 APIENTRY glGetImageHandleNV (GLuint texture, GLint level, GLboolean layered, GLint layer, GLenum format); +GLAPI void APIENTRY glMakeImageHandleResidentNV (GLuint64 handle, GLenum access); +GLAPI void APIENTRY glMakeImageHandleNonResidentNV (GLuint64 handle); +GLAPI void APIENTRY glUniformHandleui64NV (GLint location, GLuint64 value); +GLAPI void APIENTRY glUniformHandleui64vNV (GLint location, GLsizei count, const GLuint64 *value); +GLAPI void APIENTRY glProgramUniformHandleui64NV (GLuint program, GLint location, GLuint64 value); +GLAPI void APIENTRY glProgramUniformHandleui64vNV (GLuint program, GLint location, GLsizei count, const GLuint64 *values); +GLAPI GLboolean APIENTRY glIsTextureHandleResidentNV (GLuint64 handle); +GLAPI GLboolean APIENTRY glIsImageHandleResidentNV (GLuint64 handle); +#endif +#endif /* GL_NV_bindless_texture */ + +#ifndef GL_NV_blend_equation_advanced +#define GL_NV_blend_equation_advanced 1 +#define GL_BLEND_OVERLAP_NV 0x9281 +#define GL_BLEND_PREMULTIPLIED_SRC_NV 0x9280 +#define GL_BLUE_NV 0x1905 +#define GL_COLORBURN_NV 0x929A +#define GL_COLORDODGE_NV 0x9299 +#define GL_CONJOINT_NV 0x9284 +#define GL_CONTRAST_NV 0x92A1 +#define GL_DARKEN_NV 0x9297 +#define GL_DIFFERENCE_NV 0x929E +#define GL_DISJOINT_NV 0x9283 +#define GL_DST_ATOP_NV 0x928F +#define GL_DST_IN_NV 0x928B +#define GL_DST_NV 0x9287 +#define GL_DST_OUT_NV 0x928D +#define GL_DST_OVER_NV 0x9289 +#define GL_EXCLUSION_NV 0x92A0 +#define GL_GREEN_NV 0x1904 +#define GL_HARDLIGHT_NV 0x929B +#define GL_HARDMIX_NV 0x92A9 +#define GL_HSL_COLOR_NV 0x92AF +#define GL_HSL_HUE_NV 0x92AD +#define GL_HSL_LUMINOSITY_NV 0x92B0 +#define GL_HSL_SATURATION_NV 0x92AE +#define GL_INVERT_OVG_NV 0x92B4 +#define GL_INVERT_RGB_NV 0x92A3 +#define GL_LIGHTEN_NV 0x9298 +#define GL_LINEARBURN_NV 0x92A5 +#define GL_LINEARDODGE_NV 0x92A4 +#define GL_LINEARLIGHT_NV 0x92A7 +#define GL_MINUS_CLAMPED_NV 0x92B3 +#define GL_MINUS_NV 0x929F +#define GL_MULTIPLY_NV 0x9294 +#define GL_OVERLAY_NV 0x9296 +#define GL_PINLIGHT_NV 0x92A8 +#define GL_PLUS_CLAMPED_ALPHA_NV 0x92B2 +#define GL_PLUS_CLAMPED_NV 0x92B1 +#define GL_PLUS_DARKER_NV 0x9292 +#define GL_PLUS_NV 0x9291 +#define GL_RED_NV 0x1903 +#define GL_SCREEN_NV 0x9295 +#define GL_SOFTLIGHT_NV 0x929C +#define GL_SRC_ATOP_NV 0x928E +#define GL_SRC_IN_NV 0x928A +#define GL_SRC_NV 0x9286 +#define GL_SRC_OUT_NV 0x928C +#define GL_SRC_OVER_NV 0x9288 +#define GL_UNCORRELATED_NV 0x9282 +#define GL_VIVIDLIGHT_NV 0x92A6 +#define GL_XOR_NV 0x1506 +typedef void (APIENTRYP PFNGLBLENDPARAMETERINVPROC) (GLenum pname, GLint value); +typedef void (APIENTRYP PFNGLBLENDBARRIERNVPROC) (void); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBlendParameteriNV (GLenum pname, GLint value); +GLAPI void APIENTRY glBlendBarrierNV (void); +#endif +#endif /* GL_NV_blend_equation_advanced */ + +#ifndef GL_NV_blend_equation_advanced_coherent +#define GL_NV_blend_equation_advanced_coherent 1 +#define GL_BLEND_ADVANCED_COHERENT_NV 0x9285 +#endif /* GL_NV_blend_equation_advanced_coherent */ + +#ifndef GL_NV_blend_square +#define GL_NV_blend_square 1 +#endif /* GL_NV_blend_square */ + +#ifndef GL_NV_command_list +#define GL_NV_command_list 1 +#define GL_TERMINATE_SEQUENCE_COMMAND_NV 0x0000 +#define GL_NOP_COMMAND_NV 0x0001 +#define GL_DRAW_ELEMENTS_COMMAND_NV 0x0002 +#define GL_DRAW_ARRAYS_COMMAND_NV 0x0003 +#define GL_DRAW_ELEMENTS_STRIP_COMMAND_NV 0x0004 +#define GL_DRAW_ARRAYS_STRIP_COMMAND_NV 0x0005 +#define GL_DRAW_ELEMENTS_INSTANCED_COMMAND_NV 0x0006 +#define GL_DRAW_ARRAYS_INSTANCED_COMMAND_NV 0x0007 +#define GL_ELEMENT_ADDRESS_COMMAND_NV 0x0008 +#define GL_ATTRIBUTE_ADDRESS_COMMAND_NV 0x0009 +#define GL_UNIFORM_ADDRESS_COMMAND_NV 0x000A +#define GL_BLEND_COLOR_COMMAND_NV 0x000B +#define GL_STENCIL_REF_COMMAND_NV 0x000C +#define GL_LINE_WIDTH_COMMAND_NV 0x000D +#define GL_POLYGON_OFFSET_COMMAND_NV 0x000E +#define GL_ALPHA_REF_COMMAND_NV 0x000F +#define GL_VIEWPORT_COMMAND_NV 0x0010 +#define GL_SCISSOR_COMMAND_NV 0x0011 +#define GL_FRONT_FACE_COMMAND_NV 0x0012 +typedef void (APIENTRYP PFNGLCREATESTATESNVPROC) (GLsizei n, GLuint *states); +typedef void (APIENTRYP PFNGLDELETESTATESNVPROC) (GLsizei n, const GLuint *states); +typedef GLboolean (APIENTRYP PFNGLISSTATENVPROC) (GLuint state); +typedef void (APIENTRYP PFNGLSTATECAPTURENVPROC) (GLuint state, GLenum mode); +typedef GLuint (APIENTRYP PFNGLGETCOMMANDHEADERNVPROC) (GLenum tokenID, GLuint size); +typedef GLushort (APIENTRYP PFNGLGETSTAGEINDEXNVPROC) (GLenum shadertype); +typedef void (APIENTRYP PFNGLDRAWCOMMANDSNVPROC) (GLenum primitiveMode, GLuint buffer, const GLintptr *indirects, const GLsizei *sizes, GLuint count); +typedef void (APIENTRYP PFNGLDRAWCOMMANDSADDRESSNVPROC) (GLenum primitiveMode, const GLuint64 *indirects, const GLsizei *sizes, GLuint count); +typedef void (APIENTRYP PFNGLDRAWCOMMANDSSTATESNVPROC) (GLuint buffer, const GLintptr *indirects, const GLsizei *sizes, const GLuint *states, const GLuint *fbos, GLuint count); +typedef void (APIENTRYP PFNGLDRAWCOMMANDSSTATESADDRESSNVPROC) (const GLuint64 *indirects, const GLsizei *sizes, const GLuint *states, const GLuint *fbos, GLuint count); +typedef void (APIENTRYP PFNGLCREATECOMMANDLISTSNVPROC) (GLsizei n, GLuint *lists); +typedef void (APIENTRYP PFNGLDELETECOMMANDLISTSNVPROC) (GLsizei n, const GLuint *lists); +typedef GLboolean (APIENTRYP PFNGLISCOMMANDLISTNVPROC) (GLuint list); +typedef void (APIENTRYP PFNGLLISTDRAWCOMMANDSSTATESCLIENTNVPROC) (GLuint list, GLuint segment, const void **indirects, const GLsizei *sizes, const GLuint *states, const GLuint *fbos, GLuint count); +typedef void (APIENTRYP PFNGLCOMMANDLISTSEGMENTSNVPROC) (GLuint list, GLuint segments); +typedef void (APIENTRYP PFNGLCOMPILECOMMANDLISTNVPROC) (GLuint list); +typedef void (APIENTRYP PFNGLCALLCOMMANDLISTNVPROC) (GLuint list); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glCreateStatesNV (GLsizei n, GLuint *states); +GLAPI void APIENTRY glDeleteStatesNV (GLsizei n, const GLuint *states); +GLAPI GLboolean APIENTRY glIsStateNV (GLuint state); +GLAPI void APIENTRY glStateCaptureNV (GLuint state, GLenum mode); +GLAPI GLuint APIENTRY glGetCommandHeaderNV (GLenum tokenID, GLuint size); +GLAPI GLushort APIENTRY glGetStageIndexNV (GLenum shadertype); +GLAPI void APIENTRY glDrawCommandsNV (GLenum primitiveMode, GLuint buffer, const GLintptr *indirects, const GLsizei *sizes, GLuint count); +GLAPI void APIENTRY glDrawCommandsAddressNV (GLenum primitiveMode, const GLuint64 *indirects, const GLsizei *sizes, GLuint count); +GLAPI void APIENTRY glDrawCommandsStatesNV (GLuint buffer, const GLintptr *indirects, const GLsizei *sizes, const GLuint *states, const GLuint *fbos, GLuint count); +GLAPI void APIENTRY glDrawCommandsStatesAddressNV (const GLuint64 *indirects, const GLsizei *sizes, const GLuint *states, const GLuint *fbos, GLuint count); +GLAPI void APIENTRY glCreateCommandListsNV (GLsizei n, GLuint *lists); +GLAPI void APIENTRY glDeleteCommandListsNV (GLsizei n, const GLuint *lists); +GLAPI GLboolean APIENTRY glIsCommandListNV (GLuint list); +GLAPI void APIENTRY glListDrawCommandsStatesClientNV (GLuint list, GLuint segment, const void **indirects, const GLsizei *sizes, const GLuint *states, const GLuint *fbos, GLuint count); +GLAPI void APIENTRY glCommandListSegmentsNV (GLuint list, GLuint segments); +GLAPI void APIENTRY glCompileCommandListNV (GLuint list); +GLAPI void APIENTRY glCallCommandListNV (GLuint list); +#endif +#endif /* GL_NV_command_list */ + +#ifndef GL_NV_compute_program5 +#define GL_NV_compute_program5 1 +#define GL_COMPUTE_PROGRAM_NV 0x90FB +#define GL_COMPUTE_PROGRAM_PARAMETER_BUFFER_NV 0x90FC +#endif /* GL_NV_compute_program5 */ + +#ifndef GL_NV_conditional_render +#define GL_NV_conditional_render 1 +#define GL_QUERY_WAIT_NV 0x8E13 +#define GL_QUERY_NO_WAIT_NV 0x8E14 +#define GL_QUERY_BY_REGION_WAIT_NV 0x8E15 +#define GL_QUERY_BY_REGION_NO_WAIT_NV 0x8E16 +typedef void (APIENTRYP PFNGLBEGINCONDITIONALRENDERNVPROC) (GLuint id, GLenum mode); +typedef void (APIENTRYP PFNGLENDCONDITIONALRENDERNVPROC) (void); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBeginConditionalRenderNV (GLuint id, GLenum mode); +GLAPI void APIENTRY glEndConditionalRenderNV (void); +#endif +#endif /* GL_NV_conditional_render */ + +#ifndef GL_NV_conservative_raster +#define GL_NV_conservative_raster 1 +#define GL_CONSERVATIVE_RASTERIZATION_NV 0x9346 +#define GL_SUBPIXEL_PRECISION_BIAS_X_BITS_NV 0x9347 +#define GL_SUBPIXEL_PRECISION_BIAS_Y_BITS_NV 0x9348 +#define GL_MAX_SUBPIXEL_PRECISION_BIAS_BITS_NV 0x9349 +typedef void (APIENTRYP PFNGLSUBPIXELPRECISIONBIASNVPROC) (GLuint xbits, GLuint ybits); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glSubpixelPrecisionBiasNV (GLuint xbits, GLuint ybits); +#endif +#endif /* GL_NV_conservative_raster */ + +#ifndef GL_NV_copy_depth_to_color +#define GL_NV_copy_depth_to_color 1 +#define GL_DEPTH_STENCIL_TO_RGBA_NV 0x886E +#define GL_DEPTH_STENCIL_TO_BGRA_NV 0x886F +#endif /* GL_NV_copy_depth_to_color */ + +#ifndef GL_NV_copy_image +#define GL_NV_copy_image 1 +typedef void (APIENTRYP PFNGLCOPYIMAGESUBDATANVPROC) (GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srcY, GLint srcZ, GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei width, GLsizei height, GLsizei depth); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glCopyImageSubDataNV (GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srcY, GLint srcZ, GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei width, GLsizei height, GLsizei depth); +#endif +#endif /* GL_NV_copy_image */ + +#ifndef GL_NV_deep_texture3D +#define GL_NV_deep_texture3D 1 +#define GL_MAX_DEEP_3D_TEXTURE_WIDTH_HEIGHT_NV 0x90D0 +#define GL_MAX_DEEP_3D_TEXTURE_DEPTH_NV 0x90D1 +#endif /* GL_NV_deep_texture3D */ + +#ifndef GL_NV_depth_buffer_float +#define GL_NV_depth_buffer_float 1 +#define GL_DEPTH_COMPONENT32F_NV 0x8DAB +#define GL_DEPTH32F_STENCIL8_NV 0x8DAC +#define GL_FLOAT_32_UNSIGNED_INT_24_8_REV_NV 0x8DAD +#define GL_DEPTH_BUFFER_FLOAT_MODE_NV 0x8DAF +typedef void (APIENTRYP PFNGLDEPTHRANGEDNVPROC) (GLdouble zNear, GLdouble zFar); +typedef void (APIENTRYP PFNGLCLEARDEPTHDNVPROC) (GLdouble depth); +typedef void (APIENTRYP PFNGLDEPTHBOUNDSDNVPROC) (GLdouble zmin, GLdouble zmax); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glDepthRangedNV (GLdouble zNear, GLdouble zFar); +GLAPI void APIENTRY glClearDepthdNV (GLdouble depth); +GLAPI void APIENTRY glDepthBoundsdNV (GLdouble zmin, GLdouble zmax); +#endif +#endif /* GL_NV_depth_buffer_float */ + +#ifndef GL_NV_depth_clamp +#define GL_NV_depth_clamp 1 +#define GL_DEPTH_CLAMP_NV 0x864F +#endif /* GL_NV_depth_clamp */ + +#ifndef GL_NV_draw_texture +#define GL_NV_draw_texture 1 +typedef void (APIENTRYP PFNGLDRAWTEXTURENVPROC) (GLuint texture, GLuint sampler, GLfloat x0, GLfloat y0, GLfloat x1, GLfloat y1, GLfloat z, GLfloat s0, GLfloat t0, GLfloat s1, GLfloat t1); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glDrawTextureNV (GLuint texture, GLuint sampler, GLfloat x0, GLfloat y0, GLfloat x1, GLfloat y1, GLfloat z, GLfloat s0, GLfloat t0, GLfloat s1, GLfloat t1); +#endif +#endif /* GL_NV_draw_texture */ + +#ifndef GL_NV_evaluators +#define GL_NV_evaluators 1 +#define GL_EVAL_2D_NV 0x86C0 +#define GL_EVAL_TRIANGULAR_2D_NV 0x86C1 +#define GL_MAP_TESSELLATION_NV 0x86C2 +#define GL_MAP_ATTRIB_U_ORDER_NV 0x86C3 +#define GL_MAP_ATTRIB_V_ORDER_NV 0x86C4 +#define GL_EVAL_FRACTIONAL_TESSELLATION_NV 0x86C5 +#define GL_EVAL_VERTEX_ATTRIB0_NV 0x86C6 +#define GL_EVAL_VERTEX_ATTRIB1_NV 0x86C7 +#define GL_EVAL_VERTEX_ATTRIB2_NV 0x86C8 +#define GL_EVAL_VERTEX_ATTRIB3_NV 0x86C9 +#define GL_EVAL_VERTEX_ATTRIB4_NV 0x86CA +#define GL_EVAL_VERTEX_ATTRIB5_NV 0x86CB +#define GL_EVAL_VERTEX_ATTRIB6_NV 0x86CC +#define GL_EVAL_VERTEX_ATTRIB7_NV 0x86CD +#define GL_EVAL_VERTEX_ATTRIB8_NV 0x86CE +#define GL_EVAL_VERTEX_ATTRIB9_NV 0x86CF +#define GL_EVAL_VERTEX_ATTRIB10_NV 0x86D0 +#define GL_EVAL_VERTEX_ATTRIB11_NV 0x86D1 +#define GL_EVAL_VERTEX_ATTRIB12_NV 0x86D2 +#define GL_EVAL_VERTEX_ATTRIB13_NV 0x86D3 +#define GL_EVAL_VERTEX_ATTRIB14_NV 0x86D4 +#define GL_EVAL_VERTEX_ATTRIB15_NV 0x86D5 +#define GL_MAX_MAP_TESSELLATION_NV 0x86D6 +#define GL_MAX_RATIONAL_EVAL_ORDER_NV 0x86D7 +typedef void (APIENTRYP PFNGLMAPCONTROLPOINTSNVPROC) (GLenum target, GLuint index, GLenum type, GLsizei ustride, GLsizei vstride, GLint uorder, GLint vorder, GLboolean packed, const void *points); +typedef void (APIENTRYP PFNGLMAPPARAMETERIVNVPROC) (GLenum target, GLenum pname, const GLint *params); +typedef void (APIENTRYP PFNGLMAPPARAMETERFVNVPROC) (GLenum target, GLenum pname, const GLfloat *params); +typedef void (APIENTRYP PFNGLGETMAPCONTROLPOINTSNVPROC) (GLenum target, GLuint index, GLenum type, GLsizei ustride, GLsizei vstride, GLboolean packed, void *points); +typedef void (APIENTRYP PFNGLGETMAPPARAMETERIVNVPROC) (GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETMAPPARAMETERFVNVPROC) (GLenum target, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETMAPATTRIBPARAMETERIVNVPROC) (GLenum target, GLuint index, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETMAPATTRIBPARAMETERFVNVPROC) (GLenum target, GLuint index, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLEVALMAPSNVPROC) (GLenum target, GLenum mode); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glMapControlPointsNV (GLenum target, GLuint index, GLenum type, GLsizei ustride, GLsizei vstride, GLint uorder, GLint vorder, GLboolean packed, const void *points); +GLAPI void APIENTRY glMapParameterivNV (GLenum target, GLenum pname, const GLint *params); +GLAPI void APIENTRY glMapParameterfvNV (GLenum target, GLenum pname, const GLfloat *params); +GLAPI void APIENTRY glGetMapControlPointsNV (GLenum target, GLuint index, GLenum type, GLsizei ustride, GLsizei vstride, GLboolean packed, void *points); +GLAPI void APIENTRY glGetMapParameterivNV (GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetMapParameterfvNV (GLenum target, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetMapAttribParameterivNV (GLenum target, GLuint index, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetMapAttribParameterfvNV (GLenum target, GLuint index, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glEvalMapsNV (GLenum target, GLenum mode); +#endif +#endif /* GL_NV_evaluators */ + +#ifndef GL_NV_explicit_multisample +#define GL_NV_explicit_multisample 1 +#define GL_SAMPLE_POSITION_NV 0x8E50 +#define GL_SAMPLE_MASK_NV 0x8E51 +#define GL_SAMPLE_MASK_VALUE_NV 0x8E52 +#define GL_TEXTURE_BINDING_RENDERBUFFER_NV 0x8E53 +#define GL_TEXTURE_RENDERBUFFER_DATA_STORE_BINDING_NV 0x8E54 +#define GL_TEXTURE_RENDERBUFFER_NV 0x8E55 +#define GL_SAMPLER_RENDERBUFFER_NV 0x8E56 +#define GL_INT_SAMPLER_RENDERBUFFER_NV 0x8E57 +#define GL_UNSIGNED_INT_SAMPLER_RENDERBUFFER_NV 0x8E58 +#define GL_MAX_SAMPLE_MASK_WORDS_NV 0x8E59 +typedef void (APIENTRYP PFNGLGETMULTISAMPLEFVNVPROC) (GLenum pname, GLuint index, GLfloat *val); +typedef void (APIENTRYP PFNGLSAMPLEMASKINDEXEDNVPROC) (GLuint index, GLbitfield mask); +typedef void (APIENTRYP PFNGLTEXRENDERBUFFERNVPROC) (GLenum target, GLuint renderbuffer); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glGetMultisamplefvNV (GLenum pname, GLuint index, GLfloat *val); +GLAPI void APIENTRY glSampleMaskIndexedNV (GLuint index, GLbitfield mask); +GLAPI void APIENTRY glTexRenderbufferNV (GLenum target, GLuint renderbuffer); +#endif +#endif /* GL_NV_explicit_multisample */ + +#ifndef GL_NV_fence +#define GL_NV_fence 1 +#define GL_ALL_COMPLETED_NV 0x84F2 +#define GL_FENCE_STATUS_NV 0x84F3 +#define GL_FENCE_CONDITION_NV 0x84F4 +typedef void (APIENTRYP PFNGLDELETEFENCESNVPROC) (GLsizei n, const GLuint *fences); +typedef void (APIENTRYP PFNGLGENFENCESNVPROC) (GLsizei n, GLuint *fences); +typedef GLboolean (APIENTRYP PFNGLISFENCENVPROC) (GLuint fence); +typedef GLboolean (APIENTRYP PFNGLTESTFENCENVPROC) (GLuint fence); +typedef void (APIENTRYP PFNGLGETFENCEIVNVPROC) (GLuint fence, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLFINISHFENCENVPROC) (GLuint fence); +typedef void (APIENTRYP PFNGLSETFENCENVPROC) (GLuint fence, GLenum condition); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glDeleteFencesNV (GLsizei n, const GLuint *fences); +GLAPI void APIENTRY glGenFencesNV (GLsizei n, GLuint *fences); +GLAPI GLboolean APIENTRY glIsFenceNV (GLuint fence); +GLAPI GLboolean APIENTRY glTestFenceNV (GLuint fence); +GLAPI void APIENTRY glGetFenceivNV (GLuint fence, GLenum pname, GLint *params); +GLAPI void APIENTRY glFinishFenceNV (GLuint fence); +GLAPI void APIENTRY glSetFenceNV (GLuint fence, GLenum condition); +#endif +#endif /* GL_NV_fence */ + +#ifndef GL_NV_fill_rectangle +#define GL_NV_fill_rectangle 1 +#define GL_FILL_RECTANGLE_NV 0x933C +#endif /* GL_NV_fill_rectangle */ + +#ifndef GL_NV_float_buffer +#define GL_NV_float_buffer 1 +#define GL_FLOAT_R_NV 0x8880 +#define GL_FLOAT_RG_NV 0x8881 +#define GL_FLOAT_RGB_NV 0x8882 +#define GL_FLOAT_RGBA_NV 0x8883 +#define GL_FLOAT_R16_NV 0x8884 +#define GL_FLOAT_R32_NV 0x8885 +#define GL_FLOAT_RG16_NV 0x8886 +#define GL_FLOAT_RG32_NV 0x8887 +#define GL_FLOAT_RGB16_NV 0x8888 +#define GL_FLOAT_RGB32_NV 0x8889 +#define GL_FLOAT_RGBA16_NV 0x888A +#define GL_FLOAT_RGBA32_NV 0x888B +#define GL_TEXTURE_FLOAT_COMPONENTS_NV 0x888C +#define GL_FLOAT_CLEAR_COLOR_VALUE_NV 0x888D +#define GL_FLOAT_RGBA_MODE_NV 0x888E +#endif /* GL_NV_float_buffer */ + +#ifndef GL_NV_fog_distance +#define GL_NV_fog_distance 1 +#define GL_FOG_DISTANCE_MODE_NV 0x855A +#define GL_EYE_RADIAL_NV 0x855B +#define GL_EYE_PLANE_ABSOLUTE_NV 0x855C +#endif /* GL_NV_fog_distance */ + +#ifndef GL_NV_fragment_coverage_to_color +#define GL_NV_fragment_coverage_to_color 1 +#define GL_FRAGMENT_COVERAGE_TO_COLOR_NV 0x92DD +#define GL_FRAGMENT_COVERAGE_COLOR_NV 0x92DE +typedef void (APIENTRYP PFNGLFRAGMENTCOVERAGECOLORNVPROC) (GLuint color); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glFragmentCoverageColorNV (GLuint color); +#endif +#endif /* GL_NV_fragment_coverage_to_color */ + +#ifndef GL_NV_fragment_program +#define GL_NV_fragment_program 1 +#define GL_MAX_FRAGMENT_PROGRAM_LOCAL_PARAMETERS_NV 0x8868 +#define GL_FRAGMENT_PROGRAM_NV 0x8870 +#define GL_MAX_TEXTURE_COORDS_NV 0x8871 +#define GL_MAX_TEXTURE_IMAGE_UNITS_NV 0x8872 +#define GL_FRAGMENT_PROGRAM_BINDING_NV 0x8873 +#define GL_PROGRAM_ERROR_STRING_NV 0x8874 +typedef void (APIENTRYP PFNGLPROGRAMNAMEDPARAMETER4FNVPROC) (GLuint id, GLsizei len, const GLubyte *name, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +typedef void (APIENTRYP PFNGLPROGRAMNAMEDPARAMETER4FVNVPROC) (GLuint id, GLsizei len, const GLubyte *name, const GLfloat *v); +typedef void (APIENTRYP PFNGLPROGRAMNAMEDPARAMETER4DNVPROC) (GLuint id, GLsizei len, const GLubyte *name, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +typedef void (APIENTRYP PFNGLPROGRAMNAMEDPARAMETER4DVNVPROC) (GLuint id, GLsizei len, const GLubyte *name, const GLdouble *v); +typedef void (APIENTRYP PFNGLGETPROGRAMNAMEDPARAMETERFVNVPROC) (GLuint id, GLsizei len, const GLubyte *name, GLfloat *params); +typedef void (APIENTRYP PFNGLGETPROGRAMNAMEDPARAMETERDVNVPROC) (GLuint id, GLsizei len, const GLubyte *name, GLdouble *params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glProgramNamedParameter4fNV (GLuint id, GLsizei len, const GLubyte *name, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +GLAPI void APIENTRY glProgramNamedParameter4fvNV (GLuint id, GLsizei len, const GLubyte *name, const GLfloat *v); +GLAPI void APIENTRY glProgramNamedParameter4dNV (GLuint id, GLsizei len, const GLubyte *name, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +GLAPI void APIENTRY glProgramNamedParameter4dvNV (GLuint id, GLsizei len, const GLubyte *name, const GLdouble *v); +GLAPI void APIENTRY glGetProgramNamedParameterfvNV (GLuint id, GLsizei len, const GLubyte *name, GLfloat *params); +GLAPI void APIENTRY glGetProgramNamedParameterdvNV (GLuint id, GLsizei len, const GLubyte *name, GLdouble *params); +#endif +#endif /* GL_NV_fragment_program */ + +#ifndef GL_NV_fragment_program2 +#define GL_NV_fragment_program2 1 +#define GL_MAX_PROGRAM_EXEC_INSTRUCTIONS_NV 0x88F4 +#define GL_MAX_PROGRAM_CALL_DEPTH_NV 0x88F5 +#define GL_MAX_PROGRAM_IF_DEPTH_NV 0x88F6 +#define GL_MAX_PROGRAM_LOOP_DEPTH_NV 0x88F7 +#define GL_MAX_PROGRAM_LOOP_COUNT_NV 0x88F8 +#endif /* GL_NV_fragment_program2 */ + +#ifndef GL_NV_fragment_program4 +#define GL_NV_fragment_program4 1 +#endif /* GL_NV_fragment_program4 */ + +#ifndef GL_NV_fragment_program_option +#define GL_NV_fragment_program_option 1 +#endif /* GL_NV_fragment_program_option */ + +#ifndef GL_NV_fragment_shader_interlock +#define GL_NV_fragment_shader_interlock 1 +#endif /* GL_NV_fragment_shader_interlock */ + +#ifndef GL_NV_framebuffer_mixed_samples +#define GL_NV_framebuffer_mixed_samples 1 +#define GL_COVERAGE_MODULATION_TABLE_NV 0x9331 +#define GL_COLOR_SAMPLES_NV 0x8E20 +#define GL_DEPTH_SAMPLES_NV 0x932D +#define GL_STENCIL_SAMPLES_NV 0x932E +#define GL_MIXED_DEPTH_SAMPLES_SUPPORTED_NV 0x932F +#define GL_MIXED_STENCIL_SAMPLES_SUPPORTED_NV 0x9330 +#define GL_COVERAGE_MODULATION_NV 0x9332 +#define GL_COVERAGE_MODULATION_TABLE_SIZE_NV 0x9333 +typedef void (APIENTRYP PFNGLCOVERAGEMODULATIONTABLENVPROC) (GLsizei n, const GLfloat *v); +typedef void (APIENTRYP PFNGLGETCOVERAGEMODULATIONTABLENVPROC) (GLsizei bufsize, GLfloat *v); +typedef void (APIENTRYP PFNGLCOVERAGEMODULATIONNVPROC) (GLenum components); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glCoverageModulationTableNV (GLsizei n, const GLfloat *v); +GLAPI void APIENTRY glGetCoverageModulationTableNV (GLsizei bufsize, GLfloat *v); +GLAPI void APIENTRY glCoverageModulationNV (GLenum components); +#endif +#endif /* GL_NV_framebuffer_mixed_samples */ + +#ifndef GL_NV_framebuffer_multisample_coverage +#define GL_NV_framebuffer_multisample_coverage 1 +#define GL_RENDERBUFFER_COVERAGE_SAMPLES_NV 0x8CAB +#define GL_RENDERBUFFER_COLOR_SAMPLES_NV 0x8E10 +#define GL_MAX_MULTISAMPLE_COVERAGE_MODES_NV 0x8E11 +#define GL_MULTISAMPLE_COVERAGE_MODES_NV 0x8E12 +typedef void (APIENTRYP PFNGLRENDERBUFFERSTORAGEMULTISAMPLECOVERAGENVPROC) (GLenum target, GLsizei coverageSamples, GLsizei colorSamples, GLenum internalformat, GLsizei width, GLsizei height); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glRenderbufferStorageMultisampleCoverageNV (GLenum target, GLsizei coverageSamples, GLsizei colorSamples, GLenum internalformat, GLsizei width, GLsizei height); +#endif +#endif /* GL_NV_framebuffer_multisample_coverage */ + +#ifndef GL_NV_geometry_program4 +#define GL_NV_geometry_program4 1 +#define GL_GEOMETRY_PROGRAM_NV 0x8C26 +#define GL_MAX_PROGRAM_OUTPUT_VERTICES_NV 0x8C27 +#define GL_MAX_PROGRAM_TOTAL_OUTPUT_COMPONENTS_NV 0x8C28 +typedef void (APIENTRYP PFNGLPROGRAMVERTEXLIMITNVPROC) (GLenum target, GLint limit); +typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTUREEXTPROC) (GLenum target, GLenum attachment, GLuint texture, GLint level); +typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTUREFACEEXTPROC) (GLenum target, GLenum attachment, GLuint texture, GLint level, GLenum face); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glProgramVertexLimitNV (GLenum target, GLint limit); +GLAPI void APIENTRY glFramebufferTextureEXT (GLenum target, GLenum attachment, GLuint texture, GLint level); +GLAPI void APIENTRY glFramebufferTextureFaceEXT (GLenum target, GLenum attachment, GLuint texture, GLint level, GLenum face); +#endif +#endif /* GL_NV_geometry_program4 */ + +#ifndef GL_NV_geometry_shader4 +#define GL_NV_geometry_shader4 1 +#endif /* GL_NV_geometry_shader4 */ + +#ifndef GL_NV_geometry_shader_passthrough +#define GL_NV_geometry_shader_passthrough 1 +#endif /* GL_NV_geometry_shader_passthrough */ + +#ifndef GL_NV_gpu_program4 +#define GL_NV_gpu_program4 1 +#define GL_MIN_PROGRAM_TEXEL_OFFSET_NV 0x8904 +#define GL_MAX_PROGRAM_TEXEL_OFFSET_NV 0x8905 +#define GL_PROGRAM_ATTRIB_COMPONENTS_NV 0x8906 +#define GL_PROGRAM_RESULT_COMPONENTS_NV 0x8907 +#define GL_MAX_PROGRAM_ATTRIB_COMPONENTS_NV 0x8908 +#define GL_MAX_PROGRAM_RESULT_COMPONENTS_NV 0x8909 +#define GL_MAX_PROGRAM_GENERIC_ATTRIBS_NV 0x8DA5 +#define GL_MAX_PROGRAM_GENERIC_RESULTS_NV 0x8DA6 +typedef void (APIENTRYP PFNGLPROGRAMLOCALPARAMETERI4INVPROC) (GLenum target, GLuint index, GLint x, GLint y, GLint z, GLint w); +typedef void (APIENTRYP PFNGLPROGRAMLOCALPARAMETERI4IVNVPROC) (GLenum target, GLuint index, const GLint *params); +typedef void (APIENTRYP PFNGLPROGRAMLOCALPARAMETERSI4IVNVPROC) (GLenum target, GLuint index, GLsizei count, const GLint *params); +typedef void (APIENTRYP PFNGLPROGRAMLOCALPARAMETERI4UINVPROC) (GLenum target, GLuint index, GLuint x, GLuint y, GLuint z, GLuint w); +typedef void (APIENTRYP PFNGLPROGRAMLOCALPARAMETERI4UIVNVPROC) (GLenum target, GLuint index, const GLuint *params); +typedef void (APIENTRYP PFNGLPROGRAMLOCALPARAMETERSI4UIVNVPROC) (GLenum target, GLuint index, GLsizei count, const GLuint *params); +typedef void (APIENTRYP PFNGLPROGRAMENVPARAMETERI4INVPROC) (GLenum target, GLuint index, GLint x, GLint y, GLint z, GLint w); +typedef void (APIENTRYP PFNGLPROGRAMENVPARAMETERI4IVNVPROC) (GLenum target, GLuint index, const GLint *params); +typedef void (APIENTRYP PFNGLPROGRAMENVPARAMETERSI4IVNVPROC) (GLenum target, GLuint index, GLsizei count, const GLint *params); +typedef void (APIENTRYP PFNGLPROGRAMENVPARAMETERI4UINVPROC) (GLenum target, GLuint index, GLuint x, GLuint y, GLuint z, GLuint w); +typedef void (APIENTRYP PFNGLPROGRAMENVPARAMETERI4UIVNVPROC) (GLenum target, GLuint index, const GLuint *params); +typedef void (APIENTRYP PFNGLPROGRAMENVPARAMETERSI4UIVNVPROC) (GLenum target, GLuint index, GLsizei count, const GLuint *params); +typedef void (APIENTRYP PFNGLGETPROGRAMLOCALPARAMETERIIVNVPROC) (GLenum target, GLuint index, GLint *params); +typedef void (APIENTRYP PFNGLGETPROGRAMLOCALPARAMETERIUIVNVPROC) (GLenum target, GLuint index, GLuint *params); +typedef void (APIENTRYP PFNGLGETPROGRAMENVPARAMETERIIVNVPROC) (GLenum target, GLuint index, GLint *params); +typedef void (APIENTRYP PFNGLGETPROGRAMENVPARAMETERIUIVNVPROC) (GLenum target, GLuint index, GLuint *params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glProgramLocalParameterI4iNV (GLenum target, GLuint index, GLint x, GLint y, GLint z, GLint w); +GLAPI void APIENTRY glProgramLocalParameterI4ivNV (GLenum target, GLuint index, const GLint *params); +GLAPI void APIENTRY glProgramLocalParametersI4ivNV (GLenum target, GLuint index, GLsizei count, const GLint *params); +GLAPI void APIENTRY glProgramLocalParameterI4uiNV (GLenum target, GLuint index, GLuint x, GLuint y, GLuint z, GLuint w); +GLAPI void APIENTRY glProgramLocalParameterI4uivNV (GLenum target, GLuint index, const GLuint *params); +GLAPI void APIENTRY glProgramLocalParametersI4uivNV (GLenum target, GLuint index, GLsizei count, const GLuint *params); +GLAPI void APIENTRY glProgramEnvParameterI4iNV (GLenum target, GLuint index, GLint x, GLint y, GLint z, GLint w); +GLAPI void APIENTRY glProgramEnvParameterI4ivNV (GLenum target, GLuint index, const GLint *params); +GLAPI void APIENTRY glProgramEnvParametersI4ivNV (GLenum target, GLuint index, GLsizei count, const GLint *params); +GLAPI void APIENTRY glProgramEnvParameterI4uiNV (GLenum target, GLuint index, GLuint x, GLuint y, GLuint z, GLuint w); +GLAPI void APIENTRY glProgramEnvParameterI4uivNV (GLenum target, GLuint index, const GLuint *params); +GLAPI void APIENTRY glProgramEnvParametersI4uivNV (GLenum target, GLuint index, GLsizei count, const GLuint *params); +GLAPI void APIENTRY glGetProgramLocalParameterIivNV (GLenum target, GLuint index, GLint *params); +GLAPI void APIENTRY glGetProgramLocalParameterIuivNV (GLenum target, GLuint index, GLuint *params); +GLAPI void APIENTRY glGetProgramEnvParameterIivNV (GLenum target, GLuint index, GLint *params); +GLAPI void APIENTRY glGetProgramEnvParameterIuivNV (GLenum target, GLuint index, GLuint *params); +#endif +#endif /* GL_NV_gpu_program4 */ + +#ifndef GL_NV_gpu_program5 +#define GL_NV_gpu_program5 1 +#define GL_MAX_GEOMETRY_PROGRAM_INVOCATIONS_NV 0x8E5A +#define GL_MIN_FRAGMENT_INTERPOLATION_OFFSET_NV 0x8E5B +#define GL_MAX_FRAGMENT_INTERPOLATION_OFFSET_NV 0x8E5C +#define GL_FRAGMENT_PROGRAM_INTERPOLATION_OFFSET_BITS_NV 0x8E5D +#define GL_MIN_PROGRAM_TEXTURE_GATHER_OFFSET_NV 0x8E5E +#define GL_MAX_PROGRAM_TEXTURE_GATHER_OFFSET_NV 0x8E5F +#define GL_MAX_PROGRAM_SUBROUTINE_PARAMETERS_NV 0x8F44 +#define GL_MAX_PROGRAM_SUBROUTINE_NUM_NV 0x8F45 +typedef void (APIENTRYP PFNGLPROGRAMSUBROUTINEPARAMETERSUIVNVPROC) (GLenum target, GLsizei count, const GLuint *params); +typedef void (APIENTRYP PFNGLGETPROGRAMSUBROUTINEPARAMETERUIVNVPROC) (GLenum target, GLuint index, GLuint *param); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glProgramSubroutineParametersuivNV (GLenum target, GLsizei count, const GLuint *params); +GLAPI void APIENTRY glGetProgramSubroutineParameteruivNV (GLenum target, GLuint index, GLuint *param); +#endif +#endif /* GL_NV_gpu_program5 */ + +#ifndef GL_NV_gpu_program5_mem_extended +#define GL_NV_gpu_program5_mem_extended 1 +#endif /* GL_NV_gpu_program5_mem_extended */ + +#ifndef GL_NV_gpu_shader5 +#define GL_NV_gpu_shader5 1 +#endif /* GL_NV_gpu_shader5 */ + +#ifndef GL_NV_half_float +#define GL_NV_half_float 1 +typedef unsigned short GLhalfNV; +#define GL_HALF_FLOAT_NV 0x140B +typedef void (APIENTRYP PFNGLVERTEX2HNVPROC) (GLhalfNV x, GLhalfNV y); +typedef void (APIENTRYP PFNGLVERTEX2HVNVPROC) (const GLhalfNV *v); +typedef void (APIENTRYP PFNGLVERTEX3HNVPROC) (GLhalfNV x, GLhalfNV y, GLhalfNV z); +typedef void (APIENTRYP PFNGLVERTEX3HVNVPROC) (const GLhalfNV *v); +typedef void (APIENTRYP PFNGLVERTEX4HNVPROC) (GLhalfNV x, GLhalfNV y, GLhalfNV z, GLhalfNV w); +typedef void (APIENTRYP PFNGLVERTEX4HVNVPROC) (const GLhalfNV *v); +typedef void (APIENTRYP PFNGLNORMAL3HNVPROC) (GLhalfNV nx, GLhalfNV ny, GLhalfNV nz); +typedef void (APIENTRYP PFNGLNORMAL3HVNVPROC) (const GLhalfNV *v); +typedef void (APIENTRYP PFNGLCOLOR3HNVPROC) (GLhalfNV red, GLhalfNV green, GLhalfNV blue); +typedef void (APIENTRYP PFNGLCOLOR3HVNVPROC) (const GLhalfNV *v); +typedef void (APIENTRYP PFNGLCOLOR4HNVPROC) (GLhalfNV red, GLhalfNV green, GLhalfNV blue, GLhalfNV alpha); +typedef void (APIENTRYP PFNGLCOLOR4HVNVPROC) (const GLhalfNV *v); +typedef void (APIENTRYP PFNGLTEXCOORD1HNVPROC) (GLhalfNV s); +typedef void (APIENTRYP PFNGLTEXCOORD1HVNVPROC) (const GLhalfNV *v); +typedef void (APIENTRYP PFNGLTEXCOORD2HNVPROC) (GLhalfNV s, GLhalfNV t); +typedef void (APIENTRYP PFNGLTEXCOORD2HVNVPROC) (const GLhalfNV *v); +typedef void (APIENTRYP PFNGLTEXCOORD3HNVPROC) (GLhalfNV s, GLhalfNV t, GLhalfNV r); +typedef void (APIENTRYP PFNGLTEXCOORD3HVNVPROC) (const GLhalfNV *v); +typedef void (APIENTRYP PFNGLTEXCOORD4HNVPROC) (GLhalfNV s, GLhalfNV t, GLhalfNV r, GLhalfNV q); +typedef void (APIENTRYP PFNGLTEXCOORD4HVNVPROC) (const GLhalfNV *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1HNVPROC) (GLenum target, GLhalfNV s); +typedef void (APIENTRYP PFNGLMULTITEXCOORD1HVNVPROC) (GLenum target, const GLhalfNV *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2HNVPROC) (GLenum target, GLhalfNV s, GLhalfNV t); +typedef void (APIENTRYP PFNGLMULTITEXCOORD2HVNVPROC) (GLenum target, const GLhalfNV *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3HNVPROC) (GLenum target, GLhalfNV s, GLhalfNV t, GLhalfNV r); +typedef void (APIENTRYP PFNGLMULTITEXCOORD3HVNVPROC) (GLenum target, const GLhalfNV *v); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4HNVPROC) (GLenum target, GLhalfNV s, GLhalfNV t, GLhalfNV r, GLhalfNV q); +typedef void (APIENTRYP PFNGLMULTITEXCOORD4HVNVPROC) (GLenum target, const GLhalfNV *v); +typedef void (APIENTRYP PFNGLFOGCOORDHNVPROC) (GLhalfNV fog); +typedef void (APIENTRYP PFNGLFOGCOORDHVNVPROC) (const GLhalfNV *fog); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3HNVPROC) (GLhalfNV red, GLhalfNV green, GLhalfNV blue); +typedef void (APIENTRYP PFNGLSECONDARYCOLOR3HVNVPROC) (const GLhalfNV *v); +typedef void (APIENTRYP PFNGLVERTEXWEIGHTHNVPROC) (GLhalfNV weight); +typedef void (APIENTRYP PFNGLVERTEXWEIGHTHVNVPROC) (const GLhalfNV *weight); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1HNVPROC) (GLuint index, GLhalfNV x); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1HVNVPROC) (GLuint index, const GLhalfNV *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2HNVPROC) (GLuint index, GLhalfNV x, GLhalfNV y); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2HVNVPROC) (GLuint index, const GLhalfNV *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3HNVPROC) (GLuint index, GLhalfNV x, GLhalfNV y, GLhalfNV z); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3HVNVPROC) (GLuint index, const GLhalfNV *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4HNVPROC) (GLuint index, GLhalfNV x, GLhalfNV y, GLhalfNV z, GLhalfNV w); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4HVNVPROC) (GLuint index, const GLhalfNV *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBS1HVNVPROC) (GLuint index, GLsizei n, const GLhalfNV *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBS2HVNVPROC) (GLuint index, GLsizei n, const GLhalfNV *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBS3HVNVPROC) (GLuint index, GLsizei n, const GLhalfNV *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBS4HVNVPROC) (GLuint index, GLsizei n, const GLhalfNV *v); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glVertex2hNV (GLhalfNV x, GLhalfNV y); +GLAPI void APIENTRY glVertex2hvNV (const GLhalfNV *v); +GLAPI void APIENTRY glVertex3hNV (GLhalfNV x, GLhalfNV y, GLhalfNV z); +GLAPI void APIENTRY glVertex3hvNV (const GLhalfNV *v); +GLAPI void APIENTRY glVertex4hNV (GLhalfNV x, GLhalfNV y, GLhalfNV z, GLhalfNV w); +GLAPI void APIENTRY glVertex4hvNV (const GLhalfNV *v); +GLAPI void APIENTRY glNormal3hNV (GLhalfNV nx, GLhalfNV ny, GLhalfNV nz); +GLAPI void APIENTRY glNormal3hvNV (const GLhalfNV *v); +GLAPI void APIENTRY glColor3hNV (GLhalfNV red, GLhalfNV green, GLhalfNV blue); +GLAPI void APIENTRY glColor3hvNV (const GLhalfNV *v); +GLAPI void APIENTRY glColor4hNV (GLhalfNV red, GLhalfNV green, GLhalfNV blue, GLhalfNV alpha); +GLAPI void APIENTRY glColor4hvNV (const GLhalfNV *v); +GLAPI void APIENTRY glTexCoord1hNV (GLhalfNV s); +GLAPI void APIENTRY glTexCoord1hvNV (const GLhalfNV *v); +GLAPI void APIENTRY glTexCoord2hNV (GLhalfNV s, GLhalfNV t); +GLAPI void APIENTRY glTexCoord2hvNV (const GLhalfNV *v); +GLAPI void APIENTRY glTexCoord3hNV (GLhalfNV s, GLhalfNV t, GLhalfNV r); +GLAPI void APIENTRY glTexCoord3hvNV (const GLhalfNV *v); +GLAPI void APIENTRY glTexCoord4hNV (GLhalfNV s, GLhalfNV t, GLhalfNV r, GLhalfNV q); +GLAPI void APIENTRY glTexCoord4hvNV (const GLhalfNV *v); +GLAPI void APIENTRY glMultiTexCoord1hNV (GLenum target, GLhalfNV s); +GLAPI void APIENTRY glMultiTexCoord1hvNV (GLenum target, const GLhalfNV *v); +GLAPI void APIENTRY glMultiTexCoord2hNV (GLenum target, GLhalfNV s, GLhalfNV t); +GLAPI void APIENTRY glMultiTexCoord2hvNV (GLenum target, const GLhalfNV *v); +GLAPI void APIENTRY glMultiTexCoord3hNV (GLenum target, GLhalfNV s, GLhalfNV t, GLhalfNV r); +GLAPI void APIENTRY glMultiTexCoord3hvNV (GLenum target, const GLhalfNV *v); +GLAPI void APIENTRY glMultiTexCoord4hNV (GLenum target, GLhalfNV s, GLhalfNV t, GLhalfNV r, GLhalfNV q); +GLAPI void APIENTRY glMultiTexCoord4hvNV (GLenum target, const GLhalfNV *v); +GLAPI void APIENTRY glFogCoordhNV (GLhalfNV fog); +GLAPI void APIENTRY glFogCoordhvNV (const GLhalfNV *fog); +GLAPI void APIENTRY glSecondaryColor3hNV (GLhalfNV red, GLhalfNV green, GLhalfNV blue); +GLAPI void APIENTRY glSecondaryColor3hvNV (const GLhalfNV *v); +GLAPI void APIENTRY glVertexWeighthNV (GLhalfNV weight); +GLAPI void APIENTRY glVertexWeighthvNV (const GLhalfNV *weight); +GLAPI void APIENTRY glVertexAttrib1hNV (GLuint index, GLhalfNV x); +GLAPI void APIENTRY glVertexAttrib1hvNV (GLuint index, const GLhalfNV *v); +GLAPI void APIENTRY glVertexAttrib2hNV (GLuint index, GLhalfNV x, GLhalfNV y); +GLAPI void APIENTRY glVertexAttrib2hvNV (GLuint index, const GLhalfNV *v); +GLAPI void APIENTRY glVertexAttrib3hNV (GLuint index, GLhalfNV x, GLhalfNV y, GLhalfNV z); +GLAPI void APIENTRY glVertexAttrib3hvNV (GLuint index, const GLhalfNV *v); +GLAPI void APIENTRY glVertexAttrib4hNV (GLuint index, GLhalfNV x, GLhalfNV y, GLhalfNV z, GLhalfNV w); +GLAPI void APIENTRY glVertexAttrib4hvNV (GLuint index, const GLhalfNV *v); +GLAPI void APIENTRY glVertexAttribs1hvNV (GLuint index, GLsizei n, const GLhalfNV *v); +GLAPI void APIENTRY glVertexAttribs2hvNV (GLuint index, GLsizei n, const GLhalfNV *v); +GLAPI void APIENTRY glVertexAttribs3hvNV (GLuint index, GLsizei n, const GLhalfNV *v); +GLAPI void APIENTRY glVertexAttribs4hvNV (GLuint index, GLsizei n, const GLhalfNV *v); +#endif +#endif /* GL_NV_half_float */ + +#ifndef GL_NV_internalformat_sample_query +#define GL_NV_internalformat_sample_query 1 +#define GL_MULTISAMPLES_NV 0x9371 +#define GL_SUPERSAMPLE_SCALE_X_NV 0x9372 +#define GL_SUPERSAMPLE_SCALE_Y_NV 0x9373 +#define GL_CONFORMANT_NV 0x9374 +typedef void (APIENTRYP PFNGLGETINTERNALFORMATSAMPLEIVNVPROC) (GLenum target, GLenum internalformat, GLsizei samples, GLenum pname, GLsizei bufSize, GLint *params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glGetInternalformatSampleivNV (GLenum target, GLenum internalformat, GLsizei samples, GLenum pname, GLsizei bufSize, GLint *params); +#endif +#endif /* GL_NV_internalformat_sample_query */ + +#ifndef GL_NV_light_max_exponent +#define GL_NV_light_max_exponent 1 +#define GL_MAX_SHININESS_NV 0x8504 +#define GL_MAX_SPOT_EXPONENT_NV 0x8505 +#endif /* GL_NV_light_max_exponent */ + +#ifndef GL_NV_multisample_coverage +#define GL_NV_multisample_coverage 1 +#endif /* GL_NV_multisample_coverage */ + +#ifndef GL_NV_multisample_filter_hint +#define GL_NV_multisample_filter_hint 1 +#define GL_MULTISAMPLE_FILTER_HINT_NV 0x8534 +#endif /* GL_NV_multisample_filter_hint */ + +#ifndef GL_NV_occlusion_query +#define GL_NV_occlusion_query 1 +#define GL_PIXEL_COUNTER_BITS_NV 0x8864 +#define GL_CURRENT_OCCLUSION_QUERY_ID_NV 0x8865 +#define GL_PIXEL_COUNT_NV 0x8866 +#define GL_PIXEL_COUNT_AVAILABLE_NV 0x8867 +typedef void (APIENTRYP PFNGLGENOCCLUSIONQUERIESNVPROC) (GLsizei n, GLuint *ids); +typedef void (APIENTRYP PFNGLDELETEOCCLUSIONQUERIESNVPROC) (GLsizei n, const GLuint *ids); +typedef GLboolean (APIENTRYP PFNGLISOCCLUSIONQUERYNVPROC) (GLuint id); +typedef void (APIENTRYP PFNGLBEGINOCCLUSIONQUERYNVPROC) (GLuint id); +typedef void (APIENTRYP PFNGLENDOCCLUSIONQUERYNVPROC) (void); +typedef void (APIENTRYP PFNGLGETOCCLUSIONQUERYIVNVPROC) (GLuint id, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETOCCLUSIONQUERYUIVNVPROC) (GLuint id, GLenum pname, GLuint *params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glGenOcclusionQueriesNV (GLsizei n, GLuint *ids); +GLAPI void APIENTRY glDeleteOcclusionQueriesNV (GLsizei n, const GLuint *ids); +GLAPI GLboolean APIENTRY glIsOcclusionQueryNV (GLuint id); +GLAPI void APIENTRY glBeginOcclusionQueryNV (GLuint id); +GLAPI void APIENTRY glEndOcclusionQueryNV (void); +GLAPI void APIENTRY glGetOcclusionQueryivNV (GLuint id, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetOcclusionQueryuivNV (GLuint id, GLenum pname, GLuint *params); +#endif +#endif /* GL_NV_occlusion_query */ + +#ifndef GL_NV_packed_depth_stencil +#define GL_NV_packed_depth_stencil 1 +#define GL_DEPTH_STENCIL_NV 0x84F9 +#define GL_UNSIGNED_INT_24_8_NV 0x84FA +#endif /* GL_NV_packed_depth_stencil */ + +#ifndef GL_NV_parameter_buffer_object +#define GL_NV_parameter_buffer_object 1 +#define GL_MAX_PROGRAM_PARAMETER_BUFFER_BINDINGS_NV 0x8DA0 +#define GL_MAX_PROGRAM_PARAMETER_BUFFER_SIZE_NV 0x8DA1 +#define GL_VERTEX_PROGRAM_PARAMETER_BUFFER_NV 0x8DA2 +#define GL_GEOMETRY_PROGRAM_PARAMETER_BUFFER_NV 0x8DA3 +#define GL_FRAGMENT_PROGRAM_PARAMETER_BUFFER_NV 0x8DA4 +typedef void (APIENTRYP PFNGLPROGRAMBUFFERPARAMETERSFVNVPROC) (GLenum target, GLuint bindingIndex, GLuint wordIndex, GLsizei count, const GLfloat *params); +typedef void (APIENTRYP PFNGLPROGRAMBUFFERPARAMETERSIIVNVPROC) (GLenum target, GLuint bindingIndex, GLuint wordIndex, GLsizei count, const GLint *params); +typedef void (APIENTRYP PFNGLPROGRAMBUFFERPARAMETERSIUIVNVPROC) (GLenum target, GLuint bindingIndex, GLuint wordIndex, GLsizei count, const GLuint *params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glProgramBufferParametersfvNV (GLenum target, GLuint bindingIndex, GLuint wordIndex, GLsizei count, const GLfloat *params); +GLAPI void APIENTRY glProgramBufferParametersIivNV (GLenum target, GLuint bindingIndex, GLuint wordIndex, GLsizei count, const GLint *params); +GLAPI void APIENTRY glProgramBufferParametersIuivNV (GLenum target, GLuint bindingIndex, GLuint wordIndex, GLsizei count, const GLuint *params); +#endif +#endif /* GL_NV_parameter_buffer_object */ + +#ifndef GL_NV_parameter_buffer_object2 +#define GL_NV_parameter_buffer_object2 1 +#endif /* GL_NV_parameter_buffer_object2 */ + +#ifndef GL_NV_path_rendering +#define GL_NV_path_rendering 1 +#define GL_PATH_FORMAT_SVG_NV 0x9070 +#define GL_PATH_FORMAT_PS_NV 0x9071 +#define GL_STANDARD_FONT_NAME_NV 0x9072 +#define GL_SYSTEM_FONT_NAME_NV 0x9073 +#define GL_FILE_NAME_NV 0x9074 +#define GL_PATH_STROKE_WIDTH_NV 0x9075 +#define GL_PATH_END_CAPS_NV 0x9076 +#define GL_PATH_INITIAL_END_CAP_NV 0x9077 +#define GL_PATH_TERMINAL_END_CAP_NV 0x9078 +#define GL_PATH_JOIN_STYLE_NV 0x9079 +#define GL_PATH_MITER_LIMIT_NV 0x907A +#define GL_PATH_DASH_CAPS_NV 0x907B +#define GL_PATH_INITIAL_DASH_CAP_NV 0x907C +#define GL_PATH_TERMINAL_DASH_CAP_NV 0x907D +#define GL_PATH_DASH_OFFSET_NV 0x907E +#define GL_PATH_CLIENT_LENGTH_NV 0x907F +#define GL_PATH_FILL_MODE_NV 0x9080 +#define GL_PATH_FILL_MASK_NV 0x9081 +#define GL_PATH_FILL_COVER_MODE_NV 0x9082 +#define GL_PATH_STROKE_COVER_MODE_NV 0x9083 +#define GL_PATH_STROKE_MASK_NV 0x9084 +#define GL_COUNT_UP_NV 0x9088 +#define GL_COUNT_DOWN_NV 0x9089 +#define GL_PATH_OBJECT_BOUNDING_BOX_NV 0x908A +#define GL_CONVEX_HULL_NV 0x908B +#define GL_BOUNDING_BOX_NV 0x908D +#define GL_TRANSLATE_X_NV 0x908E +#define GL_TRANSLATE_Y_NV 0x908F +#define GL_TRANSLATE_2D_NV 0x9090 +#define GL_TRANSLATE_3D_NV 0x9091 +#define GL_AFFINE_2D_NV 0x9092 +#define GL_AFFINE_3D_NV 0x9094 +#define GL_TRANSPOSE_AFFINE_2D_NV 0x9096 +#define GL_TRANSPOSE_AFFINE_3D_NV 0x9098 +#define GL_UTF8_NV 0x909A +#define GL_UTF16_NV 0x909B +#define GL_BOUNDING_BOX_OF_BOUNDING_BOXES_NV 0x909C +#define GL_PATH_COMMAND_COUNT_NV 0x909D +#define GL_PATH_COORD_COUNT_NV 0x909E +#define GL_PATH_DASH_ARRAY_COUNT_NV 0x909F +#define GL_PATH_COMPUTED_LENGTH_NV 0x90A0 +#define GL_PATH_FILL_BOUNDING_BOX_NV 0x90A1 +#define GL_PATH_STROKE_BOUNDING_BOX_NV 0x90A2 +#define GL_SQUARE_NV 0x90A3 +#define GL_ROUND_NV 0x90A4 +#define GL_TRIANGULAR_NV 0x90A5 +#define GL_BEVEL_NV 0x90A6 +#define GL_MITER_REVERT_NV 0x90A7 +#define GL_MITER_TRUNCATE_NV 0x90A8 +#define GL_SKIP_MISSING_GLYPH_NV 0x90A9 +#define GL_USE_MISSING_GLYPH_NV 0x90AA +#define GL_PATH_ERROR_POSITION_NV 0x90AB +#define GL_ACCUM_ADJACENT_PAIRS_NV 0x90AD +#define GL_ADJACENT_PAIRS_NV 0x90AE +#define GL_FIRST_TO_REST_NV 0x90AF +#define GL_PATH_GEN_MODE_NV 0x90B0 +#define GL_PATH_GEN_COEFF_NV 0x90B1 +#define GL_PATH_GEN_COMPONENTS_NV 0x90B3 +#define GL_PATH_STENCIL_FUNC_NV 0x90B7 +#define GL_PATH_STENCIL_REF_NV 0x90B8 +#define GL_PATH_STENCIL_VALUE_MASK_NV 0x90B9 +#define GL_PATH_STENCIL_DEPTH_OFFSET_FACTOR_NV 0x90BD +#define GL_PATH_STENCIL_DEPTH_OFFSET_UNITS_NV 0x90BE +#define GL_PATH_COVER_DEPTH_FUNC_NV 0x90BF +#define GL_PATH_DASH_OFFSET_RESET_NV 0x90B4 +#define GL_MOVE_TO_RESETS_NV 0x90B5 +#define GL_MOVE_TO_CONTINUES_NV 0x90B6 +#define GL_CLOSE_PATH_NV 0x00 +#define GL_MOVE_TO_NV 0x02 +#define GL_RELATIVE_MOVE_TO_NV 0x03 +#define GL_LINE_TO_NV 0x04 +#define GL_RELATIVE_LINE_TO_NV 0x05 +#define GL_HORIZONTAL_LINE_TO_NV 0x06 +#define GL_RELATIVE_HORIZONTAL_LINE_TO_NV 0x07 +#define GL_VERTICAL_LINE_TO_NV 0x08 +#define GL_RELATIVE_VERTICAL_LINE_TO_NV 0x09 +#define GL_QUADRATIC_CURVE_TO_NV 0x0A +#define GL_RELATIVE_QUADRATIC_CURVE_TO_NV 0x0B +#define GL_CUBIC_CURVE_TO_NV 0x0C +#define GL_RELATIVE_CUBIC_CURVE_TO_NV 0x0D +#define GL_SMOOTH_QUADRATIC_CURVE_TO_NV 0x0E +#define GL_RELATIVE_SMOOTH_QUADRATIC_CURVE_TO_NV 0x0F +#define GL_SMOOTH_CUBIC_CURVE_TO_NV 0x10 +#define GL_RELATIVE_SMOOTH_CUBIC_CURVE_TO_NV 0x11 +#define GL_SMALL_CCW_ARC_TO_NV 0x12 +#define GL_RELATIVE_SMALL_CCW_ARC_TO_NV 0x13 +#define GL_SMALL_CW_ARC_TO_NV 0x14 +#define GL_RELATIVE_SMALL_CW_ARC_TO_NV 0x15 +#define GL_LARGE_CCW_ARC_TO_NV 0x16 +#define GL_RELATIVE_LARGE_CCW_ARC_TO_NV 0x17 +#define GL_LARGE_CW_ARC_TO_NV 0x18 +#define GL_RELATIVE_LARGE_CW_ARC_TO_NV 0x19 +#define GL_RESTART_PATH_NV 0xF0 +#define GL_DUP_FIRST_CUBIC_CURVE_TO_NV 0xF2 +#define GL_DUP_LAST_CUBIC_CURVE_TO_NV 0xF4 +#define GL_RECT_NV 0xF6 +#define GL_CIRCULAR_CCW_ARC_TO_NV 0xF8 +#define GL_CIRCULAR_CW_ARC_TO_NV 0xFA +#define GL_CIRCULAR_TANGENT_ARC_TO_NV 0xFC +#define GL_ARC_TO_NV 0xFE +#define GL_RELATIVE_ARC_TO_NV 0xFF +#define GL_BOLD_BIT_NV 0x01 +#define GL_ITALIC_BIT_NV 0x02 +#define GL_GLYPH_WIDTH_BIT_NV 0x01 +#define GL_GLYPH_HEIGHT_BIT_NV 0x02 +#define GL_GLYPH_HORIZONTAL_BEARING_X_BIT_NV 0x04 +#define GL_GLYPH_HORIZONTAL_BEARING_Y_BIT_NV 0x08 +#define GL_GLYPH_HORIZONTAL_BEARING_ADVANCE_BIT_NV 0x10 +#define GL_GLYPH_VERTICAL_BEARING_X_BIT_NV 0x20 +#define GL_GLYPH_VERTICAL_BEARING_Y_BIT_NV 0x40 +#define GL_GLYPH_VERTICAL_BEARING_ADVANCE_BIT_NV 0x80 +#define GL_GLYPH_HAS_KERNING_BIT_NV 0x100 +#define GL_FONT_X_MIN_BOUNDS_BIT_NV 0x00010000 +#define GL_FONT_Y_MIN_BOUNDS_BIT_NV 0x00020000 +#define GL_FONT_X_MAX_BOUNDS_BIT_NV 0x00040000 +#define GL_FONT_Y_MAX_BOUNDS_BIT_NV 0x00080000 +#define GL_FONT_UNITS_PER_EM_BIT_NV 0x00100000 +#define GL_FONT_ASCENDER_BIT_NV 0x00200000 +#define GL_FONT_DESCENDER_BIT_NV 0x00400000 +#define GL_FONT_HEIGHT_BIT_NV 0x00800000 +#define GL_FONT_MAX_ADVANCE_WIDTH_BIT_NV 0x01000000 +#define GL_FONT_MAX_ADVANCE_HEIGHT_BIT_NV 0x02000000 +#define GL_FONT_UNDERLINE_POSITION_BIT_NV 0x04000000 +#define GL_FONT_UNDERLINE_THICKNESS_BIT_NV 0x08000000 +#define GL_FONT_HAS_KERNING_BIT_NV 0x10000000 +#define GL_ROUNDED_RECT_NV 0xE8 +#define GL_RELATIVE_ROUNDED_RECT_NV 0xE9 +#define GL_ROUNDED_RECT2_NV 0xEA +#define GL_RELATIVE_ROUNDED_RECT2_NV 0xEB +#define GL_ROUNDED_RECT4_NV 0xEC +#define GL_RELATIVE_ROUNDED_RECT4_NV 0xED +#define GL_ROUNDED_RECT8_NV 0xEE +#define GL_RELATIVE_ROUNDED_RECT8_NV 0xEF +#define GL_RELATIVE_RECT_NV 0xF7 +#define GL_FONT_GLYPHS_AVAILABLE_NV 0x9368 +#define GL_FONT_TARGET_UNAVAILABLE_NV 0x9369 +#define GL_FONT_UNAVAILABLE_NV 0x936A +#define GL_FONT_UNINTELLIGIBLE_NV 0x936B +#define GL_CONIC_CURVE_TO_NV 0x1A +#define GL_RELATIVE_CONIC_CURVE_TO_NV 0x1B +#define GL_FONT_NUM_GLYPH_INDICES_BIT_NV 0x20000000 +#define GL_STANDARD_FONT_FORMAT_NV 0x936C +#define GL_2_BYTES_NV 0x1407 +#define GL_3_BYTES_NV 0x1408 +#define GL_4_BYTES_NV 0x1409 +#define GL_EYE_LINEAR_NV 0x2400 +#define GL_OBJECT_LINEAR_NV 0x2401 +#define GL_CONSTANT_NV 0x8576 +#define GL_PATH_FOG_GEN_MODE_NV 0x90AC +#define GL_PRIMARY_COLOR_NV 0x852C +#define GL_SECONDARY_COLOR_NV 0x852D +#define GL_PATH_GEN_COLOR_FORMAT_NV 0x90B2 +#define GL_PATH_PROJECTION_NV 0x1701 +#define GL_PATH_MODELVIEW_NV 0x1700 +#define GL_PATH_MODELVIEW_STACK_DEPTH_NV 0x0BA3 +#define GL_PATH_MODELVIEW_MATRIX_NV 0x0BA6 +#define GL_PATH_MAX_MODELVIEW_STACK_DEPTH_NV 0x0D36 +#define GL_PATH_TRANSPOSE_MODELVIEW_MATRIX_NV 0x84E3 +#define GL_PATH_PROJECTION_STACK_DEPTH_NV 0x0BA4 +#define GL_PATH_PROJECTION_MATRIX_NV 0x0BA7 +#define GL_PATH_MAX_PROJECTION_STACK_DEPTH_NV 0x0D38 +#define GL_PATH_TRANSPOSE_PROJECTION_MATRIX_NV 0x84E4 +#define GL_FRAGMENT_INPUT_NV 0x936D +typedef GLuint (APIENTRYP PFNGLGENPATHSNVPROC) (GLsizei range); +typedef void (APIENTRYP PFNGLDELETEPATHSNVPROC) (GLuint path, GLsizei range); +typedef GLboolean (APIENTRYP PFNGLISPATHNVPROC) (GLuint path); +typedef void (APIENTRYP PFNGLPATHCOMMANDSNVPROC) (GLuint path, GLsizei numCommands, const GLubyte *commands, GLsizei numCoords, GLenum coordType, const void *coords); +typedef void (APIENTRYP PFNGLPATHCOORDSNVPROC) (GLuint path, GLsizei numCoords, GLenum coordType, const void *coords); +typedef void (APIENTRYP PFNGLPATHSUBCOMMANDSNVPROC) (GLuint path, GLsizei commandStart, GLsizei commandsToDelete, GLsizei numCommands, const GLubyte *commands, GLsizei numCoords, GLenum coordType, const void *coords); +typedef void (APIENTRYP PFNGLPATHSUBCOORDSNVPROC) (GLuint path, GLsizei coordStart, GLsizei numCoords, GLenum coordType, const void *coords); +typedef void (APIENTRYP PFNGLPATHSTRINGNVPROC) (GLuint path, GLenum format, GLsizei length, const void *pathString); +typedef void (APIENTRYP PFNGLPATHGLYPHSNVPROC) (GLuint firstPathName, GLenum fontTarget, const void *fontName, GLbitfield fontStyle, GLsizei numGlyphs, GLenum type, const void *charcodes, GLenum handleMissingGlyphs, GLuint pathParameterTemplate, GLfloat emScale); +typedef void (APIENTRYP PFNGLPATHGLYPHRANGENVPROC) (GLuint firstPathName, GLenum fontTarget, const void *fontName, GLbitfield fontStyle, GLuint firstGlyph, GLsizei numGlyphs, GLenum handleMissingGlyphs, GLuint pathParameterTemplate, GLfloat emScale); +typedef void (APIENTRYP PFNGLWEIGHTPATHSNVPROC) (GLuint resultPath, GLsizei numPaths, const GLuint *paths, const GLfloat *weights); +typedef void (APIENTRYP PFNGLCOPYPATHNVPROC) (GLuint resultPath, GLuint srcPath); +typedef void (APIENTRYP PFNGLINTERPOLATEPATHSNVPROC) (GLuint resultPath, GLuint pathA, GLuint pathB, GLfloat weight); +typedef void (APIENTRYP PFNGLTRANSFORMPATHNVPROC) (GLuint resultPath, GLuint srcPath, GLenum transformType, const GLfloat *transformValues); +typedef void (APIENTRYP PFNGLPATHPARAMETERIVNVPROC) (GLuint path, GLenum pname, const GLint *value); +typedef void (APIENTRYP PFNGLPATHPARAMETERINVPROC) (GLuint path, GLenum pname, GLint value); +typedef void (APIENTRYP PFNGLPATHPARAMETERFVNVPROC) (GLuint path, GLenum pname, const GLfloat *value); +typedef void (APIENTRYP PFNGLPATHPARAMETERFNVPROC) (GLuint path, GLenum pname, GLfloat value); +typedef void (APIENTRYP PFNGLPATHDASHARRAYNVPROC) (GLuint path, GLsizei dashCount, const GLfloat *dashArray); +typedef void (APIENTRYP PFNGLPATHSTENCILFUNCNVPROC) (GLenum func, GLint ref, GLuint mask); +typedef void (APIENTRYP PFNGLPATHSTENCILDEPTHOFFSETNVPROC) (GLfloat factor, GLfloat units); +typedef void (APIENTRYP PFNGLSTENCILFILLPATHNVPROC) (GLuint path, GLenum fillMode, GLuint mask); +typedef void (APIENTRYP PFNGLSTENCILSTROKEPATHNVPROC) (GLuint path, GLint reference, GLuint mask); +typedef void (APIENTRYP PFNGLSTENCILFILLPATHINSTANCEDNVPROC) (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLenum fillMode, GLuint mask, GLenum transformType, const GLfloat *transformValues); +typedef void (APIENTRYP PFNGLSTENCILSTROKEPATHINSTANCEDNVPROC) (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLint reference, GLuint mask, GLenum transformType, const GLfloat *transformValues); +typedef void (APIENTRYP PFNGLPATHCOVERDEPTHFUNCNVPROC) (GLenum func); +typedef void (APIENTRYP PFNGLCOVERFILLPATHNVPROC) (GLuint path, GLenum coverMode); +typedef void (APIENTRYP PFNGLCOVERSTROKEPATHNVPROC) (GLuint path, GLenum coverMode); +typedef void (APIENTRYP PFNGLCOVERFILLPATHINSTANCEDNVPROC) (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLenum coverMode, GLenum transformType, const GLfloat *transformValues); +typedef void (APIENTRYP PFNGLCOVERSTROKEPATHINSTANCEDNVPROC) (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLenum coverMode, GLenum transformType, const GLfloat *transformValues); +typedef void (APIENTRYP PFNGLGETPATHPARAMETERIVNVPROC) (GLuint path, GLenum pname, GLint *value); +typedef void (APIENTRYP PFNGLGETPATHPARAMETERFVNVPROC) (GLuint path, GLenum pname, GLfloat *value); +typedef void (APIENTRYP PFNGLGETPATHCOMMANDSNVPROC) (GLuint path, GLubyte *commands); +typedef void (APIENTRYP PFNGLGETPATHCOORDSNVPROC) (GLuint path, GLfloat *coords); +typedef void (APIENTRYP PFNGLGETPATHDASHARRAYNVPROC) (GLuint path, GLfloat *dashArray); +typedef void (APIENTRYP PFNGLGETPATHMETRICSNVPROC) (GLbitfield metricQueryMask, GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLsizei stride, GLfloat *metrics); +typedef void (APIENTRYP PFNGLGETPATHMETRICRANGENVPROC) (GLbitfield metricQueryMask, GLuint firstPathName, GLsizei numPaths, GLsizei stride, GLfloat *metrics); +typedef void (APIENTRYP PFNGLGETPATHSPACINGNVPROC) (GLenum pathListMode, GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLfloat advanceScale, GLfloat kerningScale, GLenum transformType, GLfloat *returnedSpacing); +typedef GLboolean (APIENTRYP PFNGLISPOINTINFILLPATHNVPROC) (GLuint path, GLuint mask, GLfloat x, GLfloat y); +typedef GLboolean (APIENTRYP PFNGLISPOINTINSTROKEPATHNVPROC) (GLuint path, GLfloat x, GLfloat y); +typedef GLfloat (APIENTRYP PFNGLGETPATHLENGTHNVPROC) (GLuint path, GLsizei startSegment, GLsizei numSegments); +typedef GLboolean (APIENTRYP PFNGLPOINTALONGPATHNVPROC) (GLuint path, GLsizei startSegment, GLsizei numSegments, GLfloat distance, GLfloat *x, GLfloat *y, GLfloat *tangentX, GLfloat *tangentY); +typedef void (APIENTRYP PFNGLMATRIXLOAD3X2FNVPROC) (GLenum matrixMode, const GLfloat *m); +typedef void (APIENTRYP PFNGLMATRIXLOAD3X3FNVPROC) (GLenum matrixMode, const GLfloat *m); +typedef void (APIENTRYP PFNGLMATRIXLOADTRANSPOSE3X3FNVPROC) (GLenum matrixMode, const GLfloat *m); +typedef void (APIENTRYP PFNGLMATRIXMULT3X2FNVPROC) (GLenum matrixMode, const GLfloat *m); +typedef void (APIENTRYP PFNGLMATRIXMULT3X3FNVPROC) (GLenum matrixMode, const GLfloat *m); +typedef void (APIENTRYP PFNGLMATRIXMULTTRANSPOSE3X3FNVPROC) (GLenum matrixMode, const GLfloat *m); +typedef void (APIENTRYP PFNGLSTENCILTHENCOVERFILLPATHNVPROC) (GLuint path, GLenum fillMode, GLuint mask, GLenum coverMode); +typedef void (APIENTRYP PFNGLSTENCILTHENCOVERSTROKEPATHNVPROC) (GLuint path, GLint reference, GLuint mask, GLenum coverMode); +typedef void (APIENTRYP PFNGLSTENCILTHENCOVERFILLPATHINSTANCEDNVPROC) (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLenum fillMode, GLuint mask, GLenum coverMode, GLenum transformType, const GLfloat *transformValues); +typedef void (APIENTRYP PFNGLSTENCILTHENCOVERSTROKEPATHINSTANCEDNVPROC) (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLint reference, GLuint mask, GLenum coverMode, GLenum transformType, const GLfloat *transformValues); +typedef GLenum (APIENTRYP PFNGLPATHGLYPHINDEXRANGENVPROC) (GLenum fontTarget, const void *fontName, GLbitfield fontStyle, GLuint pathParameterTemplate, GLfloat emScale, GLuint baseAndCount[2]); +typedef GLenum (APIENTRYP PFNGLPATHGLYPHINDEXARRAYNVPROC) (GLuint firstPathName, GLenum fontTarget, const void *fontName, GLbitfield fontStyle, GLuint firstGlyphIndex, GLsizei numGlyphs, GLuint pathParameterTemplate, GLfloat emScale); +typedef GLenum (APIENTRYP PFNGLPATHMEMORYGLYPHINDEXARRAYNVPROC) (GLuint firstPathName, GLenum fontTarget, GLsizeiptr fontSize, const void *fontData, GLsizei faceIndex, GLuint firstGlyphIndex, GLsizei numGlyphs, GLuint pathParameterTemplate, GLfloat emScale); +typedef void (APIENTRYP PFNGLPROGRAMPATHFRAGMENTINPUTGENNVPROC) (GLuint program, GLint location, GLenum genMode, GLint components, const GLfloat *coeffs); +typedef void (APIENTRYP PFNGLGETPROGRAMRESOURCEFVNVPROC) (GLuint program, GLenum programInterface, GLuint index, GLsizei propCount, const GLenum *props, GLsizei bufSize, GLsizei *length, GLfloat *params); +typedef void (APIENTRYP PFNGLPATHCOLORGENNVPROC) (GLenum color, GLenum genMode, GLenum colorFormat, const GLfloat *coeffs); +typedef void (APIENTRYP PFNGLPATHTEXGENNVPROC) (GLenum texCoordSet, GLenum genMode, GLint components, const GLfloat *coeffs); +typedef void (APIENTRYP PFNGLPATHFOGGENNVPROC) (GLenum genMode); +typedef void (APIENTRYP PFNGLGETPATHCOLORGENIVNVPROC) (GLenum color, GLenum pname, GLint *value); +typedef void (APIENTRYP PFNGLGETPATHCOLORGENFVNVPROC) (GLenum color, GLenum pname, GLfloat *value); +typedef void (APIENTRYP PFNGLGETPATHTEXGENIVNVPROC) (GLenum texCoordSet, GLenum pname, GLint *value); +typedef void (APIENTRYP PFNGLGETPATHTEXGENFVNVPROC) (GLenum texCoordSet, GLenum pname, GLfloat *value); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI GLuint APIENTRY glGenPathsNV (GLsizei range); +GLAPI void APIENTRY glDeletePathsNV (GLuint path, GLsizei range); +GLAPI GLboolean APIENTRY glIsPathNV (GLuint path); +GLAPI void APIENTRY glPathCommandsNV (GLuint path, GLsizei numCommands, const GLubyte *commands, GLsizei numCoords, GLenum coordType, const void *coords); +GLAPI void APIENTRY glPathCoordsNV (GLuint path, GLsizei numCoords, GLenum coordType, const void *coords); +GLAPI void APIENTRY glPathSubCommandsNV (GLuint path, GLsizei commandStart, GLsizei commandsToDelete, GLsizei numCommands, const GLubyte *commands, GLsizei numCoords, GLenum coordType, const void *coords); +GLAPI void APIENTRY glPathSubCoordsNV (GLuint path, GLsizei coordStart, GLsizei numCoords, GLenum coordType, const void *coords); +GLAPI void APIENTRY glPathStringNV (GLuint path, GLenum format, GLsizei length, const void *pathString); +GLAPI void APIENTRY glPathGlyphsNV (GLuint firstPathName, GLenum fontTarget, const void *fontName, GLbitfield fontStyle, GLsizei numGlyphs, GLenum type, const void *charcodes, GLenum handleMissingGlyphs, GLuint pathParameterTemplate, GLfloat emScale); +GLAPI void APIENTRY glPathGlyphRangeNV (GLuint firstPathName, GLenum fontTarget, const void *fontName, GLbitfield fontStyle, GLuint firstGlyph, GLsizei numGlyphs, GLenum handleMissingGlyphs, GLuint pathParameterTemplate, GLfloat emScale); +GLAPI void APIENTRY glWeightPathsNV (GLuint resultPath, GLsizei numPaths, const GLuint *paths, const GLfloat *weights); +GLAPI void APIENTRY glCopyPathNV (GLuint resultPath, GLuint srcPath); +GLAPI void APIENTRY glInterpolatePathsNV (GLuint resultPath, GLuint pathA, GLuint pathB, GLfloat weight); +GLAPI void APIENTRY glTransformPathNV (GLuint resultPath, GLuint srcPath, GLenum transformType, const GLfloat *transformValues); +GLAPI void APIENTRY glPathParameterivNV (GLuint path, GLenum pname, const GLint *value); +GLAPI void APIENTRY glPathParameteriNV (GLuint path, GLenum pname, GLint value); +GLAPI void APIENTRY glPathParameterfvNV (GLuint path, GLenum pname, const GLfloat *value); +GLAPI void APIENTRY glPathParameterfNV (GLuint path, GLenum pname, GLfloat value); +GLAPI void APIENTRY glPathDashArrayNV (GLuint path, GLsizei dashCount, const GLfloat *dashArray); +GLAPI void APIENTRY glPathStencilFuncNV (GLenum func, GLint ref, GLuint mask); +GLAPI void APIENTRY glPathStencilDepthOffsetNV (GLfloat factor, GLfloat units); +GLAPI void APIENTRY glStencilFillPathNV (GLuint path, GLenum fillMode, GLuint mask); +GLAPI void APIENTRY glStencilStrokePathNV (GLuint path, GLint reference, GLuint mask); +GLAPI void APIENTRY glStencilFillPathInstancedNV (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLenum fillMode, GLuint mask, GLenum transformType, const GLfloat *transformValues); +GLAPI void APIENTRY glStencilStrokePathInstancedNV (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLint reference, GLuint mask, GLenum transformType, const GLfloat *transformValues); +GLAPI void APIENTRY glPathCoverDepthFuncNV (GLenum func); +GLAPI void APIENTRY glCoverFillPathNV (GLuint path, GLenum coverMode); +GLAPI void APIENTRY glCoverStrokePathNV (GLuint path, GLenum coverMode); +GLAPI void APIENTRY glCoverFillPathInstancedNV (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLenum coverMode, GLenum transformType, const GLfloat *transformValues); +GLAPI void APIENTRY glCoverStrokePathInstancedNV (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLenum coverMode, GLenum transformType, const GLfloat *transformValues); +GLAPI void APIENTRY glGetPathParameterivNV (GLuint path, GLenum pname, GLint *value); +GLAPI void APIENTRY glGetPathParameterfvNV (GLuint path, GLenum pname, GLfloat *value); +GLAPI void APIENTRY glGetPathCommandsNV (GLuint path, GLubyte *commands); +GLAPI void APIENTRY glGetPathCoordsNV (GLuint path, GLfloat *coords); +GLAPI void APIENTRY glGetPathDashArrayNV (GLuint path, GLfloat *dashArray); +GLAPI void APIENTRY glGetPathMetricsNV (GLbitfield metricQueryMask, GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLsizei stride, GLfloat *metrics); +GLAPI void APIENTRY glGetPathMetricRangeNV (GLbitfield metricQueryMask, GLuint firstPathName, GLsizei numPaths, GLsizei stride, GLfloat *metrics); +GLAPI void APIENTRY glGetPathSpacingNV (GLenum pathListMode, GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLfloat advanceScale, GLfloat kerningScale, GLenum transformType, GLfloat *returnedSpacing); +GLAPI GLboolean APIENTRY glIsPointInFillPathNV (GLuint path, GLuint mask, GLfloat x, GLfloat y); +GLAPI GLboolean APIENTRY glIsPointInStrokePathNV (GLuint path, GLfloat x, GLfloat y); +GLAPI GLfloat APIENTRY glGetPathLengthNV (GLuint path, GLsizei startSegment, GLsizei numSegments); +GLAPI GLboolean APIENTRY glPointAlongPathNV (GLuint path, GLsizei startSegment, GLsizei numSegments, GLfloat distance, GLfloat *x, GLfloat *y, GLfloat *tangentX, GLfloat *tangentY); +GLAPI void APIENTRY glMatrixLoad3x2fNV (GLenum matrixMode, const GLfloat *m); +GLAPI void APIENTRY glMatrixLoad3x3fNV (GLenum matrixMode, const GLfloat *m); +GLAPI void APIENTRY glMatrixLoadTranspose3x3fNV (GLenum matrixMode, const GLfloat *m); +GLAPI void APIENTRY glMatrixMult3x2fNV (GLenum matrixMode, const GLfloat *m); +GLAPI void APIENTRY glMatrixMult3x3fNV (GLenum matrixMode, const GLfloat *m); +GLAPI void APIENTRY glMatrixMultTranspose3x3fNV (GLenum matrixMode, const GLfloat *m); +GLAPI void APIENTRY glStencilThenCoverFillPathNV (GLuint path, GLenum fillMode, GLuint mask, GLenum coverMode); +GLAPI void APIENTRY glStencilThenCoverStrokePathNV (GLuint path, GLint reference, GLuint mask, GLenum coverMode); +GLAPI void APIENTRY glStencilThenCoverFillPathInstancedNV (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLenum fillMode, GLuint mask, GLenum coverMode, GLenum transformType, const GLfloat *transformValues); +GLAPI void APIENTRY glStencilThenCoverStrokePathInstancedNV (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLint reference, GLuint mask, GLenum coverMode, GLenum transformType, const GLfloat *transformValues); +GLAPI GLenum APIENTRY glPathGlyphIndexRangeNV (GLenum fontTarget, const void *fontName, GLbitfield fontStyle, GLuint pathParameterTemplate, GLfloat emScale, GLuint baseAndCount[2]); +GLAPI GLenum APIENTRY glPathGlyphIndexArrayNV (GLuint firstPathName, GLenum fontTarget, const void *fontName, GLbitfield fontStyle, GLuint firstGlyphIndex, GLsizei numGlyphs, GLuint pathParameterTemplate, GLfloat emScale); +GLAPI GLenum APIENTRY glPathMemoryGlyphIndexArrayNV (GLuint firstPathName, GLenum fontTarget, GLsizeiptr fontSize, const void *fontData, GLsizei faceIndex, GLuint firstGlyphIndex, GLsizei numGlyphs, GLuint pathParameterTemplate, GLfloat emScale); +GLAPI void APIENTRY glProgramPathFragmentInputGenNV (GLuint program, GLint location, GLenum genMode, GLint components, const GLfloat *coeffs); +GLAPI void APIENTRY glGetProgramResourcefvNV (GLuint program, GLenum programInterface, GLuint index, GLsizei propCount, const GLenum *props, GLsizei bufSize, GLsizei *length, GLfloat *params); +GLAPI void APIENTRY glPathColorGenNV (GLenum color, GLenum genMode, GLenum colorFormat, const GLfloat *coeffs); +GLAPI void APIENTRY glPathTexGenNV (GLenum texCoordSet, GLenum genMode, GLint components, const GLfloat *coeffs); +GLAPI void APIENTRY glPathFogGenNV (GLenum genMode); +GLAPI void APIENTRY glGetPathColorGenivNV (GLenum color, GLenum pname, GLint *value); +GLAPI void APIENTRY glGetPathColorGenfvNV (GLenum color, GLenum pname, GLfloat *value); +GLAPI void APIENTRY glGetPathTexGenivNV (GLenum texCoordSet, GLenum pname, GLint *value); +GLAPI void APIENTRY glGetPathTexGenfvNV (GLenum texCoordSet, GLenum pname, GLfloat *value); +#endif +#endif /* GL_NV_path_rendering */ + +#ifndef GL_NV_path_rendering_shared_edge +#define GL_NV_path_rendering_shared_edge 1 +#define GL_SHARED_EDGE_NV 0xC0 +#endif /* GL_NV_path_rendering_shared_edge */ + +#ifndef GL_NV_pixel_data_range +#define GL_NV_pixel_data_range 1 +#define GL_WRITE_PIXEL_DATA_RANGE_NV 0x8878 +#define GL_READ_PIXEL_DATA_RANGE_NV 0x8879 +#define GL_WRITE_PIXEL_DATA_RANGE_LENGTH_NV 0x887A +#define GL_READ_PIXEL_DATA_RANGE_LENGTH_NV 0x887B +#define GL_WRITE_PIXEL_DATA_RANGE_POINTER_NV 0x887C +#define GL_READ_PIXEL_DATA_RANGE_POINTER_NV 0x887D +typedef void (APIENTRYP PFNGLPIXELDATARANGENVPROC) (GLenum target, GLsizei length, const void *pointer); +typedef void (APIENTRYP PFNGLFLUSHPIXELDATARANGENVPROC) (GLenum target); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glPixelDataRangeNV (GLenum target, GLsizei length, const void *pointer); +GLAPI void APIENTRY glFlushPixelDataRangeNV (GLenum target); +#endif +#endif /* GL_NV_pixel_data_range */ + +#ifndef GL_NV_point_sprite +#define GL_NV_point_sprite 1 +#define GL_POINT_SPRITE_NV 0x8861 +#define GL_COORD_REPLACE_NV 0x8862 +#define GL_POINT_SPRITE_R_MODE_NV 0x8863 +typedef void (APIENTRYP PFNGLPOINTPARAMETERINVPROC) (GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLPOINTPARAMETERIVNVPROC) (GLenum pname, const GLint *params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glPointParameteriNV (GLenum pname, GLint param); +GLAPI void APIENTRY glPointParameterivNV (GLenum pname, const GLint *params); +#endif +#endif /* GL_NV_point_sprite */ + +#ifndef GL_NV_present_video +#define GL_NV_present_video 1 +#define GL_FRAME_NV 0x8E26 +#define GL_FIELDS_NV 0x8E27 +#define GL_CURRENT_TIME_NV 0x8E28 +#define GL_NUM_FILL_STREAMS_NV 0x8E29 +#define GL_PRESENT_TIME_NV 0x8E2A +#define GL_PRESENT_DURATION_NV 0x8E2B +typedef void (APIENTRYP PFNGLPRESENTFRAMEKEYEDNVPROC) (GLuint video_slot, GLuint64EXT minPresentTime, GLuint beginPresentTimeId, GLuint presentDurationId, GLenum type, GLenum target0, GLuint fill0, GLuint key0, GLenum target1, GLuint fill1, GLuint key1); +typedef void (APIENTRYP PFNGLPRESENTFRAMEDUALFILLNVPROC) (GLuint video_slot, GLuint64EXT minPresentTime, GLuint beginPresentTimeId, GLuint presentDurationId, GLenum type, GLenum target0, GLuint fill0, GLenum target1, GLuint fill1, GLenum target2, GLuint fill2, GLenum target3, GLuint fill3); +typedef void (APIENTRYP PFNGLGETVIDEOIVNVPROC) (GLuint video_slot, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETVIDEOUIVNVPROC) (GLuint video_slot, GLenum pname, GLuint *params); +typedef void (APIENTRYP PFNGLGETVIDEOI64VNVPROC) (GLuint video_slot, GLenum pname, GLint64EXT *params); +typedef void (APIENTRYP PFNGLGETVIDEOUI64VNVPROC) (GLuint video_slot, GLenum pname, GLuint64EXT *params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glPresentFrameKeyedNV (GLuint video_slot, GLuint64EXT minPresentTime, GLuint beginPresentTimeId, GLuint presentDurationId, GLenum type, GLenum target0, GLuint fill0, GLuint key0, GLenum target1, GLuint fill1, GLuint key1); +GLAPI void APIENTRY glPresentFrameDualFillNV (GLuint video_slot, GLuint64EXT minPresentTime, GLuint beginPresentTimeId, GLuint presentDurationId, GLenum type, GLenum target0, GLuint fill0, GLenum target1, GLuint fill1, GLenum target2, GLuint fill2, GLenum target3, GLuint fill3); +GLAPI void APIENTRY glGetVideoivNV (GLuint video_slot, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetVideouivNV (GLuint video_slot, GLenum pname, GLuint *params); +GLAPI void APIENTRY glGetVideoi64vNV (GLuint video_slot, GLenum pname, GLint64EXT *params); +GLAPI void APIENTRY glGetVideoui64vNV (GLuint video_slot, GLenum pname, GLuint64EXT *params); +#endif +#endif /* GL_NV_present_video */ + +#ifndef GL_NV_primitive_restart +#define GL_NV_primitive_restart 1 +#define GL_PRIMITIVE_RESTART_NV 0x8558 +#define GL_PRIMITIVE_RESTART_INDEX_NV 0x8559 +typedef void (APIENTRYP PFNGLPRIMITIVERESTARTNVPROC) (void); +typedef void (APIENTRYP PFNGLPRIMITIVERESTARTINDEXNVPROC) (GLuint index); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glPrimitiveRestartNV (void); +GLAPI void APIENTRY glPrimitiveRestartIndexNV (GLuint index); +#endif +#endif /* GL_NV_primitive_restart */ + +#ifndef GL_NV_register_combiners +#define GL_NV_register_combiners 1 +#define GL_REGISTER_COMBINERS_NV 0x8522 +#define GL_VARIABLE_A_NV 0x8523 +#define GL_VARIABLE_B_NV 0x8524 +#define GL_VARIABLE_C_NV 0x8525 +#define GL_VARIABLE_D_NV 0x8526 +#define GL_VARIABLE_E_NV 0x8527 +#define GL_VARIABLE_F_NV 0x8528 +#define GL_VARIABLE_G_NV 0x8529 +#define GL_CONSTANT_COLOR0_NV 0x852A +#define GL_CONSTANT_COLOR1_NV 0x852B +#define GL_SPARE0_NV 0x852E +#define GL_SPARE1_NV 0x852F +#define GL_DISCARD_NV 0x8530 +#define GL_E_TIMES_F_NV 0x8531 +#define GL_SPARE0_PLUS_SECONDARY_COLOR_NV 0x8532 +#define GL_UNSIGNED_IDENTITY_NV 0x8536 +#define GL_UNSIGNED_INVERT_NV 0x8537 +#define GL_EXPAND_NORMAL_NV 0x8538 +#define GL_EXPAND_NEGATE_NV 0x8539 +#define GL_HALF_BIAS_NORMAL_NV 0x853A +#define GL_HALF_BIAS_NEGATE_NV 0x853B +#define GL_SIGNED_IDENTITY_NV 0x853C +#define GL_SIGNED_NEGATE_NV 0x853D +#define GL_SCALE_BY_TWO_NV 0x853E +#define GL_SCALE_BY_FOUR_NV 0x853F +#define GL_SCALE_BY_ONE_HALF_NV 0x8540 +#define GL_BIAS_BY_NEGATIVE_ONE_HALF_NV 0x8541 +#define GL_COMBINER_INPUT_NV 0x8542 +#define GL_COMBINER_MAPPING_NV 0x8543 +#define GL_COMBINER_COMPONENT_USAGE_NV 0x8544 +#define GL_COMBINER_AB_DOT_PRODUCT_NV 0x8545 +#define GL_COMBINER_CD_DOT_PRODUCT_NV 0x8546 +#define GL_COMBINER_MUX_SUM_NV 0x8547 +#define GL_COMBINER_SCALE_NV 0x8548 +#define GL_COMBINER_BIAS_NV 0x8549 +#define GL_COMBINER_AB_OUTPUT_NV 0x854A +#define GL_COMBINER_CD_OUTPUT_NV 0x854B +#define GL_COMBINER_SUM_OUTPUT_NV 0x854C +#define GL_MAX_GENERAL_COMBINERS_NV 0x854D +#define GL_NUM_GENERAL_COMBINERS_NV 0x854E +#define GL_COLOR_SUM_CLAMP_NV 0x854F +#define GL_COMBINER0_NV 0x8550 +#define GL_COMBINER1_NV 0x8551 +#define GL_COMBINER2_NV 0x8552 +#define GL_COMBINER3_NV 0x8553 +#define GL_COMBINER4_NV 0x8554 +#define GL_COMBINER5_NV 0x8555 +#define GL_COMBINER6_NV 0x8556 +#define GL_COMBINER7_NV 0x8557 +typedef void (APIENTRYP PFNGLCOMBINERPARAMETERFVNVPROC) (GLenum pname, const GLfloat *params); +typedef void (APIENTRYP PFNGLCOMBINERPARAMETERFNVPROC) (GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLCOMBINERPARAMETERIVNVPROC) (GLenum pname, const GLint *params); +typedef void (APIENTRYP PFNGLCOMBINERPARAMETERINVPROC) (GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLCOMBINERINPUTNVPROC) (GLenum stage, GLenum portion, GLenum variable, GLenum input, GLenum mapping, GLenum componentUsage); +typedef void (APIENTRYP PFNGLCOMBINEROUTPUTNVPROC) (GLenum stage, GLenum portion, GLenum abOutput, GLenum cdOutput, GLenum sumOutput, GLenum scale, GLenum bias, GLboolean abDotProduct, GLboolean cdDotProduct, GLboolean muxSum); +typedef void (APIENTRYP PFNGLFINALCOMBINERINPUTNVPROC) (GLenum variable, GLenum input, GLenum mapping, GLenum componentUsage); +typedef void (APIENTRYP PFNGLGETCOMBINERINPUTPARAMETERFVNVPROC) (GLenum stage, GLenum portion, GLenum variable, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETCOMBINERINPUTPARAMETERIVNVPROC) (GLenum stage, GLenum portion, GLenum variable, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETCOMBINEROUTPUTPARAMETERFVNVPROC) (GLenum stage, GLenum portion, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETCOMBINEROUTPUTPARAMETERIVNVPROC) (GLenum stage, GLenum portion, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETFINALCOMBINERINPUTPARAMETERFVNVPROC) (GLenum variable, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETFINALCOMBINERINPUTPARAMETERIVNVPROC) (GLenum variable, GLenum pname, GLint *params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glCombinerParameterfvNV (GLenum pname, const GLfloat *params); +GLAPI void APIENTRY glCombinerParameterfNV (GLenum pname, GLfloat param); +GLAPI void APIENTRY glCombinerParameterivNV (GLenum pname, const GLint *params); +GLAPI void APIENTRY glCombinerParameteriNV (GLenum pname, GLint param); +GLAPI void APIENTRY glCombinerInputNV (GLenum stage, GLenum portion, GLenum variable, GLenum input, GLenum mapping, GLenum componentUsage); +GLAPI void APIENTRY glCombinerOutputNV (GLenum stage, GLenum portion, GLenum abOutput, GLenum cdOutput, GLenum sumOutput, GLenum scale, GLenum bias, GLboolean abDotProduct, GLboolean cdDotProduct, GLboolean muxSum); +GLAPI void APIENTRY glFinalCombinerInputNV (GLenum variable, GLenum input, GLenum mapping, GLenum componentUsage); +GLAPI void APIENTRY glGetCombinerInputParameterfvNV (GLenum stage, GLenum portion, GLenum variable, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetCombinerInputParameterivNV (GLenum stage, GLenum portion, GLenum variable, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetCombinerOutputParameterfvNV (GLenum stage, GLenum portion, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetCombinerOutputParameterivNV (GLenum stage, GLenum portion, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetFinalCombinerInputParameterfvNV (GLenum variable, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetFinalCombinerInputParameterivNV (GLenum variable, GLenum pname, GLint *params); +#endif +#endif /* GL_NV_register_combiners */ + +#ifndef GL_NV_register_combiners2 +#define GL_NV_register_combiners2 1 +#define GL_PER_STAGE_CONSTANTS_NV 0x8535 +typedef void (APIENTRYP PFNGLCOMBINERSTAGEPARAMETERFVNVPROC) (GLenum stage, GLenum pname, const GLfloat *params); +typedef void (APIENTRYP PFNGLGETCOMBINERSTAGEPARAMETERFVNVPROC) (GLenum stage, GLenum pname, GLfloat *params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glCombinerStageParameterfvNV (GLenum stage, GLenum pname, const GLfloat *params); +GLAPI void APIENTRY glGetCombinerStageParameterfvNV (GLenum stage, GLenum pname, GLfloat *params); +#endif +#endif /* GL_NV_register_combiners2 */ + +#ifndef GL_NV_sample_locations +#define GL_NV_sample_locations 1 +#define GL_SAMPLE_LOCATION_SUBPIXEL_BITS_NV 0x933D +#define GL_SAMPLE_LOCATION_PIXEL_GRID_WIDTH_NV 0x933E +#define GL_SAMPLE_LOCATION_PIXEL_GRID_HEIGHT_NV 0x933F +#define GL_PROGRAMMABLE_SAMPLE_LOCATION_TABLE_SIZE_NV 0x9340 +#define GL_SAMPLE_LOCATION_NV 0x8E50 +#define GL_PROGRAMMABLE_SAMPLE_LOCATION_NV 0x9341 +#define GL_FRAMEBUFFER_PROGRAMMABLE_SAMPLE_LOCATIONS_NV 0x9342 +#define GL_FRAMEBUFFER_SAMPLE_LOCATION_PIXEL_GRID_NV 0x9343 +typedef void (APIENTRYP PFNGLFRAMEBUFFERSAMPLELOCATIONSFVNVPROC) (GLenum target, GLuint start, GLsizei count, const GLfloat *v); +typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERSAMPLELOCATIONSFVNVPROC) (GLuint framebuffer, GLuint start, GLsizei count, const GLfloat *v); +typedef void (APIENTRYP PFNGLRESOLVEDEPTHVALUESNVPROC) (void); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glFramebufferSampleLocationsfvNV (GLenum target, GLuint start, GLsizei count, const GLfloat *v); +GLAPI void APIENTRY glNamedFramebufferSampleLocationsfvNV (GLuint framebuffer, GLuint start, GLsizei count, const GLfloat *v); +GLAPI void APIENTRY glResolveDepthValuesNV (void); +#endif +#endif /* GL_NV_sample_locations */ + +#ifndef GL_NV_sample_mask_override_coverage +#define GL_NV_sample_mask_override_coverage 1 +#endif /* GL_NV_sample_mask_override_coverage */ + +#ifndef GL_NV_shader_atomic_counters +#define GL_NV_shader_atomic_counters 1 +#endif /* GL_NV_shader_atomic_counters */ + +#ifndef GL_NV_shader_atomic_float +#define GL_NV_shader_atomic_float 1 +#endif /* GL_NV_shader_atomic_float */ + +#ifndef GL_NV_shader_atomic_fp16_vector +#define GL_NV_shader_atomic_fp16_vector 1 +#endif /* GL_NV_shader_atomic_fp16_vector */ + +#ifndef GL_NV_shader_atomic_int64 +#define GL_NV_shader_atomic_int64 1 +#endif /* GL_NV_shader_atomic_int64 */ + +#ifndef GL_NV_shader_buffer_load +#define GL_NV_shader_buffer_load 1 +#define GL_BUFFER_GPU_ADDRESS_NV 0x8F1D +#define GL_GPU_ADDRESS_NV 0x8F34 +#define GL_MAX_SHADER_BUFFER_ADDRESS_NV 0x8F35 +typedef void (APIENTRYP PFNGLMAKEBUFFERRESIDENTNVPROC) (GLenum target, GLenum access); +typedef void (APIENTRYP PFNGLMAKEBUFFERNONRESIDENTNVPROC) (GLenum target); +typedef GLboolean (APIENTRYP PFNGLISBUFFERRESIDENTNVPROC) (GLenum target); +typedef void (APIENTRYP PFNGLMAKENAMEDBUFFERRESIDENTNVPROC) (GLuint buffer, GLenum access); +typedef void (APIENTRYP PFNGLMAKENAMEDBUFFERNONRESIDENTNVPROC) (GLuint buffer); +typedef GLboolean (APIENTRYP PFNGLISNAMEDBUFFERRESIDENTNVPROC) (GLuint buffer); +typedef void (APIENTRYP PFNGLGETBUFFERPARAMETERUI64VNVPROC) (GLenum target, GLenum pname, GLuint64EXT *params); +typedef void (APIENTRYP PFNGLGETNAMEDBUFFERPARAMETERUI64VNVPROC) (GLuint buffer, GLenum pname, GLuint64EXT *params); +typedef void (APIENTRYP PFNGLGETINTEGERUI64VNVPROC) (GLenum value, GLuint64EXT *result); +typedef void (APIENTRYP PFNGLUNIFORMUI64NVPROC) (GLint location, GLuint64EXT value); +typedef void (APIENTRYP PFNGLUNIFORMUI64VNVPROC) (GLint location, GLsizei count, const GLuint64EXT *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMUI64NVPROC) (GLuint program, GLint location, GLuint64EXT value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMUI64VNVPROC) (GLuint program, GLint location, GLsizei count, const GLuint64EXT *value); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glMakeBufferResidentNV (GLenum target, GLenum access); +GLAPI void APIENTRY glMakeBufferNonResidentNV (GLenum target); +GLAPI GLboolean APIENTRY glIsBufferResidentNV (GLenum target); +GLAPI void APIENTRY glMakeNamedBufferResidentNV (GLuint buffer, GLenum access); +GLAPI void APIENTRY glMakeNamedBufferNonResidentNV (GLuint buffer); +GLAPI GLboolean APIENTRY glIsNamedBufferResidentNV (GLuint buffer); +GLAPI void APIENTRY glGetBufferParameterui64vNV (GLenum target, GLenum pname, GLuint64EXT *params); +GLAPI void APIENTRY glGetNamedBufferParameterui64vNV (GLuint buffer, GLenum pname, GLuint64EXT *params); +GLAPI void APIENTRY glGetIntegerui64vNV (GLenum value, GLuint64EXT *result); +GLAPI void APIENTRY glUniformui64NV (GLint location, GLuint64EXT value); +GLAPI void APIENTRY glUniformui64vNV (GLint location, GLsizei count, const GLuint64EXT *value); +GLAPI void APIENTRY glProgramUniformui64NV (GLuint program, GLint location, GLuint64EXT value); +GLAPI void APIENTRY glProgramUniformui64vNV (GLuint program, GLint location, GLsizei count, const GLuint64EXT *value); +#endif +#endif /* GL_NV_shader_buffer_load */ + +#ifndef GL_NV_shader_buffer_store +#define GL_NV_shader_buffer_store 1 +#define GL_SHADER_GLOBAL_ACCESS_BARRIER_BIT_NV 0x00000010 +#endif /* GL_NV_shader_buffer_store */ + +#ifndef GL_NV_shader_storage_buffer_object +#define GL_NV_shader_storage_buffer_object 1 +#endif /* GL_NV_shader_storage_buffer_object */ + +#ifndef GL_NV_shader_thread_group +#define GL_NV_shader_thread_group 1 +#define GL_WARP_SIZE_NV 0x9339 +#define GL_WARPS_PER_SM_NV 0x933A +#define GL_SM_COUNT_NV 0x933B +#endif /* GL_NV_shader_thread_group */ + +#ifndef GL_NV_shader_thread_shuffle +#define GL_NV_shader_thread_shuffle 1 +#endif /* GL_NV_shader_thread_shuffle */ + +#ifndef GL_NV_tessellation_program5 +#define GL_NV_tessellation_program5 1 +#define GL_MAX_PROGRAM_PATCH_ATTRIBS_NV 0x86D8 +#define GL_TESS_CONTROL_PROGRAM_NV 0x891E +#define GL_TESS_EVALUATION_PROGRAM_NV 0x891F +#define GL_TESS_CONTROL_PROGRAM_PARAMETER_BUFFER_NV 0x8C74 +#define GL_TESS_EVALUATION_PROGRAM_PARAMETER_BUFFER_NV 0x8C75 +#endif /* GL_NV_tessellation_program5 */ + +#ifndef GL_NV_texgen_emboss +#define GL_NV_texgen_emboss 1 +#define GL_EMBOSS_LIGHT_NV 0x855D +#define GL_EMBOSS_CONSTANT_NV 0x855E +#define GL_EMBOSS_MAP_NV 0x855F +#endif /* GL_NV_texgen_emboss */ + +#ifndef GL_NV_texgen_reflection +#define GL_NV_texgen_reflection 1 +#define GL_NORMAL_MAP_NV 0x8511 +#define GL_REFLECTION_MAP_NV 0x8512 +#endif /* GL_NV_texgen_reflection */ + +#ifndef GL_NV_texture_barrier +#define GL_NV_texture_barrier 1 +typedef void (APIENTRYP PFNGLTEXTUREBARRIERNVPROC) (void); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glTextureBarrierNV (void); +#endif +#endif /* GL_NV_texture_barrier */ + +#ifndef GL_NV_texture_compression_vtc +#define GL_NV_texture_compression_vtc 1 +#endif /* GL_NV_texture_compression_vtc */ + +#ifndef GL_NV_texture_env_combine4 +#define GL_NV_texture_env_combine4 1 +#define GL_COMBINE4_NV 0x8503 +#define GL_SOURCE3_RGB_NV 0x8583 +#define GL_SOURCE3_ALPHA_NV 0x858B +#define GL_OPERAND3_RGB_NV 0x8593 +#define GL_OPERAND3_ALPHA_NV 0x859B +#endif /* GL_NV_texture_env_combine4 */ + +#ifndef GL_NV_texture_expand_normal +#define GL_NV_texture_expand_normal 1 +#define GL_TEXTURE_UNSIGNED_REMAP_MODE_NV 0x888F +#endif /* GL_NV_texture_expand_normal */ + +#ifndef GL_NV_texture_multisample +#define GL_NV_texture_multisample 1 +#define GL_TEXTURE_COVERAGE_SAMPLES_NV 0x9045 +#define GL_TEXTURE_COLOR_SAMPLES_NV 0x9046 +typedef void (APIENTRYP PFNGLTEXIMAGE2DMULTISAMPLECOVERAGENVPROC) (GLenum target, GLsizei coverageSamples, GLsizei colorSamples, GLint internalFormat, GLsizei width, GLsizei height, GLboolean fixedSampleLocations); +typedef void (APIENTRYP PFNGLTEXIMAGE3DMULTISAMPLECOVERAGENVPROC) (GLenum target, GLsizei coverageSamples, GLsizei colorSamples, GLint internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedSampleLocations); +typedef void (APIENTRYP PFNGLTEXTUREIMAGE2DMULTISAMPLENVPROC) (GLuint texture, GLenum target, GLsizei samples, GLint internalFormat, GLsizei width, GLsizei height, GLboolean fixedSampleLocations); +typedef void (APIENTRYP PFNGLTEXTUREIMAGE3DMULTISAMPLENVPROC) (GLuint texture, GLenum target, GLsizei samples, GLint internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedSampleLocations); +typedef void (APIENTRYP PFNGLTEXTUREIMAGE2DMULTISAMPLECOVERAGENVPROC) (GLuint texture, GLenum target, GLsizei coverageSamples, GLsizei colorSamples, GLint internalFormat, GLsizei width, GLsizei height, GLboolean fixedSampleLocations); +typedef void (APIENTRYP PFNGLTEXTUREIMAGE3DMULTISAMPLECOVERAGENVPROC) (GLuint texture, GLenum target, GLsizei coverageSamples, GLsizei colorSamples, GLint internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedSampleLocations); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glTexImage2DMultisampleCoverageNV (GLenum target, GLsizei coverageSamples, GLsizei colorSamples, GLint internalFormat, GLsizei width, GLsizei height, GLboolean fixedSampleLocations); +GLAPI void APIENTRY glTexImage3DMultisampleCoverageNV (GLenum target, GLsizei coverageSamples, GLsizei colorSamples, GLint internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedSampleLocations); +GLAPI void APIENTRY glTextureImage2DMultisampleNV (GLuint texture, GLenum target, GLsizei samples, GLint internalFormat, GLsizei width, GLsizei height, GLboolean fixedSampleLocations); +GLAPI void APIENTRY glTextureImage3DMultisampleNV (GLuint texture, GLenum target, GLsizei samples, GLint internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedSampleLocations); +GLAPI void APIENTRY glTextureImage2DMultisampleCoverageNV (GLuint texture, GLenum target, GLsizei coverageSamples, GLsizei colorSamples, GLint internalFormat, GLsizei width, GLsizei height, GLboolean fixedSampleLocations); +GLAPI void APIENTRY glTextureImage3DMultisampleCoverageNV (GLuint texture, GLenum target, GLsizei coverageSamples, GLsizei colorSamples, GLint internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedSampleLocations); +#endif +#endif /* GL_NV_texture_multisample */ + +#ifndef GL_NV_texture_rectangle +#define GL_NV_texture_rectangle 1 +#define GL_TEXTURE_RECTANGLE_NV 0x84F5 +#define GL_TEXTURE_BINDING_RECTANGLE_NV 0x84F6 +#define GL_PROXY_TEXTURE_RECTANGLE_NV 0x84F7 +#define GL_MAX_RECTANGLE_TEXTURE_SIZE_NV 0x84F8 +#endif /* GL_NV_texture_rectangle */ + +#ifndef GL_NV_texture_shader +#define GL_NV_texture_shader 1 +#define GL_OFFSET_TEXTURE_RECTANGLE_NV 0x864C +#define GL_OFFSET_TEXTURE_RECTANGLE_SCALE_NV 0x864D +#define GL_DOT_PRODUCT_TEXTURE_RECTANGLE_NV 0x864E +#define GL_RGBA_UNSIGNED_DOT_PRODUCT_MAPPING_NV 0x86D9 +#define GL_UNSIGNED_INT_S8_S8_8_8_NV 0x86DA +#define GL_UNSIGNED_INT_8_8_S8_S8_REV_NV 0x86DB +#define GL_DSDT_MAG_INTENSITY_NV 0x86DC +#define GL_SHADER_CONSISTENT_NV 0x86DD +#define GL_TEXTURE_SHADER_NV 0x86DE +#define GL_SHADER_OPERATION_NV 0x86DF +#define GL_CULL_MODES_NV 0x86E0 +#define GL_OFFSET_TEXTURE_MATRIX_NV 0x86E1 +#define GL_OFFSET_TEXTURE_SCALE_NV 0x86E2 +#define GL_OFFSET_TEXTURE_BIAS_NV 0x86E3 +#define GL_OFFSET_TEXTURE_2D_MATRIX_NV 0x86E1 +#define GL_OFFSET_TEXTURE_2D_SCALE_NV 0x86E2 +#define GL_OFFSET_TEXTURE_2D_BIAS_NV 0x86E3 +#define GL_PREVIOUS_TEXTURE_INPUT_NV 0x86E4 +#define GL_CONST_EYE_NV 0x86E5 +#define GL_PASS_THROUGH_NV 0x86E6 +#define GL_CULL_FRAGMENT_NV 0x86E7 +#define GL_OFFSET_TEXTURE_2D_NV 0x86E8 +#define GL_DEPENDENT_AR_TEXTURE_2D_NV 0x86E9 +#define GL_DEPENDENT_GB_TEXTURE_2D_NV 0x86EA +#define GL_DOT_PRODUCT_NV 0x86EC +#define GL_DOT_PRODUCT_DEPTH_REPLACE_NV 0x86ED +#define GL_DOT_PRODUCT_TEXTURE_2D_NV 0x86EE +#define GL_DOT_PRODUCT_TEXTURE_CUBE_MAP_NV 0x86F0 +#define GL_DOT_PRODUCT_DIFFUSE_CUBE_MAP_NV 0x86F1 +#define GL_DOT_PRODUCT_REFLECT_CUBE_MAP_NV 0x86F2 +#define GL_DOT_PRODUCT_CONST_EYE_REFLECT_CUBE_MAP_NV 0x86F3 +#define GL_HILO_NV 0x86F4 +#define GL_DSDT_NV 0x86F5 +#define GL_DSDT_MAG_NV 0x86F6 +#define GL_DSDT_MAG_VIB_NV 0x86F7 +#define GL_HILO16_NV 0x86F8 +#define GL_SIGNED_HILO_NV 0x86F9 +#define GL_SIGNED_HILO16_NV 0x86FA +#define GL_SIGNED_RGBA_NV 0x86FB +#define GL_SIGNED_RGBA8_NV 0x86FC +#define GL_SIGNED_RGB_NV 0x86FE +#define GL_SIGNED_RGB8_NV 0x86FF +#define GL_SIGNED_LUMINANCE_NV 0x8701 +#define GL_SIGNED_LUMINANCE8_NV 0x8702 +#define GL_SIGNED_LUMINANCE_ALPHA_NV 0x8703 +#define GL_SIGNED_LUMINANCE8_ALPHA8_NV 0x8704 +#define GL_SIGNED_ALPHA_NV 0x8705 +#define GL_SIGNED_ALPHA8_NV 0x8706 +#define GL_SIGNED_INTENSITY_NV 0x8707 +#define GL_SIGNED_INTENSITY8_NV 0x8708 +#define GL_DSDT8_NV 0x8709 +#define GL_DSDT8_MAG8_NV 0x870A +#define GL_DSDT8_MAG8_INTENSITY8_NV 0x870B +#define GL_SIGNED_RGB_UNSIGNED_ALPHA_NV 0x870C +#define GL_SIGNED_RGB8_UNSIGNED_ALPHA8_NV 0x870D +#define GL_HI_SCALE_NV 0x870E +#define GL_LO_SCALE_NV 0x870F +#define GL_DS_SCALE_NV 0x8710 +#define GL_DT_SCALE_NV 0x8711 +#define GL_MAGNITUDE_SCALE_NV 0x8712 +#define GL_VIBRANCE_SCALE_NV 0x8713 +#define GL_HI_BIAS_NV 0x8714 +#define GL_LO_BIAS_NV 0x8715 +#define GL_DS_BIAS_NV 0x8716 +#define GL_DT_BIAS_NV 0x8717 +#define GL_MAGNITUDE_BIAS_NV 0x8718 +#define GL_VIBRANCE_BIAS_NV 0x8719 +#define GL_TEXTURE_BORDER_VALUES_NV 0x871A +#define GL_TEXTURE_HI_SIZE_NV 0x871B +#define GL_TEXTURE_LO_SIZE_NV 0x871C +#define GL_TEXTURE_DS_SIZE_NV 0x871D +#define GL_TEXTURE_DT_SIZE_NV 0x871E +#define GL_TEXTURE_MAG_SIZE_NV 0x871F +#endif /* GL_NV_texture_shader */ + +#ifndef GL_NV_texture_shader2 +#define GL_NV_texture_shader2 1 +#define GL_DOT_PRODUCT_TEXTURE_3D_NV 0x86EF +#endif /* GL_NV_texture_shader2 */ + +#ifndef GL_NV_texture_shader3 +#define GL_NV_texture_shader3 1 +#define GL_OFFSET_PROJECTIVE_TEXTURE_2D_NV 0x8850 +#define GL_OFFSET_PROJECTIVE_TEXTURE_2D_SCALE_NV 0x8851 +#define GL_OFFSET_PROJECTIVE_TEXTURE_RECTANGLE_NV 0x8852 +#define GL_OFFSET_PROJECTIVE_TEXTURE_RECTANGLE_SCALE_NV 0x8853 +#define GL_OFFSET_HILO_TEXTURE_2D_NV 0x8854 +#define GL_OFFSET_HILO_TEXTURE_RECTANGLE_NV 0x8855 +#define GL_OFFSET_HILO_PROJECTIVE_TEXTURE_2D_NV 0x8856 +#define GL_OFFSET_HILO_PROJECTIVE_TEXTURE_RECTANGLE_NV 0x8857 +#define GL_DEPENDENT_HILO_TEXTURE_2D_NV 0x8858 +#define GL_DEPENDENT_RGB_TEXTURE_3D_NV 0x8859 +#define GL_DEPENDENT_RGB_TEXTURE_CUBE_MAP_NV 0x885A +#define GL_DOT_PRODUCT_PASS_THROUGH_NV 0x885B +#define GL_DOT_PRODUCT_TEXTURE_1D_NV 0x885C +#define GL_DOT_PRODUCT_AFFINE_DEPTH_REPLACE_NV 0x885D +#define GL_HILO8_NV 0x885E +#define GL_SIGNED_HILO8_NV 0x885F +#define GL_FORCE_BLUE_TO_ONE_NV 0x8860 +#endif /* GL_NV_texture_shader3 */ + +#ifndef GL_NV_transform_feedback +#define GL_NV_transform_feedback 1 +#define GL_BACK_PRIMARY_COLOR_NV 0x8C77 +#define GL_BACK_SECONDARY_COLOR_NV 0x8C78 +#define GL_TEXTURE_COORD_NV 0x8C79 +#define GL_CLIP_DISTANCE_NV 0x8C7A +#define GL_VERTEX_ID_NV 0x8C7B +#define GL_PRIMITIVE_ID_NV 0x8C7C +#define GL_GENERIC_ATTRIB_NV 0x8C7D +#define GL_TRANSFORM_FEEDBACK_ATTRIBS_NV 0x8C7E +#define GL_TRANSFORM_FEEDBACK_BUFFER_MODE_NV 0x8C7F +#define GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS_NV 0x8C80 +#define GL_ACTIVE_VARYINGS_NV 0x8C81 +#define GL_ACTIVE_VARYING_MAX_LENGTH_NV 0x8C82 +#define GL_TRANSFORM_FEEDBACK_VARYINGS_NV 0x8C83 +#define GL_TRANSFORM_FEEDBACK_BUFFER_START_NV 0x8C84 +#define GL_TRANSFORM_FEEDBACK_BUFFER_SIZE_NV 0x8C85 +#define GL_TRANSFORM_FEEDBACK_RECORD_NV 0x8C86 +#define GL_PRIMITIVES_GENERATED_NV 0x8C87 +#define GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN_NV 0x8C88 +#define GL_RASTERIZER_DISCARD_NV 0x8C89 +#define GL_MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS_NV 0x8C8A +#define GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS_NV 0x8C8B +#define GL_INTERLEAVED_ATTRIBS_NV 0x8C8C +#define GL_SEPARATE_ATTRIBS_NV 0x8C8D +#define GL_TRANSFORM_FEEDBACK_BUFFER_NV 0x8C8E +#define GL_TRANSFORM_FEEDBACK_BUFFER_BINDING_NV 0x8C8F +#define GL_LAYER_NV 0x8DAA +#define GL_NEXT_BUFFER_NV -2 +#define GL_SKIP_COMPONENTS4_NV -3 +#define GL_SKIP_COMPONENTS3_NV -4 +#define GL_SKIP_COMPONENTS2_NV -5 +#define GL_SKIP_COMPONENTS1_NV -6 +typedef void (APIENTRYP PFNGLBEGINTRANSFORMFEEDBACKNVPROC) (GLenum primitiveMode); +typedef void (APIENTRYP PFNGLENDTRANSFORMFEEDBACKNVPROC) (void); +typedef void (APIENTRYP PFNGLTRANSFORMFEEDBACKATTRIBSNVPROC) (GLsizei count, const GLint *attribs, GLenum bufferMode); +typedef void (APIENTRYP PFNGLBINDBUFFERRANGENVPROC) (GLenum target, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size); +typedef void (APIENTRYP PFNGLBINDBUFFEROFFSETNVPROC) (GLenum target, GLuint index, GLuint buffer, GLintptr offset); +typedef void (APIENTRYP PFNGLBINDBUFFERBASENVPROC) (GLenum target, GLuint index, GLuint buffer); +typedef void (APIENTRYP PFNGLTRANSFORMFEEDBACKVARYINGSNVPROC) (GLuint program, GLsizei count, const GLint *locations, GLenum bufferMode); +typedef void (APIENTRYP PFNGLACTIVEVARYINGNVPROC) (GLuint program, const GLchar *name); +typedef GLint (APIENTRYP PFNGLGETVARYINGLOCATIONNVPROC) (GLuint program, const GLchar *name); +typedef void (APIENTRYP PFNGLGETACTIVEVARYINGNVPROC) (GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLsizei *size, GLenum *type, GLchar *name); +typedef void (APIENTRYP PFNGLGETTRANSFORMFEEDBACKVARYINGNVPROC) (GLuint program, GLuint index, GLint *location); +typedef void (APIENTRYP PFNGLTRANSFORMFEEDBACKSTREAMATTRIBSNVPROC) (GLsizei count, const GLint *attribs, GLsizei nbuffers, const GLint *bufstreams, GLenum bufferMode); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBeginTransformFeedbackNV (GLenum primitiveMode); +GLAPI void APIENTRY glEndTransformFeedbackNV (void); +GLAPI void APIENTRY glTransformFeedbackAttribsNV (GLsizei count, const GLint *attribs, GLenum bufferMode); +GLAPI void APIENTRY glBindBufferRangeNV (GLenum target, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size); +GLAPI void APIENTRY glBindBufferOffsetNV (GLenum target, GLuint index, GLuint buffer, GLintptr offset); +GLAPI void APIENTRY glBindBufferBaseNV (GLenum target, GLuint index, GLuint buffer); +GLAPI void APIENTRY glTransformFeedbackVaryingsNV (GLuint program, GLsizei count, const GLint *locations, GLenum bufferMode); +GLAPI void APIENTRY glActiveVaryingNV (GLuint program, const GLchar *name); +GLAPI GLint APIENTRY glGetVaryingLocationNV (GLuint program, const GLchar *name); +GLAPI void APIENTRY glGetActiveVaryingNV (GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLsizei *size, GLenum *type, GLchar *name); +GLAPI void APIENTRY glGetTransformFeedbackVaryingNV (GLuint program, GLuint index, GLint *location); +GLAPI void APIENTRY glTransformFeedbackStreamAttribsNV (GLsizei count, const GLint *attribs, GLsizei nbuffers, const GLint *bufstreams, GLenum bufferMode); +#endif +#endif /* GL_NV_transform_feedback */ + +#ifndef GL_NV_transform_feedback2 +#define GL_NV_transform_feedback2 1 +#define GL_TRANSFORM_FEEDBACK_NV 0x8E22 +#define GL_TRANSFORM_FEEDBACK_BUFFER_PAUSED_NV 0x8E23 +#define GL_TRANSFORM_FEEDBACK_BUFFER_ACTIVE_NV 0x8E24 +#define GL_TRANSFORM_FEEDBACK_BINDING_NV 0x8E25 +typedef void (APIENTRYP PFNGLBINDTRANSFORMFEEDBACKNVPROC) (GLenum target, GLuint id); +typedef void (APIENTRYP PFNGLDELETETRANSFORMFEEDBACKSNVPROC) (GLsizei n, const GLuint *ids); +typedef void (APIENTRYP PFNGLGENTRANSFORMFEEDBACKSNVPROC) (GLsizei n, GLuint *ids); +typedef GLboolean (APIENTRYP PFNGLISTRANSFORMFEEDBACKNVPROC) (GLuint id); +typedef void (APIENTRYP PFNGLPAUSETRANSFORMFEEDBACKNVPROC) (void); +typedef void (APIENTRYP PFNGLRESUMETRANSFORMFEEDBACKNVPROC) (void); +typedef void (APIENTRYP PFNGLDRAWTRANSFORMFEEDBACKNVPROC) (GLenum mode, GLuint id); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBindTransformFeedbackNV (GLenum target, GLuint id); +GLAPI void APIENTRY glDeleteTransformFeedbacksNV (GLsizei n, const GLuint *ids); +GLAPI void APIENTRY glGenTransformFeedbacksNV (GLsizei n, GLuint *ids); +GLAPI GLboolean APIENTRY glIsTransformFeedbackNV (GLuint id); +GLAPI void APIENTRY glPauseTransformFeedbackNV (void); +GLAPI void APIENTRY glResumeTransformFeedbackNV (void); +GLAPI void APIENTRY glDrawTransformFeedbackNV (GLenum mode, GLuint id); +#endif +#endif /* GL_NV_transform_feedback2 */ + +#ifndef GL_NV_uniform_buffer_unified_memory +#define GL_NV_uniform_buffer_unified_memory 1 +#define GL_UNIFORM_BUFFER_UNIFIED_NV 0x936E +#define GL_UNIFORM_BUFFER_ADDRESS_NV 0x936F +#define GL_UNIFORM_BUFFER_LENGTH_NV 0x9370 +#endif /* GL_NV_uniform_buffer_unified_memory */ + +#ifndef GL_NV_vdpau_interop +#define GL_NV_vdpau_interop 1 +typedef GLintptr GLvdpauSurfaceNV; +#define GL_SURFACE_STATE_NV 0x86EB +#define GL_SURFACE_REGISTERED_NV 0x86FD +#define GL_SURFACE_MAPPED_NV 0x8700 +#define GL_WRITE_DISCARD_NV 0x88BE +typedef void (APIENTRYP PFNGLVDPAUINITNVPROC) (const void *vdpDevice, const void *getProcAddress); +typedef void (APIENTRYP PFNGLVDPAUFININVPROC) (void); +typedef GLvdpauSurfaceNV (APIENTRYP PFNGLVDPAUREGISTERVIDEOSURFACENVPROC) (const void *vdpSurface, GLenum target, GLsizei numTextureNames, const GLuint *textureNames); +typedef GLvdpauSurfaceNV (APIENTRYP PFNGLVDPAUREGISTEROUTPUTSURFACENVPROC) (const void *vdpSurface, GLenum target, GLsizei numTextureNames, const GLuint *textureNames); +typedef GLboolean (APIENTRYP PFNGLVDPAUISSURFACENVPROC) (GLvdpauSurfaceNV surface); +typedef void (APIENTRYP PFNGLVDPAUUNREGISTERSURFACENVPROC) (GLvdpauSurfaceNV surface); +typedef void (APIENTRYP PFNGLVDPAUGETSURFACEIVNVPROC) (GLvdpauSurfaceNV surface, GLenum pname, GLsizei bufSize, GLsizei *length, GLint *values); +typedef void (APIENTRYP PFNGLVDPAUSURFACEACCESSNVPROC) (GLvdpauSurfaceNV surface, GLenum access); +typedef void (APIENTRYP PFNGLVDPAUMAPSURFACESNVPROC) (GLsizei numSurfaces, const GLvdpauSurfaceNV *surfaces); +typedef void (APIENTRYP PFNGLVDPAUUNMAPSURFACESNVPROC) (GLsizei numSurface, const GLvdpauSurfaceNV *surfaces); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glVDPAUInitNV (const void *vdpDevice, const void *getProcAddress); +GLAPI void APIENTRY glVDPAUFiniNV (void); +GLAPI GLvdpauSurfaceNV APIENTRY glVDPAURegisterVideoSurfaceNV (const void *vdpSurface, GLenum target, GLsizei numTextureNames, const GLuint *textureNames); +GLAPI GLvdpauSurfaceNV APIENTRY glVDPAURegisterOutputSurfaceNV (const void *vdpSurface, GLenum target, GLsizei numTextureNames, const GLuint *textureNames); +GLAPI GLboolean APIENTRY glVDPAUIsSurfaceNV (GLvdpauSurfaceNV surface); +GLAPI void APIENTRY glVDPAUUnregisterSurfaceNV (GLvdpauSurfaceNV surface); +GLAPI void APIENTRY glVDPAUGetSurfaceivNV (GLvdpauSurfaceNV surface, GLenum pname, GLsizei bufSize, GLsizei *length, GLint *values); +GLAPI void APIENTRY glVDPAUSurfaceAccessNV (GLvdpauSurfaceNV surface, GLenum access); +GLAPI void APIENTRY glVDPAUMapSurfacesNV (GLsizei numSurfaces, const GLvdpauSurfaceNV *surfaces); +GLAPI void APIENTRY glVDPAUUnmapSurfacesNV (GLsizei numSurface, const GLvdpauSurfaceNV *surfaces); +#endif +#endif /* GL_NV_vdpau_interop */ + +#ifndef GL_NV_vertex_array_range +#define GL_NV_vertex_array_range 1 +#define GL_VERTEX_ARRAY_RANGE_NV 0x851D +#define GL_VERTEX_ARRAY_RANGE_LENGTH_NV 0x851E +#define GL_VERTEX_ARRAY_RANGE_VALID_NV 0x851F +#define GL_MAX_VERTEX_ARRAY_RANGE_ELEMENT_NV 0x8520 +#define GL_VERTEX_ARRAY_RANGE_POINTER_NV 0x8521 +typedef void (APIENTRYP PFNGLFLUSHVERTEXARRAYRANGENVPROC) (void); +typedef void (APIENTRYP PFNGLVERTEXARRAYRANGENVPROC) (GLsizei length, const void *pointer); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glFlushVertexArrayRangeNV (void); +GLAPI void APIENTRY glVertexArrayRangeNV (GLsizei length, const void *pointer); +#endif +#endif /* GL_NV_vertex_array_range */ + +#ifndef GL_NV_vertex_array_range2 +#define GL_NV_vertex_array_range2 1 +#define GL_VERTEX_ARRAY_RANGE_WITHOUT_FLUSH_NV 0x8533 +#endif /* GL_NV_vertex_array_range2 */ + +#ifndef GL_NV_vertex_attrib_integer_64bit +#define GL_NV_vertex_attrib_integer_64bit 1 +typedef void (APIENTRYP PFNGLVERTEXATTRIBL1I64NVPROC) (GLuint index, GLint64EXT x); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL2I64NVPROC) (GLuint index, GLint64EXT x, GLint64EXT y); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL3I64NVPROC) (GLuint index, GLint64EXT x, GLint64EXT y, GLint64EXT z); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL4I64NVPROC) (GLuint index, GLint64EXT x, GLint64EXT y, GLint64EXT z, GLint64EXT w); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL1I64VNVPROC) (GLuint index, const GLint64EXT *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL2I64VNVPROC) (GLuint index, const GLint64EXT *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL3I64VNVPROC) (GLuint index, const GLint64EXT *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL4I64VNVPROC) (GLuint index, const GLint64EXT *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL1UI64NVPROC) (GLuint index, GLuint64EXT x); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL2UI64NVPROC) (GLuint index, GLuint64EXT x, GLuint64EXT y); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL3UI64NVPROC) (GLuint index, GLuint64EXT x, GLuint64EXT y, GLuint64EXT z); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL4UI64NVPROC) (GLuint index, GLuint64EXT x, GLuint64EXT y, GLuint64EXT z, GLuint64EXT w); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL1UI64VNVPROC) (GLuint index, const GLuint64EXT *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL2UI64VNVPROC) (GLuint index, const GLuint64EXT *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL3UI64VNVPROC) (GLuint index, const GLuint64EXT *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL4UI64VNVPROC) (GLuint index, const GLuint64EXT *v); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBLI64VNVPROC) (GLuint index, GLenum pname, GLint64EXT *params); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBLUI64VNVPROC) (GLuint index, GLenum pname, GLuint64EXT *params); +typedef void (APIENTRYP PFNGLVERTEXATTRIBLFORMATNVPROC) (GLuint index, GLint size, GLenum type, GLsizei stride); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glVertexAttribL1i64NV (GLuint index, GLint64EXT x); +GLAPI void APIENTRY glVertexAttribL2i64NV (GLuint index, GLint64EXT x, GLint64EXT y); +GLAPI void APIENTRY glVertexAttribL3i64NV (GLuint index, GLint64EXT x, GLint64EXT y, GLint64EXT z); +GLAPI void APIENTRY glVertexAttribL4i64NV (GLuint index, GLint64EXT x, GLint64EXT y, GLint64EXT z, GLint64EXT w); +GLAPI void APIENTRY glVertexAttribL1i64vNV (GLuint index, const GLint64EXT *v); +GLAPI void APIENTRY glVertexAttribL2i64vNV (GLuint index, const GLint64EXT *v); +GLAPI void APIENTRY glVertexAttribL3i64vNV (GLuint index, const GLint64EXT *v); +GLAPI void APIENTRY glVertexAttribL4i64vNV (GLuint index, const GLint64EXT *v); +GLAPI void APIENTRY glVertexAttribL1ui64NV (GLuint index, GLuint64EXT x); +GLAPI void APIENTRY glVertexAttribL2ui64NV (GLuint index, GLuint64EXT x, GLuint64EXT y); +GLAPI void APIENTRY glVertexAttribL3ui64NV (GLuint index, GLuint64EXT x, GLuint64EXT y, GLuint64EXT z); +GLAPI void APIENTRY glVertexAttribL4ui64NV (GLuint index, GLuint64EXT x, GLuint64EXT y, GLuint64EXT z, GLuint64EXT w); +GLAPI void APIENTRY glVertexAttribL1ui64vNV (GLuint index, const GLuint64EXT *v); +GLAPI void APIENTRY glVertexAttribL2ui64vNV (GLuint index, const GLuint64EXT *v); +GLAPI void APIENTRY glVertexAttribL3ui64vNV (GLuint index, const GLuint64EXT *v); +GLAPI void APIENTRY glVertexAttribL4ui64vNV (GLuint index, const GLuint64EXT *v); +GLAPI void APIENTRY glGetVertexAttribLi64vNV (GLuint index, GLenum pname, GLint64EXT *params); +GLAPI void APIENTRY glGetVertexAttribLui64vNV (GLuint index, GLenum pname, GLuint64EXT *params); +GLAPI void APIENTRY glVertexAttribLFormatNV (GLuint index, GLint size, GLenum type, GLsizei stride); +#endif +#endif /* GL_NV_vertex_attrib_integer_64bit */ + +#ifndef GL_NV_vertex_buffer_unified_memory +#define GL_NV_vertex_buffer_unified_memory 1 +#define GL_VERTEX_ATTRIB_ARRAY_UNIFIED_NV 0x8F1E +#define GL_ELEMENT_ARRAY_UNIFIED_NV 0x8F1F +#define GL_VERTEX_ATTRIB_ARRAY_ADDRESS_NV 0x8F20 +#define GL_VERTEX_ARRAY_ADDRESS_NV 0x8F21 +#define GL_NORMAL_ARRAY_ADDRESS_NV 0x8F22 +#define GL_COLOR_ARRAY_ADDRESS_NV 0x8F23 +#define GL_INDEX_ARRAY_ADDRESS_NV 0x8F24 +#define GL_TEXTURE_COORD_ARRAY_ADDRESS_NV 0x8F25 +#define GL_EDGE_FLAG_ARRAY_ADDRESS_NV 0x8F26 +#define GL_SECONDARY_COLOR_ARRAY_ADDRESS_NV 0x8F27 +#define GL_FOG_COORD_ARRAY_ADDRESS_NV 0x8F28 +#define GL_ELEMENT_ARRAY_ADDRESS_NV 0x8F29 +#define GL_VERTEX_ATTRIB_ARRAY_LENGTH_NV 0x8F2A +#define GL_VERTEX_ARRAY_LENGTH_NV 0x8F2B +#define GL_NORMAL_ARRAY_LENGTH_NV 0x8F2C +#define GL_COLOR_ARRAY_LENGTH_NV 0x8F2D +#define GL_INDEX_ARRAY_LENGTH_NV 0x8F2E +#define GL_TEXTURE_COORD_ARRAY_LENGTH_NV 0x8F2F +#define GL_EDGE_FLAG_ARRAY_LENGTH_NV 0x8F30 +#define GL_SECONDARY_COLOR_ARRAY_LENGTH_NV 0x8F31 +#define GL_FOG_COORD_ARRAY_LENGTH_NV 0x8F32 +#define GL_ELEMENT_ARRAY_LENGTH_NV 0x8F33 +#define GL_DRAW_INDIRECT_UNIFIED_NV 0x8F40 +#define GL_DRAW_INDIRECT_ADDRESS_NV 0x8F41 +#define GL_DRAW_INDIRECT_LENGTH_NV 0x8F42 +typedef void (APIENTRYP PFNGLBUFFERADDRESSRANGENVPROC) (GLenum pname, GLuint index, GLuint64EXT address, GLsizeiptr length); +typedef void (APIENTRYP PFNGLVERTEXFORMATNVPROC) (GLint size, GLenum type, GLsizei stride); +typedef void (APIENTRYP PFNGLNORMALFORMATNVPROC) (GLenum type, GLsizei stride); +typedef void (APIENTRYP PFNGLCOLORFORMATNVPROC) (GLint size, GLenum type, GLsizei stride); +typedef void (APIENTRYP PFNGLINDEXFORMATNVPROC) (GLenum type, GLsizei stride); +typedef void (APIENTRYP PFNGLTEXCOORDFORMATNVPROC) (GLint size, GLenum type, GLsizei stride); +typedef void (APIENTRYP PFNGLEDGEFLAGFORMATNVPROC) (GLsizei stride); +typedef void (APIENTRYP PFNGLSECONDARYCOLORFORMATNVPROC) (GLint size, GLenum type, GLsizei stride); +typedef void (APIENTRYP PFNGLFOGCOORDFORMATNVPROC) (GLenum type, GLsizei stride); +typedef void (APIENTRYP PFNGLVERTEXATTRIBFORMATNVPROC) (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride); +typedef void (APIENTRYP PFNGLVERTEXATTRIBIFORMATNVPROC) (GLuint index, GLint size, GLenum type, GLsizei stride); +typedef void (APIENTRYP PFNGLGETINTEGERUI64I_VNVPROC) (GLenum value, GLuint index, GLuint64EXT *result); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBufferAddressRangeNV (GLenum pname, GLuint index, GLuint64EXT address, GLsizeiptr length); +GLAPI void APIENTRY glVertexFormatNV (GLint size, GLenum type, GLsizei stride); +GLAPI void APIENTRY glNormalFormatNV (GLenum type, GLsizei stride); +GLAPI void APIENTRY glColorFormatNV (GLint size, GLenum type, GLsizei stride); +GLAPI void APIENTRY glIndexFormatNV (GLenum type, GLsizei stride); +GLAPI void APIENTRY glTexCoordFormatNV (GLint size, GLenum type, GLsizei stride); +GLAPI void APIENTRY glEdgeFlagFormatNV (GLsizei stride); +GLAPI void APIENTRY glSecondaryColorFormatNV (GLint size, GLenum type, GLsizei stride); +GLAPI void APIENTRY glFogCoordFormatNV (GLenum type, GLsizei stride); +GLAPI void APIENTRY glVertexAttribFormatNV (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride); +GLAPI void APIENTRY glVertexAttribIFormatNV (GLuint index, GLint size, GLenum type, GLsizei stride); +GLAPI void APIENTRY glGetIntegerui64i_vNV (GLenum value, GLuint index, GLuint64EXT *result); +#endif +#endif /* GL_NV_vertex_buffer_unified_memory */ + +#ifndef GL_NV_vertex_program +#define GL_NV_vertex_program 1 +#define GL_VERTEX_PROGRAM_NV 0x8620 +#define GL_VERTEX_STATE_PROGRAM_NV 0x8621 +#define GL_ATTRIB_ARRAY_SIZE_NV 0x8623 +#define GL_ATTRIB_ARRAY_STRIDE_NV 0x8624 +#define GL_ATTRIB_ARRAY_TYPE_NV 0x8625 +#define GL_CURRENT_ATTRIB_NV 0x8626 +#define GL_PROGRAM_LENGTH_NV 0x8627 +#define GL_PROGRAM_STRING_NV 0x8628 +#define GL_MODELVIEW_PROJECTION_NV 0x8629 +#define GL_IDENTITY_NV 0x862A +#define GL_INVERSE_NV 0x862B +#define GL_TRANSPOSE_NV 0x862C +#define GL_INVERSE_TRANSPOSE_NV 0x862D +#define GL_MAX_TRACK_MATRIX_STACK_DEPTH_NV 0x862E +#define GL_MAX_TRACK_MATRICES_NV 0x862F +#define GL_MATRIX0_NV 0x8630 +#define GL_MATRIX1_NV 0x8631 +#define GL_MATRIX2_NV 0x8632 +#define GL_MATRIX3_NV 0x8633 +#define GL_MATRIX4_NV 0x8634 +#define GL_MATRIX5_NV 0x8635 +#define GL_MATRIX6_NV 0x8636 +#define GL_MATRIX7_NV 0x8637 +#define GL_CURRENT_MATRIX_STACK_DEPTH_NV 0x8640 +#define GL_CURRENT_MATRIX_NV 0x8641 +#define GL_VERTEX_PROGRAM_POINT_SIZE_NV 0x8642 +#define GL_VERTEX_PROGRAM_TWO_SIDE_NV 0x8643 +#define GL_PROGRAM_PARAMETER_NV 0x8644 +#define GL_ATTRIB_ARRAY_POINTER_NV 0x8645 +#define GL_PROGRAM_TARGET_NV 0x8646 +#define GL_PROGRAM_RESIDENT_NV 0x8647 +#define GL_TRACK_MATRIX_NV 0x8648 +#define GL_TRACK_MATRIX_TRANSFORM_NV 0x8649 +#define GL_VERTEX_PROGRAM_BINDING_NV 0x864A +#define GL_PROGRAM_ERROR_POSITION_NV 0x864B +#define GL_VERTEX_ATTRIB_ARRAY0_NV 0x8650 +#define GL_VERTEX_ATTRIB_ARRAY1_NV 0x8651 +#define GL_VERTEX_ATTRIB_ARRAY2_NV 0x8652 +#define GL_VERTEX_ATTRIB_ARRAY3_NV 0x8653 +#define GL_VERTEX_ATTRIB_ARRAY4_NV 0x8654 +#define GL_VERTEX_ATTRIB_ARRAY5_NV 0x8655 +#define GL_VERTEX_ATTRIB_ARRAY6_NV 0x8656 +#define GL_VERTEX_ATTRIB_ARRAY7_NV 0x8657 +#define GL_VERTEX_ATTRIB_ARRAY8_NV 0x8658 +#define GL_VERTEX_ATTRIB_ARRAY9_NV 0x8659 +#define GL_VERTEX_ATTRIB_ARRAY10_NV 0x865A +#define GL_VERTEX_ATTRIB_ARRAY11_NV 0x865B +#define GL_VERTEX_ATTRIB_ARRAY12_NV 0x865C +#define GL_VERTEX_ATTRIB_ARRAY13_NV 0x865D +#define GL_VERTEX_ATTRIB_ARRAY14_NV 0x865E +#define GL_VERTEX_ATTRIB_ARRAY15_NV 0x865F +#define GL_MAP1_VERTEX_ATTRIB0_4_NV 0x8660 +#define GL_MAP1_VERTEX_ATTRIB1_4_NV 0x8661 +#define GL_MAP1_VERTEX_ATTRIB2_4_NV 0x8662 +#define GL_MAP1_VERTEX_ATTRIB3_4_NV 0x8663 +#define GL_MAP1_VERTEX_ATTRIB4_4_NV 0x8664 +#define GL_MAP1_VERTEX_ATTRIB5_4_NV 0x8665 +#define GL_MAP1_VERTEX_ATTRIB6_4_NV 0x8666 +#define GL_MAP1_VERTEX_ATTRIB7_4_NV 0x8667 +#define GL_MAP1_VERTEX_ATTRIB8_4_NV 0x8668 +#define GL_MAP1_VERTEX_ATTRIB9_4_NV 0x8669 +#define GL_MAP1_VERTEX_ATTRIB10_4_NV 0x866A +#define GL_MAP1_VERTEX_ATTRIB11_4_NV 0x866B +#define GL_MAP1_VERTEX_ATTRIB12_4_NV 0x866C +#define GL_MAP1_VERTEX_ATTRIB13_4_NV 0x866D +#define GL_MAP1_VERTEX_ATTRIB14_4_NV 0x866E +#define GL_MAP1_VERTEX_ATTRIB15_4_NV 0x866F +#define GL_MAP2_VERTEX_ATTRIB0_4_NV 0x8670 +#define GL_MAP2_VERTEX_ATTRIB1_4_NV 0x8671 +#define GL_MAP2_VERTEX_ATTRIB2_4_NV 0x8672 +#define GL_MAP2_VERTEX_ATTRIB3_4_NV 0x8673 +#define GL_MAP2_VERTEX_ATTRIB4_4_NV 0x8674 +#define GL_MAP2_VERTEX_ATTRIB5_4_NV 0x8675 +#define GL_MAP2_VERTEX_ATTRIB6_4_NV 0x8676 +#define GL_MAP2_VERTEX_ATTRIB7_4_NV 0x8677 +#define GL_MAP2_VERTEX_ATTRIB8_4_NV 0x8678 +#define GL_MAP2_VERTEX_ATTRIB9_4_NV 0x8679 +#define GL_MAP2_VERTEX_ATTRIB10_4_NV 0x867A +#define GL_MAP2_VERTEX_ATTRIB11_4_NV 0x867B +#define GL_MAP2_VERTEX_ATTRIB12_4_NV 0x867C +#define GL_MAP2_VERTEX_ATTRIB13_4_NV 0x867D +#define GL_MAP2_VERTEX_ATTRIB14_4_NV 0x867E +#define GL_MAP2_VERTEX_ATTRIB15_4_NV 0x867F +typedef GLboolean (APIENTRYP PFNGLAREPROGRAMSRESIDENTNVPROC) (GLsizei n, const GLuint *programs, GLboolean *residences); +typedef void (APIENTRYP PFNGLBINDPROGRAMNVPROC) (GLenum target, GLuint id); +typedef void (APIENTRYP PFNGLDELETEPROGRAMSNVPROC) (GLsizei n, const GLuint *programs); +typedef void (APIENTRYP PFNGLEXECUTEPROGRAMNVPROC) (GLenum target, GLuint id, const GLfloat *params); +typedef void (APIENTRYP PFNGLGENPROGRAMSNVPROC) (GLsizei n, GLuint *programs); +typedef void (APIENTRYP PFNGLGETPROGRAMPARAMETERDVNVPROC) (GLenum target, GLuint index, GLenum pname, GLdouble *params); +typedef void (APIENTRYP PFNGLGETPROGRAMPARAMETERFVNVPROC) (GLenum target, GLuint index, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETPROGRAMIVNVPROC) (GLuint id, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETPROGRAMSTRINGNVPROC) (GLuint id, GLenum pname, GLubyte *program); +typedef void (APIENTRYP PFNGLGETTRACKMATRIXIVNVPROC) (GLenum target, GLuint address, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBDVNVPROC) (GLuint index, GLenum pname, GLdouble *params); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBFVNVPROC) (GLuint index, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBIVNVPROC) (GLuint index, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBPOINTERVNVPROC) (GLuint index, GLenum pname, void **pointer); +typedef GLboolean (APIENTRYP PFNGLISPROGRAMNVPROC) (GLuint id); +typedef void (APIENTRYP PFNGLLOADPROGRAMNVPROC) (GLenum target, GLuint id, GLsizei len, const GLubyte *program); +typedef void (APIENTRYP PFNGLPROGRAMPARAMETER4DNVPROC) (GLenum target, GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +typedef void (APIENTRYP PFNGLPROGRAMPARAMETER4DVNVPROC) (GLenum target, GLuint index, const GLdouble *v); +typedef void (APIENTRYP PFNGLPROGRAMPARAMETER4FNVPROC) (GLenum target, GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +typedef void (APIENTRYP PFNGLPROGRAMPARAMETER4FVNVPROC) (GLenum target, GLuint index, const GLfloat *v); +typedef void (APIENTRYP PFNGLPROGRAMPARAMETERS4DVNVPROC) (GLenum target, GLuint index, GLsizei count, const GLdouble *v); +typedef void (APIENTRYP PFNGLPROGRAMPARAMETERS4FVNVPROC) (GLenum target, GLuint index, GLsizei count, const GLfloat *v); +typedef void (APIENTRYP PFNGLREQUESTRESIDENTPROGRAMSNVPROC) (GLsizei n, const GLuint *programs); +typedef void (APIENTRYP PFNGLTRACKMATRIXNVPROC) (GLenum target, GLuint address, GLenum matrix, GLenum transform); +typedef void (APIENTRYP PFNGLVERTEXATTRIBPOINTERNVPROC) (GLuint index, GLint fsize, GLenum type, GLsizei stride, const void *pointer); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1DNVPROC) (GLuint index, GLdouble x); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1DVNVPROC) (GLuint index, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1FNVPROC) (GLuint index, GLfloat x); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1FVNVPROC) (GLuint index, const GLfloat *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1SNVPROC) (GLuint index, GLshort x); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1SVNVPROC) (GLuint index, const GLshort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2DNVPROC) (GLuint index, GLdouble x, GLdouble y); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2DVNVPROC) (GLuint index, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2FNVPROC) (GLuint index, GLfloat x, GLfloat y); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2FVNVPROC) (GLuint index, const GLfloat *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2SNVPROC) (GLuint index, GLshort x, GLshort y); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2SVNVPROC) (GLuint index, const GLshort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3DNVPROC) (GLuint index, GLdouble x, GLdouble y, GLdouble z); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3DVNVPROC) (GLuint index, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3FNVPROC) (GLuint index, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3FVNVPROC) (GLuint index, const GLfloat *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3SNVPROC) (GLuint index, GLshort x, GLshort y, GLshort z); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3SVNVPROC) (GLuint index, const GLshort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4DNVPROC) (GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4DVNVPROC) (GLuint index, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4FNVPROC) (GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4FVNVPROC) (GLuint index, const GLfloat *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4SNVPROC) (GLuint index, GLshort x, GLshort y, GLshort z, GLshort w); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4SVNVPROC) (GLuint index, const GLshort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4UBNVPROC) (GLuint index, GLubyte x, GLubyte y, GLubyte z, GLubyte w); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4UBVNVPROC) (GLuint index, const GLubyte *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBS1DVNVPROC) (GLuint index, GLsizei count, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBS1FVNVPROC) (GLuint index, GLsizei count, const GLfloat *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBS1SVNVPROC) (GLuint index, GLsizei count, const GLshort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBS2DVNVPROC) (GLuint index, GLsizei count, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBS2FVNVPROC) (GLuint index, GLsizei count, const GLfloat *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBS2SVNVPROC) (GLuint index, GLsizei count, const GLshort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBS3DVNVPROC) (GLuint index, GLsizei count, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBS3FVNVPROC) (GLuint index, GLsizei count, const GLfloat *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBS3SVNVPROC) (GLuint index, GLsizei count, const GLshort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBS4DVNVPROC) (GLuint index, GLsizei count, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBS4FVNVPROC) (GLuint index, GLsizei count, const GLfloat *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBS4SVNVPROC) (GLuint index, GLsizei count, const GLshort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBS4UBVNVPROC) (GLuint index, GLsizei count, const GLubyte *v); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI GLboolean APIENTRY glAreProgramsResidentNV (GLsizei n, const GLuint *programs, GLboolean *residences); +GLAPI void APIENTRY glBindProgramNV (GLenum target, GLuint id); +GLAPI void APIENTRY glDeleteProgramsNV (GLsizei n, const GLuint *programs); +GLAPI void APIENTRY glExecuteProgramNV (GLenum target, GLuint id, const GLfloat *params); +GLAPI void APIENTRY glGenProgramsNV (GLsizei n, GLuint *programs); +GLAPI void APIENTRY glGetProgramParameterdvNV (GLenum target, GLuint index, GLenum pname, GLdouble *params); +GLAPI void APIENTRY glGetProgramParameterfvNV (GLenum target, GLuint index, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetProgramivNV (GLuint id, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetProgramStringNV (GLuint id, GLenum pname, GLubyte *program); +GLAPI void APIENTRY glGetTrackMatrixivNV (GLenum target, GLuint address, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetVertexAttribdvNV (GLuint index, GLenum pname, GLdouble *params); +GLAPI void APIENTRY glGetVertexAttribfvNV (GLuint index, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetVertexAttribivNV (GLuint index, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetVertexAttribPointervNV (GLuint index, GLenum pname, void **pointer); +GLAPI GLboolean APIENTRY glIsProgramNV (GLuint id); +GLAPI void APIENTRY glLoadProgramNV (GLenum target, GLuint id, GLsizei len, const GLubyte *program); +GLAPI void APIENTRY glProgramParameter4dNV (GLenum target, GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +GLAPI void APIENTRY glProgramParameter4dvNV (GLenum target, GLuint index, const GLdouble *v); +GLAPI void APIENTRY glProgramParameter4fNV (GLenum target, GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +GLAPI void APIENTRY glProgramParameter4fvNV (GLenum target, GLuint index, const GLfloat *v); +GLAPI void APIENTRY glProgramParameters4dvNV (GLenum target, GLuint index, GLsizei count, const GLdouble *v); +GLAPI void APIENTRY glProgramParameters4fvNV (GLenum target, GLuint index, GLsizei count, const GLfloat *v); +GLAPI void APIENTRY glRequestResidentProgramsNV (GLsizei n, const GLuint *programs); +GLAPI void APIENTRY glTrackMatrixNV (GLenum target, GLuint address, GLenum matrix, GLenum transform); +GLAPI void APIENTRY glVertexAttribPointerNV (GLuint index, GLint fsize, GLenum type, GLsizei stride, const void *pointer); +GLAPI void APIENTRY glVertexAttrib1dNV (GLuint index, GLdouble x); +GLAPI void APIENTRY glVertexAttrib1dvNV (GLuint index, const GLdouble *v); +GLAPI void APIENTRY glVertexAttrib1fNV (GLuint index, GLfloat x); +GLAPI void APIENTRY glVertexAttrib1fvNV (GLuint index, const GLfloat *v); +GLAPI void APIENTRY glVertexAttrib1sNV (GLuint index, GLshort x); +GLAPI void APIENTRY glVertexAttrib1svNV (GLuint index, const GLshort *v); +GLAPI void APIENTRY glVertexAttrib2dNV (GLuint index, GLdouble x, GLdouble y); +GLAPI void APIENTRY glVertexAttrib2dvNV (GLuint index, const GLdouble *v); +GLAPI void APIENTRY glVertexAttrib2fNV (GLuint index, GLfloat x, GLfloat y); +GLAPI void APIENTRY glVertexAttrib2fvNV (GLuint index, const GLfloat *v); +GLAPI void APIENTRY glVertexAttrib2sNV (GLuint index, GLshort x, GLshort y); +GLAPI void APIENTRY glVertexAttrib2svNV (GLuint index, const GLshort *v); +GLAPI void APIENTRY glVertexAttrib3dNV (GLuint index, GLdouble x, GLdouble y, GLdouble z); +GLAPI void APIENTRY glVertexAttrib3dvNV (GLuint index, const GLdouble *v); +GLAPI void APIENTRY glVertexAttrib3fNV (GLuint index, GLfloat x, GLfloat y, GLfloat z); +GLAPI void APIENTRY glVertexAttrib3fvNV (GLuint index, const GLfloat *v); +GLAPI void APIENTRY glVertexAttrib3sNV (GLuint index, GLshort x, GLshort y, GLshort z); +GLAPI void APIENTRY glVertexAttrib3svNV (GLuint index, const GLshort *v); +GLAPI void APIENTRY glVertexAttrib4dNV (GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +GLAPI void APIENTRY glVertexAttrib4dvNV (GLuint index, const GLdouble *v); +GLAPI void APIENTRY glVertexAttrib4fNV (GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +GLAPI void APIENTRY glVertexAttrib4fvNV (GLuint index, const GLfloat *v); +GLAPI void APIENTRY glVertexAttrib4sNV (GLuint index, GLshort x, GLshort y, GLshort z, GLshort w); +GLAPI void APIENTRY glVertexAttrib4svNV (GLuint index, const GLshort *v); +GLAPI void APIENTRY glVertexAttrib4ubNV (GLuint index, GLubyte x, GLubyte y, GLubyte z, GLubyte w); +GLAPI void APIENTRY glVertexAttrib4ubvNV (GLuint index, const GLubyte *v); +GLAPI void APIENTRY glVertexAttribs1dvNV (GLuint index, GLsizei count, const GLdouble *v); +GLAPI void APIENTRY glVertexAttribs1fvNV (GLuint index, GLsizei count, const GLfloat *v); +GLAPI void APIENTRY glVertexAttribs1svNV (GLuint index, GLsizei count, const GLshort *v); +GLAPI void APIENTRY glVertexAttribs2dvNV (GLuint index, GLsizei count, const GLdouble *v); +GLAPI void APIENTRY glVertexAttribs2fvNV (GLuint index, GLsizei count, const GLfloat *v); +GLAPI void APIENTRY glVertexAttribs2svNV (GLuint index, GLsizei count, const GLshort *v); +GLAPI void APIENTRY glVertexAttribs3dvNV (GLuint index, GLsizei count, const GLdouble *v); +GLAPI void APIENTRY glVertexAttribs3fvNV (GLuint index, GLsizei count, const GLfloat *v); +GLAPI void APIENTRY glVertexAttribs3svNV (GLuint index, GLsizei count, const GLshort *v); +GLAPI void APIENTRY glVertexAttribs4dvNV (GLuint index, GLsizei count, const GLdouble *v); +GLAPI void APIENTRY glVertexAttribs4fvNV (GLuint index, GLsizei count, const GLfloat *v); +GLAPI void APIENTRY glVertexAttribs4svNV (GLuint index, GLsizei count, const GLshort *v); +GLAPI void APIENTRY glVertexAttribs4ubvNV (GLuint index, GLsizei count, const GLubyte *v); +#endif +#endif /* GL_NV_vertex_program */ + +#ifndef GL_NV_vertex_program1_1 +#define GL_NV_vertex_program1_1 1 +#endif /* GL_NV_vertex_program1_1 */ + +#ifndef GL_NV_vertex_program2 +#define GL_NV_vertex_program2 1 +#endif /* GL_NV_vertex_program2 */ + +#ifndef GL_NV_vertex_program2_option +#define GL_NV_vertex_program2_option 1 +#endif /* GL_NV_vertex_program2_option */ + +#ifndef GL_NV_vertex_program3 +#define GL_NV_vertex_program3 1 +#endif /* GL_NV_vertex_program3 */ + +#ifndef GL_NV_vertex_program4 +#define GL_NV_vertex_program4 1 +#define GL_VERTEX_ATTRIB_ARRAY_INTEGER_NV 0x88FD +typedef void (APIENTRYP PFNGLVERTEXATTRIBI1IEXTPROC) (GLuint index, GLint x); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI2IEXTPROC) (GLuint index, GLint x, GLint y); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI3IEXTPROC) (GLuint index, GLint x, GLint y, GLint z); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI4IEXTPROC) (GLuint index, GLint x, GLint y, GLint z, GLint w); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI1UIEXTPROC) (GLuint index, GLuint x); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI2UIEXTPROC) (GLuint index, GLuint x, GLuint y); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI3UIEXTPROC) (GLuint index, GLuint x, GLuint y, GLuint z); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI4UIEXTPROC) (GLuint index, GLuint x, GLuint y, GLuint z, GLuint w); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI1IVEXTPROC) (GLuint index, const GLint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI2IVEXTPROC) (GLuint index, const GLint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI3IVEXTPROC) (GLuint index, const GLint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI4IVEXTPROC) (GLuint index, const GLint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI1UIVEXTPROC) (GLuint index, const GLuint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI2UIVEXTPROC) (GLuint index, const GLuint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI3UIVEXTPROC) (GLuint index, const GLuint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI4UIVEXTPROC) (GLuint index, const GLuint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI4BVEXTPROC) (GLuint index, const GLbyte *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI4SVEXTPROC) (GLuint index, const GLshort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI4UBVEXTPROC) (GLuint index, const GLubyte *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI4USVEXTPROC) (GLuint index, const GLushort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBIPOINTEREXTPROC) (GLuint index, GLint size, GLenum type, GLsizei stride, const void *pointer); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBIIVEXTPROC) (GLuint index, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBIUIVEXTPROC) (GLuint index, GLenum pname, GLuint *params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glVertexAttribI1iEXT (GLuint index, GLint x); +GLAPI void APIENTRY glVertexAttribI2iEXT (GLuint index, GLint x, GLint y); +GLAPI void APIENTRY glVertexAttribI3iEXT (GLuint index, GLint x, GLint y, GLint z); +GLAPI void APIENTRY glVertexAttribI4iEXT (GLuint index, GLint x, GLint y, GLint z, GLint w); +GLAPI void APIENTRY glVertexAttribI1uiEXT (GLuint index, GLuint x); +GLAPI void APIENTRY glVertexAttribI2uiEXT (GLuint index, GLuint x, GLuint y); +GLAPI void APIENTRY glVertexAttribI3uiEXT (GLuint index, GLuint x, GLuint y, GLuint z); +GLAPI void APIENTRY glVertexAttribI4uiEXT (GLuint index, GLuint x, GLuint y, GLuint z, GLuint w); +GLAPI void APIENTRY glVertexAttribI1ivEXT (GLuint index, const GLint *v); +GLAPI void APIENTRY glVertexAttribI2ivEXT (GLuint index, const GLint *v); +GLAPI void APIENTRY glVertexAttribI3ivEXT (GLuint index, const GLint *v); +GLAPI void APIENTRY glVertexAttribI4ivEXT (GLuint index, const GLint *v); +GLAPI void APIENTRY glVertexAttribI1uivEXT (GLuint index, const GLuint *v); +GLAPI void APIENTRY glVertexAttribI2uivEXT (GLuint index, const GLuint *v); +GLAPI void APIENTRY glVertexAttribI3uivEXT (GLuint index, const GLuint *v); +GLAPI void APIENTRY glVertexAttribI4uivEXT (GLuint index, const GLuint *v); +GLAPI void APIENTRY glVertexAttribI4bvEXT (GLuint index, const GLbyte *v); +GLAPI void APIENTRY glVertexAttribI4svEXT (GLuint index, const GLshort *v); +GLAPI void APIENTRY glVertexAttribI4ubvEXT (GLuint index, const GLubyte *v); +GLAPI void APIENTRY glVertexAttribI4usvEXT (GLuint index, const GLushort *v); +GLAPI void APIENTRY glVertexAttribIPointerEXT (GLuint index, GLint size, GLenum type, GLsizei stride, const void *pointer); +GLAPI void APIENTRY glGetVertexAttribIivEXT (GLuint index, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetVertexAttribIuivEXT (GLuint index, GLenum pname, GLuint *params); +#endif +#endif /* GL_NV_vertex_program4 */ + +#ifndef GL_NV_video_capture +#define GL_NV_video_capture 1 +#define GL_VIDEO_BUFFER_NV 0x9020 +#define GL_VIDEO_BUFFER_BINDING_NV 0x9021 +#define GL_FIELD_UPPER_NV 0x9022 +#define GL_FIELD_LOWER_NV 0x9023 +#define GL_NUM_VIDEO_CAPTURE_STREAMS_NV 0x9024 +#define GL_NEXT_VIDEO_CAPTURE_BUFFER_STATUS_NV 0x9025 +#define GL_VIDEO_CAPTURE_TO_422_SUPPORTED_NV 0x9026 +#define GL_LAST_VIDEO_CAPTURE_STATUS_NV 0x9027 +#define GL_VIDEO_BUFFER_PITCH_NV 0x9028 +#define GL_VIDEO_COLOR_CONVERSION_MATRIX_NV 0x9029 +#define GL_VIDEO_COLOR_CONVERSION_MAX_NV 0x902A +#define GL_VIDEO_COLOR_CONVERSION_MIN_NV 0x902B +#define GL_VIDEO_COLOR_CONVERSION_OFFSET_NV 0x902C +#define GL_VIDEO_BUFFER_INTERNAL_FORMAT_NV 0x902D +#define GL_PARTIAL_SUCCESS_NV 0x902E +#define GL_SUCCESS_NV 0x902F +#define GL_FAILURE_NV 0x9030 +#define GL_YCBYCR8_422_NV 0x9031 +#define GL_YCBAYCR8A_4224_NV 0x9032 +#define GL_Z6Y10Z6CB10Z6Y10Z6CR10_422_NV 0x9033 +#define GL_Z6Y10Z6CB10Z6A10Z6Y10Z6CR10Z6A10_4224_NV 0x9034 +#define GL_Z4Y12Z4CB12Z4Y12Z4CR12_422_NV 0x9035 +#define GL_Z4Y12Z4CB12Z4A12Z4Y12Z4CR12Z4A12_4224_NV 0x9036 +#define GL_Z4Y12Z4CB12Z4CR12_444_NV 0x9037 +#define GL_VIDEO_CAPTURE_FRAME_WIDTH_NV 0x9038 +#define GL_VIDEO_CAPTURE_FRAME_HEIGHT_NV 0x9039 +#define GL_VIDEO_CAPTURE_FIELD_UPPER_HEIGHT_NV 0x903A +#define GL_VIDEO_CAPTURE_FIELD_LOWER_HEIGHT_NV 0x903B +#define GL_VIDEO_CAPTURE_SURFACE_ORIGIN_NV 0x903C +typedef void (APIENTRYP PFNGLBEGINVIDEOCAPTURENVPROC) (GLuint video_capture_slot); +typedef void (APIENTRYP PFNGLBINDVIDEOCAPTURESTREAMBUFFERNVPROC) (GLuint video_capture_slot, GLuint stream, GLenum frame_region, GLintptrARB offset); +typedef void (APIENTRYP PFNGLBINDVIDEOCAPTURESTREAMTEXTURENVPROC) (GLuint video_capture_slot, GLuint stream, GLenum frame_region, GLenum target, GLuint texture); +typedef void (APIENTRYP PFNGLENDVIDEOCAPTURENVPROC) (GLuint video_capture_slot); +typedef void (APIENTRYP PFNGLGETVIDEOCAPTUREIVNVPROC) (GLuint video_capture_slot, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETVIDEOCAPTURESTREAMIVNVPROC) (GLuint video_capture_slot, GLuint stream, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETVIDEOCAPTURESTREAMFVNVPROC) (GLuint video_capture_slot, GLuint stream, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETVIDEOCAPTURESTREAMDVNVPROC) (GLuint video_capture_slot, GLuint stream, GLenum pname, GLdouble *params); +typedef GLenum (APIENTRYP PFNGLVIDEOCAPTURENVPROC) (GLuint video_capture_slot, GLuint *sequence_num, GLuint64EXT *capture_time); +typedef void (APIENTRYP PFNGLVIDEOCAPTURESTREAMPARAMETERIVNVPROC) (GLuint video_capture_slot, GLuint stream, GLenum pname, const GLint *params); +typedef void (APIENTRYP PFNGLVIDEOCAPTURESTREAMPARAMETERFVNVPROC) (GLuint video_capture_slot, GLuint stream, GLenum pname, const GLfloat *params); +typedef void (APIENTRYP PFNGLVIDEOCAPTURESTREAMPARAMETERDVNVPROC) (GLuint video_capture_slot, GLuint stream, GLenum pname, const GLdouble *params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glBeginVideoCaptureNV (GLuint video_capture_slot); +GLAPI void APIENTRY glBindVideoCaptureStreamBufferNV (GLuint video_capture_slot, GLuint stream, GLenum frame_region, GLintptrARB offset); +GLAPI void APIENTRY glBindVideoCaptureStreamTextureNV (GLuint video_capture_slot, GLuint stream, GLenum frame_region, GLenum target, GLuint texture); +GLAPI void APIENTRY glEndVideoCaptureNV (GLuint video_capture_slot); +GLAPI void APIENTRY glGetVideoCaptureivNV (GLuint video_capture_slot, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetVideoCaptureStreamivNV (GLuint video_capture_slot, GLuint stream, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetVideoCaptureStreamfvNV (GLuint video_capture_slot, GLuint stream, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetVideoCaptureStreamdvNV (GLuint video_capture_slot, GLuint stream, GLenum pname, GLdouble *params); +GLAPI GLenum APIENTRY glVideoCaptureNV (GLuint video_capture_slot, GLuint *sequence_num, GLuint64EXT *capture_time); +GLAPI void APIENTRY glVideoCaptureStreamParameterivNV (GLuint video_capture_slot, GLuint stream, GLenum pname, const GLint *params); +GLAPI void APIENTRY glVideoCaptureStreamParameterfvNV (GLuint video_capture_slot, GLuint stream, GLenum pname, const GLfloat *params); +GLAPI void APIENTRY glVideoCaptureStreamParameterdvNV (GLuint video_capture_slot, GLuint stream, GLenum pname, const GLdouble *params); +#endif +#endif /* GL_NV_video_capture */ + +#ifndef GL_NV_viewport_array2 +#define GL_NV_viewport_array2 1 +#endif /* GL_NV_viewport_array2 */ + +#ifndef GL_OML_interlace +#define GL_OML_interlace 1 +#define GL_INTERLACE_OML 0x8980 +#define GL_INTERLACE_READ_OML 0x8981 +#endif /* GL_OML_interlace */ + +#ifndef GL_OML_resample +#define GL_OML_resample 1 +#define GL_PACK_RESAMPLE_OML 0x8984 +#define GL_UNPACK_RESAMPLE_OML 0x8985 +#define GL_RESAMPLE_REPLICATE_OML 0x8986 +#define GL_RESAMPLE_ZERO_FILL_OML 0x8987 +#define GL_RESAMPLE_AVERAGE_OML 0x8988 +#define GL_RESAMPLE_DECIMATE_OML 0x8989 +#endif /* GL_OML_resample */ + +#ifndef GL_OML_subsample +#define GL_OML_subsample 1 +#define GL_FORMAT_SUBSAMPLE_24_24_OML 0x8982 +#define GL_FORMAT_SUBSAMPLE_244_244_OML 0x8983 +#endif /* GL_OML_subsample */ + +#ifndef GL_OVR_multiview +#define GL_OVR_multiview 1 +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_NUM_VIEWS_OVR 0x9630 +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_BASE_VIEW_INDEX_OVR 0x9632 +#define GL_MAX_VIEWS_OVR 0x9631 +typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTUREMULTIVIEWOVRPROC) (GLenum target, GLenum attachment, GLuint texture, GLint level, GLint baseViewIndex, GLsizei numViews); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glFramebufferTextureMultiviewOVR (GLenum target, GLenum attachment, GLuint texture, GLint level, GLint baseViewIndex, GLsizei numViews); +#endif +#endif /* GL_OVR_multiview */ + +#ifndef GL_OVR_multiview2 +#define GL_OVR_multiview2 1 +#endif /* GL_OVR_multiview2 */ + +#ifndef GL_PGI_misc_hints +#define GL_PGI_misc_hints 1 +#define GL_PREFER_DOUBLEBUFFER_HINT_PGI 0x1A1F8 +#define GL_CONSERVE_MEMORY_HINT_PGI 0x1A1FD +#define GL_RECLAIM_MEMORY_HINT_PGI 0x1A1FE +#define GL_NATIVE_GRAPHICS_HANDLE_PGI 0x1A202 +#define GL_NATIVE_GRAPHICS_BEGIN_HINT_PGI 0x1A203 +#define GL_NATIVE_GRAPHICS_END_HINT_PGI 0x1A204 +#define GL_ALWAYS_FAST_HINT_PGI 0x1A20C +#define GL_ALWAYS_SOFT_HINT_PGI 0x1A20D +#define GL_ALLOW_DRAW_OBJ_HINT_PGI 0x1A20E +#define GL_ALLOW_DRAW_WIN_HINT_PGI 0x1A20F +#define GL_ALLOW_DRAW_FRG_HINT_PGI 0x1A210 +#define GL_ALLOW_DRAW_MEM_HINT_PGI 0x1A211 +#define GL_STRICT_DEPTHFUNC_HINT_PGI 0x1A216 +#define GL_STRICT_LIGHTING_HINT_PGI 0x1A217 +#define GL_STRICT_SCISSOR_HINT_PGI 0x1A218 +#define GL_FULL_STIPPLE_HINT_PGI 0x1A219 +#define GL_CLIP_NEAR_HINT_PGI 0x1A220 +#define GL_CLIP_FAR_HINT_PGI 0x1A221 +#define GL_WIDE_LINE_HINT_PGI 0x1A222 +#define GL_BACK_NORMALS_HINT_PGI 0x1A223 +typedef void (APIENTRYP PFNGLHINTPGIPROC) (GLenum target, GLint mode); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glHintPGI (GLenum target, GLint mode); +#endif +#endif /* GL_PGI_misc_hints */ + +#ifndef GL_PGI_vertex_hints +#define GL_PGI_vertex_hints 1 +#define GL_VERTEX_DATA_HINT_PGI 0x1A22A +#define GL_VERTEX_CONSISTENT_HINT_PGI 0x1A22B +#define GL_MATERIAL_SIDE_HINT_PGI 0x1A22C +#define GL_MAX_VERTEX_HINT_PGI 0x1A22D +#define GL_COLOR3_BIT_PGI 0x00010000 +#define GL_COLOR4_BIT_PGI 0x00020000 +#define GL_EDGEFLAG_BIT_PGI 0x00040000 +#define GL_INDEX_BIT_PGI 0x00080000 +#define GL_MAT_AMBIENT_BIT_PGI 0x00100000 +#define GL_MAT_AMBIENT_AND_DIFFUSE_BIT_PGI 0x00200000 +#define GL_MAT_DIFFUSE_BIT_PGI 0x00400000 +#define GL_MAT_EMISSION_BIT_PGI 0x00800000 +#define GL_MAT_COLOR_INDEXES_BIT_PGI 0x01000000 +#define GL_MAT_SHININESS_BIT_PGI 0x02000000 +#define GL_MAT_SPECULAR_BIT_PGI 0x04000000 +#define GL_NORMAL_BIT_PGI 0x08000000 +#define GL_TEXCOORD1_BIT_PGI 0x10000000 +#define GL_TEXCOORD2_BIT_PGI 0x20000000 +#define GL_TEXCOORD3_BIT_PGI 0x40000000 +#define GL_TEXCOORD4_BIT_PGI 0x80000000 +#define GL_VERTEX23_BIT_PGI 0x00000004 +#define GL_VERTEX4_BIT_PGI 0x00000008 +#endif /* GL_PGI_vertex_hints */ + +#ifndef GL_REND_screen_coordinates +#define GL_REND_screen_coordinates 1 +#define GL_SCREEN_COORDINATES_REND 0x8490 +#define GL_INVERTED_SCREEN_W_REND 0x8491 +#endif /* GL_REND_screen_coordinates */ + +#ifndef GL_S3_s3tc +#define GL_S3_s3tc 1 +#define GL_RGB_S3TC 0x83A0 +#define GL_RGB4_S3TC 0x83A1 +#define GL_RGBA_S3TC 0x83A2 +#define GL_RGBA4_S3TC 0x83A3 +#define GL_RGBA_DXT5_S3TC 0x83A4 +#define GL_RGBA4_DXT5_S3TC 0x83A5 +#endif /* GL_S3_s3tc */ + +#ifndef GL_SGIS_detail_texture +#define GL_SGIS_detail_texture 1 +#define GL_DETAIL_TEXTURE_2D_SGIS 0x8095 +#define GL_DETAIL_TEXTURE_2D_BINDING_SGIS 0x8096 +#define GL_LINEAR_DETAIL_SGIS 0x8097 +#define GL_LINEAR_DETAIL_ALPHA_SGIS 0x8098 +#define GL_LINEAR_DETAIL_COLOR_SGIS 0x8099 +#define GL_DETAIL_TEXTURE_LEVEL_SGIS 0x809A +#define GL_DETAIL_TEXTURE_MODE_SGIS 0x809B +#define GL_DETAIL_TEXTURE_FUNC_POINTS_SGIS 0x809C +typedef void (APIENTRYP PFNGLDETAILTEXFUNCSGISPROC) (GLenum target, GLsizei n, const GLfloat *points); +typedef void (APIENTRYP PFNGLGETDETAILTEXFUNCSGISPROC) (GLenum target, GLfloat *points); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glDetailTexFuncSGIS (GLenum target, GLsizei n, const GLfloat *points); +GLAPI void APIENTRY glGetDetailTexFuncSGIS (GLenum target, GLfloat *points); +#endif +#endif /* GL_SGIS_detail_texture */ + +#ifndef GL_SGIS_fog_function +#define GL_SGIS_fog_function 1 +#define GL_FOG_FUNC_SGIS 0x812A +#define GL_FOG_FUNC_POINTS_SGIS 0x812B +#define GL_MAX_FOG_FUNC_POINTS_SGIS 0x812C +typedef void (APIENTRYP PFNGLFOGFUNCSGISPROC) (GLsizei n, const GLfloat *points); +typedef void (APIENTRYP PFNGLGETFOGFUNCSGISPROC) (GLfloat *points); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glFogFuncSGIS (GLsizei n, const GLfloat *points); +GLAPI void APIENTRY glGetFogFuncSGIS (GLfloat *points); +#endif +#endif /* GL_SGIS_fog_function */ + +#ifndef GL_SGIS_generate_mipmap +#define GL_SGIS_generate_mipmap 1 +#define GL_GENERATE_MIPMAP_SGIS 0x8191 +#define GL_GENERATE_MIPMAP_HINT_SGIS 0x8192 +#endif /* GL_SGIS_generate_mipmap */ + +#ifndef GL_SGIS_multisample +#define GL_SGIS_multisample 1 +#define GL_MULTISAMPLE_SGIS 0x809D +#define GL_SAMPLE_ALPHA_TO_MASK_SGIS 0x809E +#define GL_SAMPLE_ALPHA_TO_ONE_SGIS 0x809F +#define GL_SAMPLE_MASK_SGIS 0x80A0 +#define GL_1PASS_SGIS 0x80A1 +#define GL_2PASS_0_SGIS 0x80A2 +#define GL_2PASS_1_SGIS 0x80A3 +#define GL_4PASS_0_SGIS 0x80A4 +#define GL_4PASS_1_SGIS 0x80A5 +#define GL_4PASS_2_SGIS 0x80A6 +#define GL_4PASS_3_SGIS 0x80A7 +#define GL_SAMPLE_BUFFERS_SGIS 0x80A8 +#define GL_SAMPLES_SGIS 0x80A9 +#define GL_SAMPLE_MASK_VALUE_SGIS 0x80AA +#define GL_SAMPLE_MASK_INVERT_SGIS 0x80AB +#define GL_SAMPLE_PATTERN_SGIS 0x80AC +typedef void (APIENTRYP PFNGLSAMPLEMASKSGISPROC) (GLclampf value, GLboolean invert); +typedef void (APIENTRYP PFNGLSAMPLEPATTERNSGISPROC) (GLenum pattern); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glSampleMaskSGIS (GLclampf value, GLboolean invert); +GLAPI void APIENTRY glSamplePatternSGIS (GLenum pattern); +#endif +#endif /* GL_SGIS_multisample */ + +#ifndef GL_SGIS_pixel_texture +#define GL_SGIS_pixel_texture 1 +#define GL_PIXEL_TEXTURE_SGIS 0x8353 +#define GL_PIXEL_FRAGMENT_RGB_SOURCE_SGIS 0x8354 +#define GL_PIXEL_FRAGMENT_ALPHA_SOURCE_SGIS 0x8355 +#define GL_PIXEL_GROUP_COLOR_SGIS 0x8356 +typedef void (APIENTRYP PFNGLPIXELTEXGENPARAMETERISGISPROC) (GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLPIXELTEXGENPARAMETERIVSGISPROC) (GLenum pname, const GLint *params); +typedef void (APIENTRYP PFNGLPIXELTEXGENPARAMETERFSGISPROC) (GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLPIXELTEXGENPARAMETERFVSGISPROC) (GLenum pname, const GLfloat *params); +typedef void (APIENTRYP PFNGLGETPIXELTEXGENPARAMETERIVSGISPROC) (GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETPIXELTEXGENPARAMETERFVSGISPROC) (GLenum pname, GLfloat *params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glPixelTexGenParameteriSGIS (GLenum pname, GLint param); +GLAPI void APIENTRY glPixelTexGenParameterivSGIS (GLenum pname, const GLint *params); +GLAPI void APIENTRY glPixelTexGenParameterfSGIS (GLenum pname, GLfloat param); +GLAPI void APIENTRY glPixelTexGenParameterfvSGIS (GLenum pname, const GLfloat *params); +GLAPI void APIENTRY glGetPixelTexGenParameterivSGIS (GLenum pname, GLint *params); +GLAPI void APIENTRY glGetPixelTexGenParameterfvSGIS (GLenum pname, GLfloat *params); +#endif +#endif /* GL_SGIS_pixel_texture */ + +#ifndef GL_SGIS_point_line_texgen +#define GL_SGIS_point_line_texgen 1 +#define GL_EYE_DISTANCE_TO_POINT_SGIS 0x81F0 +#define GL_OBJECT_DISTANCE_TO_POINT_SGIS 0x81F1 +#define GL_EYE_DISTANCE_TO_LINE_SGIS 0x81F2 +#define GL_OBJECT_DISTANCE_TO_LINE_SGIS 0x81F3 +#define GL_EYE_POINT_SGIS 0x81F4 +#define GL_OBJECT_POINT_SGIS 0x81F5 +#define GL_EYE_LINE_SGIS 0x81F6 +#define GL_OBJECT_LINE_SGIS 0x81F7 +#endif /* GL_SGIS_point_line_texgen */ + +#ifndef GL_SGIS_point_parameters +#define GL_SGIS_point_parameters 1 +#define GL_POINT_SIZE_MIN_SGIS 0x8126 +#define GL_POINT_SIZE_MAX_SGIS 0x8127 +#define GL_POINT_FADE_THRESHOLD_SIZE_SGIS 0x8128 +#define GL_DISTANCE_ATTENUATION_SGIS 0x8129 +typedef void (APIENTRYP PFNGLPOINTPARAMETERFSGISPROC) (GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLPOINTPARAMETERFVSGISPROC) (GLenum pname, const GLfloat *params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glPointParameterfSGIS (GLenum pname, GLfloat param); +GLAPI void APIENTRY glPointParameterfvSGIS (GLenum pname, const GLfloat *params); +#endif +#endif /* GL_SGIS_point_parameters */ + +#ifndef GL_SGIS_sharpen_texture +#define GL_SGIS_sharpen_texture 1 +#define GL_LINEAR_SHARPEN_SGIS 0x80AD +#define GL_LINEAR_SHARPEN_ALPHA_SGIS 0x80AE +#define GL_LINEAR_SHARPEN_COLOR_SGIS 0x80AF +#define GL_SHARPEN_TEXTURE_FUNC_POINTS_SGIS 0x80B0 +typedef void (APIENTRYP PFNGLSHARPENTEXFUNCSGISPROC) (GLenum target, GLsizei n, const GLfloat *points); +typedef void (APIENTRYP PFNGLGETSHARPENTEXFUNCSGISPROC) (GLenum target, GLfloat *points); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glSharpenTexFuncSGIS (GLenum target, GLsizei n, const GLfloat *points); +GLAPI void APIENTRY glGetSharpenTexFuncSGIS (GLenum target, GLfloat *points); +#endif +#endif /* GL_SGIS_sharpen_texture */ + +#ifndef GL_SGIS_texture4D +#define GL_SGIS_texture4D 1 +#define GL_PACK_SKIP_VOLUMES_SGIS 0x8130 +#define GL_PACK_IMAGE_DEPTH_SGIS 0x8131 +#define GL_UNPACK_SKIP_VOLUMES_SGIS 0x8132 +#define GL_UNPACK_IMAGE_DEPTH_SGIS 0x8133 +#define GL_TEXTURE_4D_SGIS 0x8134 +#define GL_PROXY_TEXTURE_4D_SGIS 0x8135 +#define GL_TEXTURE_4DSIZE_SGIS 0x8136 +#define GL_TEXTURE_WRAP_Q_SGIS 0x8137 +#define GL_MAX_4D_TEXTURE_SIZE_SGIS 0x8138 +#define GL_TEXTURE_4D_BINDING_SGIS 0x814F +typedef void (APIENTRYP PFNGLTEXIMAGE4DSGISPROC) (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLsizei size4d, GLint border, GLenum format, GLenum type, const void *pixels); +typedef void (APIENTRYP PFNGLTEXSUBIMAGE4DSGISPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint woffset, GLsizei width, GLsizei height, GLsizei depth, GLsizei size4d, GLenum format, GLenum type, const void *pixels); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glTexImage4DSGIS (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLsizei size4d, GLint border, GLenum format, GLenum type, const void *pixels); +GLAPI void APIENTRY glTexSubImage4DSGIS (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint woffset, GLsizei width, GLsizei height, GLsizei depth, GLsizei size4d, GLenum format, GLenum type, const void *pixels); +#endif +#endif /* GL_SGIS_texture4D */ + +#ifndef GL_SGIS_texture_border_clamp +#define GL_SGIS_texture_border_clamp 1 +#define GL_CLAMP_TO_BORDER_SGIS 0x812D +#endif /* GL_SGIS_texture_border_clamp */ + +#ifndef GL_SGIS_texture_color_mask +#define GL_SGIS_texture_color_mask 1 +#define GL_TEXTURE_COLOR_WRITEMASK_SGIS 0x81EF +typedef void (APIENTRYP PFNGLTEXTURECOLORMASKSGISPROC) (GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glTextureColorMaskSGIS (GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha); +#endif +#endif /* GL_SGIS_texture_color_mask */ + +#ifndef GL_SGIS_texture_edge_clamp +#define GL_SGIS_texture_edge_clamp 1 +#define GL_CLAMP_TO_EDGE_SGIS 0x812F +#endif /* GL_SGIS_texture_edge_clamp */ + +#ifndef GL_SGIS_texture_filter4 +#define GL_SGIS_texture_filter4 1 +#define GL_FILTER4_SGIS 0x8146 +#define GL_TEXTURE_FILTER4_SIZE_SGIS 0x8147 +typedef void (APIENTRYP PFNGLGETTEXFILTERFUNCSGISPROC) (GLenum target, GLenum filter, GLfloat *weights); +typedef void (APIENTRYP PFNGLTEXFILTERFUNCSGISPROC) (GLenum target, GLenum filter, GLsizei n, const GLfloat *weights); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glGetTexFilterFuncSGIS (GLenum target, GLenum filter, GLfloat *weights); +GLAPI void APIENTRY glTexFilterFuncSGIS (GLenum target, GLenum filter, GLsizei n, const GLfloat *weights); +#endif +#endif /* GL_SGIS_texture_filter4 */ + +#ifndef GL_SGIS_texture_lod +#define GL_SGIS_texture_lod 1 +#define GL_TEXTURE_MIN_LOD_SGIS 0x813A +#define GL_TEXTURE_MAX_LOD_SGIS 0x813B +#define GL_TEXTURE_BASE_LEVEL_SGIS 0x813C +#define GL_TEXTURE_MAX_LEVEL_SGIS 0x813D +#endif /* GL_SGIS_texture_lod */ + +#ifndef GL_SGIS_texture_select +#define GL_SGIS_texture_select 1 +#define GL_DUAL_ALPHA4_SGIS 0x8110 +#define GL_DUAL_ALPHA8_SGIS 0x8111 +#define GL_DUAL_ALPHA12_SGIS 0x8112 +#define GL_DUAL_ALPHA16_SGIS 0x8113 +#define GL_DUAL_LUMINANCE4_SGIS 0x8114 +#define GL_DUAL_LUMINANCE8_SGIS 0x8115 +#define GL_DUAL_LUMINANCE12_SGIS 0x8116 +#define GL_DUAL_LUMINANCE16_SGIS 0x8117 +#define GL_DUAL_INTENSITY4_SGIS 0x8118 +#define GL_DUAL_INTENSITY8_SGIS 0x8119 +#define GL_DUAL_INTENSITY12_SGIS 0x811A +#define GL_DUAL_INTENSITY16_SGIS 0x811B +#define GL_DUAL_LUMINANCE_ALPHA4_SGIS 0x811C +#define GL_DUAL_LUMINANCE_ALPHA8_SGIS 0x811D +#define GL_QUAD_ALPHA4_SGIS 0x811E +#define GL_QUAD_ALPHA8_SGIS 0x811F +#define GL_QUAD_LUMINANCE4_SGIS 0x8120 +#define GL_QUAD_LUMINANCE8_SGIS 0x8121 +#define GL_QUAD_INTENSITY4_SGIS 0x8122 +#define GL_QUAD_INTENSITY8_SGIS 0x8123 +#define GL_DUAL_TEXTURE_SELECT_SGIS 0x8124 +#define GL_QUAD_TEXTURE_SELECT_SGIS 0x8125 +#endif /* GL_SGIS_texture_select */ + +#ifndef GL_SGIX_async +#define GL_SGIX_async 1 +#define GL_ASYNC_MARKER_SGIX 0x8329 +typedef void (APIENTRYP PFNGLASYNCMARKERSGIXPROC) (GLuint marker); +typedef GLint (APIENTRYP PFNGLFINISHASYNCSGIXPROC) (GLuint *markerp); +typedef GLint (APIENTRYP PFNGLPOLLASYNCSGIXPROC) (GLuint *markerp); +typedef GLuint (APIENTRYP PFNGLGENASYNCMARKERSSGIXPROC) (GLsizei range); +typedef void (APIENTRYP PFNGLDELETEASYNCMARKERSSGIXPROC) (GLuint marker, GLsizei range); +typedef GLboolean (APIENTRYP PFNGLISASYNCMARKERSGIXPROC) (GLuint marker); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glAsyncMarkerSGIX (GLuint marker); +GLAPI GLint APIENTRY glFinishAsyncSGIX (GLuint *markerp); +GLAPI GLint APIENTRY glPollAsyncSGIX (GLuint *markerp); +GLAPI GLuint APIENTRY glGenAsyncMarkersSGIX (GLsizei range); +GLAPI void APIENTRY glDeleteAsyncMarkersSGIX (GLuint marker, GLsizei range); +GLAPI GLboolean APIENTRY glIsAsyncMarkerSGIX (GLuint marker); +#endif +#endif /* GL_SGIX_async */ + +#ifndef GL_SGIX_async_histogram +#define GL_SGIX_async_histogram 1 +#define GL_ASYNC_HISTOGRAM_SGIX 0x832C +#define GL_MAX_ASYNC_HISTOGRAM_SGIX 0x832D +#endif /* GL_SGIX_async_histogram */ + +#ifndef GL_SGIX_async_pixel +#define GL_SGIX_async_pixel 1 +#define GL_ASYNC_TEX_IMAGE_SGIX 0x835C +#define GL_ASYNC_DRAW_PIXELS_SGIX 0x835D +#define GL_ASYNC_READ_PIXELS_SGIX 0x835E +#define GL_MAX_ASYNC_TEX_IMAGE_SGIX 0x835F +#define GL_MAX_ASYNC_DRAW_PIXELS_SGIX 0x8360 +#define GL_MAX_ASYNC_READ_PIXELS_SGIX 0x8361 +#endif /* GL_SGIX_async_pixel */ + +#ifndef GL_SGIX_blend_alpha_minmax +#define GL_SGIX_blend_alpha_minmax 1 +#define GL_ALPHA_MIN_SGIX 0x8320 +#define GL_ALPHA_MAX_SGIX 0x8321 +#endif /* GL_SGIX_blend_alpha_minmax */ + +#ifndef GL_SGIX_calligraphic_fragment +#define GL_SGIX_calligraphic_fragment 1 +#define GL_CALLIGRAPHIC_FRAGMENT_SGIX 0x8183 +#endif /* GL_SGIX_calligraphic_fragment */ + +#ifndef GL_SGIX_clipmap +#define GL_SGIX_clipmap 1 +#define GL_LINEAR_CLIPMAP_LINEAR_SGIX 0x8170 +#define GL_TEXTURE_CLIPMAP_CENTER_SGIX 0x8171 +#define GL_TEXTURE_CLIPMAP_FRAME_SGIX 0x8172 +#define GL_TEXTURE_CLIPMAP_OFFSET_SGIX 0x8173 +#define GL_TEXTURE_CLIPMAP_VIRTUAL_DEPTH_SGIX 0x8174 +#define GL_TEXTURE_CLIPMAP_LOD_OFFSET_SGIX 0x8175 +#define GL_TEXTURE_CLIPMAP_DEPTH_SGIX 0x8176 +#define GL_MAX_CLIPMAP_DEPTH_SGIX 0x8177 +#define GL_MAX_CLIPMAP_VIRTUAL_DEPTH_SGIX 0x8178 +#define GL_NEAREST_CLIPMAP_NEAREST_SGIX 0x844D +#define GL_NEAREST_CLIPMAP_LINEAR_SGIX 0x844E +#define GL_LINEAR_CLIPMAP_NEAREST_SGIX 0x844F +#endif /* GL_SGIX_clipmap */ + +#ifndef GL_SGIX_convolution_accuracy +#define GL_SGIX_convolution_accuracy 1 +#define GL_CONVOLUTION_HINT_SGIX 0x8316 +#endif /* GL_SGIX_convolution_accuracy */ + +#ifndef GL_SGIX_depth_pass_instrument +#define GL_SGIX_depth_pass_instrument 1 +#endif /* GL_SGIX_depth_pass_instrument */ + +#ifndef GL_SGIX_depth_texture +#define GL_SGIX_depth_texture 1 +#define GL_DEPTH_COMPONENT16_SGIX 0x81A5 +#define GL_DEPTH_COMPONENT24_SGIX 0x81A6 +#define GL_DEPTH_COMPONENT32_SGIX 0x81A7 +#endif /* GL_SGIX_depth_texture */ + +#ifndef GL_SGIX_flush_raster +#define GL_SGIX_flush_raster 1 +typedef void (APIENTRYP PFNGLFLUSHRASTERSGIXPROC) (void); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glFlushRasterSGIX (void); +#endif +#endif /* GL_SGIX_flush_raster */ + +#ifndef GL_SGIX_fog_offset +#define GL_SGIX_fog_offset 1 +#define GL_FOG_OFFSET_SGIX 0x8198 +#define GL_FOG_OFFSET_VALUE_SGIX 0x8199 +#endif /* GL_SGIX_fog_offset */ + +#ifndef GL_SGIX_fragment_lighting +#define GL_SGIX_fragment_lighting 1 +#define GL_FRAGMENT_LIGHTING_SGIX 0x8400 +#define GL_FRAGMENT_COLOR_MATERIAL_SGIX 0x8401 +#define GL_FRAGMENT_COLOR_MATERIAL_FACE_SGIX 0x8402 +#define GL_FRAGMENT_COLOR_MATERIAL_PARAMETER_SGIX 0x8403 +#define GL_MAX_FRAGMENT_LIGHTS_SGIX 0x8404 +#define GL_MAX_ACTIVE_LIGHTS_SGIX 0x8405 +#define GL_CURRENT_RASTER_NORMAL_SGIX 0x8406 +#define GL_LIGHT_ENV_MODE_SGIX 0x8407 +#define GL_FRAGMENT_LIGHT_MODEL_LOCAL_VIEWER_SGIX 0x8408 +#define GL_FRAGMENT_LIGHT_MODEL_TWO_SIDE_SGIX 0x8409 +#define GL_FRAGMENT_LIGHT_MODEL_AMBIENT_SGIX 0x840A +#define GL_FRAGMENT_LIGHT_MODEL_NORMAL_INTERPOLATION_SGIX 0x840B +#define GL_FRAGMENT_LIGHT0_SGIX 0x840C +#define GL_FRAGMENT_LIGHT1_SGIX 0x840D +#define GL_FRAGMENT_LIGHT2_SGIX 0x840E +#define GL_FRAGMENT_LIGHT3_SGIX 0x840F +#define GL_FRAGMENT_LIGHT4_SGIX 0x8410 +#define GL_FRAGMENT_LIGHT5_SGIX 0x8411 +#define GL_FRAGMENT_LIGHT6_SGIX 0x8412 +#define GL_FRAGMENT_LIGHT7_SGIX 0x8413 +typedef void (APIENTRYP PFNGLFRAGMENTCOLORMATERIALSGIXPROC) (GLenum face, GLenum mode); +typedef void (APIENTRYP PFNGLFRAGMENTLIGHTFSGIXPROC) (GLenum light, GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLFRAGMENTLIGHTFVSGIXPROC) (GLenum light, GLenum pname, const GLfloat *params); +typedef void (APIENTRYP PFNGLFRAGMENTLIGHTISGIXPROC) (GLenum light, GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLFRAGMENTLIGHTIVSGIXPROC) (GLenum light, GLenum pname, const GLint *params); +typedef void (APIENTRYP PFNGLFRAGMENTLIGHTMODELFSGIXPROC) (GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLFRAGMENTLIGHTMODELFVSGIXPROC) (GLenum pname, const GLfloat *params); +typedef void (APIENTRYP PFNGLFRAGMENTLIGHTMODELISGIXPROC) (GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLFRAGMENTLIGHTMODELIVSGIXPROC) (GLenum pname, const GLint *params); +typedef void (APIENTRYP PFNGLFRAGMENTMATERIALFSGIXPROC) (GLenum face, GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLFRAGMENTMATERIALFVSGIXPROC) (GLenum face, GLenum pname, const GLfloat *params); +typedef void (APIENTRYP PFNGLFRAGMENTMATERIALISGIXPROC) (GLenum face, GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLFRAGMENTMATERIALIVSGIXPROC) (GLenum face, GLenum pname, const GLint *params); +typedef void (APIENTRYP PFNGLGETFRAGMENTLIGHTFVSGIXPROC) (GLenum light, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETFRAGMENTLIGHTIVSGIXPROC) (GLenum light, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETFRAGMENTMATERIALFVSGIXPROC) (GLenum face, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETFRAGMENTMATERIALIVSGIXPROC) (GLenum face, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLLIGHTENVISGIXPROC) (GLenum pname, GLint param); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glFragmentColorMaterialSGIX (GLenum face, GLenum mode); +GLAPI void APIENTRY glFragmentLightfSGIX (GLenum light, GLenum pname, GLfloat param); +GLAPI void APIENTRY glFragmentLightfvSGIX (GLenum light, GLenum pname, const GLfloat *params); +GLAPI void APIENTRY glFragmentLightiSGIX (GLenum light, GLenum pname, GLint param); +GLAPI void APIENTRY glFragmentLightivSGIX (GLenum light, GLenum pname, const GLint *params); +GLAPI void APIENTRY glFragmentLightModelfSGIX (GLenum pname, GLfloat param); +GLAPI void APIENTRY glFragmentLightModelfvSGIX (GLenum pname, const GLfloat *params); +GLAPI void APIENTRY glFragmentLightModeliSGIX (GLenum pname, GLint param); +GLAPI void APIENTRY glFragmentLightModelivSGIX (GLenum pname, const GLint *params); +GLAPI void APIENTRY glFragmentMaterialfSGIX (GLenum face, GLenum pname, GLfloat param); +GLAPI void APIENTRY glFragmentMaterialfvSGIX (GLenum face, GLenum pname, const GLfloat *params); +GLAPI void APIENTRY glFragmentMaterialiSGIX (GLenum face, GLenum pname, GLint param); +GLAPI void APIENTRY glFragmentMaterialivSGIX (GLenum face, GLenum pname, const GLint *params); +GLAPI void APIENTRY glGetFragmentLightfvSGIX (GLenum light, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetFragmentLightivSGIX (GLenum light, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetFragmentMaterialfvSGIX (GLenum face, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetFragmentMaterialivSGIX (GLenum face, GLenum pname, GLint *params); +GLAPI void APIENTRY glLightEnviSGIX (GLenum pname, GLint param); +#endif +#endif /* GL_SGIX_fragment_lighting */ + +#ifndef GL_SGIX_framezoom +#define GL_SGIX_framezoom 1 +#define GL_FRAMEZOOM_SGIX 0x818B +#define GL_FRAMEZOOM_FACTOR_SGIX 0x818C +#define GL_MAX_FRAMEZOOM_FACTOR_SGIX 0x818D +typedef void (APIENTRYP PFNGLFRAMEZOOMSGIXPROC) (GLint factor); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glFrameZoomSGIX (GLint factor); +#endif +#endif /* GL_SGIX_framezoom */ + +#ifndef GL_SGIX_igloo_interface +#define GL_SGIX_igloo_interface 1 +typedef void (APIENTRYP PFNGLIGLOOINTERFACESGIXPROC) (GLenum pname, const void *params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glIglooInterfaceSGIX (GLenum pname, const void *params); +#endif +#endif /* GL_SGIX_igloo_interface */ + +#ifndef GL_SGIX_instruments +#define GL_SGIX_instruments 1 +#define GL_INSTRUMENT_BUFFER_POINTER_SGIX 0x8180 +#define GL_INSTRUMENT_MEASUREMENTS_SGIX 0x8181 +typedef GLint (APIENTRYP PFNGLGETINSTRUMENTSSGIXPROC) (void); +typedef void (APIENTRYP PFNGLINSTRUMENTSBUFFERSGIXPROC) (GLsizei size, GLint *buffer); +typedef GLint (APIENTRYP PFNGLPOLLINSTRUMENTSSGIXPROC) (GLint *marker_p); +typedef void (APIENTRYP PFNGLREADINSTRUMENTSSGIXPROC) (GLint marker); +typedef void (APIENTRYP PFNGLSTARTINSTRUMENTSSGIXPROC) (void); +typedef void (APIENTRYP PFNGLSTOPINSTRUMENTSSGIXPROC) (GLint marker); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI GLint APIENTRY glGetInstrumentsSGIX (void); +GLAPI void APIENTRY glInstrumentsBufferSGIX (GLsizei size, GLint *buffer); +GLAPI GLint APIENTRY glPollInstrumentsSGIX (GLint *marker_p); +GLAPI void APIENTRY glReadInstrumentsSGIX (GLint marker); +GLAPI void APIENTRY glStartInstrumentsSGIX (void); +GLAPI void APIENTRY glStopInstrumentsSGIX (GLint marker); +#endif +#endif /* GL_SGIX_instruments */ + +#ifndef GL_SGIX_interlace +#define GL_SGIX_interlace 1 +#define GL_INTERLACE_SGIX 0x8094 +#endif /* GL_SGIX_interlace */ + +#ifndef GL_SGIX_ir_instrument1 +#define GL_SGIX_ir_instrument1 1 +#define GL_IR_INSTRUMENT1_SGIX 0x817F +#endif /* GL_SGIX_ir_instrument1 */ + +#ifndef GL_SGIX_list_priority +#define GL_SGIX_list_priority 1 +#define GL_LIST_PRIORITY_SGIX 0x8182 +typedef void (APIENTRYP PFNGLGETLISTPARAMETERFVSGIXPROC) (GLuint list, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETLISTPARAMETERIVSGIXPROC) (GLuint list, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLLISTPARAMETERFSGIXPROC) (GLuint list, GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLLISTPARAMETERFVSGIXPROC) (GLuint list, GLenum pname, const GLfloat *params); +typedef void (APIENTRYP PFNGLLISTPARAMETERISGIXPROC) (GLuint list, GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLLISTPARAMETERIVSGIXPROC) (GLuint list, GLenum pname, const GLint *params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glGetListParameterfvSGIX (GLuint list, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetListParameterivSGIX (GLuint list, GLenum pname, GLint *params); +GLAPI void APIENTRY glListParameterfSGIX (GLuint list, GLenum pname, GLfloat param); +GLAPI void APIENTRY glListParameterfvSGIX (GLuint list, GLenum pname, const GLfloat *params); +GLAPI void APIENTRY glListParameteriSGIX (GLuint list, GLenum pname, GLint param); +GLAPI void APIENTRY glListParameterivSGIX (GLuint list, GLenum pname, const GLint *params); +#endif +#endif /* GL_SGIX_list_priority */ + +#ifndef GL_SGIX_pixel_texture +#define GL_SGIX_pixel_texture 1 +#define GL_PIXEL_TEX_GEN_SGIX 0x8139 +#define GL_PIXEL_TEX_GEN_MODE_SGIX 0x832B +typedef void (APIENTRYP PFNGLPIXELTEXGENSGIXPROC) (GLenum mode); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glPixelTexGenSGIX (GLenum mode); +#endif +#endif /* GL_SGIX_pixel_texture */ + +#ifndef GL_SGIX_pixel_tiles +#define GL_SGIX_pixel_tiles 1 +#define GL_PIXEL_TILE_BEST_ALIGNMENT_SGIX 0x813E +#define GL_PIXEL_TILE_CACHE_INCREMENT_SGIX 0x813F +#define GL_PIXEL_TILE_WIDTH_SGIX 0x8140 +#define GL_PIXEL_TILE_HEIGHT_SGIX 0x8141 +#define GL_PIXEL_TILE_GRID_WIDTH_SGIX 0x8142 +#define GL_PIXEL_TILE_GRID_HEIGHT_SGIX 0x8143 +#define GL_PIXEL_TILE_GRID_DEPTH_SGIX 0x8144 +#define GL_PIXEL_TILE_CACHE_SIZE_SGIX 0x8145 +#endif /* GL_SGIX_pixel_tiles */ + +#ifndef GL_SGIX_polynomial_ffd +#define GL_SGIX_polynomial_ffd 1 +#define GL_TEXTURE_DEFORMATION_BIT_SGIX 0x00000001 +#define GL_GEOMETRY_DEFORMATION_BIT_SGIX 0x00000002 +#define GL_GEOMETRY_DEFORMATION_SGIX 0x8194 +#define GL_TEXTURE_DEFORMATION_SGIX 0x8195 +#define GL_DEFORMATIONS_MASK_SGIX 0x8196 +#define GL_MAX_DEFORMATION_ORDER_SGIX 0x8197 +typedef void (APIENTRYP PFNGLDEFORMATIONMAP3DSGIXPROC) (GLenum target, GLdouble u1, GLdouble u2, GLint ustride, GLint uorder, GLdouble v1, GLdouble v2, GLint vstride, GLint vorder, GLdouble w1, GLdouble w2, GLint wstride, GLint worder, const GLdouble *points); +typedef void (APIENTRYP PFNGLDEFORMATIONMAP3FSGIXPROC) (GLenum target, GLfloat u1, GLfloat u2, GLint ustride, GLint uorder, GLfloat v1, GLfloat v2, GLint vstride, GLint vorder, GLfloat w1, GLfloat w2, GLint wstride, GLint worder, const GLfloat *points); +typedef void (APIENTRYP PFNGLDEFORMSGIXPROC) (GLbitfield mask); +typedef void (APIENTRYP PFNGLLOADIDENTITYDEFORMATIONMAPSGIXPROC) (GLbitfield mask); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glDeformationMap3dSGIX (GLenum target, GLdouble u1, GLdouble u2, GLint ustride, GLint uorder, GLdouble v1, GLdouble v2, GLint vstride, GLint vorder, GLdouble w1, GLdouble w2, GLint wstride, GLint worder, const GLdouble *points); +GLAPI void APIENTRY glDeformationMap3fSGIX (GLenum target, GLfloat u1, GLfloat u2, GLint ustride, GLint uorder, GLfloat v1, GLfloat v2, GLint vstride, GLint vorder, GLfloat w1, GLfloat w2, GLint wstride, GLint worder, const GLfloat *points); +GLAPI void APIENTRY glDeformSGIX (GLbitfield mask); +GLAPI void APIENTRY glLoadIdentityDeformationMapSGIX (GLbitfield mask); +#endif +#endif /* GL_SGIX_polynomial_ffd */ + +#ifndef GL_SGIX_reference_plane +#define GL_SGIX_reference_plane 1 +#define GL_REFERENCE_PLANE_SGIX 0x817D +#define GL_REFERENCE_PLANE_EQUATION_SGIX 0x817E +typedef void (APIENTRYP PFNGLREFERENCEPLANESGIXPROC) (const GLdouble *equation); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glReferencePlaneSGIX (const GLdouble *equation); +#endif +#endif /* GL_SGIX_reference_plane */ + +#ifndef GL_SGIX_resample +#define GL_SGIX_resample 1 +#define GL_PACK_RESAMPLE_SGIX 0x842E +#define GL_UNPACK_RESAMPLE_SGIX 0x842F +#define GL_RESAMPLE_REPLICATE_SGIX 0x8433 +#define GL_RESAMPLE_ZERO_FILL_SGIX 0x8434 +#define GL_RESAMPLE_DECIMATE_SGIX 0x8430 +#endif /* GL_SGIX_resample */ + +#ifndef GL_SGIX_scalebias_hint +#define GL_SGIX_scalebias_hint 1 +#define GL_SCALEBIAS_HINT_SGIX 0x8322 +#endif /* GL_SGIX_scalebias_hint */ + +#ifndef GL_SGIX_shadow +#define GL_SGIX_shadow 1 +#define GL_TEXTURE_COMPARE_SGIX 0x819A +#define GL_TEXTURE_COMPARE_OPERATOR_SGIX 0x819B +#define GL_TEXTURE_LEQUAL_R_SGIX 0x819C +#define GL_TEXTURE_GEQUAL_R_SGIX 0x819D +#endif /* GL_SGIX_shadow */ + +#ifndef GL_SGIX_shadow_ambient +#define GL_SGIX_shadow_ambient 1 +#define GL_SHADOW_AMBIENT_SGIX 0x80BF +#endif /* GL_SGIX_shadow_ambient */ + +#ifndef GL_SGIX_sprite +#define GL_SGIX_sprite 1 +#define GL_SPRITE_SGIX 0x8148 +#define GL_SPRITE_MODE_SGIX 0x8149 +#define GL_SPRITE_AXIS_SGIX 0x814A +#define GL_SPRITE_TRANSLATION_SGIX 0x814B +#define GL_SPRITE_AXIAL_SGIX 0x814C +#define GL_SPRITE_OBJECT_ALIGNED_SGIX 0x814D +#define GL_SPRITE_EYE_ALIGNED_SGIX 0x814E +typedef void (APIENTRYP PFNGLSPRITEPARAMETERFSGIXPROC) (GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLSPRITEPARAMETERFVSGIXPROC) (GLenum pname, const GLfloat *params); +typedef void (APIENTRYP PFNGLSPRITEPARAMETERISGIXPROC) (GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLSPRITEPARAMETERIVSGIXPROC) (GLenum pname, const GLint *params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glSpriteParameterfSGIX (GLenum pname, GLfloat param); +GLAPI void APIENTRY glSpriteParameterfvSGIX (GLenum pname, const GLfloat *params); +GLAPI void APIENTRY glSpriteParameteriSGIX (GLenum pname, GLint param); +GLAPI void APIENTRY glSpriteParameterivSGIX (GLenum pname, const GLint *params); +#endif +#endif /* GL_SGIX_sprite */ + +#ifndef GL_SGIX_subsample +#define GL_SGIX_subsample 1 +#define GL_PACK_SUBSAMPLE_RATE_SGIX 0x85A0 +#define GL_UNPACK_SUBSAMPLE_RATE_SGIX 0x85A1 +#define GL_PIXEL_SUBSAMPLE_4444_SGIX 0x85A2 +#define GL_PIXEL_SUBSAMPLE_2424_SGIX 0x85A3 +#define GL_PIXEL_SUBSAMPLE_4242_SGIX 0x85A4 +#endif /* GL_SGIX_subsample */ + +#ifndef GL_SGIX_tag_sample_buffer +#define GL_SGIX_tag_sample_buffer 1 +typedef void (APIENTRYP PFNGLTAGSAMPLEBUFFERSGIXPROC) (void); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glTagSampleBufferSGIX (void); +#endif +#endif /* GL_SGIX_tag_sample_buffer */ + +#ifndef GL_SGIX_texture_add_env +#define GL_SGIX_texture_add_env 1 +#define GL_TEXTURE_ENV_BIAS_SGIX 0x80BE +#endif /* GL_SGIX_texture_add_env */ + +#ifndef GL_SGIX_texture_coordinate_clamp +#define GL_SGIX_texture_coordinate_clamp 1 +#define GL_TEXTURE_MAX_CLAMP_S_SGIX 0x8369 +#define GL_TEXTURE_MAX_CLAMP_T_SGIX 0x836A +#define GL_TEXTURE_MAX_CLAMP_R_SGIX 0x836B +#endif /* GL_SGIX_texture_coordinate_clamp */ + +#ifndef GL_SGIX_texture_lod_bias +#define GL_SGIX_texture_lod_bias 1 +#define GL_TEXTURE_LOD_BIAS_S_SGIX 0x818E +#define GL_TEXTURE_LOD_BIAS_T_SGIX 0x818F +#define GL_TEXTURE_LOD_BIAS_R_SGIX 0x8190 +#endif /* GL_SGIX_texture_lod_bias */ + +#ifndef GL_SGIX_texture_multi_buffer +#define GL_SGIX_texture_multi_buffer 1 +#define GL_TEXTURE_MULTI_BUFFER_HINT_SGIX 0x812E +#endif /* GL_SGIX_texture_multi_buffer */ + +#ifndef GL_SGIX_texture_scale_bias +#define GL_SGIX_texture_scale_bias 1 +#define GL_POST_TEXTURE_FILTER_BIAS_SGIX 0x8179 +#define GL_POST_TEXTURE_FILTER_SCALE_SGIX 0x817A +#define GL_POST_TEXTURE_FILTER_BIAS_RANGE_SGIX 0x817B +#define GL_POST_TEXTURE_FILTER_SCALE_RANGE_SGIX 0x817C +#endif /* GL_SGIX_texture_scale_bias */ + +#ifndef GL_SGIX_vertex_preclip +#define GL_SGIX_vertex_preclip 1 +#define GL_VERTEX_PRECLIP_SGIX 0x83EE +#define GL_VERTEX_PRECLIP_HINT_SGIX 0x83EF +#endif /* GL_SGIX_vertex_preclip */ + +#ifndef GL_SGIX_ycrcb +#define GL_SGIX_ycrcb 1 +#define GL_YCRCB_422_SGIX 0x81BB +#define GL_YCRCB_444_SGIX 0x81BC +#endif /* GL_SGIX_ycrcb */ + +#ifndef GL_SGIX_ycrcb_subsample +#define GL_SGIX_ycrcb_subsample 1 +#endif /* GL_SGIX_ycrcb_subsample */ + +#ifndef GL_SGIX_ycrcba +#define GL_SGIX_ycrcba 1 +#define GL_YCRCB_SGIX 0x8318 +#define GL_YCRCBA_SGIX 0x8319 +#endif /* GL_SGIX_ycrcba */ + +#ifndef GL_SGI_color_matrix +#define GL_SGI_color_matrix 1 +#define GL_COLOR_MATRIX_SGI 0x80B1 +#define GL_COLOR_MATRIX_STACK_DEPTH_SGI 0x80B2 +#define GL_MAX_COLOR_MATRIX_STACK_DEPTH_SGI 0x80B3 +#define GL_POST_COLOR_MATRIX_RED_SCALE_SGI 0x80B4 +#define GL_POST_COLOR_MATRIX_GREEN_SCALE_SGI 0x80B5 +#define GL_POST_COLOR_MATRIX_BLUE_SCALE_SGI 0x80B6 +#define GL_POST_COLOR_MATRIX_ALPHA_SCALE_SGI 0x80B7 +#define GL_POST_COLOR_MATRIX_RED_BIAS_SGI 0x80B8 +#define GL_POST_COLOR_MATRIX_GREEN_BIAS_SGI 0x80B9 +#define GL_POST_COLOR_MATRIX_BLUE_BIAS_SGI 0x80BA +#define GL_POST_COLOR_MATRIX_ALPHA_BIAS_SGI 0x80BB +#endif /* GL_SGI_color_matrix */ + +#ifndef GL_SGI_color_table +#define GL_SGI_color_table 1 +#define GL_COLOR_TABLE_SGI 0x80D0 +#define GL_POST_CONVOLUTION_COLOR_TABLE_SGI 0x80D1 +#define GL_POST_COLOR_MATRIX_COLOR_TABLE_SGI 0x80D2 +#define GL_PROXY_COLOR_TABLE_SGI 0x80D3 +#define GL_PROXY_POST_CONVOLUTION_COLOR_TABLE_SGI 0x80D4 +#define GL_PROXY_POST_COLOR_MATRIX_COLOR_TABLE_SGI 0x80D5 +#define GL_COLOR_TABLE_SCALE_SGI 0x80D6 +#define GL_COLOR_TABLE_BIAS_SGI 0x80D7 +#define GL_COLOR_TABLE_FORMAT_SGI 0x80D8 +#define GL_COLOR_TABLE_WIDTH_SGI 0x80D9 +#define GL_COLOR_TABLE_RED_SIZE_SGI 0x80DA +#define GL_COLOR_TABLE_GREEN_SIZE_SGI 0x80DB +#define GL_COLOR_TABLE_BLUE_SIZE_SGI 0x80DC +#define GL_COLOR_TABLE_ALPHA_SIZE_SGI 0x80DD +#define GL_COLOR_TABLE_LUMINANCE_SIZE_SGI 0x80DE +#define GL_COLOR_TABLE_INTENSITY_SIZE_SGI 0x80DF +typedef void (APIENTRYP PFNGLCOLORTABLESGIPROC) (GLenum target, GLenum internalformat, GLsizei width, GLenum format, GLenum type, const void *table); +typedef void (APIENTRYP PFNGLCOLORTABLEPARAMETERFVSGIPROC) (GLenum target, GLenum pname, const GLfloat *params); +typedef void (APIENTRYP PFNGLCOLORTABLEPARAMETERIVSGIPROC) (GLenum target, GLenum pname, const GLint *params); +typedef void (APIENTRYP PFNGLCOPYCOLORTABLESGIPROC) (GLenum target, GLenum internalformat, GLint x, GLint y, GLsizei width); +typedef void (APIENTRYP PFNGLGETCOLORTABLESGIPROC) (GLenum target, GLenum format, GLenum type, void *table); +typedef void (APIENTRYP PFNGLGETCOLORTABLEPARAMETERFVSGIPROC) (GLenum target, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETCOLORTABLEPARAMETERIVSGIPROC) (GLenum target, GLenum pname, GLint *params); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glColorTableSGI (GLenum target, GLenum internalformat, GLsizei width, GLenum format, GLenum type, const void *table); +GLAPI void APIENTRY glColorTableParameterfvSGI (GLenum target, GLenum pname, const GLfloat *params); +GLAPI void APIENTRY glColorTableParameterivSGI (GLenum target, GLenum pname, const GLint *params); +GLAPI void APIENTRY glCopyColorTableSGI (GLenum target, GLenum internalformat, GLint x, GLint y, GLsizei width); +GLAPI void APIENTRY glGetColorTableSGI (GLenum target, GLenum format, GLenum type, void *table); +GLAPI void APIENTRY glGetColorTableParameterfvSGI (GLenum target, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetColorTableParameterivSGI (GLenum target, GLenum pname, GLint *params); +#endif +#endif /* GL_SGI_color_table */ + +#ifndef GL_SGI_texture_color_table +#define GL_SGI_texture_color_table 1 +#define GL_TEXTURE_COLOR_TABLE_SGI 0x80BC +#define GL_PROXY_TEXTURE_COLOR_TABLE_SGI 0x80BD +#endif /* GL_SGI_texture_color_table */ + +#ifndef GL_SUNX_constant_data +#define GL_SUNX_constant_data 1 +#define GL_UNPACK_CONSTANT_DATA_SUNX 0x81D5 +#define GL_TEXTURE_CONSTANT_DATA_SUNX 0x81D6 +typedef void (APIENTRYP PFNGLFINISHTEXTURESUNXPROC) (void); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glFinishTextureSUNX (void); +#endif +#endif /* GL_SUNX_constant_data */ + +#ifndef GL_SUN_convolution_border_modes +#define GL_SUN_convolution_border_modes 1 +#define GL_WRAP_BORDER_SUN 0x81D4 +#endif /* GL_SUN_convolution_border_modes */ + +#ifndef GL_SUN_global_alpha +#define GL_SUN_global_alpha 1 +#define GL_GLOBAL_ALPHA_SUN 0x81D9 +#define GL_GLOBAL_ALPHA_FACTOR_SUN 0x81DA +typedef void (APIENTRYP PFNGLGLOBALALPHAFACTORBSUNPROC) (GLbyte factor); +typedef void (APIENTRYP PFNGLGLOBALALPHAFACTORSSUNPROC) (GLshort factor); +typedef void (APIENTRYP PFNGLGLOBALALPHAFACTORISUNPROC) (GLint factor); +typedef void (APIENTRYP PFNGLGLOBALALPHAFACTORFSUNPROC) (GLfloat factor); +typedef void (APIENTRYP PFNGLGLOBALALPHAFACTORDSUNPROC) (GLdouble factor); +typedef void (APIENTRYP PFNGLGLOBALALPHAFACTORUBSUNPROC) (GLubyte factor); +typedef void (APIENTRYP PFNGLGLOBALALPHAFACTORUSSUNPROC) (GLushort factor); +typedef void (APIENTRYP PFNGLGLOBALALPHAFACTORUISUNPROC) (GLuint factor); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glGlobalAlphaFactorbSUN (GLbyte factor); +GLAPI void APIENTRY glGlobalAlphaFactorsSUN (GLshort factor); +GLAPI void APIENTRY glGlobalAlphaFactoriSUN (GLint factor); +GLAPI void APIENTRY glGlobalAlphaFactorfSUN (GLfloat factor); +GLAPI void APIENTRY glGlobalAlphaFactordSUN (GLdouble factor); +GLAPI void APIENTRY glGlobalAlphaFactorubSUN (GLubyte factor); +GLAPI void APIENTRY glGlobalAlphaFactorusSUN (GLushort factor); +GLAPI void APIENTRY glGlobalAlphaFactoruiSUN (GLuint factor); +#endif +#endif /* GL_SUN_global_alpha */ + +#ifndef GL_SUN_mesh_array +#define GL_SUN_mesh_array 1 +#define GL_QUAD_MESH_SUN 0x8614 +#define GL_TRIANGLE_MESH_SUN 0x8615 +typedef void (APIENTRYP PFNGLDRAWMESHARRAYSSUNPROC) (GLenum mode, GLint first, GLsizei count, GLsizei width); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glDrawMeshArraysSUN (GLenum mode, GLint first, GLsizei count, GLsizei width); +#endif +#endif /* GL_SUN_mesh_array */ + +#ifndef GL_SUN_slice_accum +#define GL_SUN_slice_accum 1 +#define GL_SLICE_ACCUM_SUN 0x85CC +#endif /* GL_SUN_slice_accum */ + +#ifndef GL_SUN_triangle_list +#define GL_SUN_triangle_list 1 +#define GL_RESTART_SUN 0x0001 +#define GL_REPLACE_MIDDLE_SUN 0x0002 +#define GL_REPLACE_OLDEST_SUN 0x0003 +#define GL_TRIANGLE_LIST_SUN 0x81D7 +#define GL_REPLACEMENT_CODE_SUN 0x81D8 +#define GL_REPLACEMENT_CODE_ARRAY_SUN 0x85C0 +#define GL_REPLACEMENT_CODE_ARRAY_TYPE_SUN 0x85C1 +#define GL_REPLACEMENT_CODE_ARRAY_STRIDE_SUN 0x85C2 +#define GL_REPLACEMENT_CODE_ARRAY_POINTER_SUN 0x85C3 +#define GL_R1UI_V3F_SUN 0x85C4 +#define GL_R1UI_C4UB_V3F_SUN 0x85C5 +#define GL_R1UI_C3F_V3F_SUN 0x85C6 +#define GL_R1UI_N3F_V3F_SUN 0x85C7 +#define GL_R1UI_C4F_N3F_V3F_SUN 0x85C8 +#define GL_R1UI_T2F_V3F_SUN 0x85C9 +#define GL_R1UI_T2F_N3F_V3F_SUN 0x85CA +#define GL_R1UI_T2F_C4F_N3F_V3F_SUN 0x85CB +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUISUNPROC) (GLuint code); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUSSUNPROC) (GLushort code); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUBSUNPROC) (GLubyte code); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUIVSUNPROC) (const GLuint *code); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUSVSUNPROC) (const GLushort *code); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUBVSUNPROC) (const GLubyte *code); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEPOINTERSUNPROC) (GLenum type, GLsizei stride, const void **pointer); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glReplacementCodeuiSUN (GLuint code); +GLAPI void APIENTRY glReplacementCodeusSUN (GLushort code); +GLAPI void APIENTRY glReplacementCodeubSUN (GLubyte code); +GLAPI void APIENTRY glReplacementCodeuivSUN (const GLuint *code); +GLAPI void APIENTRY glReplacementCodeusvSUN (const GLushort *code); +GLAPI void APIENTRY glReplacementCodeubvSUN (const GLubyte *code); +GLAPI void APIENTRY glReplacementCodePointerSUN (GLenum type, GLsizei stride, const void **pointer); +#endif +#endif /* GL_SUN_triangle_list */ + +#ifndef GL_SUN_vertex +#define GL_SUN_vertex 1 +typedef void (APIENTRYP PFNGLCOLOR4UBVERTEX2FSUNPROC) (GLubyte r, GLubyte g, GLubyte b, GLubyte a, GLfloat x, GLfloat y); +typedef void (APIENTRYP PFNGLCOLOR4UBVERTEX2FVSUNPROC) (const GLubyte *c, const GLfloat *v); +typedef void (APIENTRYP PFNGLCOLOR4UBVERTEX3FSUNPROC) (GLubyte r, GLubyte g, GLubyte b, GLubyte a, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLCOLOR4UBVERTEX3FVSUNPROC) (const GLubyte *c, const GLfloat *v); +typedef void (APIENTRYP PFNGLCOLOR3FVERTEX3FSUNPROC) (GLfloat r, GLfloat g, GLfloat b, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLCOLOR3FVERTEX3FVSUNPROC) (const GLfloat *c, const GLfloat *v); +typedef void (APIENTRYP PFNGLNORMAL3FVERTEX3FSUNPROC) (GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLNORMAL3FVERTEX3FVSUNPROC) (const GLfloat *n, const GLfloat *v); +typedef void (APIENTRYP PFNGLCOLOR4FNORMAL3FVERTEX3FSUNPROC) (GLfloat r, GLfloat g, GLfloat b, GLfloat a, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLCOLOR4FNORMAL3FVERTEX3FVSUNPROC) (const GLfloat *c, const GLfloat *n, const GLfloat *v); +typedef void (APIENTRYP PFNGLTEXCOORD2FVERTEX3FSUNPROC) (GLfloat s, GLfloat t, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLTEXCOORD2FVERTEX3FVSUNPROC) (const GLfloat *tc, const GLfloat *v); +typedef void (APIENTRYP PFNGLTEXCOORD4FVERTEX4FSUNPROC) (GLfloat s, GLfloat t, GLfloat p, GLfloat q, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +typedef void (APIENTRYP PFNGLTEXCOORD4FVERTEX4FVSUNPROC) (const GLfloat *tc, const GLfloat *v); +typedef void (APIENTRYP PFNGLTEXCOORD2FCOLOR4UBVERTEX3FSUNPROC) (GLfloat s, GLfloat t, GLubyte r, GLubyte g, GLubyte b, GLubyte a, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLTEXCOORD2FCOLOR4UBVERTEX3FVSUNPROC) (const GLfloat *tc, const GLubyte *c, const GLfloat *v); +typedef void (APIENTRYP PFNGLTEXCOORD2FCOLOR3FVERTEX3FSUNPROC) (GLfloat s, GLfloat t, GLfloat r, GLfloat g, GLfloat b, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLTEXCOORD2FCOLOR3FVERTEX3FVSUNPROC) (const GLfloat *tc, const GLfloat *c, const GLfloat *v); +typedef void (APIENTRYP PFNGLTEXCOORD2FNORMAL3FVERTEX3FSUNPROC) (GLfloat s, GLfloat t, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLTEXCOORD2FNORMAL3FVERTEX3FVSUNPROC) (const GLfloat *tc, const GLfloat *n, const GLfloat *v); +typedef void (APIENTRYP PFNGLTEXCOORD2FCOLOR4FNORMAL3FVERTEX3FSUNPROC) (GLfloat s, GLfloat t, GLfloat r, GLfloat g, GLfloat b, GLfloat a, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLTEXCOORD2FCOLOR4FNORMAL3FVERTEX3FVSUNPROC) (const GLfloat *tc, const GLfloat *c, const GLfloat *n, const GLfloat *v); +typedef void (APIENTRYP PFNGLTEXCOORD4FCOLOR4FNORMAL3FVERTEX4FSUNPROC) (GLfloat s, GLfloat t, GLfloat p, GLfloat q, GLfloat r, GLfloat g, GLfloat b, GLfloat a, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +typedef void (APIENTRYP PFNGLTEXCOORD4FCOLOR4FNORMAL3FVERTEX4FVSUNPROC) (const GLfloat *tc, const GLfloat *c, const GLfloat *n, const GLfloat *v); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUIVERTEX3FSUNPROC) (GLuint rc, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUIVERTEX3FVSUNPROC) (const GLuint *rc, const GLfloat *v); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUICOLOR4UBVERTEX3FSUNPROC) (GLuint rc, GLubyte r, GLubyte g, GLubyte b, GLubyte a, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUICOLOR4UBVERTEX3FVSUNPROC) (const GLuint *rc, const GLubyte *c, const GLfloat *v); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUICOLOR3FVERTEX3FSUNPROC) (GLuint rc, GLfloat r, GLfloat g, GLfloat b, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUICOLOR3FVERTEX3FVSUNPROC) (const GLuint *rc, const GLfloat *c, const GLfloat *v); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUINORMAL3FVERTEX3FSUNPROC) (GLuint rc, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUINORMAL3FVERTEX3FVSUNPROC) (const GLuint *rc, const GLfloat *n, const GLfloat *v); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUICOLOR4FNORMAL3FVERTEX3FSUNPROC) (GLuint rc, GLfloat r, GLfloat g, GLfloat b, GLfloat a, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUICOLOR4FNORMAL3FVERTEX3FVSUNPROC) (const GLuint *rc, const GLfloat *c, const GLfloat *n, const GLfloat *v); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUITEXCOORD2FVERTEX3FSUNPROC) (GLuint rc, GLfloat s, GLfloat t, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUITEXCOORD2FVERTEX3FVSUNPROC) (const GLuint *rc, const GLfloat *tc, const GLfloat *v); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUITEXCOORD2FNORMAL3FVERTEX3FSUNPROC) (GLuint rc, GLfloat s, GLfloat t, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUITEXCOORD2FNORMAL3FVERTEX3FVSUNPROC) (const GLuint *rc, const GLfloat *tc, const GLfloat *n, const GLfloat *v); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUITEXCOORD2FCOLOR4FNORMAL3FVERTEX3FSUNPROC) (GLuint rc, GLfloat s, GLfloat t, GLfloat r, GLfloat g, GLfloat b, GLfloat a, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLREPLACEMENTCODEUITEXCOORD2FCOLOR4FNORMAL3FVERTEX3FVSUNPROC) (const GLuint *rc, const GLfloat *tc, const GLfloat *c, const GLfloat *n, const GLfloat *v); +#ifdef GL_GLEXT_PROTOTYPES +GLAPI void APIENTRY glColor4ubVertex2fSUN (GLubyte r, GLubyte g, GLubyte b, GLubyte a, GLfloat x, GLfloat y); +GLAPI void APIENTRY glColor4ubVertex2fvSUN (const GLubyte *c, const GLfloat *v); +GLAPI void APIENTRY glColor4ubVertex3fSUN (GLubyte r, GLubyte g, GLubyte b, GLubyte a, GLfloat x, GLfloat y, GLfloat z); +GLAPI void APIENTRY glColor4ubVertex3fvSUN (const GLubyte *c, const GLfloat *v); +GLAPI void APIENTRY glColor3fVertex3fSUN (GLfloat r, GLfloat g, GLfloat b, GLfloat x, GLfloat y, GLfloat z); +GLAPI void APIENTRY glColor3fVertex3fvSUN (const GLfloat *c, const GLfloat *v); +GLAPI void APIENTRY glNormal3fVertex3fSUN (GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z); +GLAPI void APIENTRY glNormal3fVertex3fvSUN (const GLfloat *n, const GLfloat *v); +GLAPI void APIENTRY glColor4fNormal3fVertex3fSUN (GLfloat r, GLfloat g, GLfloat b, GLfloat a, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z); +GLAPI void APIENTRY glColor4fNormal3fVertex3fvSUN (const GLfloat *c, const GLfloat *n, const GLfloat *v); +GLAPI void APIENTRY glTexCoord2fVertex3fSUN (GLfloat s, GLfloat t, GLfloat x, GLfloat y, GLfloat z); +GLAPI void APIENTRY glTexCoord2fVertex3fvSUN (const GLfloat *tc, const GLfloat *v); +GLAPI void APIENTRY glTexCoord4fVertex4fSUN (GLfloat s, GLfloat t, GLfloat p, GLfloat q, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +GLAPI void APIENTRY glTexCoord4fVertex4fvSUN (const GLfloat *tc, const GLfloat *v); +GLAPI void APIENTRY glTexCoord2fColor4ubVertex3fSUN (GLfloat s, GLfloat t, GLubyte r, GLubyte g, GLubyte b, GLubyte a, GLfloat x, GLfloat y, GLfloat z); +GLAPI void APIENTRY glTexCoord2fColor4ubVertex3fvSUN (const GLfloat *tc, const GLubyte *c, const GLfloat *v); +GLAPI void APIENTRY glTexCoord2fColor3fVertex3fSUN (GLfloat s, GLfloat t, GLfloat r, GLfloat g, GLfloat b, GLfloat x, GLfloat y, GLfloat z); +GLAPI void APIENTRY glTexCoord2fColor3fVertex3fvSUN (const GLfloat *tc, const GLfloat *c, const GLfloat *v); +GLAPI void APIENTRY glTexCoord2fNormal3fVertex3fSUN (GLfloat s, GLfloat t, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z); +GLAPI void APIENTRY glTexCoord2fNormal3fVertex3fvSUN (const GLfloat *tc, const GLfloat *n, const GLfloat *v); +GLAPI void APIENTRY glTexCoord2fColor4fNormal3fVertex3fSUN (GLfloat s, GLfloat t, GLfloat r, GLfloat g, GLfloat b, GLfloat a, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z); +GLAPI void APIENTRY glTexCoord2fColor4fNormal3fVertex3fvSUN (const GLfloat *tc, const GLfloat *c, const GLfloat *n, const GLfloat *v); +GLAPI void APIENTRY glTexCoord4fColor4fNormal3fVertex4fSUN (GLfloat s, GLfloat t, GLfloat p, GLfloat q, GLfloat r, GLfloat g, GLfloat b, GLfloat a, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +GLAPI void APIENTRY glTexCoord4fColor4fNormal3fVertex4fvSUN (const GLfloat *tc, const GLfloat *c, const GLfloat *n, const GLfloat *v); +GLAPI void APIENTRY glReplacementCodeuiVertex3fSUN (GLuint rc, GLfloat x, GLfloat y, GLfloat z); +GLAPI void APIENTRY glReplacementCodeuiVertex3fvSUN (const GLuint *rc, const GLfloat *v); +GLAPI void APIENTRY glReplacementCodeuiColor4ubVertex3fSUN (GLuint rc, GLubyte r, GLubyte g, GLubyte b, GLubyte a, GLfloat x, GLfloat y, GLfloat z); +GLAPI void APIENTRY glReplacementCodeuiColor4ubVertex3fvSUN (const GLuint *rc, const GLubyte *c, const GLfloat *v); +GLAPI void APIENTRY glReplacementCodeuiColor3fVertex3fSUN (GLuint rc, GLfloat r, GLfloat g, GLfloat b, GLfloat x, GLfloat y, GLfloat z); +GLAPI void APIENTRY glReplacementCodeuiColor3fVertex3fvSUN (const GLuint *rc, const GLfloat *c, const GLfloat *v); +GLAPI void APIENTRY glReplacementCodeuiNormal3fVertex3fSUN (GLuint rc, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z); +GLAPI void APIENTRY glReplacementCodeuiNormal3fVertex3fvSUN (const GLuint *rc, const GLfloat *n, const GLfloat *v); +GLAPI void APIENTRY glReplacementCodeuiColor4fNormal3fVertex3fSUN (GLuint rc, GLfloat r, GLfloat g, GLfloat b, GLfloat a, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z); +GLAPI void APIENTRY glReplacementCodeuiColor4fNormal3fVertex3fvSUN (const GLuint *rc, const GLfloat *c, const GLfloat *n, const GLfloat *v); +GLAPI void APIENTRY glReplacementCodeuiTexCoord2fVertex3fSUN (GLuint rc, GLfloat s, GLfloat t, GLfloat x, GLfloat y, GLfloat z); +GLAPI void APIENTRY glReplacementCodeuiTexCoord2fVertex3fvSUN (const GLuint *rc, const GLfloat *tc, const GLfloat *v); +GLAPI void APIENTRY glReplacementCodeuiTexCoord2fNormal3fVertex3fSUN (GLuint rc, GLfloat s, GLfloat t, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z); +GLAPI void APIENTRY glReplacementCodeuiTexCoord2fNormal3fVertex3fvSUN (const GLuint *rc, const GLfloat *tc, const GLfloat *n, const GLfloat *v); +GLAPI void APIENTRY glReplacementCodeuiTexCoord2fColor4fNormal3fVertex3fSUN (GLuint rc, GLfloat s, GLfloat t, GLfloat r, GLfloat g, GLfloat b, GLfloat a, GLfloat nx, GLfloat ny, GLfloat nz, GLfloat x, GLfloat y, GLfloat z); +GLAPI void APIENTRY glReplacementCodeuiTexCoord2fColor4fNormal3fVertex3fvSUN (const GLuint *rc, const GLfloat *tc, const GLfloat *c, const GLfloat *n, const GLfloat *v); +#endif +#endif /* GL_SUN_vertex */ + +#ifndef GL_WIN_phong_shading +#define GL_WIN_phong_shading 1 +#define GL_PHONG_WIN 0x80EA +#define GL_PHONG_HINT_WIN 0x80EB +#endif /* GL_WIN_phong_shading */ + +#ifndef GL_WIN_specular_fog +#define GL_WIN_specular_fog 1 +#define GL_FOG_SPECULAR_TEXTURE_WIN 0x80EC +#endif /* GL_WIN_specular_fog */ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/Samples/3rdParty/glext/GL/wglext.h b/Samples/3rdParty/glext/GL/wglext.h new file mode 100755 index 0000000..ef9110f --- /dev/null +++ b/Samples/3rdParty/glext/GL/wglext.h @@ -0,0 +1,840 @@ +#ifndef __wglext_h_ +#define __wglext_h_ 1 + +#ifdef __cplusplus +extern "C" { +#endif + +/* +** Copyright (c) 2013-2014 The Khronos Group Inc. +** +** Permission is hereby granted, free of charge, to any person obtaining a +** copy of this software and/or associated documentation files (the +** "Materials"), to deal in the Materials without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Materials, and to +** permit persons to whom the Materials are furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be included +** in all copies or substantial portions of the Materials. +** +** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. +*/ +/* +** This header is generated from the Khronos OpenGL / OpenGL ES XML +** API Registry. The current version of the Registry, generator scripts +** used to make the header, and the header can be found at +** http://www.opengl.org/registry/ +** +** Khronos $Revision: 30969 $ on $Date: 2015-04-29 00:32:44 -0400 (Wed, 29 Apr 2015) $ +*/ + +#if defined(_WIN32) && !defined(APIENTRY) && !defined(__CYGWIN__) && !defined(__SCITECH_SNAP__) +#define WIN32_LEAN_AND_MEAN 1 +#include +#endif + +#define WGL_WGLEXT_VERSION 20150428 + +/* Generated C header for: + * API: wgl + * Versions considered: .* + * Versions emitted: _nomatch_^ + * Default extensions included: wgl + * Additional extensions included: _nomatch_^ + * Extensions removed: _nomatch_^ + */ + +#ifndef WGL_ARB_buffer_region +#define WGL_ARB_buffer_region 1 +#define WGL_FRONT_COLOR_BUFFER_BIT_ARB 0x00000001 +#define WGL_BACK_COLOR_BUFFER_BIT_ARB 0x00000002 +#define WGL_DEPTH_BUFFER_BIT_ARB 0x00000004 +#define WGL_STENCIL_BUFFER_BIT_ARB 0x00000008 +typedef HANDLE (WINAPI * PFNWGLCREATEBUFFERREGIONARBPROC) (HDC hDC, int iLayerPlane, UINT uType); +typedef VOID (WINAPI * PFNWGLDELETEBUFFERREGIONARBPROC) (HANDLE hRegion); +typedef BOOL (WINAPI * PFNWGLSAVEBUFFERREGIONARBPROC) (HANDLE hRegion, int x, int y, int width, int height); +typedef BOOL (WINAPI * PFNWGLRESTOREBUFFERREGIONARBPROC) (HANDLE hRegion, int x, int y, int width, int height, int xSrc, int ySrc); +#ifdef WGL_WGLEXT_PROTOTYPES +HANDLE WINAPI wglCreateBufferRegionARB (HDC hDC, int iLayerPlane, UINT uType); +VOID WINAPI wglDeleteBufferRegionARB (HANDLE hRegion); +BOOL WINAPI wglSaveBufferRegionARB (HANDLE hRegion, int x, int y, int width, int height); +BOOL WINAPI wglRestoreBufferRegionARB (HANDLE hRegion, int x, int y, int width, int height, int xSrc, int ySrc); +#endif +#endif /* WGL_ARB_buffer_region */ + +#ifndef WGL_ARB_context_flush_control +#define WGL_ARB_context_flush_control 1 +#define WGL_CONTEXT_RELEASE_BEHAVIOR_ARB 0x2097 +#define WGL_CONTEXT_RELEASE_BEHAVIOR_NONE_ARB 0 +#define WGL_CONTEXT_RELEASE_BEHAVIOR_FLUSH_ARB 0x2098 +#endif /* WGL_ARB_context_flush_control */ + +#ifndef WGL_ARB_create_context +#define WGL_ARB_create_context 1 +#define WGL_CONTEXT_DEBUG_BIT_ARB 0x00000001 +#define WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB 0x00000002 +#define WGL_CONTEXT_MAJOR_VERSION_ARB 0x2091 +#define WGL_CONTEXT_MINOR_VERSION_ARB 0x2092 +#define WGL_CONTEXT_LAYER_PLANE_ARB 0x2093 +#define WGL_CONTEXT_FLAGS_ARB 0x2094 +#define ERROR_INVALID_VERSION_ARB 0x2095 +typedef HGLRC (WINAPI * PFNWGLCREATECONTEXTATTRIBSARBPROC) (HDC hDC, HGLRC hShareContext, const int *attribList); +#ifdef WGL_WGLEXT_PROTOTYPES +HGLRC WINAPI wglCreateContextAttribsARB (HDC hDC, HGLRC hShareContext, const int *attribList); +#endif +#endif /* WGL_ARB_create_context */ + +#ifndef WGL_ARB_create_context_profile +#define WGL_ARB_create_context_profile 1 +#define WGL_CONTEXT_PROFILE_MASK_ARB 0x9126 +#define WGL_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001 +#define WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB 0x00000002 +#define ERROR_INVALID_PROFILE_ARB 0x2096 +#endif /* WGL_ARB_create_context_profile */ + +#ifndef WGL_ARB_create_context_robustness +#define WGL_ARB_create_context_robustness 1 +#define WGL_CONTEXT_ROBUST_ACCESS_BIT_ARB 0x00000004 +#define WGL_LOSE_CONTEXT_ON_RESET_ARB 0x8252 +#define WGL_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB 0x8256 +#define WGL_NO_RESET_NOTIFICATION_ARB 0x8261 +#endif /* WGL_ARB_create_context_robustness */ + +#ifndef WGL_ARB_extensions_string +#define WGL_ARB_extensions_string 1 +typedef const char *(WINAPI * PFNWGLGETEXTENSIONSSTRINGARBPROC) (HDC hdc); +#ifdef WGL_WGLEXT_PROTOTYPES +const char *WINAPI wglGetExtensionsStringARB (HDC hdc); +#endif +#endif /* WGL_ARB_extensions_string */ + +#ifndef WGL_ARB_framebuffer_sRGB +#define WGL_ARB_framebuffer_sRGB 1 +#define WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB 0x20A9 +#endif /* WGL_ARB_framebuffer_sRGB */ + +#ifndef WGL_ARB_make_current_read +#define WGL_ARB_make_current_read 1 +#define ERROR_INVALID_PIXEL_TYPE_ARB 0x2043 +#define ERROR_INCOMPATIBLE_DEVICE_CONTEXTS_ARB 0x2054 +typedef BOOL (WINAPI * PFNWGLMAKECONTEXTCURRENTARBPROC) (HDC hDrawDC, HDC hReadDC, HGLRC hglrc); +typedef HDC (WINAPI * PFNWGLGETCURRENTREADDCARBPROC) (void); +#ifdef WGL_WGLEXT_PROTOTYPES +BOOL WINAPI wglMakeContextCurrentARB (HDC hDrawDC, HDC hReadDC, HGLRC hglrc); +HDC WINAPI wglGetCurrentReadDCARB (void); +#endif +#endif /* WGL_ARB_make_current_read */ + +#ifndef WGL_ARB_multisample +#define WGL_ARB_multisample 1 +#define WGL_SAMPLE_BUFFERS_ARB 0x2041 +#define WGL_SAMPLES_ARB 0x2042 +#endif /* WGL_ARB_multisample */ + +#ifndef WGL_ARB_pbuffer +#define WGL_ARB_pbuffer 1 +DECLARE_HANDLE(HPBUFFERARB); +#define WGL_DRAW_TO_PBUFFER_ARB 0x202D +#define WGL_MAX_PBUFFER_PIXELS_ARB 0x202E +#define WGL_MAX_PBUFFER_WIDTH_ARB 0x202F +#define WGL_MAX_PBUFFER_HEIGHT_ARB 0x2030 +#define WGL_PBUFFER_LARGEST_ARB 0x2033 +#define WGL_PBUFFER_WIDTH_ARB 0x2034 +#define WGL_PBUFFER_HEIGHT_ARB 0x2035 +#define WGL_PBUFFER_LOST_ARB 0x2036 +typedef HPBUFFERARB (WINAPI * PFNWGLCREATEPBUFFERARBPROC) (HDC hDC, int iPixelFormat, int iWidth, int iHeight, const int *piAttribList); +typedef HDC (WINAPI * PFNWGLGETPBUFFERDCARBPROC) (HPBUFFERARB hPbuffer); +typedef int (WINAPI * PFNWGLRELEASEPBUFFERDCARBPROC) (HPBUFFERARB hPbuffer, HDC hDC); +typedef BOOL (WINAPI * PFNWGLDESTROYPBUFFERARBPROC) (HPBUFFERARB hPbuffer); +typedef BOOL (WINAPI * PFNWGLQUERYPBUFFERARBPROC) (HPBUFFERARB hPbuffer, int iAttribute, int *piValue); +#ifdef WGL_WGLEXT_PROTOTYPES +HPBUFFERARB WINAPI wglCreatePbufferARB (HDC hDC, int iPixelFormat, int iWidth, int iHeight, const int *piAttribList); +HDC WINAPI wglGetPbufferDCARB (HPBUFFERARB hPbuffer); +int WINAPI wglReleasePbufferDCARB (HPBUFFERARB hPbuffer, HDC hDC); +BOOL WINAPI wglDestroyPbufferARB (HPBUFFERARB hPbuffer); +BOOL WINAPI wglQueryPbufferARB (HPBUFFERARB hPbuffer, int iAttribute, int *piValue); +#endif +#endif /* WGL_ARB_pbuffer */ + +#ifndef WGL_ARB_pixel_format +#define WGL_ARB_pixel_format 1 +#define WGL_NUMBER_PIXEL_FORMATS_ARB 0x2000 +#define WGL_DRAW_TO_WINDOW_ARB 0x2001 +#define WGL_DRAW_TO_BITMAP_ARB 0x2002 +#define WGL_ACCELERATION_ARB 0x2003 +#define WGL_NEED_PALETTE_ARB 0x2004 +#define WGL_NEED_SYSTEM_PALETTE_ARB 0x2005 +#define WGL_SWAP_LAYER_BUFFERS_ARB 0x2006 +#define WGL_SWAP_METHOD_ARB 0x2007 +#define WGL_NUMBER_OVERLAYS_ARB 0x2008 +#define WGL_NUMBER_UNDERLAYS_ARB 0x2009 +#define WGL_TRANSPARENT_ARB 0x200A +#define WGL_TRANSPARENT_RED_VALUE_ARB 0x2037 +#define WGL_TRANSPARENT_GREEN_VALUE_ARB 0x2038 +#define WGL_TRANSPARENT_BLUE_VALUE_ARB 0x2039 +#define WGL_TRANSPARENT_ALPHA_VALUE_ARB 0x203A +#define WGL_TRANSPARENT_INDEX_VALUE_ARB 0x203B +#define WGL_SHARE_DEPTH_ARB 0x200C +#define WGL_SHARE_STENCIL_ARB 0x200D +#define WGL_SHARE_ACCUM_ARB 0x200E +#define WGL_SUPPORT_GDI_ARB 0x200F +#define WGL_SUPPORT_OPENGL_ARB 0x2010 +#define WGL_DOUBLE_BUFFER_ARB 0x2011 +#define WGL_STEREO_ARB 0x2012 +#define WGL_PIXEL_TYPE_ARB 0x2013 +#define WGL_COLOR_BITS_ARB 0x2014 +#define WGL_RED_BITS_ARB 0x2015 +#define WGL_RED_SHIFT_ARB 0x2016 +#define WGL_GREEN_BITS_ARB 0x2017 +#define WGL_GREEN_SHIFT_ARB 0x2018 +#define WGL_BLUE_BITS_ARB 0x2019 +#define WGL_BLUE_SHIFT_ARB 0x201A +#define WGL_ALPHA_BITS_ARB 0x201B +#define WGL_ALPHA_SHIFT_ARB 0x201C +#define WGL_ACCUM_BITS_ARB 0x201D +#define WGL_ACCUM_RED_BITS_ARB 0x201E +#define WGL_ACCUM_GREEN_BITS_ARB 0x201F +#define WGL_ACCUM_BLUE_BITS_ARB 0x2020 +#define WGL_ACCUM_ALPHA_BITS_ARB 0x2021 +#define WGL_DEPTH_BITS_ARB 0x2022 +#define WGL_STENCIL_BITS_ARB 0x2023 +#define WGL_AUX_BUFFERS_ARB 0x2024 +#define WGL_NO_ACCELERATION_ARB 0x2025 +#define WGL_GENERIC_ACCELERATION_ARB 0x2026 +#define WGL_FULL_ACCELERATION_ARB 0x2027 +#define WGL_SWAP_EXCHANGE_ARB 0x2028 +#define WGL_SWAP_COPY_ARB 0x2029 +#define WGL_SWAP_UNDEFINED_ARB 0x202A +#define WGL_TYPE_RGBA_ARB 0x202B +#define WGL_TYPE_COLORINDEX_ARB 0x202C +typedef BOOL (WINAPI * PFNWGLGETPIXELFORMATATTRIBIVARBPROC) (HDC hdc, int iPixelFormat, int iLayerPlane, UINT nAttributes, const int *piAttributes, int *piValues); +typedef BOOL (WINAPI * PFNWGLGETPIXELFORMATATTRIBFVARBPROC) (HDC hdc, int iPixelFormat, int iLayerPlane, UINT nAttributes, const int *piAttributes, FLOAT *pfValues); +typedef BOOL (WINAPI * PFNWGLCHOOSEPIXELFORMATARBPROC) (HDC hdc, const int *piAttribIList, const FLOAT *pfAttribFList, UINT nMaxFormats, int *piFormats, UINT *nNumFormats); +#ifdef WGL_WGLEXT_PROTOTYPES +BOOL WINAPI wglGetPixelFormatAttribivARB (HDC hdc, int iPixelFormat, int iLayerPlane, UINT nAttributes, const int *piAttributes, int *piValues); +BOOL WINAPI wglGetPixelFormatAttribfvARB (HDC hdc, int iPixelFormat, int iLayerPlane, UINT nAttributes, const int *piAttributes, FLOAT *pfValues); +BOOL WINAPI wglChoosePixelFormatARB (HDC hdc, const int *piAttribIList, const FLOAT *pfAttribFList, UINT nMaxFormats, int *piFormats, UINT *nNumFormats); +#endif +#endif /* WGL_ARB_pixel_format */ + +#ifndef WGL_ARB_pixel_format_float +#define WGL_ARB_pixel_format_float 1 +#define WGL_TYPE_RGBA_FLOAT_ARB 0x21A0 +#endif /* WGL_ARB_pixel_format_float */ + +#ifndef WGL_ARB_render_texture +#define WGL_ARB_render_texture 1 +#define WGL_BIND_TO_TEXTURE_RGB_ARB 0x2070 +#define WGL_BIND_TO_TEXTURE_RGBA_ARB 0x2071 +#define WGL_TEXTURE_FORMAT_ARB 0x2072 +#define WGL_TEXTURE_TARGET_ARB 0x2073 +#define WGL_MIPMAP_TEXTURE_ARB 0x2074 +#define WGL_TEXTURE_RGB_ARB 0x2075 +#define WGL_TEXTURE_RGBA_ARB 0x2076 +#define WGL_NO_TEXTURE_ARB 0x2077 +#define WGL_TEXTURE_CUBE_MAP_ARB 0x2078 +#define WGL_TEXTURE_1D_ARB 0x2079 +#define WGL_TEXTURE_2D_ARB 0x207A +#define WGL_MIPMAP_LEVEL_ARB 0x207B +#define WGL_CUBE_MAP_FACE_ARB 0x207C +#define WGL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB 0x207D +#define WGL_TEXTURE_CUBE_MAP_NEGATIVE_X_ARB 0x207E +#define WGL_TEXTURE_CUBE_MAP_POSITIVE_Y_ARB 0x207F +#define WGL_TEXTURE_CUBE_MAP_NEGATIVE_Y_ARB 0x2080 +#define WGL_TEXTURE_CUBE_MAP_POSITIVE_Z_ARB 0x2081 +#define WGL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB 0x2082 +#define WGL_FRONT_LEFT_ARB 0x2083 +#define WGL_FRONT_RIGHT_ARB 0x2084 +#define WGL_BACK_LEFT_ARB 0x2085 +#define WGL_BACK_RIGHT_ARB 0x2086 +#define WGL_AUX0_ARB 0x2087 +#define WGL_AUX1_ARB 0x2088 +#define WGL_AUX2_ARB 0x2089 +#define WGL_AUX3_ARB 0x208A +#define WGL_AUX4_ARB 0x208B +#define WGL_AUX5_ARB 0x208C +#define WGL_AUX6_ARB 0x208D +#define WGL_AUX7_ARB 0x208E +#define WGL_AUX8_ARB 0x208F +#define WGL_AUX9_ARB 0x2090 +typedef BOOL (WINAPI * PFNWGLBINDTEXIMAGEARBPROC) (HPBUFFERARB hPbuffer, int iBuffer); +typedef BOOL (WINAPI * PFNWGLRELEASETEXIMAGEARBPROC) (HPBUFFERARB hPbuffer, int iBuffer); +typedef BOOL (WINAPI * PFNWGLSETPBUFFERATTRIBARBPROC) (HPBUFFERARB hPbuffer, const int *piAttribList); +#ifdef WGL_WGLEXT_PROTOTYPES +BOOL WINAPI wglBindTexImageARB (HPBUFFERARB hPbuffer, int iBuffer); +BOOL WINAPI wglReleaseTexImageARB (HPBUFFERARB hPbuffer, int iBuffer); +BOOL WINAPI wglSetPbufferAttribARB (HPBUFFERARB hPbuffer, const int *piAttribList); +#endif +#endif /* WGL_ARB_render_texture */ + +#ifndef WGL_ARB_robustness_application_isolation +#define WGL_ARB_robustness_application_isolation 1 +#define WGL_CONTEXT_RESET_ISOLATION_BIT_ARB 0x00000008 +#endif /* WGL_ARB_robustness_application_isolation */ + +#ifndef WGL_ARB_robustness_share_group_isolation +#define WGL_ARB_robustness_share_group_isolation 1 +#endif /* WGL_ARB_robustness_share_group_isolation */ + +#ifndef WGL_3DFX_multisample +#define WGL_3DFX_multisample 1 +#define WGL_SAMPLE_BUFFERS_3DFX 0x2060 +#define WGL_SAMPLES_3DFX 0x2061 +#endif /* WGL_3DFX_multisample */ + +#ifndef WGL_3DL_stereo_control +#define WGL_3DL_stereo_control 1 +#define WGL_STEREO_EMITTER_ENABLE_3DL 0x2055 +#define WGL_STEREO_EMITTER_DISABLE_3DL 0x2056 +#define WGL_STEREO_POLARITY_NORMAL_3DL 0x2057 +#define WGL_STEREO_POLARITY_INVERT_3DL 0x2058 +typedef BOOL (WINAPI * PFNWGLSETSTEREOEMITTERSTATE3DLPROC) (HDC hDC, UINT uState); +#ifdef WGL_WGLEXT_PROTOTYPES +BOOL WINAPI wglSetStereoEmitterState3DL (HDC hDC, UINT uState); +#endif +#endif /* WGL_3DL_stereo_control */ + +#ifndef WGL_AMD_gpu_association +#define WGL_AMD_gpu_association 1 +#define WGL_GPU_VENDOR_AMD 0x1F00 +#define WGL_GPU_RENDERER_STRING_AMD 0x1F01 +#define WGL_GPU_OPENGL_VERSION_STRING_AMD 0x1F02 +#define WGL_GPU_FASTEST_TARGET_GPUS_AMD 0x21A2 +#define WGL_GPU_RAM_AMD 0x21A3 +#define WGL_GPU_CLOCK_AMD 0x21A4 +#define WGL_GPU_NUM_PIPES_AMD 0x21A5 +#define WGL_GPU_NUM_SIMD_AMD 0x21A6 +#define WGL_GPU_NUM_RB_AMD 0x21A7 +#define WGL_GPU_NUM_SPI_AMD 0x21A8 +typedef UINT (WINAPI * PFNWGLGETGPUIDSAMDPROC) (UINT maxCount, UINT *ids); +typedef INT (WINAPI * PFNWGLGETGPUINFOAMDPROC) (UINT id, int property, GLenum dataType, UINT size, void *data); +typedef UINT (WINAPI * PFNWGLGETCONTEXTGPUIDAMDPROC) (HGLRC hglrc); +typedef HGLRC (WINAPI * PFNWGLCREATEASSOCIATEDCONTEXTAMDPROC) (UINT id); +typedef HGLRC (WINAPI * PFNWGLCREATEASSOCIATEDCONTEXTATTRIBSAMDPROC) (UINT id, HGLRC hShareContext, const int *attribList); +typedef BOOL (WINAPI * PFNWGLDELETEASSOCIATEDCONTEXTAMDPROC) (HGLRC hglrc); +typedef BOOL (WINAPI * PFNWGLMAKEASSOCIATEDCONTEXTCURRENTAMDPROC) (HGLRC hglrc); +typedef HGLRC (WINAPI * PFNWGLGETCURRENTASSOCIATEDCONTEXTAMDPROC) (void); +typedef VOID (WINAPI * PFNWGLBLITCONTEXTFRAMEBUFFERAMDPROC) (HGLRC dstCtx, GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter); +#ifdef WGL_WGLEXT_PROTOTYPES +UINT WINAPI wglGetGPUIDsAMD (UINT maxCount, UINT *ids); +INT WINAPI wglGetGPUInfoAMD (UINT id, int property, GLenum dataType, UINT size, void *data); +UINT WINAPI wglGetContextGPUIDAMD (HGLRC hglrc); +HGLRC WINAPI wglCreateAssociatedContextAMD (UINT id); +HGLRC WINAPI wglCreateAssociatedContextAttribsAMD (UINT id, HGLRC hShareContext, const int *attribList); +BOOL WINAPI wglDeleteAssociatedContextAMD (HGLRC hglrc); +BOOL WINAPI wglMakeAssociatedContextCurrentAMD (HGLRC hglrc); +HGLRC WINAPI wglGetCurrentAssociatedContextAMD (void); +VOID WINAPI wglBlitContextFramebufferAMD (HGLRC dstCtx, GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter); +#endif +#endif /* WGL_AMD_gpu_association */ + +#ifndef WGL_ATI_pixel_format_float +#define WGL_ATI_pixel_format_float 1 +#define WGL_TYPE_RGBA_FLOAT_ATI 0x21A0 +#endif /* WGL_ATI_pixel_format_float */ + +#ifndef WGL_EXT_create_context_es2_profile +#define WGL_EXT_create_context_es2_profile 1 +#define WGL_CONTEXT_ES2_PROFILE_BIT_EXT 0x00000004 +#endif /* WGL_EXT_create_context_es2_profile */ + +#ifndef WGL_EXT_create_context_es_profile +#define WGL_EXT_create_context_es_profile 1 +#define WGL_CONTEXT_ES_PROFILE_BIT_EXT 0x00000004 +#endif /* WGL_EXT_create_context_es_profile */ + +#ifndef WGL_EXT_depth_float +#define WGL_EXT_depth_float 1 +#define WGL_DEPTH_FLOAT_EXT 0x2040 +#endif /* WGL_EXT_depth_float */ + +#ifndef WGL_EXT_display_color_table +#define WGL_EXT_display_color_table 1 +typedef GLboolean (WINAPI * PFNWGLCREATEDISPLAYCOLORTABLEEXTPROC) (GLushort id); +typedef GLboolean (WINAPI * PFNWGLLOADDISPLAYCOLORTABLEEXTPROC) (const GLushort *table, GLuint length); +typedef GLboolean (WINAPI * PFNWGLBINDDISPLAYCOLORTABLEEXTPROC) (GLushort id); +typedef VOID (WINAPI * PFNWGLDESTROYDISPLAYCOLORTABLEEXTPROC) (GLushort id); +#ifdef WGL_WGLEXT_PROTOTYPES +GLboolean WINAPI wglCreateDisplayColorTableEXT (GLushort id); +GLboolean WINAPI wglLoadDisplayColorTableEXT (const GLushort *table, GLuint length); +GLboolean WINAPI wglBindDisplayColorTableEXT (GLushort id); +VOID WINAPI wglDestroyDisplayColorTableEXT (GLushort id); +#endif +#endif /* WGL_EXT_display_color_table */ + +#ifndef WGL_EXT_extensions_string +#define WGL_EXT_extensions_string 1 +typedef const char *(WINAPI * PFNWGLGETEXTENSIONSSTRINGEXTPROC) (void); +#ifdef WGL_WGLEXT_PROTOTYPES +const char *WINAPI wglGetExtensionsStringEXT (void); +#endif +#endif /* WGL_EXT_extensions_string */ + +#ifndef WGL_EXT_framebuffer_sRGB +#define WGL_EXT_framebuffer_sRGB 1 +#define WGL_FRAMEBUFFER_SRGB_CAPABLE_EXT 0x20A9 +#endif /* WGL_EXT_framebuffer_sRGB */ + +#ifndef WGL_EXT_make_current_read +#define WGL_EXT_make_current_read 1 +#define ERROR_INVALID_PIXEL_TYPE_EXT 0x2043 +typedef BOOL (WINAPI * PFNWGLMAKECONTEXTCURRENTEXTPROC) (HDC hDrawDC, HDC hReadDC, HGLRC hglrc); +typedef HDC (WINAPI * PFNWGLGETCURRENTREADDCEXTPROC) (void); +#ifdef WGL_WGLEXT_PROTOTYPES +BOOL WINAPI wglMakeContextCurrentEXT (HDC hDrawDC, HDC hReadDC, HGLRC hglrc); +HDC WINAPI wglGetCurrentReadDCEXT (void); +#endif +#endif /* WGL_EXT_make_current_read */ + +#ifndef WGL_EXT_multisample +#define WGL_EXT_multisample 1 +#define WGL_SAMPLE_BUFFERS_EXT 0x2041 +#define WGL_SAMPLES_EXT 0x2042 +#endif /* WGL_EXT_multisample */ + +#ifndef WGL_EXT_pbuffer +#define WGL_EXT_pbuffer 1 +DECLARE_HANDLE(HPBUFFEREXT); +#define WGL_DRAW_TO_PBUFFER_EXT 0x202D +#define WGL_MAX_PBUFFER_PIXELS_EXT 0x202E +#define WGL_MAX_PBUFFER_WIDTH_EXT 0x202F +#define WGL_MAX_PBUFFER_HEIGHT_EXT 0x2030 +#define WGL_OPTIMAL_PBUFFER_WIDTH_EXT 0x2031 +#define WGL_OPTIMAL_PBUFFER_HEIGHT_EXT 0x2032 +#define WGL_PBUFFER_LARGEST_EXT 0x2033 +#define WGL_PBUFFER_WIDTH_EXT 0x2034 +#define WGL_PBUFFER_HEIGHT_EXT 0x2035 +typedef HPBUFFEREXT (WINAPI * PFNWGLCREATEPBUFFEREXTPROC) (HDC hDC, int iPixelFormat, int iWidth, int iHeight, const int *piAttribList); +typedef HDC (WINAPI * PFNWGLGETPBUFFERDCEXTPROC) (HPBUFFEREXT hPbuffer); +typedef int (WINAPI * PFNWGLRELEASEPBUFFERDCEXTPROC) (HPBUFFEREXT hPbuffer, HDC hDC); +typedef BOOL (WINAPI * PFNWGLDESTROYPBUFFEREXTPROC) (HPBUFFEREXT hPbuffer); +typedef BOOL (WINAPI * PFNWGLQUERYPBUFFEREXTPROC) (HPBUFFEREXT hPbuffer, int iAttribute, int *piValue); +#ifdef WGL_WGLEXT_PROTOTYPES +HPBUFFEREXT WINAPI wglCreatePbufferEXT (HDC hDC, int iPixelFormat, int iWidth, int iHeight, const int *piAttribList); +HDC WINAPI wglGetPbufferDCEXT (HPBUFFEREXT hPbuffer); +int WINAPI wglReleasePbufferDCEXT (HPBUFFEREXT hPbuffer, HDC hDC); +BOOL WINAPI wglDestroyPbufferEXT (HPBUFFEREXT hPbuffer); +BOOL WINAPI wglQueryPbufferEXT (HPBUFFEREXT hPbuffer, int iAttribute, int *piValue); +#endif +#endif /* WGL_EXT_pbuffer */ + +#ifndef WGL_EXT_pixel_format +#define WGL_EXT_pixel_format 1 +#define WGL_NUMBER_PIXEL_FORMATS_EXT 0x2000 +#define WGL_DRAW_TO_WINDOW_EXT 0x2001 +#define WGL_DRAW_TO_BITMAP_EXT 0x2002 +#define WGL_ACCELERATION_EXT 0x2003 +#define WGL_NEED_PALETTE_EXT 0x2004 +#define WGL_NEED_SYSTEM_PALETTE_EXT 0x2005 +#define WGL_SWAP_LAYER_BUFFERS_EXT 0x2006 +#define WGL_SWAP_METHOD_EXT 0x2007 +#define WGL_NUMBER_OVERLAYS_EXT 0x2008 +#define WGL_NUMBER_UNDERLAYS_EXT 0x2009 +#define WGL_TRANSPARENT_EXT 0x200A +#define WGL_TRANSPARENT_VALUE_EXT 0x200B +#define WGL_SHARE_DEPTH_EXT 0x200C +#define WGL_SHARE_STENCIL_EXT 0x200D +#define WGL_SHARE_ACCUM_EXT 0x200E +#define WGL_SUPPORT_GDI_EXT 0x200F +#define WGL_SUPPORT_OPENGL_EXT 0x2010 +#define WGL_DOUBLE_BUFFER_EXT 0x2011 +#define WGL_STEREO_EXT 0x2012 +#define WGL_PIXEL_TYPE_EXT 0x2013 +#define WGL_COLOR_BITS_EXT 0x2014 +#define WGL_RED_BITS_EXT 0x2015 +#define WGL_RED_SHIFT_EXT 0x2016 +#define WGL_GREEN_BITS_EXT 0x2017 +#define WGL_GREEN_SHIFT_EXT 0x2018 +#define WGL_BLUE_BITS_EXT 0x2019 +#define WGL_BLUE_SHIFT_EXT 0x201A +#define WGL_ALPHA_BITS_EXT 0x201B +#define WGL_ALPHA_SHIFT_EXT 0x201C +#define WGL_ACCUM_BITS_EXT 0x201D +#define WGL_ACCUM_RED_BITS_EXT 0x201E +#define WGL_ACCUM_GREEN_BITS_EXT 0x201F +#define WGL_ACCUM_BLUE_BITS_EXT 0x2020 +#define WGL_ACCUM_ALPHA_BITS_EXT 0x2021 +#define WGL_DEPTH_BITS_EXT 0x2022 +#define WGL_STENCIL_BITS_EXT 0x2023 +#define WGL_AUX_BUFFERS_EXT 0x2024 +#define WGL_NO_ACCELERATION_EXT 0x2025 +#define WGL_GENERIC_ACCELERATION_EXT 0x2026 +#define WGL_FULL_ACCELERATION_EXT 0x2027 +#define WGL_SWAP_EXCHANGE_EXT 0x2028 +#define WGL_SWAP_COPY_EXT 0x2029 +#define WGL_SWAP_UNDEFINED_EXT 0x202A +#define WGL_TYPE_RGBA_EXT 0x202B +#define WGL_TYPE_COLORINDEX_EXT 0x202C +typedef BOOL (WINAPI * PFNWGLGETPIXELFORMATATTRIBIVEXTPROC) (HDC hdc, int iPixelFormat, int iLayerPlane, UINT nAttributes, int *piAttributes, int *piValues); +typedef BOOL (WINAPI * PFNWGLGETPIXELFORMATATTRIBFVEXTPROC) (HDC hdc, int iPixelFormat, int iLayerPlane, UINT nAttributes, int *piAttributes, FLOAT *pfValues); +typedef BOOL (WINAPI * PFNWGLCHOOSEPIXELFORMATEXTPROC) (HDC hdc, const int *piAttribIList, const FLOAT *pfAttribFList, UINT nMaxFormats, int *piFormats, UINT *nNumFormats); +#ifdef WGL_WGLEXT_PROTOTYPES +BOOL WINAPI wglGetPixelFormatAttribivEXT (HDC hdc, int iPixelFormat, int iLayerPlane, UINT nAttributes, int *piAttributes, int *piValues); +BOOL WINAPI wglGetPixelFormatAttribfvEXT (HDC hdc, int iPixelFormat, int iLayerPlane, UINT nAttributes, int *piAttributes, FLOAT *pfValues); +BOOL WINAPI wglChoosePixelFormatEXT (HDC hdc, const int *piAttribIList, const FLOAT *pfAttribFList, UINT nMaxFormats, int *piFormats, UINT *nNumFormats); +#endif +#endif /* WGL_EXT_pixel_format */ + +#ifndef WGL_EXT_pixel_format_packed_float +#define WGL_EXT_pixel_format_packed_float 1 +#define WGL_TYPE_RGBA_UNSIGNED_FLOAT_EXT 0x20A8 +#endif /* WGL_EXT_pixel_format_packed_float */ + +#ifndef WGL_EXT_swap_control +#define WGL_EXT_swap_control 1 +typedef BOOL (WINAPI * PFNWGLSWAPINTERVALEXTPROC) (int interval); +typedef int (WINAPI * PFNWGLGETSWAPINTERVALEXTPROC) (void); +#ifdef WGL_WGLEXT_PROTOTYPES +BOOL WINAPI wglSwapIntervalEXT (int interval); +int WINAPI wglGetSwapIntervalEXT (void); +#endif +#endif /* WGL_EXT_swap_control */ + +#ifndef WGL_EXT_swap_control_tear +#define WGL_EXT_swap_control_tear 1 +#endif /* WGL_EXT_swap_control_tear */ + +#ifndef WGL_I3D_digital_video_control +#define WGL_I3D_digital_video_control 1 +#define WGL_DIGITAL_VIDEO_CURSOR_ALPHA_FRAMEBUFFER_I3D 0x2050 +#define WGL_DIGITAL_VIDEO_CURSOR_ALPHA_VALUE_I3D 0x2051 +#define WGL_DIGITAL_VIDEO_CURSOR_INCLUDED_I3D 0x2052 +#define WGL_DIGITAL_VIDEO_GAMMA_CORRECTED_I3D 0x2053 +typedef BOOL (WINAPI * PFNWGLGETDIGITALVIDEOPARAMETERSI3DPROC) (HDC hDC, int iAttribute, int *piValue); +typedef BOOL (WINAPI * PFNWGLSETDIGITALVIDEOPARAMETERSI3DPROC) (HDC hDC, int iAttribute, const int *piValue); +#ifdef WGL_WGLEXT_PROTOTYPES +BOOL WINAPI wglGetDigitalVideoParametersI3D (HDC hDC, int iAttribute, int *piValue); +BOOL WINAPI wglSetDigitalVideoParametersI3D (HDC hDC, int iAttribute, const int *piValue); +#endif +#endif /* WGL_I3D_digital_video_control */ + +#ifndef WGL_I3D_gamma +#define WGL_I3D_gamma 1 +#define WGL_GAMMA_TABLE_SIZE_I3D 0x204E +#define WGL_GAMMA_EXCLUDE_DESKTOP_I3D 0x204F +typedef BOOL (WINAPI * PFNWGLGETGAMMATABLEPARAMETERSI3DPROC) (HDC hDC, int iAttribute, int *piValue); +typedef BOOL (WINAPI * PFNWGLSETGAMMATABLEPARAMETERSI3DPROC) (HDC hDC, int iAttribute, const int *piValue); +typedef BOOL (WINAPI * PFNWGLGETGAMMATABLEI3DPROC) (HDC hDC, int iEntries, USHORT *puRed, USHORT *puGreen, USHORT *puBlue); +typedef BOOL (WINAPI * PFNWGLSETGAMMATABLEI3DPROC) (HDC hDC, int iEntries, const USHORT *puRed, const USHORT *puGreen, const USHORT *puBlue); +#ifdef WGL_WGLEXT_PROTOTYPES +BOOL WINAPI wglGetGammaTableParametersI3D (HDC hDC, int iAttribute, int *piValue); +BOOL WINAPI wglSetGammaTableParametersI3D (HDC hDC, int iAttribute, const int *piValue); +BOOL WINAPI wglGetGammaTableI3D (HDC hDC, int iEntries, USHORT *puRed, USHORT *puGreen, USHORT *puBlue); +BOOL WINAPI wglSetGammaTableI3D (HDC hDC, int iEntries, const USHORT *puRed, const USHORT *puGreen, const USHORT *puBlue); +#endif +#endif /* WGL_I3D_gamma */ + +#ifndef WGL_I3D_genlock +#define WGL_I3D_genlock 1 +#define WGL_GENLOCK_SOURCE_MULTIVIEW_I3D 0x2044 +#define WGL_GENLOCK_SOURCE_EXTERNAL_SYNC_I3D 0x2045 +#define WGL_GENLOCK_SOURCE_EXTERNAL_FIELD_I3D 0x2046 +#define WGL_GENLOCK_SOURCE_EXTERNAL_TTL_I3D 0x2047 +#define WGL_GENLOCK_SOURCE_DIGITAL_SYNC_I3D 0x2048 +#define WGL_GENLOCK_SOURCE_DIGITAL_FIELD_I3D 0x2049 +#define WGL_GENLOCK_SOURCE_EDGE_FALLING_I3D 0x204A +#define WGL_GENLOCK_SOURCE_EDGE_RISING_I3D 0x204B +#define WGL_GENLOCK_SOURCE_EDGE_BOTH_I3D 0x204C +typedef BOOL (WINAPI * PFNWGLENABLEGENLOCKI3DPROC) (HDC hDC); +typedef BOOL (WINAPI * PFNWGLDISABLEGENLOCKI3DPROC) (HDC hDC); +typedef BOOL (WINAPI * PFNWGLISENABLEDGENLOCKI3DPROC) (HDC hDC, BOOL *pFlag); +typedef BOOL (WINAPI * PFNWGLGENLOCKSOURCEI3DPROC) (HDC hDC, UINT uSource); +typedef BOOL (WINAPI * PFNWGLGETGENLOCKSOURCEI3DPROC) (HDC hDC, UINT *uSource); +typedef BOOL (WINAPI * PFNWGLGENLOCKSOURCEEDGEI3DPROC) (HDC hDC, UINT uEdge); +typedef BOOL (WINAPI * PFNWGLGETGENLOCKSOURCEEDGEI3DPROC) (HDC hDC, UINT *uEdge); +typedef BOOL (WINAPI * PFNWGLGENLOCKSAMPLERATEI3DPROC) (HDC hDC, UINT uRate); +typedef BOOL (WINAPI * PFNWGLGETGENLOCKSAMPLERATEI3DPROC) (HDC hDC, UINT *uRate); +typedef BOOL (WINAPI * PFNWGLGENLOCKSOURCEDELAYI3DPROC) (HDC hDC, UINT uDelay); +typedef BOOL (WINAPI * PFNWGLGETGENLOCKSOURCEDELAYI3DPROC) (HDC hDC, UINT *uDelay); +typedef BOOL (WINAPI * PFNWGLQUERYGENLOCKMAXSOURCEDELAYI3DPROC) (HDC hDC, UINT *uMaxLineDelay, UINT *uMaxPixelDelay); +#ifdef WGL_WGLEXT_PROTOTYPES +BOOL WINAPI wglEnableGenlockI3D (HDC hDC); +BOOL WINAPI wglDisableGenlockI3D (HDC hDC); +BOOL WINAPI wglIsEnabledGenlockI3D (HDC hDC, BOOL *pFlag); +BOOL WINAPI wglGenlockSourceI3D (HDC hDC, UINT uSource); +BOOL WINAPI wglGetGenlockSourceI3D (HDC hDC, UINT *uSource); +BOOL WINAPI wglGenlockSourceEdgeI3D (HDC hDC, UINT uEdge); +BOOL WINAPI wglGetGenlockSourceEdgeI3D (HDC hDC, UINT *uEdge); +BOOL WINAPI wglGenlockSampleRateI3D (HDC hDC, UINT uRate); +BOOL WINAPI wglGetGenlockSampleRateI3D (HDC hDC, UINT *uRate); +BOOL WINAPI wglGenlockSourceDelayI3D (HDC hDC, UINT uDelay); +BOOL WINAPI wglGetGenlockSourceDelayI3D (HDC hDC, UINT *uDelay); +BOOL WINAPI wglQueryGenlockMaxSourceDelayI3D (HDC hDC, UINT *uMaxLineDelay, UINT *uMaxPixelDelay); +#endif +#endif /* WGL_I3D_genlock */ + +#ifndef WGL_I3D_image_buffer +#define WGL_I3D_image_buffer 1 +#define WGL_IMAGE_BUFFER_MIN_ACCESS_I3D 0x00000001 +#define WGL_IMAGE_BUFFER_LOCK_I3D 0x00000002 +typedef LPVOID (WINAPI * PFNWGLCREATEIMAGEBUFFERI3DPROC) (HDC hDC, DWORD dwSize, UINT uFlags); +typedef BOOL (WINAPI * PFNWGLDESTROYIMAGEBUFFERI3DPROC) (HDC hDC, LPVOID pAddress); +typedef BOOL (WINAPI * PFNWGLASSOCIATEIMAGEBUFFEREVENTSI3DPROC) (HDC hDC, const HANDLE *pEvent, const LPVOID *pAddress, const DWORD *pSize, UINT count); +typedef BOOL (WINAPI * PFNWGLRELEASEIMAGEBUFFEREVENTSI3DPROC) (HDC hDC, const LPVOID *pAddress, UINT count); +#ifdef WGL_WGLEXT_PROTOTYPES +LPVOID WINAPI wglCreateImageBufferI3D (HDC hDC, DWORD dwSize, UINT uFlags); +BOOL WINAPI wglDestroyImageBufferI3D (HDC hDC, LPVOID pAddress); +BOOL WINAPI wglAssociateImageBufferEventsI3D (HDC hDC, const HANDLE *pEvent, const LPVOID *pAddress, const DWORD *pSize, UINT count); +BOOL WINAPI wglReleaseImageBufferEventsI3D (HDC hDC, const LPVOID *pAddress, UINT count); +#endif +#endif /* WGL_I3D_image_buffer */ + +#ifndef WGL_I3D_swap_frame_lock +#define WGL_I3D_swap_frame_lock 1 +typedef BOOL (WINAPI * PFNWGLENABLEFRAMELOCKI3DPROC) (void); +typedef BOOL (WINAPI * PFNWGLDISABLEFRAMELOCKI3DPROC) (void); +typedef BOOL (WINAPI * PFNWGLISENABLEDFRAMELOCKI3DPROC) (BOOL *pFlag); +typedef BOOL (WINAPI * PFNWGLQUERYFRAMELOCKMASTERI3DPROC) (BOOL *pFlag); +#ifdef WGL_WGLEXT_PROTOTYPES +BOOL WINAPI wglEnableFrameLockI3D (void); +BOOL WINAPI wglDisableFrameLockI3D (void); +BOOL WINAPI wglIsEnabledFrameLockI3D (BOOL *pFlag); +BOOL WINAPI wglQueryFrameLockMasterI3D (BOOL *pFlag); +#endif +#endif /* WGL_I3D_swap_frame_lock */ + +#ifndef WGL_I3D_swap_frame_usage +#define WGL_I3D_swap_frame_usage 1 +typedef BOOL (WINAPI * PFNWGLGETFRAMEUSAGEI3DPROC) (float *pUsage); +typedef BOOL (WINAPI * PFNWGLBEGINFRAMETRACKINGI3DPROC) (void); +typedef BOOL (WINAPI * PFNWGLENDFRAMETRACKINGI3DPROC) (void); +typedef BOOL (WINAPI * PFNWGLQUERYFRAMETRACKINGI3DPROC) (DWORD *pFrameCount, DWORD *pMissedFrames, float *pLastMissedUsage); +#ifdef WGL_WGLEXT_PROTOTYPES +BOOL WINAPI wglGetFrameUsageI3D (float *pUsage); +BOOL WINAPI wglBeginFrameTrackingI3D (void); +BOOL WINAPI wglEndFrameTrackingI3D (void); +BOOL WINAPI wglQueryFrameTrackingI3D (DWORD *pFrameCount, DWORD *pMissedFrames, float *pLastMissedUsage); +#endif +#endif /* WGL_I3D_swap_frame_usage */ + +#ifndef WGL_NV_DX_interop +#define WGL_NV_DX_interop 1 +#define WGL_ACCESS_READ_ONLY_NV 0x00000000 +#define WGL_ACCESS_READ_WRITE_NV 0x00000001 +#define WGL_ACCESS_WRITE_DISCARD_NV 0x00000002 +typedef BOOL (WINAPI * PFNWGLDXSETRESOURCESHAREHANDLENVPROC) (void *dxObject, HANDLE shareHandle); +typedef HANDLE (WINAPI * PFNWGLDXOPENDEVICENVPROC) (void *dxDevice); +typedef BOOL (WINAPI * PFNWGLDXCLOSEDEVICENVPROC) (HANDLE hDevice); +typedef HANDLE (WINAPI * PFNWGLDXREGISTEROBJECTNVPROC) (HANDLE hDevice, void *dxObject, GLuint name, GLenum type, GLenum access); +typedef BOOL (WINAPI * PFNWGLDXUNREGISTEROBJECTNVPROC) (HANDLE hDevice, HANDLE hObject); +typedef BOOL (WINAPI * PFNWGLDXOBJECTACCESSNVPROC) (HANDLE hObject, GLenum access); +typedef BOOL (WINAPI * PFNWGLDXLOCKOBJECTSNVPROC) (HANDLE hDevice, GLint count, HANDLE *hObjects); +typedef BOOL (WINAPI * PFNWGLDXUNLOCKOBJECTSNVPROC) (HANDLE hDevice, GLint count, HANDLE *hObjects); +#ifdef WGL_WGLEXT_PROTOTYPES +BOOL WINAPI wglDXSetResourceShareHandleNV (void *dxObject, HANDLE shareHandle); +HANDLE WINAPI wglDXOpenDeviceNV (void *dxDevice); +BOOL WINAPI wglDXCloseDeviceNV (HANDLE hDevice); +HANDLE WINAPI wglDXRegisterObjectNV (HANDLE hDevice, void *dxObject, GLuint name, GLenum type, GLenum access); +BOOL WINAPI wglDXUnregisterObjectNV (HANDLE hDevice, HANDLE hObject); +BOOL WINAPI wglDXObjectAccessNV (HANDLE hObject, GLenum access); +BOOL WINAPI wglDXLockObjectsNV (HANDLE hDevice, GLint count, HANDLE *hObjects); +BOOL WINAPI wglDXUnlockObjectsNV (HANDLE hDevice, GLint count, HANDLE *hObjects); +#endif +#endif /* WGL_NV_DX_interop */ + +#ifndef WGL_NV_DX_interop2 +#define WGL_NV_DX_interop2 1 +#endif /* WGL_NV_DX_interop2 */ + +#ifndef WGL_NV_copy_image +#define WGL_NV_copy_image 1 +typedef BOOL (WINAPI * PFNWGLCOPYIMAGESUBDATANVPROC) (HGLRC hSrcRC, GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srcY, GLint srcZ, HGLRC hDstRC, GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei width, GLsizei height, GLsizei depth); +#ifdef WGL_WGLEXT_PROTOTYPES +BOOL WINAPI wglCopyImageSubDataNV (HGLRC hSrcRC, GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srcY, GLint srcZ, HGLRC hDstRC, GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei width, GLsizei height, GLsizei depth); +#endif +#endif /* WGL_NV_copy_image */ + +#ifndef WGL_NV_delay_before_swap +#define WGL_NV_delay_before_swap 1 +typedef BOOL (WINAPI * PFNWGLDELAYBEFORESWAPNVPROC) (HDC hDC, GLfloat seconds); +#ifdef WGL_WGLEXT_PROTOTYPES +BOOL WINAPI wglDelayBeforeSwapNV (HDC hDC, GLfloat seconds); +#endif +#endif /* WGL_NV_delay_before_swap */ + +#ifndef WGL_NV_float_buffer +#define WGL_NV_float_buffer 1 +#define WGL_FLOAT_COMPONENTS_NV 0x20B0 +#define WGL_BIND_TO_TEXTURE_RECTANGLE_FLOAT_R_NV 0x20B1 +#define WGL_BIND_TO_TEXTURE_RECTANGLE_FLOAT_RG_NV 0x20B2 +#define WGL_BIND_TO_TEXTURE_RECTANGLE_FLOAT_RGB_NV 0x20B3 +#define WGL_BIND_TO_TEXTURE_RECTANGLE_FLOAT_RGBA_NV 0x20B4 +#define WGL_TEXTURE_FLOAT_R_NV 0x20B5 +#define WGL_TEXTURE_FLOAT_RG_NV 0x20B6 +#define WGL_TEXTURE_FLOAT_RGB_NV 0x20B7 +#define WGL_TEXTURE_FLOAT_RGBA_NV 0x20B8 +#endif /* WGL_NV_float_buffer */ + +#ifndef WGL_NV_gpu_affinity +#define WGL_NV_gpu_affinity 1 +DECLARE_HANDLE(HGPUNV); +struct _GPU_DEVICE { + DWORD cb; + CHAR DeviceName[32]; + CHAR DeviceString[128]; + DWORD Flags; + RECT rcVirtualScreen; +}; +typedef struct _GPU_DEVICE *PGPU_DEVICE; +#define ERROR_INCOMPATIBLE_AFFINITY_MASKS_NV 0x20D0 +#define ERROR_MISSING_AFFINITY_MASK_NV 0x20D1 +typedef BOOL (WINAPI * PFNWGLENUMGPUSNVPROC) (UINT iGpuIndex, HGPUNV *phGpu); +typedef BOOL (WINAPI * PFNWGLENUMGPUDEVICESNVPROC) (HGPUNV hGpu, UINT iDeviceIndex, PGPU_DEVICE lpGpuDevice); +typedef HDC (WINAPI * PFNWGLCREATEAFFINITYDCNVPROC) (const HGPUNV *phGpuList); +typedef BOOL (WINAPI * PFNWGLENUMGPUSFROMAFFINITYDCNVPROC) (HDC hAffinityDC, UINT iGpuIndex, HGPUNV *hGpu); +typedef BOOL (WINAPI * PFNWGLDELETEDCNVPROC) (HDC hdc); +#ifdef WGL_WGLEXT_PROTOTYPES +BOOL WINAPI wglEnumGpusNV (UINT iGpuIndex, HGPUNV *phGpu); +BOOL WINAPI wglEnumGpuDevicesNV (HGPUNV hGpu, UINT iDeviceIndex, PGPU_DEVICE lpGpuDevice); +HDC WINAPI wglCreateAffinityDCNV (const HGPUNV *phGpuList); +BOOL WINAPI wglEnumGpusFromAffinityDCNV (HDC hAffinityDC, UINT iGpuIndex, HGPUNV *hGpu); +BOOL WINAPI wglDeleteDCNV (HDC hdc); +#endif +#endif /* WGL_NV_gpu_affinity */ + +#ifndef WGL_NV_multisample_coverage +#define WGL_NV_multisample_coverage 1 +#define WGL_COVERAGE_SAMPLES_NV 0x2042 +#define WGL_COLOR_SAMPLES_NV 0x20B9 +#endif /* WGL_NV_multisample_coverage */ + +#ifndef WGL_NV_present_video +#define WGL_NV_present_video 1 +DECLARE_HANDLE(HVIDEOOUTPUTDEVICENV); +#define WGL_NUM_VIDEO_SLOTS_NV 0x20F0 +typedef int (WINAPI * PFNWGLENUMERATEVIDEODEVICESNVPROC) (HDC hDC, HVIDEOOUTPUTDEVICENV *phDeviceList); +typedef BOOL (WINAPI * PFNWGLBINDVIDEODEVICENVPROC) (HDC hDC, unsigned int uVideoSlot, HVIDEOOUTPUTDEVICENV hVideoDevice, const int *piAttribList); +typedef BOOL (WINAPI * PFNWGLQUERYCURRENTCONTEXTNVPROC) (int iAttribute, int *piValue); +#ifdef WGL_WGLEXT_PROTOTYPES +int WINAPI wglEnumerateVideoDevicesNV (HDC hDC, HVIDEOOUTPUTDEVICENV *phDeviceList); +BOOL WINAPI wglBindVideoDeviceNV (HDC hDC, unsigned int uVideoSlot, HVIDEOOUTPUTDEVICENV hVideoDevice, const int *piAttribList); +BOOL WINAPI wglQueryCurrentContextNV (int iAttribute, int *piValue); +#endif +#endif /* WGL_NV_present_video */ + +#ifndef WGL_NV_render_depth_texture +#define WGL_NV_render_depth_texture 1 +#define WGL_BIND_TO_TEXTURE_DEPTH_NV 0x20A3 +#define WGL_BIND_TO_TEXTURE_RECTANGLE_DEPTH_NV 0x20A4 +#define WGL_DEPTH_TEXTURE_FORMAT_NV 0x20A5 +#define WGL_TEXTURE_DEPTH_COMPONENT_NV 0x20A6 +#define WGL_DEPTH_COMPONENT_NV 0x20A7 +#endif /* WGL_NV_render_depth_texture */ + +#ifndef WGL_NV_render_texture_rectangle +#define WGL_NV_render_texture_rectangle 1 +#define WGL_BIND_TO_TEXTURE_RECTANGLE_RGB_NV 0x20A0 +#define WGL_BIND_TO_TEXTURE_RECTANGLE_RGBA_NV 0x20A1 +#define WGL_TEXTURE_RECTANGLE_NV 0x20A2 +#endif /* WGL_NV_render_texture_rectangle */ + +#ifndef WGL_NV_swap_group +#define WGL_NV_swap_group 1 +typedef BOOL (WINAPI * PFNWGLJOINSWAPGROUPNVPROC) (HDC hDC, GLuint group); +typedef BOOL (WINAPI * PFNWGLBINDSWAPBARRIERNVPROC) (GLuint group, GLuint barrier); +typedef BOOL (WINAPI * PFNWGLQUERYSWAPGROUPNVPROC) (HDC hDC, GLuint *group, GLuint *barrier); +typedef BOOL (WINAPI * PFNWGLQUERYMAXSWAPGROUPSNVPROC) (HDC hDC, GLuint *maxGroups, GLuint *maxBarriers); +typedef BOOL (WINAPI * PFNWGLQUERYFRAMECOUNTNVPROC) (HDC hDC, GLuint *count); +typedef BOOL (WINAPI * PFNWGLRESETFRAMECOUNTNVPROC) (HDC hDC); +#ifdef WGL_WGLEXT_PROTOTYPES +BOOL WINAPI wglJoinSwapGroupNV (HDC hDC, GLuint group); +BOOL WINAPI wglBindSwapBarrierNV (GLuint group, GLuint barrier); +BOOL WINAPI wglQuerySwapGroupNV (HDC hDC, GLuint *group, GLuint *barrier); +BOOL WINAPI wglQueryMaxSwapGroupsNV (HDC hDC, GLuint *maxGroups, GLuint *maxBarriers); +BOOL WINAPI wglQueryFrameCountNV (HDC hDC, GLuint *count); +BOOL WINAPI wglResetFrameCountNV (HDC hDC); +#endif +#endif /* WGL_NV_swap_group */ + +#ifndef WGL_NV_vertex_array_range +#define WGL_NV_vertex_array_range 1 +typedef void *(WINAPI * PFNWGLALLOCATEMEMORYNVPROC) (GLsizei size, GLfloat readfreq, GLfloat writefreq, GLfloat priority); +typedef void (WINAPI * PFNWGLFREEMEMORYNVPROC) (void *pointer); +#ifdef WGL_WGLEXT_PROTOTYPES +void *WINAPI wglAllocateMemoryNV (GLsizei size, GLfloat readfreq, GLfloat writefreq, GLfloat priority); +void WINAPI wglFreeMemoryNV (void *pointer); +#endif +#endif /* WGL_NV_vertex_array_range */ + +#ifndef WGL_NV_video_capture +#define WGL_NV_video_capture 1 +DECLARE_HANDLE(HVIDEOINPUTDEVICENV); +#define WGL_UNIQUE_ID_NV 0x20CE +#define WGL_NUM_VIDEO_CAPTURE_SLOTS_NV 0x20CF +typedef BOOL (WINAPI * PFNWGLBINDVIDEOCAPTUREDEVICENVPROC) (UINT uVideoSlot, HVIDEOINPUTDEVICENV hDevice); +typedef UINT (WINAPI * PFNWGLENUMERATEVIDEOCAPTUREDEVICESNVPROC) (HDC hDc, HVIDEOINPUTDEVICENV *phDeviceList); +typedef BOOL (WINAPI * PFNWGLLOCKVIDEOCAPTUREDEVICENVPROC) (HDC hDc, HVIDEOINPUTDEVICENV hDevice); +typedef BOOL (WINAPI * PFNWGLQUERYVIDEOCAPTUREDEVICENVPROC) (HDC hDc, HVIDEOINPUTDEVICENV hDevice, int iAttribute, int *piValue); +typedef BOOL (WINAPI * PFNWGLRELEASEVIDEOCAPTUREDEVICENVPROC) (HDC hDc, HVIDEOINPUTDEVICENV hDevice); +#ifdef WGL_WGLEXT_PROTOTYPES +BOOL WINAPI wglBindVideoCaptureDeviceNV (UINT uVideoSlot, HVIDEOINPUTDEVICENV hDevice); +UINT WINAPI wglEnumerateVideoCaptureDevicesNV (HDC hDc, HVIDEOINPUTDEVICENV *phDeviceList); +BOOL WINAPI wglLockVideoCaptureDeviceNV (HDC hDc, HVIDEOINPUTDEVICENV hDevice); +BOOL WINAPI wglQueryVideoCaptureDeviceNV (HDC hDc, HVIDEOINPUTDEVICENV hDevice, int iAttribute, int *piValue); +BOOL WINAPI wglReleaseVideoCaptureDeviceNV (HDC hDc, HVIDEOINPUTDEVICENV hDevice); +#endif +#endif /* WGL_NV_video_capture */ + +#ifndef WGL_NV_video_output +#define WGL_NV_video_output 1 +DECLARE_HANDLE(HPVIDEODEV); +#define WGL_BIND_TO_VIDEO_RGB_NV 0x20C0 +#define WGL_BIND_TO_VIDEO_RGBA_NV 0x20C1 +#define WGL_BIND_TO_VIDEO_RGB_AND_DEPTH_NV 0x20C2 +#define WGL_VIDEO_OUT_COLOR_NV 0x20C3 +#define WGL_VIDEO_OUT_ALPHA_NV 0x20C4 +#define WGL_VIDEO_OUT_DEPTH_NV 0x20C5 +#define WGL_VIDEO_OUT_COLOR_AND_ALPHA_NV 0x20C6 +#define WGL_VIDEO_OUT_COLOR_AND_DEPTH_NV 0x20C7 +#define WGL_VIDEO_OUT_FRAME 0x20C8 +#define WGL_VIDEO_OUT_FIELD_1 0x20C9 +#define WGL_VIDEO_OUT_FIELD_2 0x20CA +#define WGL_VIDEO_OUT_STACKED_FIELDS_1_2 0x20CB +#define WGL_VIDEO_OUT_STACKED_FIELDS_2_1 0x20CC +typedef BOOL (WINAPI * PFNWGLGETVIDEODEVICENVPROC) (HDC hDC, int numDevices, HPVIDEODEV *hVideoDevice); +typedef BOOL (WINAPI * PFNWGLRELEASEVIDEODEVICENVPROC) (HPVIDEODEV hVideoDevice); +typedef BOOL (WINAPI * PFNWGLBINDVIDEOIMAGENVPROC) (HPVIDEODEV hVideoDevice, HPBUFFERARB hPbuffer, int iVideoBuffer); +typedef BOOL (WINAPI * PFNWGLRELEASEVIDEOIMAGENVPROC) (HPBUFFERARB hPbuffer, int iVideoBuffer); +typedef BOOL (WINAPI * PFNWGLSENDPBUFFERTOVIDEONVPROC) (HPBUFFERARB hPbuffer, int iBufferType, unsigned long *pulCounterPbuffer, BOOL bBlock); +typedef BOOL (WINAPI * PFNWGLGETVIDEOINFONVPROC) (HPVIDEODEV hpVideoDevice, unsigned long *pulCounterOutputPbuffer, unsigned long *pulCounterOutputVideo); +#ifdef WGL_WGLEXT_PROTOTYPES +BOOL WINAPI wglGetVideoDeviceNV (HDC hDC, int numDevices, HPVIDEODEV *hVideoDevice); +BOOL WINAPI wglReleaseVideoDeviceNV (HPVIDEODEV hVideoDevice); +BOOL WINAPI wglBindVideoImageNV (HPVIDEODEV hVideoDevice, HPBUFFERARB hPbuffer, int iVideoBuffer); +BOOL WINAPI wglReleaseVideoImageNV (HPBUFFERARB hPbuffer, int iVideoBuffer); +BOOL WINAPI wglSendPbufferToVideoNV (HPBUFFERARB hPbuffer, int iBufferType, unsigned long *pulCounterPbuffer, BOOL bBlock); +BOOL WINAPI wglGetVideoInfoNV (HPVIDEODEV hpVideoDevice, unsigned long *pulCounterOutputPbuffer, unsigned long *pulCounterOutputVideo); +#endif +#endif /* WGL_NV_video_output */ + +#ifndef WGL_OML_sync_control +#define WGL_OML_sync_control 1 +typedef BOOL (WINAPI * PFNWGLGETSYNCVALUESOMLPROC) (HDC hdc, INT64 *ust, INT64 *msc, INT64 *sbc); +typedef BOOL (WINAPI * PFNWGLGETMSCRATEOMLPROC) (HDC hdc, INT32 *numerator, INT32 *denominator); +typedef INT64 (WINAPI * PFNWGLSWAPBUFFERSMSCOMLPROC) (HDC hdc, INT64 target_msc, INT64 divisor, INT64 remainder); +typedef INT64 (WINAPI * PFNWGLSWAPLAYERBUFFERSMSCOMLPROC) (HDC hdc, int fuPlanes, INT64 target_msc, INT64 divisor, INT64 remainder); +typedef BOOL (WINAPI * PFNWGLWAITFORMSCOMLPROC) (HDC hdc, INT64 target_msc, INT64 divisor, INT64 remainder, INT64 *ust, INT64 *msc, INT64 *sbc); +typedef BOOL (WINAPI * PFNWGLWAITFORSBCOMLPROC) (HDC hdc, INT64 target_sbc, INT64 *ust, INT64 *msc, INT64 *sbc); +#ifdef WGL_WGLEXT_PROTOTYPES +BOOL WINAPI wglGetSyncValuesOML (HDC hdc, INT64 *ust, INT64 *msc, INT64 *sbc); +BOOL WINAPI wglGetMscRateOML (HDC hdc, INT32 *numerator, INT32 *denominator); +INT64 WINAPI wglSwapBuffersMscOML (HDC hdc, INT64 target_msc, INT64 divisor, INT64 remainder); +INT64 WINAPI wglSwapLayerBuffersMscOML (HDC hdc, int fuPlanes, INT64 target_msc, INT64 divisor, INT64 remainder); +BOOL WINAPI wglWaitForMscOML (HDC hdc, INT64 target_msc, INT64 divisor, INT64 remainder, INT64 *ust, INT64 *msc, INT64 *sbc); +BOOL WINAPI wglWaitForSbcOML (HDC hdc, INT64 target_sbc, INT64 *ust, INT64 *msc, INT64 *sbc); +#endif +#endif /* WGL_OML_sync_control */ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/Samples/3rdParty/khronos/ktx/include/ktx.h b/Samples/3rdParty/khronos/ktx/include/ktx.h new file mode 100755 index 0000000..3f37b6d --- /dev/null +++ b/Samples/3rdParty/khronos/ktx/include/ktx.h @@ -0,0 +1,1461 @@ +/* -*- tab-width: 4; -*- */ +/* vi: set sw=2 ts=4 expandtab: */ + +#ifndef KTX_H_A55A6F00956F42F3A137C11929827FE1 +#define KTX_H_A55A6F00956F42F3A137C11929827FE1 + +/* + * Copyright 2010-2018 The Khronos Group, Inc. + * SPDX-License-Identifier: Apache-2.0 + * + * See the accompanying LICENSE.md for licensing details for all files in + * the KTX library and KTX loader tests. + */ + +/** + * @file + * @~English + * + * @brief Declares the public functions and structures of the + * KTX API. + * + * @author Mark Callow, Edgewise Consulting and while at HI Corporation + * @author Based on original work by Georg Kolling, Imagination Technology + * + * @snippet{doc} version.h API version + */ + +#include +#include + +/* + * Don't use khrplatform.h in order not to break apps existing + * before these definitions were needed. + */ +#if defined(KHRONOS_STATIC) + #define KTX_API +#elif defined(_WIN32) + #if !defined(KTX_API) + #define KTX_API __declspec(dllimport) + #endif +#elif defined(__ANDROID__) + #define KTX_API __attribute__((visibility("default"))) +#else + #define KTX_API +#endif + +#if defined(_WIN32) && !defined(KHRONOS_STATIC) + #if !defined(KTX_APIENTRY) + #define KTX_APIENTRY __stdcall + #endif +#else + #define KTX_APIENTRY +#endif + +/* To avoid including define our own types. */ +typedef unsigned char ktx_uint8_t; +typedef bool ktx_bool_t; +#ifdef _MSC_VER +typedef unsigned __int16 ktx_uint16_t; +typedef signed __int16 ktx_int16_t; +typedef unsigned __int32 ktx_uint32_t; +typedef signed __int32 ktx_int32_t; +typedef size_t ktx_size_t; +typedef unsigned __int64 ktx_uint64_t; +typedef signed __int64 ktx_int64_t; +#else +#include +typedef uint16_t ktx_uint16_t; +typedef int16_t ktx_int16_t; +typedef uint32_t ktx_uint32_t; +typedef int32_t ktx_int32_t; +typedef size_t ktx_size_t; +typedef uint64_t ktx_uint64_t; +typedef int64_t ktx_int64_t; +#endif + +/* This will cause compilation to fail if size of uint32 != 4. */ +typedef unsigned char ktx_uint32_t_SIZE_ASSERT[sizeof(ktx_uint32_t) == 4]; + +/* + * This #if allows libktx to be compiled with strict c99. It avoids + * compiler warnings or even errors when a gl.h is already included. + * "Redefinition of (type) is a c11 feature". Obviously this doesn't help if + * gl.h comes after. However nobody has complained about the unguarded typedefs + * since they were introduced so this is unlikely to be a problem in practice. + * Presumably everybody is using platform default compilers not c99 or else + * they are using C++. + */ +#if !defined(GL_NO_ERROR) + /* + * To avoid having to including gl.h ... + */ + typedef unsigned char GLboolean; + typedef unsigned int GLenum; + typedef int GLint; + typedef int GLsizei; + typedef unsigned int GLuint; + typedef unsigned char GLubyte; +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @~English + * @brief Key string for standard writer metadata. + */ +#define KTX_ANIMDATA_KEY "KTXanimData" +/** + * @~English + * @brief Key string for standard orientation metadata. + */ +#define KTX_ORIENTATION_KEY "KTXorientation" +/** + * @~English + * @brief Key string for standard swizzle metadata. + */ +#define KTX_SWIZZLE_KEY "KTXswizzle" +/** + * @~English + * @brief Key string for standard writer metadata. + */ +#define KTX_WRITER_KEY "KTXwriter" +/** + * @~English + * @brief Key string for standard writer supercompression parameter metadata. + */ +#define KTX_WRITER_SCPARAMS_KEY "KTXwriterScParams" +/** + * @~English + * @brief Standard KTX 1 format for 1D orientation value. + */ +#define KTX_ORIENTATION1_FMT "S=%c" +/** + * @~English + * @brief Standard KTX 1 format for 2D orientation value. + */ +#define KTX_ORIENTATION2_FMT "S=%c,T=%c" +/** + * @~English + * @brief Standard KTX 1 format for 3D orientation value. + */ +#define KTX_ORIENTATION3_FMT "S=%c,T=%c,R=%c" +/** + * @~English + * @brief Required unpack alignment + */ +#define KTX_GL_UNPACK_ALIGNMENT 4 + +#define KTX_TRUE true +#define KTX_FALSE false + +/** + * @~English + * @brief Error codes returned by library functions. + */ +typedef enum ktx_error_code_e { + KTX_SUCCESS = 0, /*!< Operation was successful. */ + KTX_FILE_DATA_ERROR, /*!< The data in the file is inconsistent with the spec. */ + KTX_FILE_ISPIPE, /*!< The file is a pipe or named pipe. */ + KTX_FILE_OPEN_FAILED, /*!< The target file could not be opened. */ + KTX_FILE_OVERFLOW, /*!< The operation would exceed the max file size. */ + KTX_FILE_READ_ERROR, /*!< An error occurred while reading from the file. */ + KTX_FILE_SEEK_ERROR, /*!< An error occurred while seeking in the file. */ + KTX_FILE_UNEXPECTED_EOF, /*!< File does not have enough data to satisfy request. */ + KTX_FILE_WRITE_ERROR, /*!< An error occurred while writing to the file. */ + KTX_GL_ERROR, /*!< GL operations resulted in an error. */ + KTX_INVALID_OPERATION, /*!< The operation is not allowed in the current state. */ + KTX_INVALID_VALUE, /*!< A parameter value was not valid */ + KTX_NOT_FOUND, /*!< Requested key was not found */ + KTX_OUT_OF_MEMORY, /*!< Not enough memory to complete the operation. */ + KTX_TRANSCODE_FAILED, /*!< Transcoding of block compressed texture failed. */ + KTX_UNKNOWN_FILE_FORMAT, /*!< The file not a KTX file */ + KTX_UNSUPPORTED_TEXTURE_TYPE, /*!< The KTX file specifies an unsupported texture type. */ + KTX_UNSUPPORTED_FEATURE, /*!< Feature not included in in-use library or not yet implemented. */ + KTX_LIBRARY_NOT_LINKED, /*!< Library dependency (OpenGL or Vulkan) not linked into application. */ + KTX_ERROR_MAX_ENUM = KTX_LIBRARY_NOT_LINKED /*!< For safety checks. */ +} ktx_error_code_e; +/** + * @deprecated + * @~English + * @brief For backward compatibility + */ +#define KTX_error_code ktx_error_code_e + +#define KTX_IDENTIFIER_REF { 0xAB, 0x4B, 0x54, 0x58, 0x20, 0x31, 0x31, 0xBB, 0x0D, 0x0A, 0x1A, 0x0A } +#define KTX_ENDIAN_REF (0x04030201) +#define KTX_ENDIAN_REF_REV (0x01020304) +#define KTX_HEADER_SIZE (64) + +/** + * @~English + * @brief Result codes returned by library functions. + */ + typedef enum ktx_error_code_e ktxResult; + +/** + * @class ktxHashList + * @~English + * @brief Opaque handle to a ktxHashList. + */ +typedef struct ktxKVListEntry* ktxHashList; + +#define KTX_APIENTRYP KTX_APIENTRY * +/** + * @class ktxHashListEntry + * @~English + * @brief Opaque handle to an entry in a @ref ktxHashList. + */ +typedef struct ktxKVListEntry ktxHashListEntry; + +typedef enum ktxOrientationX { + KTX_ORIENT_X_LEFT = 'l', KTX_ORIENT_X_RIGHT = 'r' +} ktxOrientationX; + +typedef enum ktxOrientationY { + KTX_ORIENT_Y_UP = 'u', KTX_ORIENT_Y_DOWN = 'd' +} ktxOrientationY; + +typedef enum ktxOrientationZ { + KTX_ORIENT_Z_IN = 'i', KTX_ORIENT_Z_OUT = 'o' +} ktxOrientationZ; + +typedef enum class_id { + ktxTexture1_c = 1, + ktxTexture2_c = 2 +} class_id; + +/** + * @~English + * @brief Struct describing the logical orientation of an image. + */ +struct ktxOrientation { + ktxOrientationX x; /*!< Orientation in X */ + ktxOrientationY y; /*!< Orientation in Y */ + ktxOrientationZ z; /*!< Orientation in Z */ +}; + +#define KTXTEXTURECLASSDEFN \ + class_id classId; \ + struct ktxTexture_vtbl* vtbl; \ + struct ktxTexture_vvtbl* vvtbl; \ + struct ktxTexture_protected* _protected; \ + ktx_bool_t isArray; \ + ktx_bool_t isCubemap; \ + ktx_bool_t isCompressed; \ + ktx_bool_t generateMipmaps; \ + ktx_uint32_t baseWidth; \ + ktx_uint32_t baseHeight; \ + ktx_uint32_t baseDepth; \ + ktx_uint32_t numDimensions; \ + ktx_uint32_t numLevels; \ + ktx_uint32_t numLayers; \ + ktx_uint32_t numFaces; \ + struct ktxOrientation orientation; \ + ktxHashList kvDataHead; \ + ktx_uint32_t kvDataLen; \ + ktx_uint8_t* kvData; \ + ktx_size_t dataSize; \ + ktx_uint8_t* pData; + + +/** + * @class ktxTexture + * @~English + * @brief Base class representing a texture. + * + * ktxTextures should be created only by one of the provided + * functions and these fields should be considered read-only. + */ +typedef struct ktxTexture { + KTXTEXTURECLASSDEFN +} ktxTexture; +/** + * @typedef ktxTexture::classId + * @~English + * @brief Identify the class type. + * + * Since there are no public ktxTexture constructors, this can only have + * values of ktxTexture1_c or ktxTexture2_c. + */ +/** + * @typedef ktxTexture::vtbl + * @~English + * @brief Pointer to the class's vtble. + */ +/** + * @typedef ktxTexture::vvtbl + * @~English + * @brief Pointer to the class's vtble for Vulkan functions. + * + * A separate vtble is used so this header does not need to include vulkan.h. + */ +/** + * @typedef ktxTexture::_protected + * @~English + * @brief Opaque pointer to the class's protected variables. + */ +/** + * @typedef ktxTexture::isArray + * @~English + * + * KTX_TRUE if the texture is an array texture, i.e, + * a GL_TEXTURE_*_ARRAY target is to be used. + */ +/** + * @typedef ktxTexture::isCubemap + * @~English + * + * KTX_TRUE if the texture is a cubemap or cubemap array. + */ +/** + * @typedef ktxTexture::isCubemap + * @~English + * + * KTX_TRUE if the texture's format is a block compressed format. + */ +/** + * @typedef ktxTexture::generateMipmaps + * @~English + * + * KTX_TRUE if mipmaps should be generated for the texture by + * ktxTexture_GLUpload() or ktxTexture_VkUpload(). + */ +/** + * @typedef ktxTexture::baseWidth + * @~English + * @brief Width of the texture's base level. + */ +/** + * @typedef ktxTexture::baseHeight + * @~English + * @brief Height of the texture's base level. + */ +/** + * @typedef ktxTexture::baseDepth + * @~English + * @brief Depth of the texture's base level. + */ +/** + * @typedef ktxTexture::numDimensions + * @~English + * @brief Number of dimensions in the texture: 1, 2 or 3. + */ +/** + * @typedef ktxTexture::numLevels + * @~English + * @brief Number of mip levels in the texture. + * + * Must be 1, if @c generateMipmaps is KTX_TRUE. Can be less than a + * full pyramid but always starts at the base level. + */ +/** + * @typedef ktxTexture::numLevels + * @~English + * @brief Number of array layers in the texture. + */ +/** + * @typedef ktxTexture::numFaces + * @~English + * @brief Number of faces: 6 for cube maps, 1 otherwise. + */ +/** + * @typedef ktxTexture::orientation + * @~English + * @brief Describes the logical orientation of the images in each dimension. + * + * ktxOrientationX for X, ktxOrientationY for Y and ktxOrientationZ for Z. + */ +/** + * @typedef ktxTexture::kvDataHead + * @~English + * @brief Head of the hash list of metadata. + */ +/** + * @typedef ktxTexture::kvDataLen + * @~English + * @brief Length of the metadata, if it has been extracted in its raw form, + * otherwise 0. + */ +/** + * @typedef ktxTexture::kvData + * @~English + * @brief Pointer to the metadata, if it has been extracted in its raw form, + * otherwise NULL. + */ +/** + * @typedef ktxTexture::dataSize + * @~English + * @brief Byte length of the texture's uncompressed image data. + */ +/** + * @typedef ktxTexture::pData + * @~English + * @brief Pointer to the start of the image data. + */ + +/** + * @memberof ktxTexture + * @~English + * @brief Signature of function called by the ktxTexture_Iterate* + * functions to receive image data. + * + * The function parameters are used to pass values which change for each image. + * Obtain values which are uniform across all images from the @c ktxTexture + * object. + * + * @param [in] miplevel MIP level from 0 to the max level which is + * dependent on the texture size. + * @param [in] face usually 0; for cube maps, one of the 6 cube + * faces in the order +X, -X, +Y, -Y, +Z, -Z, + * 0 to 5. + * @param [in] width width of the image. + * @param [in] height height of the image or, for 1D textures + * textures, 1. + * @param [in] depth depth of the image or, for 1D & 2D + * textures, 1. + * @param [in] faceLodSize number of bytes of data pointed at by + * @p pixels. + * @param [in] pixels pointer to the image data. + * @param [in,out] userdata pointer for the application to pass data to and + * from the callback function. + */ + +typedef KTX_error_code + (* PFNKTXITERCB)(int miplevel, int face, + int width, int height, int depth, + ktx_uint64_t faceLodSize, + void* pixels, void* userdata); + +/* Don't use KTX_APIENTRYP to avoid a Doxygen bug. */ +typedef void (KTX_APIENTRY* PFNKTEXDESTROY)(ktxTexture* This); +typedef KTX_error_code + (KTX_APIENTRY* PFNKTEXGETIMAGEOFFSET)(ktxTexture* This, ktx_uint32_t level, + ktx_uint32_t layer, + ktx_uint32_t faceSlice, + ktx_size_t* pOffset); +typedef ktx_size_t + (KTX_APIENTRY* PFNKTEXGETDATASIZEUNCOMPRESSED)(ktxTexture* This); +typedef ktx_size_t + (KTX_APIENTRY* PFNKTEXGETIMAGESIZE)(ktxTexture* This, ktx_uint32_t level); +typedef KTX_error_code + (KTX_APIENTRY* PFNKTEXITERATELEVELS)(ktxTexture* This, PFNKTXITERCB iterCb, + void* userdata); + +typedef KTX_error_code + (KTX_APIENTRY* PFNKTEXITERATELOADLEVELFACES)(ktxTexture* This, + PFNKTXITERCB iterCb, + void* userdata); +typedef KTX_error_code + (KTX_APIENTRY* PFNKTEXLOADIMAGEDATA)(ktxTexture* This, + ktx_uint8_t* pBuffer, + ktx_size_t bufSize); +typedef ktx_bool_t + (KTX_APIENTRY* PFNKTEXNEEDSTRANSCODING)(ktxTexture* This); + +typedef KTX_error_code + (KTX_APIENTRY* PFNKTEXSETIMAGEFROMMEMORY)(ktxTexture* This, + ktx_uint32_t level, + ktx_uint32_t layer, + ktx_uint32_t faceSlice, + const ktx_uint8_t* src, + ktx_size_t srcSize); + +typedef KTX_error_code + (KTX_APIENTRY* PFNKTEXSETIMAGEFROMSTDIOSTREAM)(ktxTexture* This, + ktx_uint32_t level, + ktx_uint32_t layer, + ktx_uint32_t faceSlice, + FILE* src, ktx_size_t srcSize); +typedef KTX_error_code + (KTX_APIENTRY* PFNKTEXWRITETOSTDIOSTREAM)(ktxTexture* This, FILE* dstsstr); +typedef KTX_error_code + (KTX_APIENTRY* PFNKTEXWRITETONAMEDFILE)(ktxTexture* This, + const char* const dstname); +typedef KTX_error_code + (KTX_APIENTRY* PFNKTEXWRITETOMEMORY)(ktxTexture* This, + ktx_uint8_t** bytes, ktx_size_t* size); + +/** + * @memberof ktxTexture + * @~English + * @brief Table of virtual ktxTexture methods. + */ + struct ktxTexture_vtbl { + PFNKTEXDESTROY Destroy; + PFNKTEXGETIMAGEOFFSET GetImageOffset; + PFNKTEXGETDATASIZEUNCOMPRESSED GetDataSizeUncompressed; + PFNKTEXGETIMAGESIZE GetImageSize; + PFNKTEXITERATELEVELS IterateLevels; + PFNKTEXITERATELOADLEVELFACES IterateLoadLevelFaces; + PFNKTEXNEEDSTRANSCODING NeedsTranscoding; + PFNKTEXLOADIMAGEDATA LoadImageData; + PFNKTEXSETIMAGEFROMMEMORY SetImageFromMemory; + PFNKTEXSETIMAGEFROMSTDIOSTREAM SetImageFromStdioStream; + PFNKTEXWRITETOSTDIOSTREAM WriteToStdioStream; + PFNKTEXWRITETONAMEDFILE WriteToNamedFile; + PFNKTEXWRITETOMEMORY WriteToMemory; +}; + +/**************************************************************** + * Macros to give some backward compatibility to the previous API + ****************************************************************/ + +/** + * @~English + * @brief Helper for calling the Destroy virtual method of a ktxTexture. + * @copydoc ktxTexture2_Destroy + */ +#define ktxTexture_Destroy(This) (This)->vtbl->Destroy(This) + +/** + * @~English + * @brief Helper for calling the GetImageOffset virtual method of a + * ktxTexture. + * @copydoc ktxTexture2_GetImageOffset + */ +#define ktxTexture_GetImageOffset(This, faceSlice, layer, level, pOffset) \ + (This)->vtbl->GetImageOffset(This, faceSlice, layer, level, pOffset) + +/** + * @~English + * @brief Helper for calling the GetDataSizeUncompressed virtual method of a ktxTexture. + * + * For a ktxTexture1 this will always return the value of This->dataSize. + * + * @copydetails ktxTexture2_GetDataSizeUncompressed + */ +#define ktxTexture_GetDataSizeUncompressed(This) \ + (This)->vtbl->GetDataSizeUncompressed(This) + +/** + * @~English + * @brief Helper for calling the GetImageSize virtual method of a ktxTexture. + * @copydoc ktxTexture2_GetImageSize + */ +#define ktxTexture_GetImageSize(This, level) \ + (This)->vtbl->GetImageSize(This, level) + +/** + * @~English + * @brief Helper for calling the IterateLevels virtual method of a ktxTexture. + * @copydoc ktxTexture2_IterateLevels + */ +#define ktxTexture_IterateLevels(This, iterCb, userdata) \ + (This)->vtbl->IterateLevels(This, iterCb, userdata) + +/** + * @~English + * @brief Helper for calling the IterateLoadLevelFaces virtual method of a + * ktxTexture. + * @copydoc ktxTexture2_IterateLoadLevelFaces + */ + #define ktxTexture_IterateLoadLevelFaces(This, iterCb, userdata) \ + (This)->vtbl->IterateLoadLevelFaces(This, iterCb, userdata) + +/** + * @~English + * @brief Helper for calling the LoadImageData virtual method of a ktxTexture. + * @copydoc ktxTexture2_LoadImageData + */ +#define ktxTexture_LoadImageData(This, pBuffer, bufSize) \ + (This)->vtbl->LoadImageData(This, pBuffer, bufSize) + +/** + * @~English + * @brief Helper for calling the NeedsTranscoding virtual method of a ktxTexture. + * @copydoc ktxTexture2_NeedsTranscoding + */ +#define ktxTexture_NeedsTranscoding(This) (This)->vtbl->NeedsTranscoding(This) + +/** + * @~English + * @brief Helper for calling the SetImageFromMemory virtual method of a + * ktxTexture. + * @copydoc ktxTexture2_SetImageFromMemory + */ +#define ktxTexture_SetImageFromMemory(This, level, layer, faceSlice, \ + src, srcSize) \ + (This)->vtbl->SetImageFromMemory(This, level, layer, faceSlice, src, srcSize) + +/** + * @~English + * @brief Helper for calling the SetImageFromStdioStream virtual method of a + * ktxTexture. + * @copydoc ktxTexture2_SetImageFromStdioStream + */ +#define ktxTexture_SetImageFromStdioStream(This, level, layer, faceSlice, \ + src, srcSize) \ + (This)->vtbl->SetImageFromStdioStream(This, level, layer, faceSlice, \ + src, srcSize) + +/** + * @~English + * @brief Helper for calling the WriteToStdioStream virtual method of a + * ktxTexture. + * @copydoc ktxTexture2_WriteToStdioStream + */ +#define ktxTexture_WriteToStdioStream(This, dstsstr) \ + (This)->vtbl->WriteToStdioStream(This, dstsstr) + +/** + * @~English + * @brief Helper for calling the WriteToNamedfile virtual method of a + * ktxTexture. + * @copydoc ktxTexture2_WriteToNamedFile + */ +#define ktxTexture_WriteToNamedFile(This, dstname) \ + (This)->vtbl->WriteToNamedFile(This, dstname) + +/** + * @~English + * @brief Helper for calling the WriteToMemory virtual method of a ktxTexture. + * @copydoc ktxTexture2_WriteToMemory + */ +#define ktxTexture_WriteToMemory(This, ppDstBytes, pSize) \ + (This)->vtbl->WriteToMemory(This, ppDstBytes, pSize) + + +/** + * @class ktxTexture1 + * @~English + * @brief Class representing a KTX version 1 format texture. + * + * ktxTextures should be created only by one of the ktxTexture_Create* + * functions and these fields should be considered read-only. + */ +typedef struct ktxTexture1 { + KTXTEXTURECLASSDEFN + ktx_uint32_t glFormat; /*!< Format of the texture data, e.g., GL_RGB. */ + ktx_uint32_t glInternalformat; /*!< Internal format of the texture data, + e.g., GL_RGB8. */ + ktx_uint32_t glBaseInternalformat; /*!< Base format of the texture data, + e.g., GL_RGB. */ + ktx_uint32_t glType; /*!< Type of the texture data, e.g, GL_UNSIGNED_BYTE.*/ + struct ktxTexture1_private* _private; /*!< Private data. */ +} ktxTexture1; + +/*===========================================================* +* KTX format version 2 * +*===========================================================*/ + +/** + * @~English + * @brief Enumerators identifying the supercompression scheme. + */ +typedef enum ktxSupercmpScheme { + KTX_SS_NONE = 0, /*!< No supercompression. */ + KTX_SS_BASIS_LZ = 1, /*!< Basis LZ supercompression. */ + KTX_SS_ZSTD = 2, /*!< ZStd supercompression. */ + KTX_SS_BEGIN_RANGE = KTX_SS_NONE, + KTX_SS_END_RANGE = KTX_SS_ZSTD, + KTX_SS_BEGIN_VENDOR_RANGE = 0x10000, + KTX_SS_END_VENDOR_RANGE = 0x1ffff, + KTX_SS_BEGIN_RESERVED = 0x20000, + KTX_SUPERCOMPRESSION_BASIS = KTX_SS_BASIS_LZ, + /*!< @deprecated Will be removed before v4 release. Use KTX_SS_BASIS_LZ instead. */ + KTX_SUPERCOMPRESSION_ZSTD = KTX_SS_ZSTD + /*!< @deprecated Will be removed before v4 release. Use KTX_SS_ZSTD instead. */ +} ktxSupercmpScheme; + +/** + * @class ktxTexture2 + * @~English + * @brief Class representing a KTX version 2 format texture. + * + * ktxTextures should be created only by one of the ktxTexture_Create* + * functions and these fields should be considered read-only. + */ +typedef struct ktxTexture2 { + KTXTEXTURECLASSDEFN + ktx_uint32_t vkFormat; + ktx_uint32_t* pDfd; + ktxSupercmpScheme supercompressionScheme; + ktx_bool_t isVideo; + ktx_uint32_t duration; + ktx_uint32_t timescale; + ktx_uint32_t loopcount; + struct ktxTexture2_private* _private; /*!< Private data. */ +} ktxTexture2; + +#define ktxTexture(t) ((ktxTexture*)t) + +/** + * @memberof ktxTexture + * @~English + * @brief Structure for passing texture information to ktxTexture[12]_Create(). + * + * @sa ktxTexture_Create() + */ +typedef struct +{ + ktx_uint32_t glInternalformat; /*!< Internal format for the texture, e.g., + GL_RGB8. Ignored when creating a + ktxTexture2. */ + ktx_uint32_t vkFormat; /*!< VkFormat for texture. Ignored when creating a + ktxTexture1. */ + ktx_uint32_t* pDfd; /*!< Pointer to DFD. Used only when creating a + ktxTexture2 and only if vkFormat is + VK_FORMAT_UNDEFINED. */ + ktx_uint32_t baseWidth; /*!< Width of the base level of the texture. */ + ktx_uint32_t baseHeight; /*!< Height of the base level of the texture. */ + ktx_uint32_t baseDepth; /*!< Depth of the base level of the texture. */ + ktx_uint32_t numDimensions; /*!< Number of dimensions in the texture, 1, 2 + or 3. */ + ktx_uint32_t numLevels; /*!< Number of mip levels in the texture. Should be + 1 if @c generateMipmaps is KTX_TRUE; */ + ktx_uint32_t numLayers; /*!< Number of array layers in the texture. */ + ktx_uint32_t numFaces; /*!< Number of faces: 6 for cube maps, 1 otherwise. */ + ktx_bool_t isArray; /*!< Set to KTX_TRUE if the texture is to be an + array texture. Means OpenGL will use a + GL_TEXTURE_*_ARRAY target. */ + ktx_bool_t generateMipmaps; /*!< Set to KTX_TRUE if mipmaps should be + generated for the texture when loading + into a 3D API. */ +} ktxTextureCreateInfo; + +/** + * @memberof ktxTexture + * @~English + * @brief Enum for requesting, or not, allocation of storage for images. + * + * @sa ktxTexture_Create() + */ +typedef enum { + KTX_TEXTURE_CREATE_NO_STORAGE = 0, /*!< Don't allocate any image storage. */ + KTX_TEXTURE_CREATE_ALLOC_STORAGE = 1 /*!< Allocate image storage. */ +} ktxTextureCreateStorageEnum; + +/** + * @memberof ktxTexture + * @~English + * @brief Flags for requesting services during creation. + * + * @sa ktxTexture_CreateFrom* + */ +enum ktxTextureCreateFlagBits { + KTX_TEXTURE_CREATE_NO_FLAGS = 0x00, + KTX_TEXTURE_CREATE_LOAD_IMAGE_DATA_BIT = 0x01, + /*!< Load the images from the KTX source. */ + KTX_TEXTURE_CREATE_RAW_KVDATA_BIT = 0x02, + /*!< Load the raw key-value data instead of + creating a @c ktxHashList from it. */ + KTX_TEXTURE_CREATE_SKIP_KVDATA_BIT = 0x04 + /*!< Skip any key-value data. This overrides + the RAW_KVDATA_BIT. */ +}; +/** + * @memberof ktxTexture + * @~English + * @brief Type for TextureCreateFlags parameters. + * + * @sa ktxTexture_CreateFrom*() + */ +typedef ktx_uint32_t ktxTextureCreateFlags; + +/* + * See the implementation files for the full documentation of the following + * functions. + */ + +/* + * These three create a ktxTexture1 or ktxTexture2 according to the data + * header, and return a pointer to the base ktxTexture class. + */ +KTX_API KTX_error_code KTX_APIENTRY +ktxTexture_CreateFromStdioStream(FILE* stdioStream, + ktxTextureCreateFlags createFlags, + ktxTexture** newTex); + +KTX_API KTX_error_code KTX_APIENTRY +ktxTexture_CreateFromNamedFile(const char* const filename, + ktxTextureCreateFlags createFlags, + ktxTexture** newTex); + +KTX_API KTX_error_code KTX_APIENTRY +ktxTexture_CreateFromMemory(const ktx_uint8_t* bytes, ktx_size_t size, + ktxTextureCreateFlags createFlags, + ktxTexture** newTex); + +/* + * Returns a pointer to the image data of a ktxTexture object. + */ +KTX_API ktx_uint8_t* KTX_APIENTRY +ktxTexture_GetData(ktxTexture* This); + +/* + * Returns the pitch of a row of an image at the specified level. + * Similar to the rowPitch in a VkSubResourceLayout. + */ +KTX_API ktx_uint32_t KTX_APIENTRY +ktxTexture_GetRowPitch(ktxTexture* This, ktx_uint32_t level); + + /* + * Return the element size of the texture's images. + */ +KTX_API ktx_uint32_t KTX_APIENTRY +ktxTexture_GetElementSize(ktxTexture* This); + +/* + * Returns the size of all the image data of a ktxTexture object in bytes. + */ +KTX_API ktx_size_t KTX_APIENTRY +ktxTexture_GetDataSize(ktxTexture* This); + +/* Uploads a texture to OpenGL {,ES}. */ +KTX_API KTX_error_code KTX_APIENTRY +ktxTexture_GLUpload(ktxTexture* This, GLuint* pTexture, GLenum* pTarget, + GLenum* pGlerror); + +/* + * Iterate over the levels or faces in a ktxTexture object. + */ +KTX_API KTX_error_code KTX_APIENTRY +ktxTexture_IterateLevelFaces(ktxTexture* This, PFNKTXITERCB iterCb, + void* userdata); +/* + * Create a new ktxTexture1. + */ +KTX_API KTX_error_code KTX_APIENTRY +ktxTexture1_Create(ktxTextureCreateInfo* createInfo, + ktxTextureCreateStorageEnum storageAllocation, + ktxTexture1** newTex); + +/* + * These three create a ktxTexture1 provided the data is in KTX format. + */ +KTX_API KTX_error_code KTX_APIENTRY +ktxTexture1_CreateFromStdioStream(FILE* stdioStream, + ktxTextureCreateFlags createFlags, + ktxTexture1** newTex); + +KTX_API KTX_error_code KTX_APIENTRY +ktxTexture1_CreateFromNamedFile(const char* const filename, + ktxTextureCreateFlags createFlags, + ktxTexture1** newTex); + +KTX_API KTX_error_code KTX_APIENTRY +ktxTexture1_CreateFromMemory(const ktx_uint8_t* bytes, ktx_size_t size, + ktxTextureCreateFlags createFlags, + ktxTexture1** newTex); + +KTX_API ktx_bool_t KTX_APIENTRY +ktxTexture1_NeedsTranscoding(ktxTexture1* This); + +/* + * Write a ktxTexture object to a stdio stream in KTX format. + */ +KTX_API KTX_error_code KTX_APIENTRY +ktxTexture1_WriteKTX2ToStdioStream(ktxTexture1* This, FILE* dstsstr); + +/* + * Write a ktxTexture object to a named file in KTX format. + */ +KTX_API KTX_error_code KTX_APIENTRY +ktxTexture1_WriteKTX2ToNamedFile(ktxTexture1* This, const char* const dstname); + +/* + * Write a ktxTexture object to a block of memory in KTX format. + */ +KTX_API KTX_error_code KTX_APIENTRY +ktxTexture1_WriteKTX2ToMemory(ktxTexture1* This, + ktx_uint8_t** bytes, ktx_size_t* size); + +/* + * Create a new ktxTexture2. + */ +KTX_API KTX_error_code KTX_APIENTRY +ktxTexture2_Create(ktxTextureCreateInfo* createInfo, + ktxTextureCreateStorageEnum storageAllocation, + ktxTexture2** newTex); + +/* + * Create a new ktxTexture2 as a copy of an existing texture. + */ +KTX_API KTX_error_code KTX_APIENTRY +ktxTexture2_CreateCopy(ktxTexture2* orig, ktxTexture2** newTex); + + /* + * These three create a ktxTexture2 provided the data is in KTX2 format. + */ +KTX_API KTX_error_code KTX_APIENTRY +ktxTexture2_CreateFromStdioStream(FILE* stdioStream, + ktxTextureCreateFlags createFlags, + ktxTexture2** newTex); + +KTX_API KTX_error_code KTX_APIENTRY +ktxTexture2_CreateFromNamedFile(const char* const filename, + ktxTextureCreateFlags createFlags, + ktxTexture2** newTex); + +KTX_API KTX_error_code KTX_APIENTRY +ktxTexture2_CreateFromMemory(const ktx_uint8_t* bytes, ktx_size_t size, + ktxTextureCreateFlags createFlags, + ktxTexture2** newTex); + +KTX_API KTX_error_code KTX_APIENTRY +ktxTexture2_CompressBasis(ktxTexture2* This, ktx_uint32_t quality); + +KTX_API KTX_error_code KTX_APIENTRY +ktxTexture2_DeflateZstd(ktxTexture2* This, ktx_uint32_t level); + +KTX_API void KTX_APIENTRY +ktxTexture2_GetComponentInfo(ktxTexture2* This, ktx_uint32_t* numComponents, + ktx_uint32_t* componentByteLength); + +KTX_API ktx_uint32_t KTX_APIENTRY +ktxTexture2_GetNumComponents(ktxTexture2* This); + +KTX_API ktx_uint32_t KTX_APIENTRY +ktxTexture2_GetOETF(ktxTexture2* This); + +KTX_API ktx_bool_t KTX_APIENTRY +ktxTexture2_GetPremultipliedAlpha(ktxTexture2* This); + +KTX_API ktx_bool_t KTX_APIENTRY +ktxTexture2_NeedsTranscoding(ktxTexture2* This); + +/** + * @~English + * @brief Flags specifiying UASTC encoding options. + */ +typedef enum ktx_pack_uastc_flag_bits_e { + KTX_PACK_UASTC_LEVEL_FASTEST = 0, + /*!< Fastest compression. 43.45dB. */ + KTX_PACK_UASTC_LEVEL_FASTER = 1, + /*!< Faster compression. 46.49dB. */ + KTX_PACK_UASTC_LEVEL_DEFAULT = 2, + /*!< Default compression. 47.47dB. */ + KTX_PACK_UASTC_LEVEL_SLOWER = 3, + /*!< Slower compression. 48.01dB. */ + KTX_PACK_UASTC_LEVEL_VERYSLOW = 4, + /*!< Very slow compression. 48.24dB. */ + KTX_PACK_UASTC_MAX_LEVEL = KTX_PACK_UASTC_LEVEL_VERYSLOW, + /*!< Maximum supported quality level. */ + KTX_PACK_UASTC_LEVEL_MASK = 0xF, + /*!< Mask to extract the level from the other bits. */ + KTX_PACK_UASTC_FAVOR_UASTC_ERROR = 8, + /*!< Optimize for lowest UASTC error. */ + KTX_PACK_UASTC_FAVOR_BC7_ERROR = 16, + /*!< Optimize for lowest BC7 error. */ + KTX_PACK_UASTC_ETC1_FASTER_HINTS = 64, + /*!< Optimize for faster transcoding to ETC1. */ + KTX_PACK_UASTC_ETC1_FASTEST_HINTS = 128, + /*!< Optimize for fastest transcoding to ETC1. */ + KTX_PACK_UASTC__ETC1_DISABLE_FLIP_AND_INDIVIDUAL = 256 + /*!< Not documented in BasisU code. */ +} ktx_pack_uastc_flag_bits_e; +typedef ktx_uint32_t ktx_pack_uastc_flags; + +extern KTX_API const ktx_uint32_t KTX_ETC1S_DEFAULT_COMPRESSION_LEVEL; + +/** + * @memberof ktxTexture2 + * @~English + * @brief Structure for passing extended parameters to + * ktxTexture2_CompressBasisEx. + * + * Passing a struct initialized to 0 (e.g. " = {};") will use the default + * values. Only those settings to be modified need be non-zero. + */ +typedef struct ktxBasisParams { + ktx_uint32_t structSize; + /*!< Size of this struct. Used so library can tell which version + of struct is being passed. + */ + ktx_bool_t uastc; + /*!< True to use UASTC base, false to use ETC1S base. */ + ktx_uint32_t threadCount; + /*!< Number of threads used for compression. Default is 1. */ + + /* ETC1S params */ + + ktx_uint32_t compressionLevel; + /*!< Encoding speed vs. quality tradeoff. Range is 0 - 5. Higher values + are slower, but give higher quality. There is no default. Callers + must explicitly set this value. Callers can use + KTX_ETC1S_DEFAULT_COMPRESSION_LEVEL as a default value. + Currently this is 1. + */ + ktx_uint32_t qualityLevel; + /*!< Compression quality. Range is 1 - 255. Lower gives better + compression/lower quality/faster. Higher gives less compression + /higher quality/slower. This automatically determines values for + @c maxEndpoints, @c maxSelectors, + @c endpointRDOThreshold and @c selectorRDOThreshold + for the target quality level. Setting these parameters overrides + the values determined by @c qualityLevel which defaults to + 128 if neither it nor both of @c maxEndpoints and + @c maxSelectors have been set. + @note @e Both of @c maxEndpoints and @c maxSelectors + must be set for them to have any effect. + @note qualityLevel will only determine values for + @c endpointRDOThreshold and @c selectorRDOThreshold + when its value exceeds 128, otherwise their defaults will be used. + */ + ktx_uint32_t maxEndpoints; + /*!< Manually set the max number of color endpoint clusters + from 1-16128. Default is 0, unset. If this is set, maxSelectors + must also be set, otherwise the value will be ignored. + */ + float endpointRDOThreshold; + /*!< Set endpoint RDO quality threshold. The default is 1.25. Lower is + higher quality but less quality per output bit (try 1.0-3.0). + This will override the value chosen by @c qualityLevel. + */ + ktx_uint32_t maxSelectors; + /*!< Manually set the max number of color selector clusters + from 1-16128. Default is 0, unset. If this is set, maxEndpoints + must also be set, otherwise the value will be ignored. + */ + float selectorRDOThreshold; + /*!< Set selector RDO quality threshold. The default is 1.5. Lower is + higher quality but less quality per output bit (try 1.0-3.0). + This will override the value chosen by @c qualityLevel. + */ + ktx_bool_t normalMap; + /*!< Tunes codec parameters for better quality on normal maps (no + selector RDO, no endpoint RDO). Only valid for linear textures. + */ + ktx_bool_t separateRGToRGB_A; + /*!< Separates the input R and G channels to RGB and A (for tangent + space XY normal maps). Only valid for 2-component textures. + */ + ktx_bool_t preSwizzle; + /*!< If the texture has @c KTXswizzle metadata, apply it before + compressing. Swizzling, like @c rabb may yield drastically + different error metrics if done after supercompression. + */ + ktx_bool_t noEndpointRDO; + /*!< Disable endpoint rate distortion optimizations. Slightly faster, + less noisy output, but lower quality per output bit. Default is + KTX_FALSE. + */ + ktx_bool_t noSelectorRDO; + /*!< Disable selector rate distortion optimizations. Slightly faster, + less noisy output, but lower quality per output bit. Default is + KTX_FALSE. + */ + + /* UASTC params */ + + ktx_pack_uastc_flags uastcFlags; + /*!< A set of ::ktx_pack_uastc_flag_bits_e controlling UASTC + encoding. The most important value is the level given in the + least-significant 4 bits which selects a speed vs quality tradeoff + as shown in the following table: + + Level/Speed | Quality + :-----: | :-------: + KTX_PACK_UASTC_LEVEL_FASTEST | 43.45dB + KTX_PACK_UASTC_LEVEL_FASTER | 46.49dB + KTX_PACK_UASTC_LEVEL_DEFAULT | 47.47dB + KTX_PACK_UASTC_LEVEL_SLOWER | 48.01dB + KTX_PACK_UASTC_LEVEL_VERYSLOW | 48.24dB + */ + ktx_bool_t uastcRDO; + /*!< Enable Rate Distortion Optimization (RDO) post-processing. */ + float uastcRDOQualityScalar; + /*!< Set UASTC RDO quality scalar. Lower values yield higher quality/larger LZ + compressed files, higher values yield lower quality/smaller LZ compressed + files. A good range to try is [.2-4]." Full range is .001 to 10.0. Default is + 1.0.*/ + ktx_uint32_t uastcRDODictSize; + /*!< Set UASTC RDO dictionary size in bytes. Default is 32768. Lower + values=faster, but give less compression. Possible range is 256 to + 65536.*/ + +} ktxBasisParams; + +KTX_API KTX_error_code KTX_APIENTRY +ktxTexture2_CompressBasisEx(ktxTexture2* This, ktxBasisParams* params); + +/** + * @~English + * @brief Enumerators for specifying the transcode target format. + * + * For BasisU/ETC1S format, @e Opaque and @e alpha here refer to 2 separate + * RGB images, a.k.a slices within the BasisU compressed data. For UASTC + * format they refer to the RGB and the alpha components of the UASTC data. If + * the original image had only 2 components, R will be in the opaque portion and G + * in the alpha portion. The R value will be replicated in the RGB components. In + * the case of BasisU the G value will be replicated in all 3 components of the + * alpha slice. If the original image had only 1 component it's value is replicated in all + * 3 components of the opaque portion and there is no alpha. + * + * @note You should not transcode sRGB encoded data to @c KTX_TTF_BC4_R, @c KTX_TTF_BC5_RG, + * @c KTX_TTF_ETC2_EAC_R{,G}11, @c KTX_TTF_RGB565, @c KTX_TTF_BGR565 or + * @c KTX_TTF_RGBA4444 formats as neither OpenGL nor Vulkan support sRGB variants of these. Doing + * sRGB decoding in the shader will not produce correct results if any texture filtering is being used. + */ +typedef enum ktx_transcode_fmt_e { + // Compressed formats + + // ETC1-2 + KTX_TTF_ETC1_RGB = 0, + /*!< Opaque only. Returns RGB or alpha data, if + KTX_TF_TRANSCODE_ALPHA_DATA_TO_OPAQUE_FORMATS flag is specified. */ + KTX_TTF_ETC2_RGBA = 1, + /*!< Opaque+alpha. EAC_A8 block followed by an ETC1 block. The alpha channel will be + opaque for textures without an alpha channel. */ + + // BC1-5, BC7 (desktop, some mobile devices) + KTX_TTF_BC1_RGB = 2, + /*!< Opaque only, no punchthrough alpha support yet. Returns RGB or alpha data, if + KTX_TF_TRANSCODE_ALPHA_DATA_TO_OPAQUE_FORMATS flag is specified. */ + KTX_TTF_BC3_RGBA = 3, + /*!< Opaque+alpha. BC4 block with alpha followed by a BC1 block. The alpha channel will + be opaque for textures without an alpha channel. */ + KTX_TTF_BC4_R = 4, + /*!< One BC4 block. R = opaque.g or alpha.g, if + KTX_TF_TRANSCODE_ALPHA_DATA_TO_OPAQUE_FORMATS flag is specified. */ + KTX_TTF_BC5_RG = 5, + /*! to use it. + +Changed: +@li ktx.h to not depend on KHR/khrplatform.h and GL{,ES*}/gl{corearb,}.h. + Applications using OpenGL must now include these files themselves. +@li ktxLoadTexture[FMN], removing the hack of loading 1D textures as 2D textures + when the OpenGL context does not support 1D textures. + KTX_UNSUPPORTED_TEXTURE_TYPE is now returned. + +@section v5 Version 2.0.2 +Added: +@li Support for cubemap arrays. + +Changed: +@li New build system + +Fixed: +@li GitHub issue #40: failure to byte-swap key-value lengths. +@li GitHub issue #33: returning incorrect target when loading cubemaps. +@li GitHub PR #42: loading of texture arrays. +@li GitHub PR #41: compilation error when KTX_OPENGL_ES2=1 defined. +@li GitHub issue #39: stack-buffer-overflow in toktx +@li Don't use GL_EXTENSIONS on recent OpenGL versions. + +@section v4 Version 2.0.1 +Added: +@li CMake build files. Thanks to Pavel Rotjberg for the initial version. + +Changed: +@li ktxWriteKTXF to check the validity of the type & format combinations + passed to it. + +Fixed: +@li Public Bugzilla 999: 16-bit luminance texture cannot be written. +@li compile warnings from compilers stricter than MS Visual C++. Thanks to + Pavel Rotjberg. + +@section v3 Version 2.0 +Added: +@li support for decoding ETC2 and EAC formats in the absence of a hardware + decoder. +@li support for converting textures with legacy LUMINANCE, LUMINANCE_ALPHA, + etc. formats to the equivalent R, RG, etc. format with an + appropriate swizzle, when loading in OpenGL Core Profile contexts. +@li ktxErrorString function to return a string corresponding to an error code. +@li tests for ktxLoadTexture[FN] that run under OpenGL ES 3.0 and OpenGL 3.3. + The latter includes an EGL on WGL wrapper that makes porting apps between + OpenGL ES and OpenGL easier on Windows. +@li more texture formats to ktxLoadTexture[FN] and toktx tests. + +Changed: +@li ktxLoadTexture[FMN] to discover the capabilities of the GL context at + run time and load textures, or not, according to those capabilities. + +Fixed: +@li failure of ktxWriteKTXF to pad image rows to 4 bytes as required by the KTX + format. +@li ktxWriteKTXF exiting with KTX_FILE_WRITE_ERROR when attempting to write + more than 1 byte of face-LOD padding. + +Although there is only a very minor API change, the addition of ktxErrorString, +the functional changes are large enough to justify bumping the major revision +number. + +@section v2 Version 1.0.1 +Implemented ktxLoadTextureM. +Fixed the following: +@li Public Bugzilla 571: crash when null passed for pIsMipmapped. +@li Public Bugzilla 572: memory leak when unpacking ETC textures. +@li Public Bugzilla 573: potential crash when unpacking ETC textures with unused padding pixels. +@li Public Bugzilla 576: various small fixes. + +Thanks to Krystian Bigaj for the ktxLoadTextureM implementation and these fixes. + +@section v1 Version 1.0 +Initial release. + +*/ + +#endif /* KTX_H_A55A6F00956F42F3A137C11929827FE1 */ + diff --git a/Samples/3rdParty/khronos/ktx/lib/android/arm64-v8a/libktx.so b/Samples/3rdParty/khronos/ktx/lib/android/arm64-v8a/libktx.so new file mode 100755 index 0000000..90111d2 Binary files /dev/null and b/Samples/3rdParty/khronos/ktx/lib/android/arm64-v8a/libktx.so differ diff --git a/Samples/3rdParty/khronos/ktx/lib/android/arm64-v8a/libktx_read.so b/Samples/3rdParty/khronos/ktx/lib/android/arm64-v8a/libktx_read.so new file mode 100755 index 0000000..ae52448 Binary files /dev/null and b/Samples/3rdParty/khronos/ktx/lib/android/arm64-v8a/libktx_read.so differ diff --git a/Samples/3rdParty/khronos/ktx/lib/android/arm64-v8a/libobjUtil.a b/Samples/3rdParty/khronos/ktx/lib/android/arm64-v8a/libobjUtil.a new file mode 100644 index 0000000..69c9320 Binary files /dev/null and b/Samples/3rdParty/khronos/ktx/lib/android/arm64-v8a/libobjUtil.a differ diff --git a/Samples/3rdParty/khronos/ktx/licenses/Apache-2.0.txt b/Samples/3rdParty/khronos/ktx/licenses/Apache-2.0.txt new file mode 100644 index 0000000..4ed90b9 --- /dev/null +++ b/Samples/3rdParty/khronos/ktx/licenses/Apache-2.0.txt @@ -0,0 +1,208 @@ +Apache License + +Version 2.0, January 2004 + +http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, +AND DISTRIBUTION + + 1. Definitions. + + + +"License" shall mean the terms and conditions for use, reproduction, and distribution +as defined by Sections 1 through 9 of this document. + + + +"Licensor" shall mean the copyright owner or entity authorized by the copyright +owner that is granting the License. + + + +"Legal Entity" shall mean the union of the acting entity and all other entities +that control, are controlled by, or are under common control with that entity. +For the purposes of this definition, "control" means (i) the power, direct +or indirect, to cause the direction or management of such entity, whether +by contract or otherwise, or (ii) ownership of fifty percent (50%) or more +of the outstanding shares, or (iii) beneficial ownership of such entity. + + + +"You" (or "Your") shall mean an individual or Legal Entity exercising permissions +granted by this License. + + + +"Source" form shall mean the preferred form for making modifications, including +but not limited to software source code, documentation source, and configuration +files. + + + +"Object" form shall mean any form resulting from mechanical transformation +or translation of a Source form, including but not limited to compiled object +code, generated documentation, and conversions to other media types. + + + +"Work" shall mean the work of authorship, whether in Source or Object form, +made available under the License, as indicated by a copyright notice that +is included in or attached to the work (an example is provided in the Appendix +below). + + + +"Derivative Works" shall mean any work, whether in Source or Object form, +that is based on (or derived from) the Work and for which the editorial revisions, +annotations, elaborations, or other modifications represent, as a whole, an +original work of authorship. For the purposes of this License, Derivative +Works shall not include works that remain separable from, or merely link (or +bind by name) to the interfaces of, the Work and Derivative Works thereof. + + + +"Contribution" shall mean any work of authorship, including the original version +of the Work and any modifications or additions to that Work or Derivative +Works thereof, that is intentionally submitted to Licensor for inclusion in +the Work by the copyright owner or by an individual or Legal Entity authorized +to submit on behalf of the copyright owner. For the purposes of this definition, +"submitted" means any form of electronic, verbal, or written communication +sent to the Licensor or its representatives, including but not limited to +communication on electronic mailing lists, source code control systems, and +issue tracking systems that are managed by, or on behalf of, the Licensor +for the purpose of discussing and improving the Work, but excluding communication +that is conspicuously marked or otherwise designated in writing by the copyright +owner as "Not a Contribution." + + + +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf +of whom a Contribution has been received by Licensor and subsequently incorporated +within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of this +License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, +no-charge, royalty-free, irrevocable copyright license to reproduce, prepare +Derivative Works of, publicly display, publicly perform, sublicense, and distribute +the Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of this License, +each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, +no-charge, royalty-free, irrevocable (except as stated in this section) patent +license to make, have made, use, offer to sell, sell, import, and otherwise +transfer the Work, where such license applies only to those patent claims +licensable by such Contributor that are necessarily infringed by their Contribution(s) +alone or by combination of their Contribution(s) with the Work to which such +Contribution(s) was submitted. If You institute patent litigation against +any entity (including a cross-claim or counterclaim in a lawsuit) alleging +that the Work or a Contribution incorporated within the Work constitutes direct +or contributory patent infringement, then any patent licenses granted to You +under this License for that Work shall terminate as of the date such litigation +is filed. + +4. Redistribution. You may reproduce and distribute copies of the Work or +Derivative Works thereof in any medium, with or without modifications, and +in Source or Object form, provided that You meet the following conditions: + +(a) You must give any other recipients of the Work or Derivative Works a copy +of this License; and + +(b) You must cause any modified files to carry prominent notices stating that +You changed the files; and + +(c) You must retain, in the Source form of any Derivative Works that You distribute, +all copyright, patent, trademark, and attribution notices from the Source +form of the Work, excluding those notices that do not pertain to any part +of the Derivative Works; and + +(d) If the Work includes a "NOTICE" text file as part of its distribution, +then any Derivative Works that You distribute must include a readable copy +of the attribution notices contained within such NOTICE file, excluding those +notices that do not pertain to any part of the Derivative Works, in at least +one of the following places: within a NOTICE text file distributed as part +of the Derivative Works; within the Source form or documentation, if provided +along with the Derivative Works; or, within a display generated by the Derivative +Works, if and wherever such third-party notices normally appear. The contents +of the NOTICE file are for informational purposes only and do not modify the +License. You may add Your own attribution notices within Derivative Works +that You distribute, alongside or as an addendum to the NOTICE text from the +Work, provided that such additional attribution notices cannot be construed +as modifying the License. + +You may add Your own copyright statement to Your modifications and may provide +additional or different license terms and conditions for use, reproduction, +or distribution of Your modifications, or for any such Derivative Works as +a whole, provided Your use, reproduction, and distribution of the Work otherwise +complies with the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, any +Contribution intentionally submitted for inclusion in the Work by You to the +Licensor shall be under the terms and conditions of this License, without +any additional terms or conditions. Notwithstanding the above, nothing herein +shall supersede or modify the terms of any separate license agreement you +may have executed with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade names, +trademarks, service marks, or product names of the Licensor, except as required +for reasonable and customary use in describing the origin of the Work and +reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or agreed to +in writing, Licensor provides the Work (and each Contributor provides its +Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +KIND, either express or implied, including, without limitation, any warranties +or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR +A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness +of using or redistributing the Work and assume any risks associated with Your +exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, whether +in tort (including negligence), contract, or otherwise, unless required by +applicable law (such as deliberate and grossly negligent acts) or agreed to +in writing, shall any Contributor be liable to You for damages, including +any direct, indirect, special, incidental, or consequential damages of any +character arising as a result of this License or out of the use or inability +to use the Work (including but not limited to damages for loss of goodwill, +work stoppage, computer failure or malfunction, or any and all other commercial +damages or losses), even if such Contributor has been advised of the possibility +of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing the Work +or Derivative Works thereof, You may choose to offer, and charge a fee for, +acceptance of support, warranty, indemnity, or other liability obligations +and/or rights consistent with this License. However, in accepting such obligations, +You may act only on Your own behalf and on Your sole responsibility, not on +behalf of any other Contributor, and only if You agree to indemnify, defend, +and hold each Contributor harmless for any liability incurred by, or claims +asserted against, such Contributor by reason of your accepting any such warranty +or additional liability. END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + +To apply the Apache License to your work, attach the following boilerplate +notice, with the fields enclosed by brackets "[]" replaced with your own identifying +information. (Don't include the brackets!) The text should be enclosed in +the appropriate comment syntax for the file format. We also recommend that +a file or class name and description of purpose be included on the same "printed +page" as the copyright notice for easier identification within third-party +archives. + +Copyright [yyyy] [name of copyright owner] + +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. diff --git a/Samples/3rdParty/khronos/ktx/licenses/BSD-1-Clause.txt b/Samples/3rdParty/khronos/ktx/licenses/BSD-1-Clause.txt new file mode 100644 index 0000000..bff05ec --- /dev/null +++ b/Samples/3rdParty/khronos/ktx/licenses/BSD-1-Clause.txt @@ -0,0 +1,17 @@ +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER +OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/Samples/3rdParty/khronos/ktx/licenses/BSD-2-Clause.txt b/Samples/3rdParty/khronos/ktx/licenses/BSD-2-Clause.txt new file mode 100644 index 0000000..cdf557b --- /dev/null +++ b/Samples/3rdParty/khronos/ktx/licenses/BSD-2-Clause.txt @@ -0,0 +1,23 @@ +Copyright (c) . All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, +this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. diff --git a/Samples/3rdParty/khronos/ktx/licenses/BSD-3-Clause.txt b/Samples/3rdParty/khronos/ktx/licenses/BSD-3-Clause.txt new file mode 100644 index 0000000..9cc70bc --- /dev/null +++ b/Samples/3rdParty/khronos/ktx/licenses/BSD-3-Clause.txt @@ -0,0 +1,27 @@ +Copyright (c) . All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, +this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its +contributors may be used to endorse or promote products derived from this +software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. diff --git a/Samples/3rdParty/khronos/ktx/licenses/BSL-1.0.txt b/Samples/3rdParty/khronos/ktx/licenses/BSL-1.0.txt new file mode 100644 index 0000000..36b7cd9 --- /dev/null +++ b/Samples/3rdParty/khronos/ktx/licenses/BSL-1.0.txt @@ -0,0 +1,23 @@ +Boost Software License - Version 1.0 - August 17th, 2003 + +Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the software and accompanying documentation covered by +this license (the "Software") to use, reproduce, display, distribute, +execute, and transmit the Software, and to prepare derivative works of the +Software, and to permit third-parties to whom the Software is furnished to +do so, all subject to the following: + +The copyright notices in the Software and this entire statement, including +the above license grant, this restriction and the following disclaimer, +must be included in all copies of the Software, in whole or in part, and +all derivative works of the Software, unless such copies or derivative +works are solely in the form of machine-executable object code generated by +a source language processor. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/Samples/3rdParty/khronos/ktx/licenses/CC-BY-3.0.txt b/Samples/3rdParty/khronos/ktx/licenses/CC-BY-3.0.txt new file mode 100644 index 0000000..55be1f3 --- /dev/null +++ b/Samples/3rdParty/khronos/ktx/licenses/CC-BY-3.0.txt @@ -0,0 +1,59 @@ +Creative Commons Attribution 3.0 Unported + +CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE LEGAL SERVICES. DISTRIBUTION OF THIS LICENSE DOES NOT CREATE AN ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES REGARDING THE INFORMATION PROVIDED, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM ITS USE. + +License + +THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED. + +BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE TO BE BOUND BY THE TERMS OF THIS LICENSE. TO THE EXTENT THIS LICENSE MAY BE CONSIDERED TO BE A CONTRACT, THE LICENSOR GRANTS YOU THE RIGHTS CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND CONDITIONS. + +1. Definitions + +"Adaptation" means a work based upon the Work, or upon the Work and other pre-existing works, such as a translation, adaptation, derivative work, arrangement of music or other alterations of a literary or artistic work, or phonogram or performance and includes cinematographic adaptations or any other form in which the Work may be recast, transformed, or adapted including in any form recognizably derived from the original, except that a work that constitutes a Collection will not be considered an Adaptation for the purpose of this License. For the avoidance of doubt, where the Work is a musical work, performance or phonogram, the synchronization of the Work in timed-relation with a moving image ("synching") will be considered an Adaptation for the purpose of this License. +"Collection" means a collection of literary or artistic works, such as encyclopedias and anthologies, or performances, phonograms or broadcasts, or other works or subject matter other than works listed in Section 1(f) below, which, by reason of the selection and arrangement of their contents, constitute intellectual creations, in which the Work is included in its entirety in unmodified form along with one or more other contributions, each constituting separate and independent works in themselves, which together are assembled into a collective whole. A work that constitutes a Collection will not be considered an Adaptation (as defined above) for the purposes of this License. +"Distribute" means to make available to the public the original and copies of the Work or Adaptation, as appropriate, through sale or other transfer of ownership. +"Licensor" means the individual, individuals, entity or entities that offer(s) the Work under the terms of this License. +"Original Author" means, in the case of a literary or artistic work, the individual, individuals, entity or entities who created the Work or if no individual or entity can be identified, the publisher; and in addition (i) in the case of a performance the actors, singers, musicians, dancers, and other persons who act, sing, deliver, declaim, play in, interpret or otherwise perform literary or artistic works or expressions of folklore; (ii) in the case of a phonogram the producer being the person or legal entity who first fixes the sounds of a performance or other sounds; and, (iii) in the case of broadcasts, the organization that transmits the broadcast. +"Work" means the literary and/or artistic work offered under the terms of this License including without limitation any production in the literary, scientific and artistic domain, whatever may be the mode or form of its expression including digital form, such as a book, pamphlet and other writing; a lecture, address, sermon or other work of the same nature; a dramatic or dramatico-musical work; a choreographic work or entertainment in dumb show; a musical composition with or without words; a cinematographic work to which are assimilated works expressed by a process analogous to cinematography; a work of drawing, painting, architecture, sculpture, engraving or lithography; a photographic work to which are assimilated works expressed by a process analogous to photography; a work of applied art; an illustration, map, plan, sketch or three-dimensional work relative to geography, topography, architecture or science; a performance; a broadcast; a phonogram; a compilation of data to the extent it is protected as a copyrightable work; or a work performed by a variety or circus performer to the extent it is not otherwise considered a literary or artistic work. +"You" means an individual or entity exercising rights under this License who has not previously violated the terms of this License with respect to the Work, or who has received express permission from the Licensor to exercise rights under this License despite a previous violation. +"Publicly Perform" means to perform public recitations of the Work and to communicate to the public those public recitations, by any means or process, including by wire or wireless means or public digital performances; to make available to the public Works in such a way that members of the public may access these Works from a place and at a place individually chosen by them; to perform the Work to the public by any means or process and the communication to the public of the performances of the Work, including by public digital performance; to broadcast and rebroadcast the Work by any means including signs, sounds or images. +"Reproduce" means to make copies of the Work by any means including without limitation by sound or visual recordings and the right of fixation and reproducing fixations of the Work, including storage of a protected performance or phonogram in digital form or other electronic medium. +2. Fair Dealing Rights. Nothing in this License is intended to reduce, limit, or restrict any uses free from copyright or rights arising from limitations or exceptions that are provided for in connection with the copyright protection under copyright law or other applicable laws. + +3. License Grant. Subject to the terms and conditions of this License, Licensor hereby grants You a worldwide, royalty-free, non-exclusive, perpetual (for the duration of the applicable copyright) license to exercise the rights in the Work as stated below: + +to Reproduce the Work, to incorporate the Work into one or more Collections, and to Reproduce the Work as incorporated in the Collections; +to create and Reproduce Adaptations provided that any such Adaptation, including any translation in any medium, takes reasonable steps to clearly label, demarcate or otherwise identify that changes were made to the original Work. For example, a translation could be marked "The original work was translated from English to Spanish," or a modification could indicate "The original work has been modified."; +to Distribute and Publicly Perform the Work including as incorporated in Collections; and, +to Distribute and Publicly Perform Adaptations. +For the avoidance of doubt: + +Non-waivable Compulsory License Schemes. In those jurisdictions in which the right to collect royalties through any statutory or compulsory licensing scheme cannot be waived, the Licensor reserves the exclusive right to collect such royalties for any exercise by You of the rights granted under this License; +Waivable Compulsory License Schemes. In those jurisdictions in which the right to collect royalties through any statutory or compulsory licensing scheme can be waived, the Licensor waives the exclusive right to collect such royalties for any exercise by You of the rights granted under this License; and, +Voluntary License Schemes. The Licensor waives the right to collect royalties, whether individually or, in the event that the Licensor is a member of a collecting society that administers voluntary licensing schemes, via that society, from any exercise by You of the rights granted under this License. +The above rights may be exercised in all media and formats whether now known or hereafter devised. The above rights include the right to make such modifications as are technically necessary to exercise the rights in other media and formats. Subject to Section 8(f), all rights not expressly granted by Licensor are hereby reserved. + +4. Restrictions. The license granted in Section 3 above is expressly made subject to and limited by the following restrictions: + +You may Distribute or Publicly Perform the Work only under the terms of this License. You must include a copy of, or the Uniform Resource Identifier (URI) for, this License with every copy of the Work You Distribute or Publicly Perform. You may not offer or impose any terms on the Work that restrict the terms of this License or the ability of the recipient of the Work to exercise the rights granted to that recipient under the terms of the License. You may not sublicense the Work. You must keep intact all notices that refer to this License and to the disclaimer of warranties with every copy of the Work You Distribute or Publicly Perform. When You Distribute or Publicly Perform the Work, You may not impose any effective technological measures on the Work that restrict the ability of a recipient of the Work from You to exercise the rights granted to that recipient under the terms of the License. This Section 4(a) applies to the Work as incorporated in a Collection, but this does not require the Collection apart from the Work itself to be made subject to the terms of this License. If You create a Collection, upon notice from any Licensor You must, to the extent practicable, remove from the Collection any credit as required by Section 4(b), as requested. If You create an Adaptation, upon notice from any Licensor You must, to the extent practicable, remove from the Adaptation any credit as required by Section 4(b), as requested. +If You Distribute, or Publicly Perform the Work or any Adaptations or Collections, You must, unless a request has been made pursuant to Section 4(a), keep intact all copyright notices for the Work and provide, reasonable to the medium or means You are utilizing: (i) the name of the Original Author (or pseudonym, if applicable) if supplied, and/or if the Original Author and/or Licensor designate another party or parties (e.g., a sponsor institute, publishing entity, journal) for attribution ("Attribution Parties") in Licensor's copyright notice, terms of service or by other reasonable means, the name of such party or parties; (ii) the title of the Work if supplied; (iii) to the extent reasonably practicable, the URI, if any, that Licensor specifies to be associated with the Work, unless such URI does not refer to the copyright notice or licensing information for the Work; and (iv) , consistent with Section 3(b), in the case of an Adaptation, a credit identifying the use of the Work in the Adaptation (e.g., "French translation of the Work by Original Author," or "Screenplay based on original Work by Original Author"). The credit required by this Section 4 (b) may be implemented in any reasonable manner; provided, however, that in the case of a Adaptation or Collection, at a minimum such credit will appear, if a credit for all contributing authors of the Adaptation or Collection appears, then as part of these credits and in a manner at least as prominent as the credits for the other contributing authors. For the avoidance of doubt, You may only use the credit required by this Section for the purpose of attribution in the manner set out above and, by exercising Your rights under this License, You may not implicitly or explicitly assert or imply any connection with, sponsorship or endorsement by the Original Author, Licensor and/or Attribution Parties, as appropriate, of You or Your use of the Work, without the separate, express prior written permission of the Original Author, Licensor and/or Attribution Parties. +Except as otherwise agreed in writing by the Licensor or as may be otherwise permitted by applicable law, if You Reproduce, Distribute or Publicly Perform the Work either by itself or as part of any Adaptations or Collections, You must not distort, mutilate, modify or take other derogatory action in relation to the Work which would be prejudicial to the Original Author's honor or reputation. Licensor agrees that in those jurisdictions (e.g. Japan), in which any exercise of the right granted in Section 3(b) of this License (the right to make Adaptations) would be deemed to be a distortion, mutilation, modification or other derogatory action prejudicial to the Original Author's honor and reputation, the Licensor will waive or not assert, as appropriate, this Section, to the fullest extent permitted by the applicable national law, to enable You to reasonably exercise Your right under Section 3(b) of this License (right to make Adaptations) but not otherwise. +5. Representations, Warranties and Disclaimer + +UNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN WRITING, LICENSOR OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY KIND CONCERNING THE WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTIBILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS, WHETHER OR NOT DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF IMPLIED WARRANTIES, SO SUCH EXCLUSION MAY NOT APPLY TO YOU. + +6. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +7. Termination + +This License and the rights granted hereunder will terminate automatically upon any breach by You of the terms of this License. Individuals or entities who have received Adaptations or Collections from You under this License, however, will not have their licenses terminated provided such individuals or entities remain in full compliance with those licenses. Sections 1, 2, 5, 6, 7, and 8 will survive any termination of this License. +Subject to the above terms and conditions, the license granted here is perpetual (for the duration of the applicable copyright in the Work). Notwithstanding the above, Licensor reserves the right to release the Work under different license terms or to stop distributing the Work at any time; provided, however that any such election will not serve to withdraw this License (or any other license that has been, or is required to be, granted under the terms of this License), and this License will continue in full force and effect unless terminated as stated above. +8. Miscellaneous + +Each time You Distribute or Publicly Perform the Work or a Collection, the Licensor offers to the recipient a license to the Work on the same terms and conditions as the license granted to You under this License. +Each time You Distribute or Publicly Perform an Adaptation, Licensor offers to the recipient a license to the original Work on the same terms and conditions as the license granted to You under this License. +If any provision of this License is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this License, and without further action by the parties to this agreement, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable. +No term or provision of this License shall be deemed waived and no breach consented to unless such waiver or consent shall be in writing and signed by the party to be charged with such waiver or consent. +This License constitutes the entire agreement between the parties with respect to the Work licensed here. There are no understandings, agreements or representations with respect to the Work not specified here. Licensor shall not be bound by any additional provisions that may appear in any communication from You. This License may not be modified without the mutual written agreement of the Licensor and You. +The rights granted under, and the subject matter referenced, in this License were drafted utilizing the terminology of the Berne Convention for the Protection of Literary and Artistic Works (as amended on September 28, 1979), the Rome Convention of 1961, the WIPO Copyright Treaty of 1996, the WIPO Performances and Phonograms Treaty of 1996 and the Universal Copyright Convention (as revised on July 24, 1971). These rights and subject matter take effect in the relevant jurisdiction in which the License terms are sought to be enforced according to the corresponding provisions of the implementation of those treaty provisions in the applicable national law. If the standard suite of rights granted under applicable copyright law includes additional rights not granted under this License, such additional rights are deemed to be included in the License; this License is not intended to restrict the license of any rights under applicable law. diff --git a/Samples/3rdParty/khronos/ktx/licenses/CC0-1.0.txt b/Samples/3rdParty/khronos/ktx/licenses/CC0-1.0.txt new file mode 100644 index 0000000..730f2e5 --- /dev/null +++ b/Samples/3rdParty/khronos/ktx/licenses/CC0-1.0.txt @@ -0,0 +1,29 @@ +Creative Commons Legal Code + +CC0 1.0 Universal + +CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED HEREUNDER. + +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer exclusive Copyright and Related Rights (defined below) upon the creator and subsequent owner(s) (each and all, an "owner") of an original work of authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for the purpose of contributing to a commons of creative, cultural and scientific works ("Commons") that the public can reliably and without fear of later claims of infringement build upon, modify, incorporate in other works, reuse and redistribute as freely as possible in any form whatsoever and for any purposes, including without limitation commercial purposes. These owners may contribute to the Commons to promote the ideal of a free culture and the further production of creative, cultural and scientific works, or to gain reputation or greater distribution for their Work in part through the use and efforts of others. + +For these and/or other purposes and motivations, and without any expectation of additional consideration or compensation, the person associating CC0 with a Work (the "Affirmer"), to the extent that he or she is an owner of Copyright and Related Rights in the Work, voluntarily elects to apply CC0 to the Work and publicly distribute the Work under its terms, with knowledge of his or her Copyright and Related Rights in the Work and the meaning and intended legal effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be protected by copyright and related or neighboring rights ("Copyright and Related Rights"). Copyright and Related Rights include, but are not limited to, the following: +i. the right to reproduce, adapt, distribute, perform, display, communicate, and translate a Work; +ii. moral rights retained by the original author(s) and/or performer(s); +iii. publicity and privacy rights pertaining to a person's image or likeness depicted in a Work; +iv. rights protecting against unfair competition in regards to a Work, subject to the limitations in paragraph 4(a), below; +v. rights protecting the extraction, dissemination, use and reuse of data in a Work; +vi. database rights (such as those arising under Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, and under any national implementation thereof, including any amended or successor version of such directive); and +vii. other similar, equivalent or corresponding rights throughout the world based on applicable law or treaty, and any national implementations thereof. +2. Waiver. To the greatest extent permitted by, but not in contravention of, applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and unconditionally waives, abandons, and surrenders all of Affirmer's Copyright and Related Rights and associated claims and causes of action, whether now known or unknown (including existing as well as future claims and causes of action), in the Work (i) in all territories worldwide, (ii) for the maximum duration provided by applicable law or treaty (including future time extensions), (iii) in any current or future medium and for any number of copies, and (iv) for any purpose whatsoever, including without limitation commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each member of the public at large and to the detriment of Affirmer's heirs and successors, fully intending that such Waiver shall not be subject to revocation, rescission, cancellation, termination, or any other legal or equitable action to disrupt the quiet enjoyment of the Work by the public as contemplated by Affirmer's express Statement of Purpose. +3. Public License Fallback. Should any part of the Waiver for any reason be judged legally invalid or ineffective under applicable law, then the Waiver shall be preserved to the maximum extent permitted taking into account Affirmer's express Statement of Purpose. In addition, to the extent the Waiver is so judged Affirmer hereby grants to each affected person a royalty-free, non transferable, non sublicensable, non exclusive, irrevocable and unconditional license to exercise Affirmer's Copyright and Related Rights in the Work (i) in all territories worldwide, (ii) for the maximum duration provided by applicable law or treaty (including future time extensions), (iii) in any current or future medium and for any number of copies, and (iv) for any purpose whatsoever, including without limitation commercial, advertising or promotional purposes (the "License"). The License shall be deemed effective as of the date CC0 was applied by Affirmer to the Work. Should any part of the License for any reason be judged legally invalid or ineffective under applicable law, such partial invalidity or ineffectiveness shall not invalidate the remainder of the License, and in such case Affirmer hereby affirms that he or she will not (i) exercise any of his or her remaining Copyright and Related Rights in the Work or (ii) assert any associated claims and causes of action with respect to the Work, in either case contrary to Affirmer's express Statement of Purpose. +4. Limitations and Disclaimers. +a. No trademark or patent rights held by Affirmer are waived, abandoned, surrendered, licensed or otherwise affected by this document. +b. Affirmer offers the Work as-is and makes no representations or warranties of any kind concerning the Work, express, implied, statutory or otherwise, including without limitation warranties of title, merchantability, fitness for a particular purpose, non infringement, or the absence of latent or other defects, accuracy, or the present or absence of errors, whether or not discoverable, all to the greatest extent permissible under applicable law. +c. Affirmer disclaims responsibility for clearing rights of other persons that may apply to the Work or any use thereof, including without limitation any person's Copyright and Related Rights in the Work. Further, Affirmer disclaims responsibility for obtaining any necessary consents, permissions or other rights required for any use of the Work. +d. Affirmer understands and acknowledges that Creative Commons is not a party to this document and has no duty or obligation with respect to this CC0 or use of the Work. diff --git a/Samples/3rdParty/khronos/ktx/licenses/GPL-2.0-or-later.txt b/Samples/3rdParty/khronos/ktx/licenses/GPL-2.0-or-later.txt new file mode 100644 index 0000000..607deb3 --- /dev/null +++ b/Samples/3rdParty/khronos/ktx/licenses/GPL-2.0-or-later.txt @@ -0,0 +1,103 @@ +GNU GENERAL PUBLIC LICENSE +Version 2, June 1991 + +Copyright (C) 1989, 1991 Free Software Foundation, Inc. +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + +Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. + +Preamble + +The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. + +When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. + +To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. + +For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. + +We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. + +Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. + +Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. + +The precise terms and conditions for copying, distribution and modification follow. + +TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + +0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". +Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. + +1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. +You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. + +2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: +a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. +b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. +c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) +These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. + +3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: +a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, +b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, +c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) +The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. + +If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. + +4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. +5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. +6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. +7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. +If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. + +This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. + +8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. +9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. +Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. + +10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. +NO WARRANTY + +11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. +12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. +END OF TERMS AND CONDITIONS + +How to Apply These Terms to Your New Programs + +If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. + +To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. + + +Copyright (C) + +This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this when it starts in an interactive mode: + +Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: + +Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. + +, 1 April 1989 Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. diff --git a/Samples/3rdParty/khronos/ktx/licenses/LicenseRef-Cesium-Trademark-Terms.txt b/Samples/3rdParty/khronos/ktx/licenses/LicenseRef-Cesium-Trademark-Terms.txt new file mode 100644 index 0000000..06d1b85 --- /dev/null +++ b/Samples/3rdParty/khronos/ktx/licenses/LicenseRef-Cesium-Trademark-Terms.txt @@ -0,0 +1,39 @@ +The following text is copied and pasted from https://github.com/AnalyticalGraphicsInc/cesium/wiki/CesiumTrademark.pdf, which are the "Trademark Terms and Conditions" under which an image in this repository was contributed by AGI. Please refer to the original link as the authoritative text; this file exists only for REUSE license-checker compliance. + +Trademark Terms and Conditions + +Analytical Graphics®, Cesium®, and Cesium Pro™ are trademarks owned by AGI. AGI uses these +trademarks/logos to identify AGI as a company and also AGI’s products, services and activities. AGI maintains +control over the usage of its trademarks, and this document sets forth who may use these trademarks, and under what +terms and conditions these trademarks may be used. +Fair Use +AGI’s trademarks/logos may be used in instances when the use of such falls under a category of fair use. Examples +of fair use include research, teaching, and educational purposes. If you use AGI’s trademarks/logos for this purpose, +you must give AGI proper credit and identify AGI as the owner of the trademarks/logos. +Do not use AGI’s trademarks/logos in any of your products or services that compete with any of AGI’s products or +services. Use of AGI’s trademark/logos in your competing product or service is trademark infringement, and AGI +will take legal action against you for violating this provision. +Laymen’s Terms: You are not allowed to use the Cesium trademark to compete with AGI. If you do, AGI will take +you to court. You can use the trademark without our permission for socially productive purposes, such as presenting +Cesium in a conference. If you do so, make sure you identify AGI as the owner of the trademark. +Usage Guidelines +Avoid mistakes when reproducing AGI’s trademarks/logos. Do not separate the elements of the logo or alter the +logo in any way. Do not rotate or animate it, and do not use any part of the logo as a graphic element, background, +or pattern in any way that competes with AGI. +Do not translate or localize the logos, and do not add anything to the logos. Do not attempt to set the logotype, +change the font, or alter the size, proportions, or space between letters. You must use AGI’s trademarks/logos +exactly as they are provided by AGI. +Laymen’s Terms: When using our trademark(s), do not modify or change them in any way. +Other Uses +For any uses other than those identified in this document, you must contact AGI for express written permission. AGI +reserves the right to reject your request to use AGI’s trademarks/logos for any reason. AGI’s contact information is +below. +Laymen’s Terms: You need our permission to use our trademarks for any reasons not stated above. If you send us a +request to use our trademarks, we can say no for any reason. + +Corporate Contact Info: +Analytical Graphics, Inc. +220 Valley Creek Blvd. +Exton, Pennsylvania 19341 +1.610.981-8000 +contracts@agi.com diff --git a/Samples/3rdParty/khronos/ktx/licenses/LicenseRef-ETCSLA.txt b/Samples/3rdParty/khronos/ktx/licenses/LicenseRef-ETCSLA.txt new file mode 100644 index 0000000..d47217a --- /dev/null +++ b/Samples/3rdParty/khronos/ktx/licenses/LicenseRef-ETCSLA.txt @@ -0,0 +1,109 @@ +(C) Ericsson AB 2013. All Rights Reserved. + +Software License Agreement + +PLEASE REVIEW THE FOLLOWING TERMS AND CONDITIONS PRIOR TO USING THE +ERICSSON TEXTURE COMPRESSION CODEC SOFTWARE (THE "SOFTWARE"). THE USE +OF THE SOFTWARE IS SUBJECT TO THE TERMS AND CONDITIONS OF THE +FOLLOWING SOFTWARE LICENSE AGREEMENT (THE "SLA"). IF YOU DO NOT ACCEPT +SUCH TERMS AND CONDITIONS YOU MAY NOT USE THE SOFTWARE. + +Subject to the terms and conditions of the SLA, the licensee of the +Software (the "Licensee") hereby, receives a non-exclusive, +non-transferable, limited, free-of-charge, perpetual and worldwide +license, to copy, use, distribute and modify the Software, but only +for the purpose of developing, manufacturing, selling, using and +distributing products including the Software in binary form, which +products are used for compression and/or decompression according to +the Khronos standard specifications OpenGL, OpenGL ES and +WebGL. Notwithstanding anything of the above, Licensee may distribute +[etcdec.cxx] in source code form provided (i) it is in unmodified +form; and (ii) it is included in software owned by Licensee. + +If Licensee institutes, or threatens to institute, patent litigation +against Ericsson or Ericsson's affiliates for using the Software for +developing, having developed, manufacturing, having manufactured, +selling, offer for sale, importing, using, leasing, operating, +repairing and/or distributing products (i) within the scope of the +Khronos framework; or (ii) using software or other intellectual +property rights owned by Ericsson or its affiliates and provided under +the Khronos framework, Ericsson shall have the right to terminate this +SLA with immediate effect. Moreover, if Licensee institutes, or +threatens to institute, patent litigation against any other licensee +of the Software for using the Software in products within the scope of +the Khronos framework, Ericsson shall have the right to terminate this +SLA with immediate effect. However, should Licensee institute, or +threaten to institute, patent litigation against any other licensee of +the Software based on such other licensee's use of any other software +together with the Software, then Ericsson shall have no right to +terminate this SLA. + +This SLA does not transfer to Licensee any ownership to any Ericsson +or third party intellectual property rights. All rights not expressly +granted by Ericsson under this SLA are hereby expressly +reserved. Furthermore, nothing in this SLA shall be construed as a +right to use or sell products in a manner which conveys or purports to +convey whether explicitly, by principles of implied license, or +otherwise, any rights to any third party, under any patent of Ericsson +or of Ericsson's affiliates covering or relating to any combination of +the Software with any other software or product (not licensed +hereunder) where the right applies specifically to the combination and +not to the software or product itself. + +THE SOFTWARE IS PROVIDED "AS IS". ERICSSON MAKES NO REPRESENTATIONS OF +ANY KIND, EXTENDS NO WARRANTIES OR CONDITIONS OF ANY KIND, EITHER +EXPRESS, IMPLIED OR STATUTORY; INCLUDING, BUT NOT LIMITED TO, EXPRESS, +IMPLIED OR STATUTORY WARRANTIES OR CONDITIONS OF TITLE, +MERCHANTABILITY, SATISFACTORY QUALITY, SUITABILITY, AND FITNESS FOR A +PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE +OF THE SOFTWARE IS WITH THE LICENSEE. SHOULD THE SOFTWARE PROVE +DEFECTIVE, THE LICENSEE ASSUMES THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. ERICSSON MAKES NO WARRANTY THAT THE MANUFACTURE, +SALE, OFFERING FOR SALE, DISTRIBUTION, LEASE, USE OR IMPORTATION UNDER +THE SLA WILL BE FREE FROM INFRINGEMENT OF PATENTS, COPYRIGHTS OR OTHER +INTELLECTUAL PROPERTY RIGHTS OF OTHERS, AND THE VALIDITY OF THE +LICENSE AND THE SLA ARE SUBJECT TO LICENSEE'S SOLE RESPONSIBILITY TO +MAKE SUCH DETERMINATION AND ACQUIRE SUCH LICENSES AS MAY BE NECESSARY +WITH RESPECT TO PATENTS, COPYRIGHT AND OTHER INTELLECTUAL PROPERTY OF +THIRD PARTIES. + +THE LICENSEE ACKNOWLEDGES AND ACCEPTS THAT THE SOFTWARE (I) IS NOT +LICENSED FOR; (II) IS NOT DESIGNED FOR OR INTENDED FOR; AND (III) MAY +NOT BE USED FOR; ANY MISSION CRITICAL APPLICATIONS SUCH AS, BUT NOT +LIMITED TO OPERATION OF NUCLEAR OR HEALTHCARE COMPUTER SYSTEMS AND/OR +NETWORKS, AIRCRAFT OR TRAIN CONTROL AND/OR COMMUNICATION SYSTEMS OR +ANY OTHER COMPUTER SYSTEMS AND/OR NETWORKS OR CONTROL AND/OR +COMMUNICATION SYSTEMS ALL IN WHICH CASE THE FAILURE OF THE SOFTWARE +COULD LEAD TO DEATH, PERSONAL INJURY, OR SEVERE PHYSICAL, MATERIAL OR +ENVIRONMENTAL DAMAGE. LICENSEE'S RIGHTS UNDER THIS LICENSE WILL +TERMINATE AUTOMATICALLY AND IMMEDIATELY WITHOUT NOTICE IF LICENSEE +FAILS TO COMPLY WITH THIS PARAGRAPH. + +IN NO EVENT SHALL ERICSSON BE LIABLE FOR ANY DAMAGES WHATSOEVER, +INCLUDING BUT NOT LIMITED TO PERSONAL INJURY, ANY GENERAL, SPECIAL, +INDIRECT, INCIDENTAL OR CONSEQUENTIAL DAMAGES, ARISING OUT OF OR IN +CONNECTION WITH THE USE OR INABILITY TO USE THE SOFTWARE (INCLUDING +BUT NOT LIMITED TO LOSS OF PROFITS, BUSINESS INTERUPTIONS, OR ANY +OTHER COMMERCIAL DAMAGES OR LOSSES, LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY THE LICENSEE OR THIRD +PARTIES OR A FAILURE OF THE SOFTWARE TO OPERATE WITH ANY OTHER +SOFTWARE) REGARDLESS OF THE THEORY OF LIABILITY (CONTRACT, TORT, OR +OTHERWISE), EVEN IF THE LICENSEE OR ANY OTHER PARTY HAS BEEN ADVISED +OF THE POSSIBILITY OF SUCH DAMAGES. + +Licensee acknowledges that "ERICSSON ///" is the corporate trademark +of Telefonaktiebolaget LM Ericsson and that both "Ericsson" and the +figure "///" are important features of the trade names of +Telefonaktiebolaget LM Ericsson. Nothing contained in these terms and +conditions shall be deemed to grant Licensee any right, title or +interest in the word "Ericsson" or the figure "///". No delay or +omission by Ericsson to exercise any right or power shall impair any +such right or power to be construed to be a waiver thereof. Consent by +Ericsson to, or waiver of, a breach by the Licensee shall not +constitute consent to, waiver of, or excuse for any other different or +subsequent breach. + +This SLA shall be governed by the substantive law of Sweden. Any +dispute, controversy or claim arising out of or in connection with +this SLA, or the breach, termination or invalidity thereof, shall be +submitted to the exclusive jurisdiction of the Swedish Courts. diff --git a/Samples/3rdParty/khronos/ktx/licenses/LicenseRef-HI-Trademark.txt b/Samples/3rdParty/khronos/ktx/licenses/LicenseRef-HI-Trademark.txt new file mode 100644 index 0000000..0ccddc9 --- /dev/null +++ b/Samples/3rdParty/khronos/ktx/licenses/LicenseRef-HI-Trademark.txt @@ -0,0 +1,5 @@ +The HI logo textures are copyright by & trademarks of HI Corporation and are +provided for use only in testing the KTX loader. Any other use requires +specific prior written permission from HI. Furthermore the name HI may +not be used to endorse or promote products derived from this software +without specific prior written permission. diff --git a/Samples/3rdParty/khronos/ktx/licenses/LicenseRef-Kodak.txt b/Samples/3rdParty/khronos/ktx/licenses/LicenseRef-Kodak.txt new file mode 100644 index 0000000..9a7f26b --- /dev/null +++ b/Samples/3rdParty/khronos/ktx/licenses/LicenseRef-Kodak.txt @@ -0,0 +1,7 @@ +This LICENSES file applies to images from the Kodak Lossless True Color +Image Suite, and exists only for REUSE compliance. According to +http://r0k.us/graphics/kodak/ , for such images: + +"It is my understanding they have been released by the Eastman Kodak Company +for unrestricted usage. Many sites use them as a standard test suite for +compression testing, etc." diff --git a/Samples/3rdParty/khronos/ktx/licenses/LicenseRef-PNGSuite.txt b/Samples/3rdParty/khronos/ktx/licenses/LicenseRef-PNGSuite.txt new file mode 100644 index 0000000..1bb39a9 --- /dev/null +++ b/Samples/3rdParty/khronos/ktx/licenses/LicenseRef-PNGSuite.txt @@ -0,0 +1,14 @@ +The following text is copied and pasted from +http://www.schaik.com/pngsuite/PngSuite.LICENSE , which is the license under +which an image from the PngSuite project is used. Please refer to the +original link as the authoritative text; this file exists only for REUSE +license-checker compliance. + +PngSuite +-------- + +Permission to use, copy, modify and distribute these images for any +purpose and without fee is hereby granted. + + +(c) Willem van Schaik, 1996, 2011 diff --git a/Samples/3rdParty/khronos/ktx/licenses/MIT.txt b/Samples/3rdParty/khronos/ktx/licenses/MIT.txt new file mode 100644 index 0000000..204b93d --- /dev/null +++ b/Samples/3rdParty/khronos/ktx/licenses/MIT.txt @@ -0,0 +1,19 @@ +MIT License Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice (including the next +paragraph) shall be included in all copies or substantial portions of the +Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS +OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF +OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/Samples/3rdParty/khronos/ktx/licenses/Zlib.txt b/Samples/3rdParty/khronos/ktx/licenses/Zlib.txt new file mode 100644 index 0000000..508a9b5 --- /dev/null +++ b/Samples/3rdParty/khronos/ktx/licenses/Zlib.txt @@ -0,0 +1,20 @@ +Copyright (c) + +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. diff --git a/Samples/3rdParty/khronos/ktx/licenses/ktx_LICENSE.md b/Samples/3rdParty/khronos/ktx/licenses/ktx_LICENSE.md new file mode 100644 index 0000000..7b6d05f --- /dev/null +++ b/Samples/3rdParty/khronos/ktx/licenses/ktx_LICENSE.md @@ -0,0 +1,36 @@ +LICENSE file for the KhronosGroup/KTX-Software project {#license} +====================================================== + + + +Files unique to this repository generally fall under the Apache 2.0 license +with copyright holders including Mark Callow, the KTX-Software author; The +Khronos Group Inc., which has supported KTX development; and other +contributors to the KTX project. + +Because KTX-Software incorporates material and contributions from many other +projects, which often have their own licenses, there are many other licenses +in use in this repository. While there are many licenses in this repository, +with rare exceptions all are open source licenses that we believe to be +mutually compatible. + +The complete text of each of the licenses used in this repository is found +in LICENSES/*.txt . Additionally, we have updated the repository to pass the +REUSE compliance checker tool (see https://reuse.software/). REUSE verifies +that every file in a git repository either incorporates a license, or that +the license is present in auxiliary files such as .reuse/dep5 . To obtain a +bill of materials for the repository identifying the license for each file, +install the REUSE tool and run + + reuse spdx + +inside the repository. + +## Special Cases + +The file lib/etcdec.cxx is not open source. It is made available under the +terms of an Ericsson license, found in the file itself. diff --git a/Samples/3rdParty/khronos/openxr/OpenXR-SDK/src/common/xr_linear.h b/Samples/3rdParty/khronos/openxr/OpenXR-SDK/src/common/xr_linear.h new file mode 100755 index 0000000..2b295ed --- /dev/null +++ b/Samples/3rdParty/khronos/openxr/OpenXR-SDK/src/common/xr_linear.h @@ -0,0 +1,869 @@ +// Copyright (c) 2017-2024, The Khronos Group Inc. +// Copyright (c) 2016, Oculus VR, LLC. +// +// SPDX-License-Identifier: Apache-2.0 +// +// 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. +// +// Author: J.M.P. van Waveren +// + +#ifndef XR_LINEAR_H_ +#define XR_LINEAR_H_ + +#include + +/* REUSE-IgnoreStart */ +/* The following has copyright notices that duplicate the header above */ + +/* +================================================================================================ + +Description : Vector, matrix and quaternion math. +Orig. Author : J.M.P. van Waveren +Orig. Date : 12/10/2016 +Language : C99 +Copyright : Copyright (c) 2016 Oculus VR, LLC. All Rights reserved. + + +DESCRIPTION +=========== + +All matrices are column-major. + +INTERFACE +========= + +XrVector2f +XrVector3f +XrVector4f +XrQuaternionf +XrPosef +XrMatrix4x4f + +inline static void XrVector3f_Set(XrVector3f* v, const float value); +inline static void XrVector3f_Add(XrVector3f* result, const XrVector3f* a, const XrVector3f* b); +inline static void XrVector3f_Sub(XrVector3f* result, const XrVector3f* a, const XrVector3f* b); +inline static void XrVector3f_Min(XrVector3f* result, const XrVector3f* a, const XrVector3f* b); +inline static void XrVector3f_Max(XrVector3f* result, const XrVector3f* a, const XrVector3f* b); +inline static void XrVector3f_Decay(XrVector3f* result, const XrVector3f* a, const float value); +inline static void XrVector3f_Lerp(XrVector3f* result, const XrVector3f* a, const XrVector3f* b, const float fraction); +inline static void XrVector3f_Scale(XrVector3f* result, const XrVector3f* a, const float scaleFactor); +inline static void XrVector3f_Normalize(XrVector3f* v); +inline static float XrVector3f_Length(const XrVector3f* v); + +inline static void XrQuaternionf_CreateIdentity(XrQuaternionf* q); +inline static void XrQuaternionf_CreateFromAxisAngle(XrQuaternionf* result, const XrVector3f* axis, const float angleInRadians); +inline static void XrQuaternionf_Lerp(XrQuaternionf* result, const XrQuaternionf* a, const XrQuaternionf* b, const float fraction); +inline static void XrQuaternionf_Multiply(XrQuaternionf* result, const XrQuaternionf* a, const XrQuaternionf* b); +inline static void XrQuaternionf_Invert(XrQuaternionf* result, const XrQuaternionf* q); +inline static void XrQuaternionf_Normalize(XrQuaternionf* q); +inline static void XrQuaternionf_RotateVector3f(XrVector3f* result, const XrQuaternionf* a, const XrVector3f* v); + +inline static void XrPosef_CreateIdentity(XrPosef* result); +inline static void XrPosef_TransformVector3f(XrVector3f* result, const XrPosef* a, const XrVector3f* v); +inline static void XrPosef_Multiply(XrPosef* result, const XrPosef* a, const XrPosef* b); +inline static void XrPosef_Invert(XrPosef* result, const XrPosef* a); + +inline static void XrMatrix4x4f_CreateIdentity(XrMatrix4x4f* result); +inline static void XrMatrix4x4f_CreateTranslation(XrMatrix4x4f* result, const float x, const float y, const float z); +inline static void XrMatrix4x4f_CreateRotation(XrMatrix4x4f* result, const float degreesX, const float degreesY, + const float degreesZ); +inline static void XrMatrix4x4f_CreateScale(XrMatrix4x4f* result, const float x, const float y, const float z); +inline static void XrMatrix4x4f_CreateTranslationRotationScale(XrMatrix4x4f* result, const XrVector3f* translation, + const XrQuaternionf* rotation, const XrVector3f* scale); +inline static void XrMatrix4x4f_CreateFromRigidTransform(XrMatrix4x4f* result, const XrPosef* s); +inline static void XrMatrix4x4f_CreateProjection(XrMatrix4x4f* result, GraphicsAPI graphicsApi, const float tanAngleLeft, + const float tanAngleRight, const float tanAngleUp, float const tanAngleDown, + const float nearZ, const float farZ); +inline static void XrMatrix4x4f_CreateProjectionFov(XrMatrix4x4f* result, GraphicsAPI graphicsApi, const XrFovf fov, + const float nearZ, const float farZ); +inline static void XrMatrix4x4f_CreateFromQuaternion(XrMatrix4x4f* result, const XrQuaternionf* quat); +inline static void XrMatrix4x4f_CreateOffsetScaleForBounds(XrMatrix4x4f* result, const XrMatrix4x4f* matrix, const XrVector3f* mins, + const XrVector3f* maxs); + +inline static bool XrMatrix4x4f_IsAffine(const XrMatrix4x4f* matrix, const float epsilon); +inline static bool XrMatrix4x4f_IsOrthogonal(const XrMatrix4x4f* matrix, const float epsilon); +inline static bool XrMatrix4x4f_IsOrthonormal(const XrMatrix4x4f* matrix, const float epsilon); +inline static bool XrMatrix4x4f_IsRigidBody(const XrMatrix4x4f* matrix, const float epsilon); + +inline static void XrMatrix4x4f_GetTranslation(XrVector3f* result, const XrMatrix4x4f* src); +inline static void XrMatrix4x4f_GetRotation(XrQuaternionf* result, const XrMatrix4x4f* src); +inline static void XrMatrix4x4f_GetScale(XrVector3f* result, const XrMatrix4x4f* src); + +inline static void XrMatrix4x4f_Multiply(XrMatrix4x4f* result, const XrMatrix4x4f* a, const XrMatrix4x4f* b); +inline static void XrMatrix4x4f_Transpose(XrMatrix4x4f* result, const XrMatrix4x4f* src); +inline static void XrMatrix4x4f_Invert(XrMatrix4x4f* result, const XrMatrix4x4f* src); +inline static void XrMatrix4x4f_InvertRigidBody(XrMatrix4x4f* result, const XrMatrix4x4f* src); + +inline static void XrMatrix4x4f_TransformVector3f(XrVector3f* result, const XrMatrix4x4f* m, const XrVector3f* v); +inline static void XrMatrix4x4f_TransformVector4f(XrVector4f* result, const XrMatrix4x4f* m, const XrVector4f* v); + +inline static void XrMatrix4x4f_TransformBounds(XrVector3f* resultMins, XrVector3f* resultMaxs, const XrMatrix4x4f* matrix, + const XrVector3f* mins, const XrVector3f* maxs); +inline static bool XrMatrix4x4f_CullBounds(const XrMatrix4x4f* mvp, const XrVector3f* mins, const XrVector3f* maxs); + +================================================================================================ +*/ + +#include +#include +#include + +#define MATH_PI 3.14159265358979323846f + +#define DEFAULT_NEAR_Z 0.015625f // exact floating point representation +#define INFINITE_FAR_Z 0.0f + +static const XrColor4f XrColorRed = {1.0f, 0.0f, 0.0f, 1.0f}; +static const XrColor4f XrColorGreen = {0.0f, 1.0f, 0.0f, 1.0f}; +static const XrColor4f XrColorBlue = {0.0f, 0.0f, 1.0f, 1.0f}; +static const XrColor4f XrColorYellow = {1.0f, 1.0f, 0.0f, 1.0f}; +static const XrColor4f XrColorPurple = {1.0f, 0.0f, 1.0f, 1.0f}; +static const XrColor4f XrColorCyan = {0.0f, 1.0f, 1.0f, 1.0f}; +static const XrColor4f XrColorLightGrey = {0.7f, 0.7f, 0.7f, 1.0f}; +static const XrColor4f XrColorDarkGrey = {0.3f, 0.3f, 0.3f, 1.0f}; + +typedef enum GraphicsAPI { GRAPHICS_VULKAN, GRAPHICS_OPENGL, GRAPHICS_OPENGL_ES, GRAPHICS_D3D } GraphicsAPI; + +// Column-major, pre-multiplied. This type does not exist in the OpenXR API and is provided for convenience. +typedef struct XrMatrix4x4f { + float m[16]; +} XrMatrix4x4f; + +inline static float XrRcpSqrt(const float x) { + const float SMALLEST_NON_DENORMAL = 1.1754943508222875e-038f; // ( 1U << 23 ) + const float rcp = (x >= SMALLEST_NON_DENORMAL) ? 1.0f / sqrtf(x) : 1.0f; + return rcp; +} + +inline static float XrVector2f_Length(const XrVector2f* v) { return sqrtf(v->x * v->x + v->y * v->y); } + +inline static void XrVector3f_Set(XrVector3f* v, const float value) { + v->x = value; + v->y = value; + v->z = value; +} + +inline static void XrVector3f_Add(XrVector3f* result, const XrVector3f* a, const XrVector3f* b) { + result->x = a->x + b->x; + result->y = a->y + b->y; + result->z = a->z + b->z; +} + +inline static void XrVector3f_Sub(XrVector3f* result, const XrVector3f* a, const XrVector3f* b) { + result->x = a->x - b->x; + result->y = a->y - b->y; + result->z = a->z - b->z; +} + +inline static void XrVector3f_Min(XrVector3f* result, const XrVector3f* a, const XrVector3f* b) { + result->x = (a->x < b->x) ? a->x : b->x; + result->y = (a->y < b->y) ? a->y : b->y; + result->z = (a->z < b->z) ? a->z : b->z; +} + +inline static void XrVector3f_Max(XrVector3f* result, const XrVector3f* a, const XrVector3f* b) { + result->x = (a->x > b->x) ? a->x : b->x; + result->y = (a->y > b->y) ? a->y : b->y; + result->z = (a->z > b->z) ? a->z : b->z; +} + +inline static void XrVector3f_Decay(XrVector3f* result, const XrVector3f* a, const float value) { + result->x = (fabsf(a->x) > value) ? ((a->x > 0.0f) ? (a->x - value) : (a->x + value)) : 0.0f; + result->y = (fabsf(a->y) > value) ? ((a->y > 0.0f) ? (a->y - value) : (a->y + value)) : 0.0f; + result->z = (fabsf(a->z) > value) ? ((a->z > 0.0f) ? (a->z - value) : (a->z + value)) : 0.0f; +} + +inline static void XrVector3f_Lerp(XrVector3f* result, const XrVector3f* a, const XrVector3f* b, const float fraction) { + result->x = a->x + fraction * (b->x - a->x); + result->y = a->y + fraction * (b->y - a->y); + result->z = a->z + fraction * (b->z - a->z); +} + +inline static void XrVector3f_Scale(XrVector3f* result, const XrVector3f* a, const float scaleFactor) { + result->x = a->x * scaleFactor; + result->y = a->y * scaleFactor; + result->z = a->z * scaleFactor; +} + +inline static float XrVector3f_Dot(const XrVector3f* a, const XrVector3f* b) { return a->x * b->x + a->y * b->y + a->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 Cross(a, b). +inline static void XrVector3f_Cross(XrVector3f* result, const XrVector3f* a, const XrVector3f* b) { + result->x = a->y * b->z - a->z * b->y; + result->y = a->z * b->x - a->x * b->z; + result->z = a->x * b->y - a->y * b->x; +} + +inline static void XrVector3f_Normalize(XrVector3f* v) { + const float lengthRcp = XrRcpSqrt(v->x * v->x + v->y * v->y + v->z * v->z); + v->x *= lengthRcp; + v->y *= lengthRcp; + v->z *= lengthRcp; +} + +inline static float XrVector3f_Length(const XrVector3f* v) { return sqrtf(v->x * v->x + v->y * v->y + v->z * v->z); } + +inline static void XrQuaternionf_CreateIdentity(XrQuaternionf* q) { + q->x = 0.0f; + q->y = 0.0f; + q->z = 0.0f; + q->w = 1.0f; +} + +inline static void XrQuaternionf_CreateFromAxisAngle(XrQuaternionf* result, const XrVector3f* axis, const float angleInRadians) { + float s = sinf(angleInRadians / 2.0f); + float lengthRcp = XrRcpSqrt(axis->x * axis->x + axis->y * axis->y + axis->z * axis->z); + result->x = s * axis->x * lengthRcp; + result->y = s * axis->y * lengthRcp; + result->z = s * axis->z * lengthRcp; + result->w = cosf(angleInRadians / 2.0f); +} + +inline static void XrQuaternionf_Lerp(XrQuaternionf* result, const XrQuaternionf* a, const XrQuaternionf* b, const float fraction) { + const float s = a->x * b->x + a->y * b->y + a->z * b->z + a->w * b->w; + const float fa = 1.0f - fraction; + const float fb = (s < 0.0f) ? -fraction : fraction; + const float x = a->x * fa + b->x * fb; + const float y = a->y * fa + b->y * fb; + const float z = a->z * fa + b->z * fb; + const float w = a->w * fa + b->w * fb; + const float lengthRcp = XrRcpSqrt(x * x + y * y + z * z + w * w); + result->x = x * lengthRcp; + result->y = y * lengthRcp; + result->z = z * lengthRcp; + result->w = w * lengthRcp; +} + +inline static void XrQuaternionf_Multiply(XrQuaternionf* result, const XrQuaternionf* a, const XrQuaternionf* b) { + result->x = (b->w * a->x) + (b->x * a->w) + (b->y * a->z) - (b->z * a->y); + result->y = (b->w * a->y) - (b->x * a->z) + (b->y * a->w) + (b->z * a->x); + result->z = (b->w * a->z) + (b->x * a->y) - (b->y * a->x) + (b->z * a->w); + result->w = (b->w * a->w) - (b->x * a->x) - (b->y * a->y) - (b->z * a->z); +} + +inline static void XrQuaternionf_Invert(XrQuaternionf* result, const XrQuaternionf* q) { + result->x = -q->x; + result->y = -q->y; + result->z = -q->z; + result->w = q->w; +} + +inline static void XrQuaternionf_Normalize(XrQuaternionf* q) { + const float lengthRcp = XrRcpSqrt(q->x * q->x + q->y * q->y + q->z * q->z + q->w * q->w); + q->x *= lengthRcp; + q->y *= lengthRcp; + q->z *= lengthRcp; + q->w *= lengthRcp; +} + +inline static void XrQuaternionf_RotateVector3f(XrVector3f* result, const XrQuaternionf* a, const XrVector3f* v) { + XrQuaternionf q = {v->x, v->y, v->z, 0.0f}; + XrQuaternionf aq; + XrQuaternionf_Multiply(&aq, &q, a); + XrQuaternionf aInv; + XrQuaternionf_Invert(&aInv, a); + XrQuaternionf aqaInv; + XrQuaternionf_Multiply(&aqaInv, &aInv, &aq); + + result->x = aqaInv.x; + result->y = aqaInv.y; + result->z = aqaInv.z; +} + +inline static void XrPosef_CreateIdentity(XrPosef* result) { + XrQuaternionf_CreateIdentity(&result->orientation); + XrVector3f_Set(&result->position, 0); +} + +inline static void XrPosef_TransformVector3f(XrVector3f* result, const XrPosef* a, const XrVector3f* v) { + XrVector3f r0; + XrQuaternionf_RotateVector3f(&r0, &a->orientation, v); + XrVector3f_Add(result, &r0, &a->position); +} + +inline static void XrPosef_Multiply(XrPosef* result, const XrPosef* a, const XrPosef* b) { + XrQuaternionf_Multiply(&result->orientation, &b->orientation, &a->orientation); + XrPosef_TransformVector3f(&result->position, a, &b->position); +} + +inline static void XrPosef_Invert(XrPosef* result, const XrPosef* a) { + XrQuaternionf_Invert(&result->orientation, &a->orientation); + XrVector3f aPosNeg; + XrVector3f_Scale(&aPosNeg, &a->position, -1.0f); + XrQuaternionf_RotateVector3f(&result->position, &result->orientation, &aPosNeg); +} + +// Use left-multiplication to accumulate transformations. +inline static void XrMatrix4x4f_Multiply(XrMatrix4x4f* result, const XrMatrix4x4f* a, const XrMatrix4x4f* b) { + result->m[0] = a->m[0] * b->m[0] + a->m[4] * b->m[1] + a->m[8] * b->m[2] + a->m[12] * b->m[3]; + result->m[1] = a->m[1] * b->m[0] + a->m[5] * b->m[1] + a->m[9] * b->m[2] + a->m[13] * b->m[3]; + result->m[2] = a->m[2] * b->m[0] + a->m[6] * b->m[1] + a->m[10] * b->m[2] + a->m[14] * b->m[3]; + result->m[3] = a->m[3] * b->m[0] + a->m[7] * b->m[1] + a->m[11] * b->m[2] + a->m[15] * b->m[3]; + + result->m[4] = a->m[0] * b->m[4] + a->m[4] * b->m[5] + a->m[8] * b->m[6] + a->m[12] * b->m[7]; + result->m[5] = a->m[1] * b->m[4] + a->m[5] * b->m[5] + a->m[9] * b->m[6] + a->m[13] * b->m[7]; + result->m[6] = a->m[2] * b->m[4] + a->m[6] * b->m[5] + a->m[10] * b->m[6] + a->m[14] * b->m[7]; + result->m[7] = a->m[3] * b->m[4] + a->m[7] * b->m[5] + a->m[11] * b->m[6] + a->m[15] * b->m[7]; + + result->m[8] = a->m[0] * b->m[8] + a->m[4] * b->m[9] + a->m[8] * b->m[10] + a->m[12] * b->m[11]; + result->m[9] = a->m[1] * b->m[8] + a->m[5] * b->m[9] + a->m[9] * b->m[10] + a->m[13] * b->m[11]; + result->m[10] = a->m[2] * b->m[8] + a->m[6] * b->m[9] + a->m[10] * b->m[10] + a->m[14] * b->m[11]; + result->m[11] = a->m[3] * b->m[8] + a->m[7] * b->m[9] + a->m[11] * b->m[10] + a->m[15] * b->m[11]; + + result->m[12] = a->m[0] * b->m[12] + a->m[4] * b->m[13] + a->m[8] * b->m[14] + a->m[12] * b->m[15]; + result->m[13] = a->m[1] * b->m[12] + a->m[5] * b->m[13] + a->m[9] * b->m[14] + a->m[13] * b->m[15]; + result->m[14] = a->m[2] * b->m[12] + a->m[6] * b->m[13] + a->m[10] * b->m[14] + a->m[14] * b->m[15]; + result->m[15] = a->m[3] * b->m[12] + a->m[7] * b->m[13] + a->m[11] * b->m[14] + a->m[15] * b->m[15]; +} + +// Creates the transpose of the given matrix. +inline static void XrMatrix4x4f_Transpose(XrMatrix4x4f* result, const XrMatrix4x4f* src) { + result->m[0] = src->m[0]; + result->m[1] = src->m[4]; + result->m[2] = src->m[8]; + result->m[3] = src->m[12]; + + result->m[4] = src->m[1]; + result->m[5] = src->m[5]; + result->m[6] = src->m[9]; + result->m[7] = src->m[13]; + + result->m[8] = src->m[2]; + result->m[9] = src->m[6]; + result->m[10] = src->m[10]; + result->m[11] = src->m[14]; + + result->m[12] = src->m[3]; + result->m[13] = src->m[7]; + result->m[14] = src->m[11]; + result->m[15] = src->m[15]; +} + +// Returns a 3x3 minor of a 4x4 matrix. +inline static float XrMatrix4x4f_Minor(const XrMatrix4x4f* matrix, int r0, int r1, int r2, int c0, int c1, int c2) { + return matrix->m[4 * r0 + c0] * + (matrix->m[4 * r1 + c1] * matrix->m[4 * r2 + c2] - matrix->m[4 * r2 + c1] * matrix->m[4 * r1 + c2]) - + matrix->m[4 * r0 + c1] * + (matrix->m[4 * r1 + c0] * matrix->m[4 * r2 + c2] - matrix->m[4 * r2 + c0] * matrix->m[4 * r1 + c2]) + + matrix->m[4 * r0 + c2] * + (matrix->m[4 * r1 + c0] * matrix->m[4 * r2 + c1] - matrix->m[4 * r2 + c0] * matrix->m[4 * r1 + c1]); +} + +// Calculates the inverse of a 4x4 matrix. +inline static void XrMatrix4x4f_Invert(XrMatrix4x4f* result, const XrMatrix4x4f* src) { + const float rcpDet = + 1.0f / (src->m[0] * XrMatrix4x4f_Minor(src, 1, 2, 3, 1, 2, 3) - src->m[1] * XrMatrix4x4f_Minor(src, 1, 2, 3, 0, 2, 3) + + src->m[2] * XrMatrix4x4f_Minor(src, 1, 2, 3, 0, 1, 3) - src->m[3] * XrMatrix4x4f_Minor(src, 1, 2, 3, 0, 1, 2)); + + result->m[0] = XrMatrix4x4f_Minor(src, 1, 2, 3, 1, 2, 3) * rcpDet; + result->m[1] = -XrMatrix4x4f_Minor(src, 0, 2, 3, 1, 2, 3) * rcpDet; + result->m[2] = XrMatrix4x4f_Minor(src, 0, 1, 3, 1, 2, 3) * rcpDet; + result->m[3] = -XrMatrix4x4f_Minor(src, 0, 1, 2, 1, 2, 3) * rcpDet; + result->m[4] = -XrMatrix4x4f_Minor(src, 1, 2, 3, 0, 2, 3) * rcpDet; + result->m[5] = XrMatrix4x4f_Minor(src, 0, 2, 3, 0, 2, 3) * rcpDet; + result->m[6] = -XrMatrix4x4f_Minor(src, 0, 1, 3, 0, 2, 3) * rcpDet; + result->m[7] = XrMatrix4x4f_Minor(src, 0, 1, 2, 0, 2, 3) * rcpDet; + result->m[8] = XrMatrix4x4f_Minor(src, 1, 2, 3, 0, 1, 3) * rcpDet; + result->m[9] = -XrMatrix4x4f_Minor(src, 0, 2, 3, 0, 1, 3) * rcpDet; + result->m[10] = XrMatrix4x4f_Minor(src, 0, 1, 3, 0, 1, 3) * rcpDet; + result->m[11] = -XrMatrix4x4f_Minor(src, 0, 1, 2, 0, 1, 3) * rcpDet; + result->m[12] = -XrMatrix4x4f_Minor(src, 1, 2, 3, 0, 1, 2) * rcpDet; + result->m[13] = XrMatrix4x4f_Minor(src, 0, 2, 3, 0, 1, 2) * rcpDet; + result->m[14] = -XrMatrix4x4f_Minor(src, 0, 1, 3, 0, 1, 2) * rcpDet; + result->m[15] = XrMatrix4x4f_Minor(src, 0, 1, 2, 0, 1, 2) * rcpDet; +} + +// Calculates the inverse of a rigid body transform. +inline static void XrMatrix4x4f_InvertRigidBody(XrMatrix4x4f* result, const XrMatrix4x4f* src) { + result->m[0] = src->m[0]; + result->m[1] = src->m[4]; + result->m[2] = src->m[8]; + result->m[3] = 0.0f; + result->m[4] = src->m[1]; + result->m[5] = src->m[5]; + result->m[6] = src->m[9]; + result->m[7] = 0.0f; + result->m[8] = src->m[2]; + result->m[9] = src->m[6]; + result->m[10] = src->m[10]; + result->m[11] = 0.0f; + result->m[12] = -(src->m[0] * src->m[12] + src->m[1] * src->m[13] + src->m[2] * src->m[14]); + result->m[13] = -(src->m[4] * src->m[12] + src->m[5] * src->m[13] + src->m[6] * src->m[14]); + result->m[14] = -(src->m[8] * src->m[12] + src->m[9] * src->m[13] + src->m[10] * src->m[14]); + result->m[15] = 1.0f; +} + +// Creates an identity matrix. +inline static void XrMatrix4x4f_CreateIdentity(XrMatrix4x4f* result) { + result->m[0] = 1.0f; + result->m[1] = 0.0f; + result->m[2] = 0.0f; + result->m[3] = 0.0f; + result->m[4] = 0.0f; + result->m[5] = 1.0f; + result->m[6] = 0.0f; + result->m[7] = 0.0f; + result->m[8] = 0.0f; + result->m[9] = 0.0f; + result->m[10] = 1.0f; + result->m[11] = 0.0f; + result->m[12] = 0.0f; + result->m[13] = 0.0f; + result->m[14] = 0.0f; + result->m[15] = 1.0f; +} + +// Creates a translation matrix. +inline static void XrMatrix4x4f_CreateTranslation(XrMatrix4x4f* result, const float x, const float y, const float z) { + result->m[0] = 1.0f; + result->m[1] = 0.0f; + result->m[2] = 0.0f; + result->m[3] = 0.0f; + result->m[4] = 0.0f; + result->m[5] = 1.0f; + result->m[6] = 0.0f; + result->m[7] = 0.0f; + result->m[8] = 0.0f; + result->m[9] = 0.0f; + result->m[10] = 1.0f; + result->m[11] = 0.0f; + result->m[12] = x; + result->m[13] = y; + result->m[14] = z; + result->m[15] = 1.0f; +} + +// Creates a rotation matrix. +// If -Z=forward, +Y=up, +X=right, then radiansX=pitch, radiansY=yaw, radiansZ=roll. +inline static void XrMatrix4x4f_CreateRotationRadians(XrMatrix4x4f* result, const float radiansX, const float radiansY, + const float radiansZ) { + const float sinX = sinf(radiansX); + const float cosX = cosf(radiansX); + const XrMatrix4x4f rotationX = {{1, 0, 0, 0, 0, cosX, sinX, 0, 0, -sinX, cosX, 0, 0, 0, 0, 1}}; + const float sinY = sinf(radiansY); + const float cosY = cosf(radiansY); + const XrMatrix4x4f rotationY = {{cosY, 0, -sinY, 0, 0, 1, 0, 0, sinY, 0, cosY, 0, 0, 0, 0, 1}}; + const float sinZ = sinf(radiansZ); + const float cosZ = cosf(radiansZ); + const XrMatrix4x4f rotationZ = {{cosZ, sinZ, 0, 0, -sinZ, cosZ, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1}}; + XrMatrix4x4f rotationXY; + XrMatrix4x4f_Multiply(&rotationXY, &rotationY, &rotationX); + XrMatrix4x4f_Multiply(result, &rotationZ, &rotationXY); +} + +// Creates a rotation matrix. +// If -Z=forward, +Y=up, +X=right, then degreesX=pitch, degreesY=yaw, degreesZ=roll. +inline static void XrMatrix4x4f_CreateRotation(XrMatrix4x4f* result, const float degreesX, const float degreesY, + const float degreesZ) { + XrMatrix4x4f_CreateRotationRadians(result, degreesX * (MATH_PI / 180.0f), degreesY * (MATH_PI / 180.0f), + degreesZ * (MATH_PI / 180.0f)); +} + +// Creates a scale matrix. +inline static void XrMatrix4x4f_CreateScale(XrMatrix4x4f* result, const float x, const float y, const float z) { + result->m[0] = x; + result->m[1] = 0.0f; + result->m[2] = 0.0f; + result->m[3] = 0.0f; + result->m[4] = 0.0f; + result->m[5] = y; + result->m[6] = 0.0f; + result->m[7] = 0.0f; + result->m[8] = 0.0f; + result->m[9] = 0.0f; + result->m[10] = z; + result->m[11] = 0.0f; + result->m[12] = 0.0f; + result->m[13] = 0.0f; + result->m[14] = 0.0f; + result->m[15] = 1.0f; +} + +// Creates a matrix from a quaternion. +inline static void XrMatrix4x4f_CreateFromQuaternion(XrMatrix4x4f* result, const XrQuaternionf* quat) { + const float x2 = quat->x + quat->x; + const float y2 = quat->y + quat->y; + const float z2 = quat->z + quat->z; + + const float xx2 = quat->x * x2; + const float yy2 = quat->y * y2; + const float zz2 = quat->z * z2; + + const float yz2 = quat->y * z2; + const float wx2 = quat->w * x2; + const float xy2 = quat->x * y2; + const float wz2 = quat->w * z2; + const float xz2 = quat->x * z2; + const float wy2 = quat->w * y2; + + result->m[0] = 1.0f - yy2 - zz2; + result->m[1] = xy2 + wz2; + result->m[2] = xz2 - wy2; + result->m[3] = 0.0f; + + result->m[4] = xy2 - wz2; + result->m[5] = 1.0f - xx2 - zz2; + result->m[6] = yz2 + wx2; + result->m[7] = 0.0f; + + result->m[8] = xz2 + wy2; + result->m[9] = yz2 - wx2; + result->m[10] = 1.0f - xx2 - yy2; + result->m[11] = 0.0f; + + result->m[12] = 0.0f; + result->m[13] = 0.0f; + result->m[14] = 0.0f; + result->m[15] = 1.0f; +} + +// Creates a combined translation(rotation(scale(object))) matrix. +inline static void XrMatrix4x4f_CreateTranslationRotationScale(XrMatrix4x4f* result, const XrVector3f* translation, + const XrQuaternionf* rotation, const XrVector3f* scale) { + XrMatrix4x4f scaleMatrix; + XrMatrix4x4f_CreateScale(&scaleMatrix, scale->x, scale->y, scale->z); + + XrMatrix4x4f rotationMatrix; + XrMatrix4x4f_CreateFromQuaternion(&rotationMatrix, rotation); + + XrMatrix4x4f translationMatrix; + XrMatrix4x4f_CreateTranslation(&translationMatrix, translation->x, translation->y, translation->z); + + XrMatrix4x4f combinedMatrix; + XrMatrix4x4f_Multiply(&combinedMatrix, &rotationMatrix, &scaleMatrix); + XrMatrix4x4f_Multiply(result, &translationMatrix, &combinedMatrix); +} + +inline static void XrMatrix4x4f_CreateFromRigidTransform(XrMatrix4x4f* result, const XrPosef* s) { + const XrVector3f identityScale = {1.0f, 1.0f, 1.0f}; + XrMatrix4x4f_CreateTranslationRotationScale(result, &s->position, &s->orientation, &identityScale); +} + +// Creates a projection matrix based on the specified dimensions. +// The projection matrix transforms -Z=forward, +Y=up, +X=right to the appropriate clip space for the graphics API. +// The far plane is placed at infinity if farZ <= nearZ. +// An infinite projection matrix is preferred for rasterization because, except for +// things *right* up against the near plane, it always provides better precision: +// "Tightening the Precision of Perspective Rendering" +// Paul Upchurch, Mathieu Desbrun +// Journal of Graphics Tools, Volume 16, Issue 1, 2012 +inline static void XrMatrix4x4f_CreateProjection(XrMatrix4x4f* result, GraphicsAPI graphicsApi, const float tanAngleLeft, + const float tanAngleRight, const float tanAngleUp, float const tanAngleDown, + const float nearZ, const float farZ) { + const float tanAngleWidth = tanAngleRight - tanAngleLeft; + + // Set to tanAngleDown - tanAngleUp for a clip space with positive Y down (Vulkan). + // Set to tanAngleUp - tanAngleDown for a clip space with positive Y up (OpenGL / D3D / Metal). + const float tanAngleHeight = graphicsApi == GRAPHICS_VULKAN ? (tanAngleDown - tanAngleUp) : (tanAngleUp - tanAngleDown); + + // Set to nearZ for a [-1,1] Z clip space (OpenGL / OpenGL ES). + // Set to zero for a [0,1] Z clip space (Vulkan / D3D / Metal). + const float offsetZ = (graphicsApi == GRAPHICS_OPENGL || graphicsApi == GRAPHICS_OPENGL_ES) ? nearZ : 0; + + if (farZ <= nearZ) { + // place the far plane at infinity + result->m[0] = 2.0f / tanAngleWidth; + result->m[4] = 0.0f; + result->m[8] = (tanAngleRight + tanAngleLeft) / tanAngleWidth; + result->m[12] = 0.0f; + + result->m[1] = 0.0f; + result->m[5] = 2.0f / tanAngleHeight; + result->m[9] = (tanAngleUp + tanAngleDown) / tanAngleHeight; + result->m[13] = 0.0f; + + result->m[2] = 0.0f; + result->m[6] = 0.0f; + result->m[10] = -1.0f; + result->m[14] = -(nearZ + offsetZ); + + result->m[3] = 0.0f; + result->m[7] = 0.0f; + result->m[11] = -1.0f; + result->m[15] = 0.0f; + } else { + // normal projection + result->m[0] = 2.0f / tanAngleWidth; + result->m[4] = 0.0f; + result->m[8] = (tanAngleRight + tanAngleLeft) / tanAngleWidth; + result->m[12] = 0.0f; + + result->m[1] = 0.0f; + result->m[5] = 2.0f / tanAngleHeight; + result->m[9] = (tanAngleUp + tanAngleDown) / tanAngleHeight; + result->m[13] = 0.0f; + + result->m[2] = 0.0f; + result->m[6] = 0.0f; + result->m[10] = -(farZ + offsetZ) / (farZ - nearZ); + result->m[14] = -(farZ * (nearZ + offsetZ)) / (farZ - nearZ); + + result->m[3] = 0.0f; + result->m[7] = 0.0f; + result->m[11] = -1.0f; + result->m[15] = 0.0f; + } +} + +// Creates a projection matrix based on the specified FOV. +inline static void XrMatrix4x4f_CreateProjectionFov(XrMatrix4x4f* result, GraphicsAPI graphicsApi, const XrFovf fov, + const float nearZ, const float farZ) { + const float tanLeft = tanf(fov.angleLeft); + const float tanRight = tanf(fov.angleRight); + + const float tanDown = tanf(fov.angleDown); + const float tanUp = tanf(fov.angleUp); + + XrMatrix4x4f_CreateProjection(result, graphicsApi, tanLeft, tanRight, tanUp, tanDown, nearZ, farZ); +} + +// Creates a matrix that transforms the -1 to 1 cube to cover the given 'mins' and 'maxs' transformed with the given 'matrix'. +inline static void XrMatrix4x4f_CreateOffsetScaleForBounds(XrMatrix4x4f* result, const XrMatrix4x4f* matrix, const XrVector3f* mins, + const XrVector3f* maxs) { + const XrVector3f offset = {(maxs->x + mins->x) * 0.5f, (maxs->y + mins->y) * 0.5f, (maxs->z + mins->z) * 0.5f}; + const XrVector3f scale = {(maxs->x - mins->x) * 0.5f, (maxs->y - mins->y) * 0.5f, (maxs->z - mins->z) * 0.5f}; + + result->m[0] = matrix->m[0] * scale.x; + result->m[1] = matrix->m[1] * scale.x; + result->m[2] = matrix->m[2] * scale.x; + result->m[3] = matrix->m[3] * scale.x; + + result->m[4] = matrix->m[4] * scale.y; + result->m[5] = matrix->m[5] * scale.y; + result->m[6] = matrix->m[6] * scale.y; + result->m[7] = matrix->m[7] * scale.y; + + result->m[8] = matrix->m[8] * scale.z; + result->m[9] = matrix->m[9] * scale.z; + result->m[10] = matrix->m[10] * scale.z; + result->m[11] = matrix->m[11] * scale.z; + + result->m[12] = matrix->m[12] + matrix->m[0] * offset.x + matrix->m[4] * offset.y + matrix->m[8] * offset.z; + result->m[13] = matrix->m[13] + matrix->m[1] * offset.x + matrix->m[5] * offset.y + matrix->m[9] * offset.z; + result->m[14] = matrix->m[14] + matrix->m[2] * offset.x + matrix->m[6] * offset.y + matrix->m[10] * offset.z; + result->m[15] = matrix->m[15] + matrix->m[3] * offset.x + matrix->m[7] * offset.y + matrix->m[11] * offset.z; +} + +// Returns true if the given matrix is affine. +inline static bool XrMatrix4x4f_IsAffine(const XrMatrix4x4f* matrix, const float epsilon) { + return fabsf(matrix->m[3]) <= epsilon && fabsf(matrix->m[7]) <= epsilon && fabsf(matrix->m[11]) <= epsilon && + fabsf(matrix->m[15] - 1.0f) <= epsilon; +} + +// Returns true if the given matrix is orthogonal. +inline static bool XrMatrix4x4f_IsOrthogonal(const XrMatrix4x4f* matrix, const float epsilon) { + for (int i = 0; i < 3; i++) { + for (int j = 0; j < 3; j++) { + if (i != j) { + if (fabsf(matrix->m[4 * i + 0] * matrix->m[4 * j + 0] + matrix->m[4 * i + 1] * matrix->m[4 * j + 1] + + matrix->m[4 * i + 2] * matrix->m[4 * j + 2]) > epsilon) { + return false; + } + if (fabsf(matrix->m[4 * 0 + i] * matrix->m[4 * 0 + j] + matrix->m[4 * 1 + i] * matrix->m[4 * 1 + j] + + matrix->m[4 * 2 + i] * matrix->m[4 * 2 + j]) > epsilon) { + return false; + } + } + } + } + return true; +} + +// Returns true if the given matrix is orthonormal. +inline static bool XrMatrix4x4f_IsOrthonormal(const XrMatrix4x4f* matrix, const float epsilon) { + for (int i = 0; i < 3; i++) { + for (int j = 0; j < 3; j++) { + const float kd = (i == j) ? 1.0f : 0.0f; // Kronecker delta + if (fabsf(kd - (matrix->m[4 * i + 0] * matrix->m[4 * j + 0] + matrix->m[4 * i + 1] * matrix->m[4 * j + 1] + + matrix->m[4 * i + 2] * matrix->m[4 * j + 2])) > epsilon) { + return false; + } + if (fabsf(kd - (matrix->m[4 * 0 + i] * matrix->m[4 * 0 + j] + matrix->m[4 * 1 + i] * matrix->m[4 * 1 + j] + + matrix->m[4 * 2 + i] * matrix->m[4 * 2 + j])) > epsilon) { + return false; + } + } + } + return true; +} + +// Returns true if the given matrix is a rigid body transform. +inline static bool XrMatrix4x4f_IsRigidBody(const XrMatrix4x4f* matrix, const float epsilon) { + return XrMatrix4x4f_IsAffine(matrix, epsilon) && XrMatrix4x4f_IsOrthonormal(matrix, epsilon); +} + +// Get the translation from a combined translation(rotation(scale(object))) matrix. +inline static void XrMatrix4x4f_GetTranslation(XrVector3f* result, const XrMatrix4x4f* src) { + assert(XrMatrix4x4f_IsAffine(src, 1e-4f)); + assert(XrMatrix4x4f_IsOrthogonal(src, 1e-4f)); + + result->x = src->m[12]; + result->y = src->m[13]; + result->z = src->m[14]; +} + +// Get the rotation from a combined translation(rotation(scale(object))) matrix. +inline static void XrMatrix4x4f_GetRotation(XrQuaternionf* result, const XrMatrix4x4f* src) { + assert(XrMatrix4x4f_IsAffine(src, 1e-4f)); + assert(XrMatrix4x4f_IsOrthogonal(src, 1e-4f)); + + const float rcpScaleX = XrRcpSqrt(src->m[0] * src->m[0] + src->m[1] * src->m[1] + src->m[2] * src->m[2]); + const float rcpScaleY = XrRcpSqrt(src->m[4] * src->m[4] + src->m[5] * src->m[5] + src->m[6] * src->m[6]); + const float rcpScaleZ = XrRcpSqrt(src->m[8] * src->m[8] + src->m[9] * src->m[9] + src->m[10] * src->m[10]); + const float m[9] = {src->m[0] * rcpScaleX, src->m[1] * rcpScaleX, src->m[2] * rcpScaleX, + src->m[4] * rcpScaleY, src->m[5] * rcpScaleY, src->m[6] * rcpScaleY, + src->m[8] * rcpScaleZ, src->m[9] * rcpScaleZ, src->m[10] * rcpScaleZ}; + if (m[0 * 3 + 0] + m[1 * 3 + 1] + m[2 * 3 + 2] > 0.0f) { + float t = +m[0 * 3 + 0] + m[1 * 3 + 1] + m[2 * 3 + 2] + 1.0f; + float s = XrRcpSqrt(t) * 0.5f; + result->w = s * t; + result->z = (m[0 * 3 + 1] - m[1 * 3 + 0]) * s; + result->y = (m[2 * 3 + 0] - m[0 * 3 + 2]) * s; + result->x = (m[1 * 3 + 2] - m[2 * 3 + 1]) * s; + } else if (m[0 * 3 + 0] > m[1 * 3 + 1] && m[0 * 3 + 0] > m[2 * 3 + 2]) { + float t = +m[0 * 3 + 0] - m[1 * 3 + 1] - m[2 * 3 + 2] + 1.0f; + float s = XrRcpSqrt(t) * 0.5f; + result->x = s * t; + result->y = (m[0 * 3 + 1] + m[1 * 3 + 0]) * s; + result->z = (m[2 * 3 + 0] + m[0 * 3 + 2]) * s; + result->w = (m[1 * 3 + 2] - m[2 * 3 + 1]) * s; + } else if (m[1 * 3 + 1] > m[2 * 3 + 2]) { + float t = -m[0 * 3 + 0] + m[1 * 3 + 1] - m[2 * 3 + 2] + 1.0f; + float s = XrRcpSqrt(t) * 0.5f; + result->y = s * t; + result->x = (m[0 * 3 + 1] + m[1 * 3 + 0]) * s; + result->w = (m[2 * 3 + 0] - m[0 * 3 + 2]) * s; + result->z = (m[1 * 3 + 2] + m[2 * 3 + 1]) * s; + } else { + float t = -m[0 * 3 + 0] - m[1 * 3 + 1] + m[2 * 3 + 2] + 1.0f; + float s = XrRcpSqrt(t) * 0.5f; + result->z = s * t; + result->w = (m[0 * 3 + 1] - m[1 * 3 + 0]) * s; + result->x = (m[2 * 3 + 0] + m[0 * 3 + 2]) * s; + result->y = (m[1 * 3 + 2] + m[2 * 3 + 1]) * s; + } +} + +// Get the scale from a combined translation(rotation(scale(object))) matrix. +inline static void XrMatrix4x4f_GetScale(XrVector3f* result, const XrMatrix4x4f* src) { + assert(XrMatrix4x4f_IsAffine(src, 1e-4f)); + assert(XrMatrix4x4f_IsOrthogonal(src, 1e-4f)); + + result->x = sqrtf(src->m[0] * src->m[0] + src->m[1] * src->m[1] + src->m[2] * src->m[2]); + result->y = sqrtf(src->m[4] * src->m[4] + src->m[5] * src->m[5] + src->m[6] * src->m[6]); + result->z = sqrtf(src->m[8] * src->m[8] + src->m[9] * src->m[9] + src->m[10] * src->m[10]); +} + +// Transforms a 3D vector. +inline static void XrMatrix4x4f_TransformVector3f(XrVector3f* result, const XrMatrix4x4f* m, const XrVector3f* v) { + const float w = m->m[3] * v->x + m->m[7] * v->y + m->m[11] * v->z + m->m[15]; + const float rcpW = 1.0f / w; + result->x = (m->m[0] * v->x + m->m[4] * v->y + m->m[8] * v->z + m->m[12]) * rcpW; + result->y = (m->m[1] * v->x + m->m[5] * v->y + m->m[9] * v->z + m->m[13]) * rcpW; + result->z = (m->m[2] * v->x + m->m[6] * v->y + m->m[10] * v->z + m->m[14]) * rcpW; +} + +// Transforms a 4D vector. +inline static void XrMatrix4x4f_TransformVector4f(XrVector4f* result, const XrMatrix4x4f* m, const XrVector4f* v) { + result->x = m->m[0] * v->x + m->m[4] * v->y + m->m[8] * v->z + m->m[12] * v->w; + result->y = m->m[1] * v->x + m->m[5] * v->y + m->m[9] * v->z + m->m[13] * v->w; + result->z = m->m[2] * v->x + m->m[6] * v->y + m->m[10] * v->z + m->m[14] * v->w; + result->w = m->m[3] * v->x + m->m[7] * v->y + m->m[11] * v->z + m->m[15] * v->w; +} + +// Transforms the 'mins' and 'maxs' bounds with the given 'matrix'. +inline static void XrMatrix4x4f_TransformBounds(XrVector3f* resultMins, XrVector3f* resultMaxs, const XrMatrix4x4f* matrix, + const XrVector3f* mins, const XrVector3f* maxs) { + assert(XrMatrix4x4f_IsAffine(matrix, 1e-4f)); + + const XrVector3f center = {(mins->x + maxs->x) * 0.5f, (mins->y + maxs->y) * 0.5f, (mins->z + maxs->z) * 0.5f}; + const XrVector3f extents = {maxs->x - center.x, maxs->y - center.y, maxs->z - center.z}; + const XrVector3f newCenter = {matrix->m[0] * center.x + matrix->m[4] * center.y + matrix->m[8] * center.z + matrix->m[12], + matrix->m[1] * center.x + matrix->m[5] * center.y + matrix->m[9] * center.z + matrix->m[13], + matrix->m[2] * center.x + matrix->m[6] * center.y + matrix->m[10] * center.z + matrix->m[14]}; + const XrVector3f newExtents = { + fabsf(extents.x * matrix->m[0]) + fabsf(extents.y * matrix->m[4]) + fabsf(extents.z * matrix->m[8]), + fabsf(extents.x * matrix->m[1]) + fabsf(extents.y * matrix->m[5]) + fabsf(extents.z * matrix->m[9]), + fabsf(extents.x * matrix->m[2]) + fabsf(extents.y * matrix->m[6]) + fabsf(extents.z * matrix->m[10])}; + XrVector3f_Sub(resultMins, &newCenter, &newExtents); + XrVector3f_Add(resultMaxs, &newCenter, &newExtents); +} + +// Returns true if the 'mins' and 'maxs' bounds is completely off to one side of the projection matrix. +inline static bool XrMatrix4x4f_CullBounds(const XrMatrix4x4f* mvp, const XrVector3f* mins, const XrVector3f* maxs) { + if (maxs->x <= mins->x && maxs->y <= mins->y && maxs->z <= mins->z) { + return false; + } + + XrVector4f c[8]; + for (int i = 0; i < 8; i++) { + const XrVector4f corner = {(i & 1) != 0 ? maxs->x : mins->x, (i & 2) != 0 ? maxs->y : mins->y, + (i & 4) != 0 ? maxs->z : mins->z, 1.0f}; + XrMatrix4x4f_TransformVector4f(&c[i], mvp, &corner); + } + + int i; + for (i = 0; i < 8; i++) { + if (c[i].x > -c[i].w) { + break; + } + } + if (i == 8) { + return true; + } + for (i = 0; i < 8; i++) { + if (c[i].x < c[i].w) { + break; + } + } + if (i == 8) { + return true; + } + + for (i = 0; i < 8; i++) { + if (c[i].y > -c[i].w) { + break; + } + } + if (i == 8) { + return true; + } + for (i = 0; i < 8; i++) { + if (c[i].y < c[i].w) { + break; + } + } + if (i == 8) { + return true; + } + for (i = 0; i < 8; i++) { + if (c[i].z > -c[i].w) { + break; + } + } + if (i == 8) { + return true; + } + for (i = 0; i < 8; i++) { + if (c[i].z < c[i].w) { + break; + } + } + return i == 8; +} + +#endif // XR_LINEAR_H_ diff --git a/Samples/3rdParty/minizip/src/crypt.h b/Samples/3rdParty/minizip/src/crypt.h new file mode 100755 index 0000000..a01d08d --- /dev/null +++ b/Samples/3rdParty/minizip/src/crypt.h @@ -0,0 +1,131 @@ +/* crypt.h -- base code for crypt/uncrypt ZIPfile + + + Version 1.01e, February 12th, 2005 + + Copyright (C) 1998-2005 Gilles Vollant + + This code is a modified version of crypting code in Infozip distribution + + The encryption/decryption parts of this source code (as opposed to the + non-echoing password parts) were originally written in Europe. The + whole source package can be freely distributed, including from the USA. + (Prior to January 2000, re-export from the US was a violation of US law.) + + This encryption code is a direct transcription of the algorithm from + Roger Schlafly, described by Phil Katz in the file appnote.txt. This + file (appnote.txt) is distributed with the PKZIP program (even in the + version without encryption capabilities). + + If you don't need crypting in your application, just define symbols + NOCRYPT and NOUNCRYPT. + + This code support the "Traditional PKWARE Encryption". + + The new AES encryption added on Zip format by Winzip (see the page + http://www.winzip.com/aes_info.htm ) and PKWare PKZip 5.x Strong + Encryption is not supported. +*/ + +#define CRC32(c, b) ((*(pcrc_32_tab+(((int)(c) ^ (b)) & 0xff))) ^ ((c) >> 8)) + +/*********************************************************************** + * Return the next byte in the pseudo-random sequence + */ +static int decrypt_byte(unsigned long* pkeys, const unsigned long* pcrc_32_tab) +{ + unsigned temp; /* POTENTIAL BUG: temp*(temp^1) may overflow in an + * unpredictable manner on 16-bit systems; not a problem + * with any known compiler so far, though */ + + temp = ((unsigned)(*(pkeys+2)) & 0xffff) | 2; + return (int)(((temp * (temp ^ 1)) >> 8) & 0xff); +} + +/*********************************************************************** + * Update the encryption keys with the next byte of plain text + */ +static int update_keys(unsigned long* pkeys,const unsigned long* pcrc_32_tab,int c) +{ + (*(pkeys+0)) = CRC32((*(pkeys+0)), c); + (*(pkeys+1)) += (*(pkeys+0)) & 0xff; + (*(pkeys+1)) = (*(pkeys+1)) * 134775813L + 1; + { + register int keyshift = (int)((*(pkeys+1)) >> 24); + (*(pkeys+2)) = CRC32((*(pkeys+2)), keyshift); + } + return c; +} + + +/*********************************************************************** + * Initialize the encryption keys and the random header according to + * the given password. + */ +static void init_keys(const char* passwd,unsigned long* pkeys,const unsigned long* pcrc_32_tab) +{ + *(pkeys+0) = 305419896L; + *(pkeys+1) = 591751049L; + *(pkeys+2) = 878082192L; + while (*passwd != '\0') { + update_keys(pkeys,pcrc_32_tab,(int)*passwd); + passwd++; + } +} + +#define zdecode(pkeys,pcrc_32_tab,c) \ + (update_keys(pkeys,pcrc_32_tab,c ^= decrypt_byte(pkeys,pcrc_32_tab))) + +#define zencode(pkeys,pcrc_32_tab,c,t) \ + (t=decrypt_byte(pkeys,pcrc_32_tab), update_keys(pkeys,pcrc_32_tab,c), t^(c)) + +#ifdef INCLUDECRYPTINGCODE_IFCRYPTALLOWED + +#define RAND_HEAD_LEN 12 + /* "last resort" source for second part of crypt seed pattern */ +# ifndef ZCR_SEED2 +# define ZCR_SEED2 3141592654UL /* use PI as default pattern */ +# endif + +static int crypthead(const char* passwd, /* password string */ + unsigned char* buf, /* where to write header */ + int bufSize, + unsigned long* pkeys, + const unsigned long* pcrc_32_tab, + unsigned long crcForCrypting) +{ + int n; /* index in random header */ + int t; /* temporary */ + int c; /* random byte */ + unsigned char header[RAND_HEAD_LEN-2]; /* random header */ + static unsigned calls = 0; /* ensure different random header each time */ + + if (bufSize> 7) & 0xff; + header[n] = (unsigned char)zencode(pkeys, pcrc_32_tab, c, t); + } + /* Encrypt random header (last two bytes is high word of crc) */ + init_keys(passwd, pkeys, pcrc_32_tab); + for (n = 0; n < RAND_HEAD_LEN-2; n++) + { + buf[n] = (unsigned char)zencode(pkeys, pcrc_32_tab, header[n], t); + } + buf[n++] = (unsigned char)zencode(pkeys, pcrc_32_tab, (int)(crcForCrypting >> 16) & 0xff, t); + buf[n++] = (unsigned char)zencode(pkeys, pcrc_32_tab, (int)(crcForCrypting >> 24) & 0xff, t); + return n; +} + +#endif diff --git a/Samples/3rdParty/minizip/src/ioapi.c b/Samples/3rdParty/minizip/src/ioapi.c new file mode 100755 index 0000000..988bd5e --- /dev/null +++ b/Samples/3rdParty/minizip/src/ioapi.c @@ -0,0 +1,255 @@ +/* ioapi.h -- IO base function header for compress/uncompress .zip + part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html ) + + Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html ) + + Modifications for Zip64 support + Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com ) + + For more info read MiniZip_info.txt + +*/ + +#if defined(_WIN32) && (!(defined(_CRT_SECURE_NO_WARNINGS))) + #define _CRT_SECURE_NO_WARNINGS +#endif +#define IOAPI_NO_64 + +#include // for ftello + +#if defined(__APPLE__) || defined(IOAPI_NO_64) +// In darwin and perhaps other BSD variants off_t is a 64 bit value, hence no need for specific 64 bit functions +#define FOPEN_FUNC(filename, mode) fopen(filename, mode) +#if defined(_WIN32) +#define FTELLO_FUNC(stream) _ftelli64(stream) +#define FSEEKO_FUNC(stream, offset, origin) _fseeki64(stream, offset, origin) +#else +#define FTELLO_FUNC(stream) ftello(stream) +#define FSEEKO_FUNC(stream, offset, origin) fseeko(stream, offset, origin) +#endif +#else +#define FOPEN_FUNC(filename, mode) fopen64(filename, mode) +#define FTELLO_FUNC(stream) ftello64(stream) +#define FSEEKO_FUNC(stream, offset, origin) fseeko64(stream, offset, origin) +#endif + + +#include "ioapi.h" + +voidpf call_zopen64 (const zlib_filefunc64_32_def* pfilefunc,const void*filename,int mode) +{ + if (pfilefunc->zfile_func64.zopen64_file != NULL) + return (*(pfilefunc->zfile_func64.zopen64_file)) (pfilefunc->zfile_func64.opaque,filename,mode); + else + { + return (*(pfilefunc->zopen32_file))(pfilefunc->zfile_func64.opaque,(const char*)filename,mode); + } +} + +long call_zseek64 (const zlib_filefunc64_32_def* pfilefunc,voidpf filestream, ZPOS64_T offset, int origin) +{ + if (pfilefunc->zfile_func64.zseek64_file != NULL) + return (*(pfilefunc->zfile_func64.zseek64_file)) (pfilefunc->zfile_func64.opaque,filestream,offset,origin); + else + { + uLong offsetTruncated = (uLong)offset; + if (offsetTruncated != offset) + return -1; + else + return (*(pfilefunc->zseek32_file))(pfilefunc->zfile_func64.opaque,filestream,offsetTruncated,origin); + } +} + +ZPOS64_T call_ztell64 (const zlib_filefunc64_32_def* pfilefunc,voidpf filestream) +{ + if (pfilefunc->zfile_func64.zseek64_file != NULL) + return (*(pfilefunc->zfile_func64.ztell64_file)) (pfilefunc->zfile_func64.opaque,filestream); + else + { + uLong tell_uLong = (*(pfilefunc->ztell32_file))(pfilefunc->zfile_func64.opaque,filestream); + if ((tell_uLong) == MAXU32) + return (ZPOS64_T)-1; + else + return tell_uLong; + } +} + +void fill_zlib_filefunc64_32_def_from_filefunc32(zlib_filefunc64_32_def* p_filefunc64_32,const zlib_filefunc_def* p_filefunc32) +{ + p_filefunc64_32->zfile_func64.zopen64_file = NULL; + p_filefunc64_32->zopen32_file = p_filefunc32->zopen_file; + p_filefunc64_32->zfile_func64.zerror_file = p_filefunc32->zerror_file; + p_filefunc64_32->zfile_func64.zread_file = p_filefunc32->zread_file; + p_filefunc64_32->zfile_func64.zwrite_file = p_filefunc32->zwrite_file; + p_filefunc64_32->zfile_func64.ztell64_file = NULL; + p_filefunc64_32->zfile_func64.zseek64_file = NULL; + p_filefunc64_32->zfile_func64.zclose_file = p_filefunc32->zclose_file; + p_filefunc64_32->zfile_func64.zerror_file = p_filefunc32->zerror_file; + p_filefunc64_32->zfile_func64.opaque = p_filefunc32->opaque; + p_filefunc64_32->zseek32_file = p_filefunc32->zseek_file; + p_filefunc64_32->ztell32_file = p_filefunc32->ztell_file; +} + + + +static voidpf ZCALLBACK fopen_file_func OF((voidpf opaque, const char* filename, int mode)); +static uLong ZCALLBACK fread_file_func OF((voidpf opaque, voidpf stream, void* buf, uLong size)); +static uLong ZCALLBACK fwrite_file_func OF((voidpf opaque, voidpf stream, const void* buf,uLong size)); +static ZPOS64_T ZCALLBACK ftell64_file_func OF((voidpf opaque, voidpf stream)); +static long ZCALLBACK fseek64_file_func OF((voidpf opaque, voidpf stream, ZPOS64_T offset, int origin)); +static int ZCALLBACK fclose_file_func OF((voidpf opaque, voidpf stream)); +static int ZCALLBACK ferror_file_func OF((voidpf opaque, voidpf stream)); + +static voidpf ZCALLBACK fopen_file_func (voidpf opaque, const char* filename, int mode) +{ + FILE* file = NULL; + const char* mode_fopen = NULL; + if ((mode & ZLIB_FILEFUNC_MODE_READWRITEFILTER)==ZLIB_FILEFUNC_MODE_READ) + mode_fopen = "rb"; + else + if (mode & ZLIB_FILEFUNC_MODE_EXISTING) + mode_fopen = "r+b"; + else + if (mode & ZLIB_FILEFUNC_MODE_CREATE) + mode_fopen = "wb"; + + if ((filename!=NULL) && (mode_fopen != NULL)) + file = fopen(filename, mode_fopen); + return file; +} + +static voidpf ZCALLBACK fopen64_file_func (voidpf opaque, const void* filename, int mode) +{ + FILE* file = NULL; + const char* mode_fopen = NULL; + if ((mode & ZLIB_FILEFUNC_MODE_READWRITEFILTER)==ZLIB_FILEFUNC_MODE_READ) + mode_fopen = "rb"; + else + if (mode & ZLIB_FILEFUNC_MODE_EXISTING) + mode_fopen = "r+b"; + else + if (mode & ZLIB_FILEFUNC_MODE_CREATE) + mode_fopen = "wb"; + + if ((filename!=NULL) && (mode_fopen != NULL)) + file = FOPEN_FUNC((const char*)filename, mode_fopen); + return file; +} + + +static uLong ZCALLBACK fread_file_func (voidpf opaque, voidpf stream, void* buf, uLong size) +{ + uLong ret; + ret = (uLong)fread(buf, 1, (size_t)size, (FILE *)stream); + return ret; +} + +static uLong ZCALLBACK fwrite_file_func (voidpf opaque, voidpf stream, const void* buf, uLong size) +{ + uLong ret; + ret = (uLong)fwrite(buf, 1, (size_t)size, (FILE *)stream); + return ret; +} + +static long ZCALLBACK ftell_file_func (voidpf opaque, voidpf stream) +{ + long ret; + ret = ftell((FILE *)stream); + return ret; +} + + +static ZPOS64_T ZCALLBACK ftell64_file_func (voidpf opaque, voidpf stream) +{ + ZPOS64_T ret; + ret = FTELLO_FUNC((FILE *)stream); + return ret; +} + +static long ZCALLBACK fseek_file_func (voidpf opaque, voidpf stream, uLong offset, int origin) +{ + int fseek_origin=0; + long ret; + switch (origin) + { + case ZLIB_FILEFUNC_SEEK_CUR : + fseek_origin = SEEK_CUR; + break; + case ZLIB_FILEFUNC_SEEK_END : + fseek_origin = SEEK_END; + break; + case ZLIB_FILEFUNC_SEEK_SET : + fseek_origin = SEEK_SET; + break; + default: return -1; + } + ret = 0; + if (fseek((FILE *)stream, offset, fseek_origin) != 0) + ret = -1; + return ret; +} + +static long ZCALLBACK fseek64_file_func (voidpf opaque, voidpf stream, ZPOS64_T offset, int origin) +{ + int fseek_origin=0; + long ret; + switch (origin) + { + case ZLIB_FILEFUNC_SEEK_CUR : + fseek_origin = SEEK_CUR; + break; + case ZLIB_FILEFUNC_SEEK_END : + fseek_origin = SEEK_END; + break; + case ZLIB_FILEFUNC_SEEK_SET : + fseek_origin = SEEK_SET; + break; + default: return -1; + } + ret = 0; + + if(FSEEKO_FUNC((FILE *)stream, offset, fseek_origin) != 0) + ret = -1; + + return ret; +} + + +static int ZCALLBACK fclose_file_func (voidpf opaque, voidpf stream) +{ + int ret; + ret = fclose((FILE *)stream); + return ret; +} + +static int ZCALLBACK ferror_file_func (voidpf opaque, voidpf stream) +{ + int ret; + ret = ferror((FILE *)stream); + return ret; +} + +void fill_fopen_filefunc (pzlib_filefunc_def) + zlib_filefunc_def* pzlib_filefunc_def; +{ + pzlib_filefunc_def->zopen_file = fopen_file_func; + pzlib_filefunc_def->zread_file = fread_file_func; + pzlib_filefunc_def->zwrite_file = fwrite_file_func; + pzlib_filefunc_def->ztell_file = ftell_file_func; + pzlib_filefunc_def->zseek_file = fseek_file_func; + pzlib_filefunc_def->zclose_file = fclose_file_func; + pzlib_filefunc_def->zerror_file = ferror_file_func; + pzlib_filefunc_def->opaque = NULL; +} + +void fill_fopen64_filefunc (zlib_filefunc64_def* pzlib_filefunc_def) +{ + pzlib_filefunc_def->zopen64_file = fopen64_file_func; + pzlib_filefunc_def->zread_file = fread_file_func; + pzlib_filefunc_def->zwrite_file = fwrite_file_func; + pzlib_filefunc_def->ztell64_file = ftell64_file_func; + pzlib_filefunc_def->zseek64_file = fseek64_file_func; + pzlib_filefunc_def->zclose_file = fclose_file_func; + pzlib_filefunc_def->zerror_file = ferror_file_func; + pzlib_filefunc_def->opaque = NULL; +} diff --git a/Samples/3rdParty/minizip/src/ioapi.h b/Samples/3rdParty/minizip/src/ioapi.h new file mode 100755 index 0000000..8dcbdb0 --- /dev/null +++ b/Samples/3rdParty/minizip/src/ioapi.h @@ -0,0 +1,208 @@ +/* ioapi.h -- IO base function header for compress/uncompress .zip + part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html ) + + Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html ) + + Modifications for Zip64 support + Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com ) + + For more info read MiniZip_info.txt + + Changes + + Oct-2009 - Defined ZPOS64_T to fpos_t on windows and u_int64_t on linux. (might need to find a better why for this) + Oct-2009 - Change to fseeko64, ftello64 and fopen64 so large files would work on linux. + More if/def section may be needed to support other platforms + Oct-2009 - Defined fxxxx64 calls to normal fopen/ftell/fseek so they would compile on windows. + (but you should use iowin32.c for windows instead) + +*/ + +#ifndef _ZLIBIOAPI64_H +#define _ZLIBIOAPI64_H + +#if (!defined(_WIN32)) && (!defined(WIN32)) && (!defined(__APPLE__)) + + // Linux needs this to support file operation on files larger then 4+GB + // But might need better if/def to select just the platforms that needs them. + + #ifndef __USE_FILE_OFFSET64 + #define __USE_FILE_OFFSET64 + #endif + #ifndef __USE_LARGEFILE64 + #define __USE_LARGEFILE64 + #endif + #ifndef _LARGEFILE64_SOURCE + #define _LARGEFILE64_SOURCE + #endif + #ifndef _FILE_OFFSET_BIT + #define _FILE_OFFSET_BIT 64 + #endif + +#endif + +#include +#include +#include "zlib.h" + +#if defined(USE_FILE32API) +#define fopen64 fopen +#define ftello64 ftell +#define fseeko64 fseek +#else +#ifdef __FreeBSD__ +#define fopen64 fopen +#define ftello64 ftello +#define fseeko64 fseeko +#endif +#ifdef _MSC_VER + #define fopen64 fopen + #if (_MSC_VER >= 1400) && (!(defined(NO_MSCVER_FILE64_FUNC))) + #define ftello64 _ftelli64 + #define fseeko64 _fseeki64 + #else // old MSC + #define ftello64 ftell + #define fseeko64 fseek + #endif +#endif +#endif + +/* +#ifndef ZPOS64_T + #ifdef _WIN32 + #define ZPOS64_T fpos_t + #else + #include + #define ZPOS64_T uint64_t + #endif +#endif +*/ + +#ifdef HAVE_MINIZIP64_CONF_H +#include "mz64conf.h" +#endif + +/* a type choosen by DEFINE */ +#ifdef HAVE_64BIT_INT_CUSTOM +typedef 64BIT_INT_CUSTOM_TYPE ZPOS64_T; +#else +#ifdef HAS_STDINT_H +#include "stdint.h" +typedef uint64_t ZPOS64_T; +#else + +/* Maximum unsigned 32-bit value used as placeholder for zip64 */ +#define MAXU32 0xffffffff + +#if defined(_MSC_VER) || defined(__BORLANDC__) +typedef unsigned __int64 ZPOS64_T; +#else +typedef unsigned long long int ZPOS64_T; +#endif +#endif +#endif + + + +#ifdef __cplusplus +extern "C" { +#endif + + +#define ZLIB_FILEFUNC_SEEK_CUR (1) +#define ZLIB_FILEFUNC_SEEK_END (2) +#define ZLIB_FILEFUNC_SEEK_SET (0) + +#define ZLIB_FILEFUNC_MODE_READ (1) +#define ZLIB_FILEFUNC_MODE_WRITE (2) +#define ZLIB_FILEFUNC_MODE_READWRITEFILTER (3) + +#define ZLIB_FILEFUNC_MODE_EXISTING (4) +#define ZLIB_FILEFUNC_MODE_CREATE (8) + + +#ifndef ZCALLBACK + #if (defined(WIN32) || defined(_WIN32) || defined (WINDOWS) || defined (_WINDOWS)) && defined(CALLBACK) && defined (USEWINDOWS_CALLBACK) + #define ZCALLBACK CALLBACK + #else + #define ZCALLBACK + #endif +#endif + + + + +typedef voidpf (ZCALLBACK *open_file_func) OF((voidpf opaque, const char* filename, int mode)); +typedef uLong (ZCALLBACK *read_file_func) OF((voidpf opaque, voidpf stream, void* buf, uLong size)); +typedef uLong (ZCALLBACK *write_file_func) OF((voidpf opaque, voidpf stream, const void* buf, uLong size)); +typedef int (ZCALLBACK *close_file_func) OF((voidpf opaque, voidpf stream)); +typedef int (ZCALLBACK *testerror_file_func) OF((voidpf opaque, voidpf stream)); + +typedef long (ZCALLBACK *tell_file_func) OF((voidpf opaque, voidpf stream)); +typedef long (ZCALLBACK *seek_file_func) OF((voidpf opaque, voidpf stream, uLong offset, int origin)); + + +/* here is the "old" 32 bits structure structure */ +typedef struct zlib_filefunc_def_s +{ + open_file_func zopen_file; + read_file_func zread_file; + write_file_func zwrite_file; + tell_file_func ztell_file; + seek_file_func zseek_file; + close_file_func zclose_file; + testerror_file_func zerror_file; + voidpf opaque; +} zlib_filefunc_def; + +typedef ZPOS64_T (ZCALLBACK *tell64_file_func) OF((voidpf opaque, voidpf stream)); +typedef long (ZCALLBACK *seek64_file_func) OF((voidpf opaque, voidpf stream, ZPOS64_T offset, int origin)); +typedef voidpf (ZCALLBACK *open64_file_func) OF((voidpf opaque, const void* filename, int mode)); + +typedef struct zlib_filefunc64_def_s +{ + open64_file_func zopen64_file; + read_file_func zread_file; + write_file_func zwrite_file; + tell64_file_func ztell64_file; + seek64_file_func zseek64_file; + close_file_func zclose_file; + testerror_file_func zerror_file; + voidpf opaque; +} zlib_filefunc64_def; + +void fill_fopen64_filefunc OF((zlib_filefunc64_def* pzlib_filefunc_def)); +void fill_fopen_filefunc OF((zlib_filefunc_def* pzlib_filefunc_def)); + +/* now internal definition, only for zip.c and unzip.h */ +typedef struct zlib_filefunc64_32_def_s +{ + zlib_filefunc64_def zfile_func64; + open_file_func zopen32_file; + tell_file_func ztell32_file; + seek_file_func zseek32_file; +} zlib_filefunc64_32_def; + + +#define ZREAD64(filefunc,filestream,buf,size) ((*((filefunc).zfile_func64.zread_file)) ((filefunc).zfile_func64.opaque,filestream,buf,size)) +#define ZWRITE64(filefunc,filestream,buf,size) ((*((filefunc).zfile_func64.zwrite_file)) ((filefunc).zfile_func64.opaque,filestream,buf,size)) +//#define ZTELL64(filefunc,filestream) ((*((filefunc).ztell64_file)) ((filefunc).opaque,filestream)) +//#define ZSEEK64(filefunc,filestream,pos,mode) ((*((filefunc).zseek64_file)) ((filefunc).opaque,filestream,pos,mode)) +#define ZCLOSE64(filefunc,filestream) ((*((filefunc).zfile_func64.zclose_file)) ((filefunc).zfile_func64.opaque,filestream)) +#define ZERROR64(filefunc,filestream) ((*((filefunc).zfile_func64.zerror_file)) ((filefunc).zfile_func64.opaque,filestream)) + +voidpf call_zopen64 OF((const zlib_filefunc64_32_def* pfilefunc,const void*filename,int mode)); +long call_zseek64 OF((const zlib_filefunc64_32_def* pfilefunc,voidpf filestream, ZPOS64_T offset, int origin)); +ZPOS64_T call_ztell64 OF((const zlib_filefunc64_32_def* pfilefunc,voidpf filestream)); + +void fill_zlib_filefunc64_32_def_from_filefunc32(zlib_filefunc64_32_def* p_filefunc64_32,const zlib_filefunc_def* p_filefunc32); + +#define ZOPEN64(filefunc,filename,mode) (call_zopen64((&(filefunc)),(filename),(mode))) +#define ZTELL64(filefunc,filestream) (call_ztell64((&(filefunc)),(filestream))) +#define ZSEEK64(filefunc,filestream,pos,mode) (call_zseek64((&(filefunc)),(filestream),(pos),(mode))) + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/Samples/3rdParty/minizip/src/iowin32.c b/Samples/3rdParty/minizip/src/iowin32.c new file mode 100755 index 0000000..6a2a883 --- /dev/null +++ b/Samples/3rdParty/minizip/src/iowin32.c @@ -0,0 +1,389 @@ +/* iowin32.c -- IO base function header for compress/uncompress .zip + Version 1.1, February 14h, 2010 + part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html ) + + Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html ) + + Modifications for Zip64 support + Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com ) + + For more info read MiniZip_info.txt + +*/ + +#include + +#include "zlib.h" +#include "ioapi.h" +#include "iowin32.h" + +#ifndef INVALID_HANDLE_VALUE +#define INVALID_HANDLE_VALUE (0xFFFFFFFF) +#endif + +#ifndef INVALID_SET_FILE_POINTER +#define INVALID_SET_FILE_POINTER ((DWORD)-1) +#endif + +voidpf ZCALLBACK win32_open_file_func OF((voidpf opaque, const char* filename, int mode)); +uLong ZCALLBACK win32_read_file_func OF((voidpf opaque, voidpf stream, void* buf, uLong size)); +uLong ZCALLBACK win32_write_file_func OF((voidpf opaque, voidpf stream, const void* buf, uLong size)); +ZPOS64_T ZCALLBACK win32_tell64_file_func OF((voidpf opaque, voidpf stream)); +long ZCALLBACK win32_seek64_file_func OF((voidpf opaque, voidpf stream, ZPOS64_T offset, int origin)); +int ZCALLBACK win32_close_file_func OF((voidpf opaque, voidpf stream)); +int ZCALLBACK win32_error_file_func OF((voidpf opaque, voidpf stream)); + +typedef struct +{ + HANDLE hf; + int error; +} WIN32FILE_IOWIN; + + +static void win32_translate_open_mode(int mode, + DWORD* lpdwDesiredAccess, + DWORD* lpdwCreationDisposition, + DWORD* lpdwShareMode, + DWORD* lpdwFlagsAndAttributes) +{ + *lpdwDesiredAccess = *lpdwShareMode = *lpdwFlagsAndAttributes = *lpdwCreationDisposition = 0; + + if ((mode & ZLIB_FILEFUNC_MODE_READWRITEFILTER)==ZLIB_FILEFUNC_MODE_READ) + { + *lpdwDesiredAccess = GENERIC_READ; + *lpdwCreationDisposition = OPEN_EXISTING; + *lpdwShareMode = FILE_SHARE_READ; + } + else if (mode & ZLIB_FILEFUNC_MODE_EXISTING) + { + *lpdwDesiredAccess = GENERIC_WRITE | GENERIC_READ; + *lpdwCreationDisposition = OPEN_EXISTING; + } + else if (mode & ZLIB_FILEFUNC_MODE_CREATE) + { + *lpdwDesiredAccess = GENERIC_WRITE | GENERIC_READ; + *lpdwCreationDisposition = CREATE_ALWAYS; + } +} + +static voidpf win32_build_iowin(HANDLE hFile) +{ + voidpf ret=NULL; + + if ((hFile != NULL) && (hFile != INVALID_HANDLE_VALUE)) + { + WIN32FILE_IOWIN w32fiow; + w32fiow.hf = hFile; + w32fiow.error = 0; + ret = malloc(sizeof(WIN32FILE_IOWIN)); + + if (ret==NULL) + CloseHandle(hFile); + else + *((WIN32FILE_IOWIN*)ret) = w32fiow; + } + return ret; +} + +voidpf ZCALLBACK win32_open64_file_func (voidpf opaque,const void* filename,int mode) +{ + const char* mode_fopen = NULL; + DWORD dwDesiredAccess,dwCreationDisposition,dwShareMode,dwFlagsAndAttributes ; + HANDLE hFile = NULL; + + win32_translate_open_mode(mode,&dwDesiredAccess,&dwCreationDisposition,&dwShareMode,&dwFlagsAndAttributes); + + if ((filename!=NULL) && (dwDesiredAccess != 0)) + hFile = CreateFile((LPCTSTR)filename, dwDesiredAccess, dwShareMode, NULL, dwCreationDisposition, dwFlagsAndAttributes, NULL); + + return win32_build_iowin(hFile); +} + + +voidpf ZCALLBACK win32_open64_file_funcA (voidpf opaque,const void* filename,int mode) +{ + const char* mode_fopen = NULL; + DWORD dwDesiredAccess,dwCreationDisposition,dwShareMode,dwFlagsAndAttributes ; + HANDLE hFile = NULL; + + win32_translate_open_mode(mode,&dwDesiredAccess,&dwCreationDisposition,&dwShareMode,&dwFlagsAndAttributes); + + if ((filename!=NULL) && (dwDesiredAccess != 0)) + hFile = CreateFileA((LPCSTR)filename, dwDesiredAccess, dwShareMode, NULL, dwCreationDisposition, dwFlagsAndAttributes, NULL); + + return win32_build_iowin(hFile); +} + + +voidpf ZCALLBACK win32_open64_file_funcW (voidpf opaque,const void* filename,int mode) +{ + const char* mode_fopen = NULL; + DWORD dwDesiredAccess,dwCreationDisposition,dwShareMode,dwFlagsAndAttributes ; + HANDLE hFile = NULL; + + win32_translate_open_mode(mode,&dwDesiredAccess,&dwCreationDisposition,&dwShareMode,&dwFlagsAndAttributes); + + if ((filename!=NULL) && (dwDesiredAccess != 0)) + hFile = CreateFileW((LPCWSTR)filename, dwDesiredAccess, dwShareMode, NULL, dwCreationDisposition, dwFlagsAndAttributes, NULL); + + return win32_build_iowin(hFile); +} + + +voidpf ZCALLBACK win32_open_file_func (voidpf opaque,const char* filename,int mode) +{ + const char* mode_fopen = NULL; + DWORD dwDesiredAccess,dwCreationDisposition,dwShareMode,dwFlagsAndAttributes ; + HANDLE hFile = NULL; + + win32_translate_open_mode(mode,&dwDesiredAccess,&dwCreationDisposition,&dwShareMode,&dwFlagsAndAttributes); + + if ((filename!=NULL) && (dwDesiredAccess != 0)) + hFile = CreateFile((LPCTSTR)filename, dwDesiredAccess, dwShareMode, NULL, dwCreationDisposition, dwFlagsAndAttributes, NULL); + + return win32_build_iowin(hFile); +} + + +uLong ZCALLBACK win32_read_file_func (voidpf opaque, voidpf stream, void* buf,uLong size) +{ + uLong ret=0; + HANDLE hFile = NULL; + if (stream!=NULL) + hFile = ((WIN32FILE_IOWIN*)stream) -> hf; + + if (hFile != NULL) + { + if (!ReadFile(hFile, buf, size, &ret, NULL)) + { + DWORD dwErr = GetLastError(); + if (dwErr == ERROR_HANDLE_EOF) + dwErr = 0; + ((WIN32FILE_IOWIN*)stream) -> error=(int)dwErr; + } + } + + return ret; +} + + +uLong ZCALLBACK win32_write_file_func (voidpf opaque,voidpf stream,const void* buf,uLong size) +{ + uLong ret=0; + HANDLE hFile = NULL; + if (stream!=NULL) + hFile = ((WIN32FILE_IOWIN*)stream) -> hf; + + if (hFile != NULL) + { + if (!WriteFile(hFile, buf, size, &ret, NULL)) + { + DWORD dwErr = GetLastError(); + if (dwErr == ERROR_HANDLE_EOF) + dwErr = 0; + ((WIN32FILE_IOWIN*)stream) -> error=(int)dwErr; + } + } + + return ret; +} + +long ZCALLBACK win32_tell_file_func (voidpf opaque,voidpf stream) +{ + long ret=-1; + HANDLE hFile = NULL; + if (stream!=NULL) + hFile = ((WIN32FILE_IOWIN*)stream) -> hf; + if (hFile != NULL) + { + DWORD dwSet = SetFilePointer(hFile, 0, NULL, FILE_CURRENT); + if (dwSet == INVALID_SET_FILE_POINTER) + { + DWORD dwErr = GetLastError(); + ((WIN32FILE_IOWIN*)stream) -> error=(int)dwErr; + ret = -1; + } + else + ret=(long)dwSet; + } + return ret; +} + +ZPOS64_T ZCALLBACK win32_tell64_file_func (voidpf opaque, voidpf stream) +{ + ZPOS64_T ret= (ZPOS64_T)-1; + HANDLE hFile = NULL; + if (stream!=NULL) + hFile = ((WIN32FILE_IOWIN*)stream)->hf; + + if (hFile) + { + LARGE_INTEGER li; + li.QuadPart = 0; + li.u.LowPart = SetFilePointer(hFile, li.u.LowPart, &li.u.HighPart, FILE_CURRENT); + if ( (li.LowPart == 0xFFFFFFFF) && (GetLastError() != NO_ERROR)) + { + DWORD dwErr = GetLastError(); + ((WIN32FILE_IOWIN*)stream) -> error=(int)dwErr; + ret = (ZPOS64_T)-1; + } + else + ret=li.QuadPart; + } + return ret; +} + + +long ZCALLBACK win32_seek_file_func (voidpf opaque,voidpf stream,uLong offset,int origin) +{ + DWORD dwMoveMethod=0xFFFFFFFF; + HANDLE hFile = NULL; + + long ret=-1; + if (stream!=NULL) + hFile = ((WIN32FILE_IOWIN*)stream) -> hf; + switch (origin) + { + case ZLIB_FILEFUNC_SEEK_CUR : + dwMoveMethod = FILE_CURRENT; + break; + case ZLIB_FILEFUNC_SEEK_END : + dwMoveMethod = FILE_END; + break; + case ZLIB_FILEFUNC_SEEK_SET : + dwMoveMethod = FILE_BEGIN; + break; + default: return -1; + } + + if (hFile != NULL) + { + DWORD dwSet = SetFilePointer(hFile, offset, NULL, dwMoveMethod); + if (dwSet == INVALID_SET_FILE_POINTER) + { + DWORD dwErr = GetLastError(); + ((WIN32FILE_IOWIN*)stream) -> error=(int)dwErr; + ret = -1; + } + else + ret=0; + } + return ret; +} + +long ZCALLBACK win32_seek64_file_func (voidpf opaque, voidpf stream,ZPOS64_T offset,int origin) +{ + DWORD dwMoveMethod=0xFFFFFFFF; + HANDLE hFile = NULL; + long ret=-1; + + if (stream!=NULL) + hFile = ((WIN32FILE_IOWIN*)stream)->hf; + + switch (origin) + { + case ZLIB_FILEFUNC_SEEK_CUR : + dwMoveMethod = FILE_CURRENT; + break; + case ZLIB_FILEFUNC_SEEK_END : + dwMoveMethod = FILE_END; + break; + case ZLIB_FILEFUNC_SEEK_SET : + dwMoveMethod = FILE_BEGIN; + break; + default: return -1; + } + + if (hFile) + { + LARGE_INTEGER* li = (LARGE_INTEGER*)&offset; + DWORD dwSet = SetFilePointer(hFile, li->u.LowPart, &li->u.HighPart, dwMoveMethod); + if (dwSet == INVALID_SET_FILE_POINTER) + { + DWORD dwErr = GetLastError(); + ((WIN32FILE_IOWIN*)stream) -> error=(int)dwErr; + ret = -1; + } + else + ret=0; + } + return ret; +} + +int ZCALLBACK win32_close_file_func (voidpf opaque, voidpf stream) +{ + int ret=-1; + + if (stream!=NULL) + { + HANDLE hFile; + hFile = ((WIN32FILE_IOWIN*)stream) -> hf; + if (hFile != NULL) + { + CloseHandle(hFile); + ret=0; + } + free(stream); + } + return ret; +} + +int ZCALLBACK win32_error_file_func (voidpf opaque,voidpf stream) +{ + int ret=-1; + if (stream!=NULL) + { + ret = ((WIN32FILE_IOWIN*)stream) -> error; + } + return ret; +} + +void fill_win32_filefunc (zlib_filefunc_def* pzlib_filefunc_def) +{ + pzlib_filefunc_def->zopen_file = win32_open_file_func; + pzlib_filefunc_def->zread_file = win32_read_file_func; + pzlib_filefunc_def->zwrite_file = win32_write_file_func; + pzlib_filefunc_def->ztell_file = win32_tell_file_func; + pzlib_filefunc_def->zseek_file = win32_seek_file_func; + pzlib_filefunc_def->zclose_file = win32_close_file_func; + pzlib_filefunc_def->zerror_file = win32_error_file_func; + pzlib_filefunc_def->opaque = NULL; +} + +void fill_win32_filefunc64(zlib_filefunc64_def* pzlib_filefunc_def) +{ + pzlib_filefunc_def->zopen64_file = win32_open64_file_func; + pzlib_filefunc_def->zread_file = win32_read_file_func; + pzlib_filefunc_def->zwrite_file = win32_write_file_func; + pzlib_filefunc_def->ztell64_file = win32_tell64_file_func; + pzlib_filefunc_def->zseek64_file = win32_seek64_file_func; + pzlib_filefunc_def->zclose_file = win32_close_file_func; + pzlib_filefunc_def->zerror_file = win32_error_file_func; + pzlib_filefunc_def->opaque = NULL; +} + + +void fill_win32_filefunc64A(zlib_filefunc64_def* pzlib_filefunc_def) +{ + pzlib_filefunc_def->zopen64_file = win32_open64_file_funcA; + pzlib_filefunc_def->zread_file = win32_read_file_func; + pzlib_filefunc_def->zwrite_file = win32_write_file_func; + pzlib_filefunc_def->ztell64_file = win32_tell64_file_func; + pzlib_filefunc_def->zseek64_file = win32_seek64_file_func; + pzlib_filefunc_def->zclose_file = win32_close_file_func; + pzlib_filefunc_def->zerror_file = win32_error_file_func; + pzlib_filefunc_def->opaque = NULL; +} + + +void fill_win32_filefunc64W(zlib_filefunc64_def* pzlib_filefunc_def) +{ + pzlib_filefunc_def->zopen64_file = win32_open64_file_funcW; + pzlib_filefunc_def->zread_file = win32_read_file_func; + pzlib_filefunc_def->zwrite_file = win32_write_file_func; + pzlib_filefunc_def->ztell64_file = win32_tell64_file_func; + pzlib_filefunc_def->zseek64_file = win32_seek64_file_func; + pzlib_filefunc_def->zclose_file = win32_close_file_func; + pzlib_filefunc_def->zerror_file = win32_error_file_func; + pzlib_filefunc_def->opaque = NULL; +} diff --git a/Samples/3rdParty/minizip/src/iowin32.h b/Samples/3rdParty/minizip/src/iowin32.h new file mode 100755 index 0000000..0ca0969 --- /dev/null +++ b/Samples/3rdParty/minizip/src/iowin32.h @@ -0,0 +1,28 @@ +/* iowin32.h -- IO base function header for compress/uncompress .zip + Version 1.1, February 14h, 2010 + part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html ) + + Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html ) + + Modifications for Zip64 support + Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com ) + + For more info read MiniZip_info.txt + +*/ + +#include + + +#ifdef __cplusplus +extern "C" { +#endif + +void fill_win32_filefunc OF((zlib_filefunc_def* pzlib_filefunc_def)); +void fill_win32_filefunc64 OF((zlib_filefunc64_def* pzlib_filefunc_def)); +void fill_win32_filefunc64A OF((zlib_filefunc64_def* pzlib_filefunc_def)); +void fill_win32_filefunc64W OF((zlib_filefunc64_def* pzlib_filefunc_def)); + +#ifdef __cplusplus +} +#endif diff --git a/Samples/3rdParty/minizip/src/miniunz.c b/Samples/3rdParty/minizip/src/miniunz.c new file mode 100755 index 0000000..e46c40b --- /dev/null +++ b/Samples/3rdParty/minizip/src/miniunz.c @@ -0,0 +1,661 @@ +/* + miniunz.c + Version 1.1, February 14h, 2010 + sample part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html ) + + Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html ) + + Modifications of Unzip for Zip64 + Copyright (C) 2007-2008 Even Rouault + + Modifications for Zip64 support on both zip and unzip + Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com ) +*/ + +#if (!defined(_WIN32)) && (!defined(WIN32)) && (!defined(__APPLE__)) + #ifndef __USE_FILE_OFFSET64 + #define __USE_FILE_OFFSET64 + #endif + #ifndef __USE_LARGEFILE64 + #define __USE_LARGEFILE64 + #endif + #ifndef _LARGEFILE64_SOURCE + #define _LARGEFILE64_SOURCE + #endif + #ifndef _FILE_OFFSET_BIT + #define _FILE_OFFSET_BIT 64 + #endif +#endif + +#if 1 //defined__APPLE__ +// In darwin and perhaps other BSD variants off_t is a 64 bit value, hence no need for specific 64 bit functions +#define FOPEN_FUNC(filename, mode) fopen(filename, mode) +#define FTELLO_FUNC(stream) ftello(stream) +#define FSEEKO_FUNC(stream, offset, origin) fseeko(stream, offset, origin) +#else +#define FOPEN_FUNC(filename, mode) fopen64(filename, mode) +#define FTELLO_FUNC(stream) ftello64(stream) +#define FSEEKO_FUNC(stream, offset, origin) fseeko64(stream, offset, origin) +#endif + + +#include +#include +#include +#include +#include +#include + +#ifdef _WIN32 +# include +# include +#else +# include +# include +# include // JDC: added for mkdir() declaration +#endif + + +#include "unzip.h" + +#define CASESENSITIVITY (0) +#define WRITEBUFFERSIZE (8192) +#define MAXFILENAME (256) + +#ifdef _WIN32 +#define USEWIN32IOAPI +#include "iowin32.h" +#endif +/* + mini unzip, demo of unzip package + + usage : + Usage : miniunz [-exvlo] file.zip [file_to_extract] [-d extractdir] + + list the file in the zipfile, and print the content of FILE_ID.ZIP or README.TXT + if it exists +*/ + + +/* change_file_date : change the date/time of a file + filename : the filename of the file where date/time must be modified + dosdate : the new date at the MSDos format (4 bytes) + tmu_date : the SAME new date at the tm_unz format */ +void change_file_date(filename,dosdate,tmu_date) + const char *filename; + uLong dosdate; + tm_unz tmu_date; +{ +#ifdef _WIN32 + HANDLE hFile; + FILETIME ftm,ftLocal,ftCreate,ftLastAcc,ftLastWrite; + + hFile = CreateFileA(filename,GENERIC_READ | GENERIC_WRITE, + 0,NULL,OPEN_EXISTING,0,NULL); + GetFileTime(hFile,&ftCreate,&ftLastAcc,&ftLastWrite); + DosDateTimeToFileTime((WORD)(dosdate>>16),(WORD)dosdate,&ftLocal); + LocalFileTimeToFileTime(&ftLocal,&ftm); + SetFileTime(hFile,&ftm,&ftLastAcc,&ftm); + CloseHandle(hFile); +#else +#if 1//def unix || __APPLE__ + struct utimbuf ut; + struct tm newdate; + newdate.tm_sec = tmu_date.tm_sec; + newdate.tm_min=tmu_date.tm_min; + newdate.tm_hour=tmu_date.tm_hour; + newdate.tm_mday=tmu_date.tm_mday; + newdate.tm_mon=tmu_date.tm_mon; + if (tmu_date.tm_year > 1900) + newdate.tm_year=tmu_date.tm_year - 1900; + else + newdate.tm_year=tmu_date.tm_year ; + newdate.tm_isdst=-1; + + ut.actime=ut.modtime=mktime(&newdate); + utime(filename,&ut); +#endif +#endif +} + + +/* mymkdir and change_file_date are not 100 % portable + As I don't know well Unix, I wait feedback for the unix portion */ + +int mymkdir(dirname) + const char* dirname; +{ + int ret=0; +#ifdef _WIN32 + ret = _mkdir(dirname); +#elif unix + ret = mkdir (dirname,0775); +#elif __APPLE__ + ret = mkdir (dirname,0775); +#endif + return ret; +} + +int makedir (newdir) + const char *newdir; +{ + char *buffer ; + char *p; + int len = (int)strlen(newdir); + + if (len <= 0) + return 0; + + buffer = (char*)malloc(len+1); + if (buffer==NULL) + { + printf("Error allocating memory\n"); + return UNZ_INTERNALERROR; + } + strcpy(buffer,newdir); + + if (buffer[len-1] == '/') { + buffer[len-1] = '\0'; + } + if (mymkdir(buffer) == 0) + { + free(buffer); + return 1; + } + + p = buffer+1; + while (1) + { + char hold; + + while(*p && *p != '\\' && *p != '/') + p++; + hold = *p; + *p = 0; + if ((mymkdir(buffer) == -1) && (errno == ENOENT)) + { + printf("couldn't create directory %s\n",buffer); + free(buffer); + return 0; + } + if (hold == 0) + break; + *p++ = hold; + } + free(buffer); + return 1; +} + +void do_banner() +{ + printf("MiniUnz 1.01b, demo of zLib + Unz package written by Gilles Vollant\n"); + printf("more info at http://www.winimage.com/zLibDll/unzip.html\n\n"); +} + +void do_help() +{ + printf("Usage : miniunz [-e] [-x] [-v] [-l] [-o] [-p password] file.zip [file_to_extr.] [-d extractdir]\n\n" \ + " -e Extract without pathname (junk paths)\n" \ + " -x Extract with pathname\n" \ + " -v list files\n" \ + " -l list files\n" \ + " -d directory to extract into\n" \ + " -o overwrite files without prompting\n" \ + " -p extract crypted file using password\n\n"); +} + +void Display64BitsSize(ZPOS64_T n, int size_char) +{ + /* to avoid compatibility problem , we do here the conversion */ + char number[21]; + int offset=19; + int pos_string = 19; + number[20]=0; + for (;;) { + number[offset]=(char)((n%10)+'0'); + if (number[offset] != '0') + pos_string=offset; + n/=10; + if (offset==0) + break; + offset--; + } + { + int size_display_string = 19-pos_string; + while (size_char > size_display_string) + { + size_char--; + printf(" "); + } + } + + printf("%s",&number[pos_string]); +} + +int do_list(uf) + unzFile uf; +{ + uLong i; + unz_global_info64 gi; + int err; + + err = unzGetGlobalInfo64(uf,&gi); + if (err!=UNZ_OK) + printf("error %d with zipfile in unzGetGlobalInfo \n",err); + printf(" Length Method Size Ratio Date Time CRC-32 Name\n"); + printf(" ------ ------ ---- ----- ---- ---- ------ ----\n"); + for (i=0;i0) + ratio = (uLong)((file_info.compressed_size*100)/file_info.uncompressed_size); + + /* display a '*' if the file is crypted */ + if ((file_info.flag & 1) != 0) + charCrypt='*'; + + if (file_info.compression_method==0) + string_method="Stored"; + else + if (file_info.compression_method==Z_DEFLATED) + { + uInt iLevel=(uInt)((file_info.flag & 0x6)/2); + if (iLevel==0) + string_method="Defl:N"; + else if (iLevel==1) + string_method="Defl:X"; + else if ((iLevel==2) || (iLevel==3)) + string_method="Defl:F"; /* 2:fast , 3 : extra fast*/ + } + else + if (file_info.compression_method==Z_BZIP2ED) + { + string_method="BZip2 "; + } + else + string_method="Unkn. "; + + Display64BitsSize(file_info.uncompressed_size,7); + printf(" %6s%c",string_method,charCrypt); + Display64BitsSize(file_info.compressed_size,7); + printf(" %3lu%% %2.2lu-%2.2lu-%2.2lu %2.2lu:%2.2lu %8.8lx %s\n", + ratio, + (uLong)file_info.tmu_date.tm_mon + 1, + (uLong)file_info.tmu_date.tm_mday, + (uLong)file_info.tmu_date.tm_year % 100, + (uLong)file_info.tmu_date.tm_hour,(uLong)file_info.tmu_date.tm_min, + (uLong)file_info.crc,filename_inzip); + if ((i+1)='a') && (rep<='z')) + rep -= 0x20; + } + while ((rep!='Y') && (rep!='N') && (rep!='A')); + } + + if (rep == 'N') + skip = 1; + + if (rep == 'A') + *popt_overwrite=1; + } + + if ((skip==0) && (err==UNZ_OK)) + { + fout=FOPEN_FUNC(write_filename,"wb"); + /* some zipfile don't contain directory alone before file */ + if ((fout==NULL) && ((*popt_extract_without_path)==0) && + (filename_withoutpath!=(char*)filename_inzip)) + { + char c=*(filename_withoutpath-1); + *(filename_withoutpath-1)='\0'; + makedir(write_filename); + *(filename_withoutpath-1)=c; + fout=FOPEN_FUNC(write_filename,"wb"); + } + + if (fout==NULL) + { + printf("error opening %s\n",write_filename); + } + } + + if (fout!=NULL) + { + printf(" extracting: %s\n",write_filename); + + do + { + err = unzReadCurrentFile(uf,buf,size_buf); + if (err<0) + { + printf("error %d with zipfile in unzReadCurrentFile\n",err); + break; + } + if (err>0) + if (fwrite(buf,err,1,fout)!=1) + { + printf("error in writing extracted file\n"); + err=UNZ_ERRNO; + break; + } + } + while (err>0); + if (fout) + fclose(fout); + + if (err==0) + change_file_date(write_filename,file_info.dosDate, + file_info.tmu_date); + } + + if (err==UNZ_OK) + { + err = unzCloseCurrentFile (uf); + if (err!=UNZ_OK) + { + printf("error %d with zipfile in unzCloseCurrentFile\n",err); + } + } + else + unzCloseCurrentFile(uf); /* don't lose the error */ + } + + free(buf); + return err; +} + + +int do_extract(uf,opt_extract_without_path,opt_overwrite,password) + unzFile uf; + int opt_extract_without_path; + int opt_overwrite; + const char* password; +{ + uLong i; + unz_global_info64 gi; + int err; + // JDC: removed for unused warning: FILE* fout=NULL; + + err = unzGetGlobalInfo64(uf,&gi); + if (err!=UNZ_OK) + printf("error %d with zipfile in unzGetGlobalInfo \n",err); + + for (i=0;i +#include +#include +#include +#include +#include + +#ifdef _WIN32 +# include +# include +#else +# include +# include +# include +# include +#endif + +#include "zip.h" + +#ifdef _WIN32 + #define USEWIN32IOAPI + #include "iowin32.h" +#endif + + + +#define WRITEBUFFERSIZE (16384) +#define MAXFILENAME (256) + +#ifdef _WIN32 +uLong filetime(f, tmzip, dt) + char *f; /* name of file to get info on */ + tm_zip *tmzip; /* return value: access, modific. and creation times */ + uLong *dt; /* dostime */ +{ + int ret = 0; + { + FILETIME ftLocal; + HANDLE hFind; + WIN32_FIND_DATAA ff32; + + hFind = FindFirstFileA(f,&ff32); + if (hFind != INVALID_HANDLE_VALUE) + { + FileTimeToLocalFileTime(&(ff32.ftLastWriteTime),&ftLocal); + FileTimeToDosDateTime(&ftLocal,((LPWORD)dt)+1,((LPWORD)dt)+0); + FindClose(hFind); + ret = 1; + } + } + return ret; +} +#else +#if defined unix || defined __APPLE__ +uLong filetime(f, tmzip, dt) + char *f; /* name of file to get info on */ + tm_zip *tmzip; /* return value: access, modific. and creation times */ + uLong *dt; /* dostime */ +{ + int ret=0; + struct stat s; /* results of stat() */ + struct tm* filedate; + time_t tm_t=0; + + if (strcmp(f,"-")!=0) + { + char name[MAXFILENAME+1]; + int len = strlen(f); + if (len > MAXFILENAME) + len = MAXFILENAME; + + strncpy(name, f,MAXFILENAME-1); + /* strncpy doesnt append the trailing NULL, of the string is too long. */ + name[ MAXFILENAME ] = '\0'; + + if (name[len - 1] == '/') + name[len - 1] = '\0'; + /* not all systems allow stat'ing a file with / appended */ + if (stat(name,&s)==0) + { + tm_t = s.st_mtime; + ret = 1; + } + } + filedate = localtime(&tm_t); + + tmzip->tm_sec = filedate->tm_sec; + tmzip->tm_min = filedate->tm_min; + tmzip->tm_hour = filedate->tm_hour; + tmzip->tm_mday = filedate->tm_mday; + tmzip->tm_mon = filedate->tm_mon ; + tmzip->tm_year = filedate->tm_year; + + return ret; +} +#else +uLong filetime(f, tmzip, dt) + char *f; /* name of file to get info on */ + tm_zip *tmzip; /* return value: access, modific. and creation times */ + uLong *dt; /* dostime */ +{ + return 0; +} +#endif +#endif + + + + +int check_exist_file(filename) + const char* filename; +{ + FILE* ftestexist; + int ret = 1; + ftestexist = FOPEN_FUNC(filename,"rb"); + if (ftestexist==NULL) + ret = 0; + else + fclose(ftestexist); + return ret; +} + +void do_banner() +{ + printf("MiniZip 1.1, demo of zLib + MiniZip64 package, written by Gilles Vollant\n"); + printf("more info on MiniZip at http://www.winimage.com/zLibDll/minizip.html\n\n"); +} + +void do_help() +{ + printf("Usage : minizip [-o] [-a] [-0 to -9] [-p password] [-j] file.zip [files_to_add]\n\n" \ + " -o Overwrite existing file.zip\n" \ + " -a Append to existing file.zip\n" \ + " -0 Store only\n" \ + " -1 Compress faster\n" \ + " -9 Compress better\n\n" \ + " -j exclude path. store only the file name.\n\n"); +} + +/* calculate the CRC32 of a file, + because to encrypt a file, we need known the CRC32 of the file before */ +int getFileCrc(const char* filenameinzip,void*buf,unsigned long size_buf,unsigned long* result_crc) +{ + unsigned long calculate_crc=0; + int err=ZIP_OK; + FILE * fin = FOPEN_FUNC(filenameinzip,"rb"); + + unsigned long size_read = 0; + unsigned long total_read = 0; + if (fin==NULL) + { + err = ZIP_ERRNO; + } + + if (err == ZIP_OK) + do + { + err = ZIP_OK; + size_read = (int)fread(buf,1,size_buf,fin); + if (size_read < size_buf) + if (feof(fin)==0) + { + printf("error in reading %s\n",filenameinzip); + err = ZIP_ERRNO; + } + + if (size_read>0) + calculate_crc = crc32(calculate_crc,buf,size_read); + total_read += size_read; + + } while ((err == ZIP_OK) && (size_read>0)); + + if (fin) + fclose(fin); + + *result_crc=calculate_crc; + printf("file %s crc %lx\n", filenameinzip, calculate_crc); + return err; +} + +int isLargeFile(const char* filename) +{ + int largeFile = 0; + ZPOS64_T pos = 0; + FILE* pFile = FOPEN_FUNC(filename, "rb"); + + if(pFile != NULL) + { + int n = FSEEKO_FUNC(pFile, 0, SEEK_END); + pos = FTELLO_FUNC(pFile); + + printf("File : %s is %lld bytes\n", filename, pos); + + if(pos >= 0xffffffff) + largeFile = 1; + + fclose(pFile); + } + + return largeFile; +} + +int main(argc,argv) + int argc; + char *argv[]; +{ + int i; + int opt_overwrite=0; + int opt_compress_level=Z_DEFAULT_COMPRESSION; + int opt_exclude_path=0; + int zipfilenamearg = 0; + char filename_try[MAXFILENAME+16]; + int zipok; + int err=0; + int size_buf=0; + void* buf=NULL; + const char* password=NULL; + + + do_banner(); + if (argc==1) + { + do_help(); + return 0; + } + else + { + for (i=1;i='0') && (c<='9')) + opt_compress_level = c-'0'; + if ((c=='j') || (c=='J')) + opt_exclude_path = 1; + + if (((c=='p') || (c=='P')) && (i+1='a') && (rep<='z')) + rep -= 0x20; + } + while ((rep!='Y') && (rep!='N') && (rep!='A')); + if (rep=='N') + zipok = 0; + if (rep=='A') + opt_overwrite = 2; + } + } + + if (zipok==1) + { + zipFile zf; + int errclose; +# ifdef USEWIN32IOAPI + zlib_filefunc64_def ffunc; + fill_win32_filefunc64A(&ffunc); + zf = zipOpen2_64(filename_try,(opt_overwrite==2) ? 2 : 0,NULL,&ffunc); +# else + zf = zipOpen64(filename_try,(opt_overwrite==2) ? 2 : 0); +# endif + + if (zf == NULL) + { + printf("error opening %s\n",filename_try); + err= ZIP_ERRNO; + } + else + printf("creating %s\n",filename_try); + + for (i=zipfilenamearg+1;(i='0') || (argv[i][1]<='9'))) && + (strlen(argv[i]) == 2))) + { + FILE * fin; + int size_read; + const char* filenameinzip = argv[i]; + const char *savefilenameinzip; + zip_fileinfo zi; + unsigned long crcFile=0; + int zip64 = 0; + + zi.tmz_date.tm_sec = zi.tmz_date.tm_min = zi.tmz_date.tm_hour = + zi.tmz_date.tm_mday = zi.tmz_date.tm_mon = zi.tmz_date.tm_year = 0; + zi.dosDate = 0; + zi.internal_fa = 0; + zi.external_fa = 0; + filetime(filenameinzip,&zi.tmz_date,&zi.dosDate); + +/* + err = zipOpenNewFileInZip(zf,filenameinzip,&zi, + NULL,0,NULL,0,NULL / * comment * /, + (opt_compress_level != 0) ? Z_DEFLATED : 0, + opt_compress_level); +*/ + if ((password != NULL) && (err==ZIP_OK)) + err = getFileCrc(filenameinzip,buf,size_buf,&crcFile); + + zip64 = isLargeFile(filenameinzip); + + /* The path name saved, should not include a leading slash. */ + /*if it did, windows/xp and dynazip couldn't read the zip file. */ + savefilenameinzip = filenameinzip; + while( savefilenameinzip[0] == '\\' || savefilenameinzip[0] == '/' ) + { + savefilenameinzip++; + } + + /*should the zip file contain any path at all?*/ + if( opt_exclude_path ) + { + const char *tmpptr; + const char *lastslash = 0; + for( tmpptr = savefilenameinzip; *tmpptr; tmpptr++) + { + if( *tmpptr == '\\' || *tmpptr == '/') + { + lastslash = tmpptr; + } + } + if( lastslash != NULL ) + { + savefilenameinzip = lastslash+1; // base filename follows last slash. + } + } + + /**/ + err = zipOpenNewFileInZip3_64(zf,savefilenameinzip,&zi, + NULL,0,NULL,0,NULL /* comment*/, + (opt_compress_level != 0) ? Z_DEFLATED : 0, + opt_compress_level,0, + /* -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, */ + -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, + password,crcFile, zip64); + + if (err != ZIP_OK) + printf("error in opening %s in zipfile\n",filenameinzip); + else + { + fin = FOPEN_FUNC(filenameinzip,"rb"); + if (fin==NULL) + { + err=ZIP_ERRNO; + printf("error in opening %s for reading\n",filenameinzip); + } + } + + if (err == ZIP_OK) + do + { + err = ZIP_OK; + size_read = (int)fread(buf,1,size_buf,fin); + if (size_read < size_buf) + if (feof(fin)==0) + { + printf("error in reading %s\n",filenameinzip); + err = ZIP_ERRNO; + } + + if (size_read>0) + { + err = zipWriteInFileInZip (zf,buf,size_read); + if (err<0) + { + printf("error in writing %s in the zipfile\n", + filenameinzip); + } + + } + } while ((err == ZIP_OK) && (size_read>0)); + + if (fin) + fclose(fin); + + if (err<0) + err=ZIP_ERRNO; + else + { + err = zipCloseFileInZip(zf); + if (err!=ZIP_OK) + printf("error in closing %s in the zipfile\n", + filenameinzip); + } + } + } + errclose = zipClose(zf,NULL); + if (errclose != ZIP_OK) + printf("error in closing %s\n",filename_try); + } + else + { + do_help(); + } + + free(buf); + return 0; +} diff --git a/Samples/3rdParty/minizip/src/mztools.c b/Samples/3rdParty/minizip/src/mztools.c new file mode 100755 index 0000000..96891c2 --- /dev/null +++ b/Samples/3rdParty/minizip/src/mztools.c @@ -0,0 +1,291 @@ +/* + Additional tools for Minizip + Code: Xavier Roche '2004 + License: Same as ZLIB (www.gzip.org) +*/ + +/* Code */ +#include +#include +#include +#include "zlib.h" +#include "unzip.h" + +#define READ_8(adr) ((unsigned char)*(adr)) +#define READ_16(adr) ( READ_8(adr) | (READ_8(adr+1) << 8) ) +#define READ_32(adr) ( READ_16(adr) | (READ_16((adr)+2) << 16) ) + +#define WRITE_8(buff, n) do { \ + *((unsigned char*)(buff)) = (unsigned char) ((n) & 0xff); \ +} while(0) +#define WRITE_16(buff, n) do { \ + WRITE_8((unsigned char*)(buff), n); \ + WRITE_8(((unsigned char*)(buff)) + 1, (n) >> 8); \ +} while(0) +#define WRITE_32(buff, n) do { \ + WRITE_16((unsigned char*)(buff), (n) & 0xffff); \ + WRITE_16((unsigned char*)(buff) + 2, (n) >> 16); \ +} while(0) + +extern int ZEXPORT unzRepair(file, fileOut, fileOutTmp, nRecovered, bytesRecovered) +const char* file; +const char* fileOut; +const char* fileOutTmp; +uLong* nRecovered; +uLong* bytesRecovered; +{ + int err = Z_OK; + FILE* fpZip = fopen(file, "rb"); + FILE* fpOut = fopen(fileOut, "wb"); + FILE* fpOutCD = fopen(fileOutTmp, "wb"); + if (fpZip != NULL && fpOut != NULL) { + int entries = 0; + uLong totalBytes = 0; + char header[30]; + char filename[1024]; + char extra[1024]; + int offset = 0; + int offsetCD = 0; + while ( fread(header, 1, 30, fpZip) == 30 ) { + int currentOffset = offset; + + /* File entry */ + if (READ_32(header) == 0x04034b50) { + unsigned int version = READ_16(header + 4); + unsigned int gpflag = READ_16(header + 6); + unsigned int method = READ_16(header + 8); + unsigned int filetime = READ_16(header + 10); + unsigned int filedate = READ_16(header + 12); + unsigned int crc = READ_32(header + 14); /* crc */ + unsigned int cpsize = READ_32(header + 18); /* compressed size */ + unsigned int uncpsize = READ_32(header + 22); /* uncompressed sz */ + unsigned int fnsize = READ_16(header + 26); /* file name length */ + unsigned int extsize = READ_16(header + 28); /* extra field length */ + filename[0] = extra[0] = '\0'; + + /* Header */ + if (fwrite(header, 1, 30, fpOut) == 30) { + offset += 30; + } else { + err = Z_ERRNO; + break; + } + + /* Filename */ + if (fnsize > 0) { + if (fnsize < sizeof(filename)) { + if (fread(filename, 1, fnsize, fpZip) == fnsize) { + if (fwrite(filename, 1, fnsize, fpOut) == fnsize) { + offset += fnsize; + } else { + err = Z_ERRNO; + break; + } + } else { + err = Z_ERRNO; + break; + } + } else { + err = Z_ERRNO; + break; + } + } else { + err = Z_STREAM_ERROR; + break; + } + + /* Extra field */ + if (extsize > 0) { + if (extsize < sizeof(extra)) { + if (fread(extra, 1, extsize, fpZip) == extsize) { + if (fwrite(extra, 1, extsize, fpOut) == extsize) { + offset += extsize; + } else { + err = Z_ERRNO; + break; + } + } else { + err = Z_ERRNO; + break; + } + } else { + err = Z_ERRNO; + break; + } + } + + /* Data */ + { + int dataSize = cpsize; + if (dataSize == 0) { + dataSize = uncpsize; + } + if (dataSize > 0) { + char* data = malloc(dataSize); + if (data != NULL) { + if ((int)fread(data, 1, dataSize, fpZip) == dataSize) { + if ((int)fwrite(data, 1, dataSize, fpOut) == dataSize) { + offset += dataSize; + totalBytes += dataSize; + } else { + err = Z_ERRNO; + } + } else { + err = Z_ERRNO; + } + free(data); + if (err != Z_OK) { + break; + } + } else { + err = Z_MEM_ERROR; + break; + } + } + } + + /* Central directory entry */ + { + char header[46]; + char* comment = ""; + int comsize = (int) strlen(comment); + WRITE_32(header, 0x02014b50); + WRITE_16(header + 4, version); + WRITE_16(header + 6, version); + WRITE_16(header + 8, gpflag); + WRITE_16(header + 10, method); + WRITE_16(header + 12, filetime); + WRITE_16(header + 14, filedate); + WRITE_32(header + 16, crc); + WRITE_32(header + 20, cpsize); + WRITE_32(header + 24, uncpsize); + WRITE_16(header + 28, fnsize); + WRITE_16(header + 30, extsize); + WRITE_16(header + 32, comsize); + WRITE_16(header + 34, 0); /* disk # */ + WRITE_16(header + 36, 0); /* int attrb */ + WRITE_32(header + 38, 0); /* ext attrb */ + WRITE_32(header + 42, currentOffset); + /* Header */ + if (fwrite(header, 1, 46, fpOutCD) == 46) { + offsetCD += 46; + + /* Filename */ + if (fnsize > 0) { + if (fwrite(filename, 1, fnsize, fpOutCD) == fnsize) { + offsetCD += fnsize; + } else { + err = Z_ERRNO; + break; + } + } else { + err = Z_STREAM_ERROR; + break; + } + + /* Extra field */ + if (extsize > 0) { + if (fwrite(extra, 1, extsize, fpOutCD) == extsize) { + offsetCD += extsize; + } else { + err = Z_ERRNO; + break; + } + } + + /* Comment field */ + if (comsize > 0) { + if ((int)fwrite(comment, 1, comsize, fpOutCD) == comsize) { + offsetCD += comsize; + } else { + err = Z_ERRNO; + break; + } + } + + + } else { + err = Z_ERRNO; + break; + } + } + + /* Success */ + entries++; + + } else { + break; + } + } + + /* Final central directory */ + { + int entriesZip = entries; + char header[22]; + char* comment = ""; // "ZIP File recovered by zlib/minizip/mztools"; + int comsize = (int) strlen(comment); + if (entriesZip > 0xffff) { + entriesZip = 0xffff; + } + WRITE_32(header, 0x06054b50); + WRITE_16(header + 4, 0); /* disk # */ + WRITE_16(header + 6, 0); /* disk # */ + WRITE_16(header + 8, entriesZip); /* hack */ + WRITE_16(header + 10, entriesZip); /* hack */ + WRITE_32(header + 12, offsetCD); /* size of CD */ + WRITE_32(header + 16, offset); /* offset to CD */ + WRITE_16(header + 20, comsize); /* comment */ + + /* Header */ + if (fwrite(header, 1, 22, fpOutCD) == 22) { + + /* Comment field */ + if (comsize > 0) { + if ((int)fwrite(comment, 1, comsize, fpOutCD) != comsize) { + err = Z_ERRNO; + } + } + + } else { + err = Z_ERRNO; + } + } + + /* Final merge (file + central directory) */ + fclose(fpOutCD); + if (err == Z_OK) { + fpOutCD = fopen(fileOutTmp, "rb"); + if (fpOutCD != NULL) { + int nRead; + char buffer[8192]; + while ( (nRead = (int)fread(buffer, 1, sizeof(buffer), fpOutCD)) > 0) { + if ((int)fwrite(buffer, 1, nRead, fpOut) != nRead) { + err = Z_ERRNO; + break; + } + } + fclose(fpOutCD); + } + } + + /* Close */ + fclose(fpZip); + fclose(fpOut); + + /* Wipe temporary file */ + (void)remove(fileOutTmp); + + /* Number of recovered entries */ + if (err == Z_OK) { + if (nRecovered != NULL) { + *nRecovered = entries; + } + if (bytesRecovered != NULL) { + *bytesRecovered = totalBytes; + } + } + } else { + err = Z_STREAM_ERROR; + } + return err; +} diff --git a/Samples/3rdParty/minizip/src/mztools.h b/Samples/3rdParty/minizip/src/mztools.h new file mode 100755 index 0000000..a49a426 --- /dev/null +++ b/Samples/3rdParty/minizip/src/mztools.h @@ -0,0 +1,37 @@ +/* + Additional tools for Minizip + Code: Xavier Roche '2004 + License: Same as ZLIB (www.gzip.org) +*/ + +#ifndef _zip_tools_H +#define _zip_tools_H + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef _ZLIB_H +#include "zlib.h" +#endif + +#include "unzip.h" + +/* Repair a ZIP file (missing central directory) + file: file to recover + fileOut: output file after recovery + fileOutTmp: temporary file name used for recovery +*/ +extern int ZEXPORT unzRepair(const char* file, + const char* fileOut, + const char* fileOutTmp, + uLong* nRecovered, + uLong* bytesRecovered); + + +#ifdef __cplusplus +} +#endif + + +#endif diff --git a/Samples/3rdParty/minizip/src/unzip.c b/Samples/3rdParty/minizip/src/unzip.c new file mode 100755 index 0000000..affad4b --- /dev/null +++ b/Samples/3rdParty/minizip/src/unzip.c @@ -0,0 +1,2125 @@ +/* unzip.c -- IO for uncompress .zip files using zlib + Version 1.1, February 14h, 2010 + part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html ) + + Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html ) + + Modifications of Unzip for Zip64 + Copyright (C) 2007-2008 Even Rouault + + Modifications for Zip64 support on both zip and unzip + Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com ) + + For more info read MiniZip_info.txt + + + ------------------------------------------------------------------------------------ + Decryption code comes from crypt.c by Info-ZIP but has been greatly reduced in terms of + compatibility with older software. The following is from the original crypt.c. + Code woven in by Terry Thorsen 1/2003. + + Copyright (c) 1990-2000 Info-ZIP. All rights reserved. + + See the accompanying file LICENSE, version 2000-Apr-09 or later + (the contents of which are also included in zip.h) for terms of use. + If, for some reason, all these files are missing, the Info-ZIP license + also may be found at: ftp://ftp.info-zip.org/pub/infozip/license.html + + crypt.c (full version) by Info-ZIP. Last revised: [see crypt.h] + + The encryption/decryption parts of this source code (as opposed to the + non-echoing password parts) were originally written in Europe. The + whole source package can be freely distributed, including from the USA. + (Prior to January 2000, re-export from the US was a violation of US law.) + + This encryption code is a direct transcription of the algorithm from + Roger Schlafly, described by Phil Katz in the file appnote.txt. This + file (appnote.txt) is distributed with the PKZIP program (even in the + version without encryption capabilities). + + ------------------------------------------------------------------------------------ + + Changes in unzip.c + + 2007-2008 - Even Rouault - Addition of cpl_unzGetCurrentFileZStreamPos + 2007-2008 - Even Rouault - Decoration of symbol names unz* -> cpl_unz* + 2007-2008 - Even Rouault - Remove old C style function prototypes + 2007-2008 - Even Rouault - Add unzip support for ZIP64 + + Copyright (C) 2007-2008 Even Rouault + + + Oct-2009 - Mathias Svensson - Removed cpl_* from symbol names (Even Rouault added them but since this is now moved to a new project (minizip64) I renamed them again). + Oct-2009 - Mathias Svensson - Fixed problem if uncompressed size was > 4G and compressed size was <4G + should only read the compressed/uncompressed size from the Zip64 format if + the size from normal header was 0xFFFFFFFF + Oct-2009 - Mathias Svensson - Applied some bug fixes from paches recived from Gilles Vollant + Oct-2009 - Mathias Svensson - Applied support to unzip files with compression mathod BZIP2 (bzip2 lib is required) + Patch created by Daniel Borca + + Jan-2010 - back to unzip and minizip 1.0 name scheme, with compatibility layer + + Copyright (C) 1998 - 2010 Gilles Vollant, Even Rouault, Mathias Svensson + +*/ + + +#include +#include +#include + +#ifndef NOUNCRYPT + #define NOUNCRYPT +#endif + +#include "zlib.h" +#include "unzip.h" + +#ifdef STDC +# include +# include +# include +#endif +#ifdef NO_ERRNO_H + extern int errno; +#else +# include +#endif + + +#ifndef local +# define local static +#endif +/* compile with -Dlocal if your debugger can't find static symbols */ + + +#ifndef CASESENSITIVITYDEFAULT_NO +# if !defined(unix) && !defined(CASESENSITIVITYDEFAULT_YES) +# define CASESENSITIVITYDEFAULT_NO +# endif +#endif + + +#ifndef UNZ_BUFSIZE +#define UNZ_BUFSIZE (16384) +#endif + +#ifndef UNZ_MAXFILENAMEINZIP +#define UNZ_MAXFILENAMEINZIP (256) +#endif + +#ifndef ALLOC +# define ALLOC(size) (malloc(size)) +#endif +#ifndef TRYFREE +# define TRYFREE(p) {if (p) free(p);} +#endif + +#define SIZECENTRALDIRITEM (0x2e) +#define SIZEZIPLOCALHEADER (0x1e) + + +const char unz_copyright[] = + " unzip 1.01 Copyright 1998-2004 Gilles Vollant - http://www.winimage.com/zLibDll"; + +/* unz_file_info_interntal contain internal info about a file in zipfile*/ +typedef struct unz_file_info64_internal_s +{ + ZPOS64_T offset_curfile;/* relative offset of local header 8 bytes */ +} unz_file_info64_internal; + + +/* file_in_zip_read_info_s contain internal information about a file in zipfile, + when reading and decompress it */ +typedef struct +{ + char *read_buffer; /* internal buffer for compressed data */ + z_stream stream; /* zLib stream structure for inflate */ + +#ifdef HAVE_BZIP2 + bz_stream bstream; /* bzLib stream structure for bziped */ +#endif + + ZPOS64_T pos_in_zipfile; /* position in byte on the zipfile, for fseek*/ + uLong stream_initialised; /* flag set if stream structure is initialised*/ + + ZPOS64_T offset_local_extrafield;/* offset of the local extra field */ + uInt size_local_extrafield;/* size of the local extra field */ + ZPOS64_T pos_local_extrafield; /* position in the local extra field in read*/ + ZPOS64_T total_out_64; + + uLong crc32; /* crc32 of all data uncompressed */ + uLong crc32_wait; /* crc32 we must obtain after decompress all */ + ZPOS64_T rest_read_compressed; /* number of byte to be decompressed */ + ZPOS64_T rest_read_uncompressed;/*number of byte to be obtained after decomp*/ + zlib_filefunc64_32_def z_filefunc; + voidpf filestream; /* io structore of the zipfile */ + uLong compression_method; /* compression method (0==store) */ + ZPOS64_T byte_before_the_zipfile;/* byte before the zipfile, (>0 for sfx)*/ + int raw; +} file_in_zip64_read_info_s; + + +/* unz64_s contain internal information about the zipfile +*/ +typedef struct +{ + zlib_filefunc64_32_def z_filefunc; + int is64bitOpenFunction; + voidpf filestream; /* io structore of the zipfile */ + unz_global_info64 gi; /* public global information */ + ZPOS64_T byte_before_the_zipfile;/* byte before the zipfile, (>0 for sfx)*/ + ZPOS64_T num_file; /* number of the current file in the zipfile*/ + ZPOS64_T pos_in_central_dir; /* pos of the current file in the central dir*/ + ZPOS64_T current_file_ok; /* flag about the usability of the current file*/ + ZPOS64_T central_pos; /* position of the beginning of the central dir*/ + + ZPOS64_T size_central_dir; /* size of the central directory */ + ZPOS64_T offset_central_dir; /* offset of start of central directory with + respect to the starting disk number */ + + unz_file_info64 cur_file_info; /* public info about the current file in zip*/ + unz_file_info64_internal cur_file_info_internal; /* private info about it*/ + file_in_zip64_read_info_s* pfile_in_zip_read; /* structure about the current + file if we are decompressing it */ + int encrypted; + + int isZip64; + +# ifndef NOUNCRYPT + unsigned long keys[3]; /* keys defining the pseudo-random sequence */ + const unsigned long* pcrc_32_tab; +# endif +} unz64_s; + + +#ifndef NOUNCRYPT +#include "crypt.h" +#endif + +/* =========================================================================== + Read a byte from a gz_stream; update next_in and avail_in. Return EOF + for end of file. + IN assertion: the stream s has been sucessfully opened for reading. +*/ + + +local int unz64local_getByte OF(( + const zlib_filefunc64_32_def* pzlib_filefunc_def, + voidpf filestream, + int *pi)); + +local int unz64local_getByte(const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, int *pi) +{ + unsigned char c; + int err = (int)ZREAD64(*pzlib_filefunc_def,filestream,&c,1); + if (err==1) + { + *pi = (int)c; + return UNZ_OK; + } + else + { + if (ZERROR64(*pzlib_filefunc_def,filestream)) + return UNZ_ERRNO; + else + return UNZ_EOF; + } +} + + +/* =========================================================================== + Reads a long in LSB order from the given gz_stream. Sets +*/ +local int unz64local_getShort OF(( + const zlib_filefunc64_32_def* pzlib_filefunc_def, + voidpf filestream, + uLong *pX)); + +local int unz64local_getShort (const zlib_filefunc64_32_def* pzlib_filefunc_def, + voidpf filestream, + uLong *pX) +{ + uLong x ; + int i = 0; + int err; + + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x = (uLong)i; + + if (err==UNZ_OK) + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x |= ((uLong)i)<<8; + + if (err==UNZ_OK) + *pX = x; + else + *pX = 0; + return err; +} + +local int unz64local_getLong OF(( + const zlib_filefunc64_32_def* pzlib_filefunc_def, + voidpf filestream, + uLong *pX)); + +local int unz64local_getLong (const zlib_filefunc64_32_def* pzlib_filefunc_def, + voidpf filestream, + uLong *pX) +{ + uLong x ; + int i = 0; + int err; + + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x = (uLong)i; + + if (err==UNZ_OK) + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x |= ((uLong)i)<<8; + + if (err==UNZ_OK) + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x |= ((uLong)i)<<16; + + if (err==UNZ_OK) + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x += ((uLong)i)<<24; + + if (err==UNZ_OK) + *pX = x; + else + *pX = 0; + return err; +} + +local int unz64local_getLong64 OF(( + const zlib_filefunc64_32_def* pzlib_filefunc_def, + voidpf filestream, + ZPOS64_T *pX)); + + +local int unz64local_getLong64 (const zlib_filefunc64_32_def* pzlib_filefunc_def, + voidpf filestream, + ZPOS64_T *pX) +{ + ZPOS64_T x ; + int i = 0; + int err; + + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x = (ZPOS64_T)i; + + if (err==UNZ_OK) + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x |= ((ZPOS64_T)i)<<8; + + if (err==UNZ_OK) + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x |= ((ZPOS64_T)i)<<16; + + if (err==UNZ_OK) + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x |= ((ZPOS64_T)i)<<24; + + if (err==UNZ_OK) + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x |= ((ZPOS64_T)i)<<32; + + if (err==UNZ_OK) + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x |= ((ZPOS64_T)i)<<40; + + if (err==UNZ_OK) + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x |= ((ZPOS64_T)i)<<48; + + if (err==UNZ_OK) + err = unz64local_getByte(pzlib_filefunc_def,filestream,&i); + x |= ((ZPOS64_T)i)<<56; + + if (err==UNZ_OK) + *pX = x; + else + *pX = 0; + return err; +} + +/* My own strcmpi / strcasecmp */ +local int strcmpcasenosensitive_internal (const char* fileName1, const char* fileName2) +{ + for (;;) + { + char c1=*(fileName1++); + char c2=*(fileName2++); + if ((c1>='a') && (c1<='z')) + c1 -= 0x20; + if ((c2>='a') && (c2<='z')) + c2 -= 0x20; + if (c1=='\0') + return ((c2=='\0') ? 0 : -1); + if (c2=='\0') + return 1; + if (c1c2) + return 1; + } +} + + +#ifdef CASESENSITIVITYDEFAULT_NO +#define CASESENSITIVITYDEFAULTVALUE 2 +#else +#define CASESENSITIVITYDEFAULTVALUE 1 +#endif + +#ifndef STRCMPCASENOSENTIVEFUNCTION +#define STRCMPCASENOSENTIVEFUNCTION strcmpcasenosensitive_internal +#endif + +/* + Compare two filename (fileName1,fileName2). + If iCaseSenisivity = 1, comparision is case sensitivity (like strcmp) + If iCaseSenisivity = 2, comparision is not case sensitivity (like strcmpi + or strcasecmp) + If iCaseSenisivity = 0, case sensitivity is defaut of your operating system + (like 1 on Unix, 2 on Windows) + +*/ +extern int ZEXPORT unzStringFileNameCompare (const char* fileName1, + const char* fileName2, + int iCaseSensitivity) + +{ + if (iCaseSensitivity==0) + iCaseSensitivity=CASESENSITIVITYDEFAULTVALUE; + + if (iCaseSensitivity==1) + return strcmp(fileName1,fileName2); + + return STRCMPCASENOSENTIVEFUNCTION(fileName1,fileName2); +} + +#ifndef BUFREADCOMMENT +#define BUFREADCOMMENT (0x400) +#endif + +/* + Locate the Central directory of a zipfile (at the end, just before + the global comment) +*/ +local ZPOS64_T unz64local_SearchCentralDir OF((const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream)); +local ZPOS64_T unz64local_SearchCentralDir(const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream) +{ + unsigned char* buf; + ZPOS64_T uSizeFile; + ZPOS64_T uBackRead; + ZPOS64_T uMaxBack=0xffff; /* maximum size of global comment */ + ZPOS64_T uPosFound=0; + + if (ZSEEK64(*pzlib_filefunc_def,filestream,0,ZLIB_FILEFUNC_SEEK_END) != 0) + return 0; + + + uSizeFile = ZTELL64(*pzlib_filefunc_def,filestream); + + if (uMaxBack>uSizeFile) + uMaxBack = uSizeFile; + + buf = (unsigned char*)ALLOC(BUFREADCOMMENT+4); + if (buf==NULL) + return 0; + + uBackRead = 4; + while (uBackReaduMaxBack) + uBackRead = uMaxBack; + else + uBackRead+=BUFREADCOMMENT; + uReadPos = uSizeFile-uBackRead ; + + uReadSize = ((BUFREADCOMMENT+4) < (uSizeFile-uReadPos)) ? + (BUFREADCOMMENT+4) : (uLong)(uSizeFile-uReadPos); + if (ZSEEK64(*pzlib_filefunc_def,filestream,uReadPos,ZLIB_FILEFUNC_SEEK_SET)!=0) + break; + + if (ZREAD64(*pzlib_filefunc_def,filestream,buf,uReadSize)!=uReadSize) + break; + + for (i=(int)uReadSize-3; (i--)>0;) + if (((*(buf+i))==0x50) && ((*(buf+i+1))==0x4b) && + ((*(buf+i+2))==0x05) && ((*(buf+i+3))==0x06)) + { + uPosFound = uReadPos+i; + break; + } + + if (uPosFound!=0) + break; + } + TRYFREE(buf); + return uPosFound; +} + + +/* + Locate the Central directory 64 of a zipfile (at the end, just before + the global comment) +*/ +local ZPOS64_T unz64local_SearchCentralDir64 OF(( + const zlib_filefunc64_32_def* pzlib_filefunc_def, + voidpf filestream)); + +local ZPOS64_T unz64local_SearchCentralDir64(const zlib_filefunc64_32_def* pzlib_filefunc_def, + voidpf filestream) +{ + unsigned char* buf; + ZPOS64_T uSizeFile; + ZPOS64_T uBackRead; + ZPOS64_T uMaxBack=0xffff; /* maximum size of global comment */ + ZPOS64_T uPosFound=0; + uLong uL; + ZPOS64_T relativeOffset; + + if (ZSEEK64(*pzlib_filefunc_def,filestream,0,ZLIB_FILEFUNC_SEEK_END) != 0) + return 0; + + + uSizeFile = ZTELL64(*pzlib_filefunc_def,filestream); + + if (uMaxBack>uSizeFile) + uMaxBack = uSizeFile; + + buf = (unsigned char*)ALLOC(BUFREADCOMMENT+4); + if (buf==NULL) + return 0; + + uBackRead = 4; + while (uBackReaduMaxBack) + uBackRead = uMaxBack; + else + uBackRead+=BUFREADCOMMENT; + uReadPos = uSizeFile-uBackRead ; + + uReadSize = ((BUFREADCOMMENT+4) < (uSizeFile-uReadPos)) ? + (BUFREADCOMMENT+4) : (uLong)(uSizeFile-uReadPos); + if (ZSEEK64(*pzlib_filefunc_def,filestream,uReadPos,ZLIB_FILEFUNC_SEEK_SET)!=0) + break; + + if (ZREAD64(*pzlib_filefunc_def,filestream,buf,uReadSize)!=uReadSize) + break; + + for (i=(int)uReadSize-3; (i--)>0;) + if (((*(buf+i))==0x50) && ((*(buf+i+1))==0x4b) && + ((*(buf+i+2))==0x06) && ((*(buf+i+3))==0x07)) + { + uPosFound = uReadPos+i; + break; + } + + if (uPosFound!=0) + break; + } + TRYFREE(buf); + if (uPosFound == 0) + return 0; + + /* Zip64 end of central directory locator */ + if (ZSEEK64(*pzlib_filefunc_def,filestream, uPosFound,ZLIB_FILEFUNC_SEEK_SET)!=0) + return 0; + + /* the signature, already checked */ + if (unz64local_getLong(pzlib_filefunc_def,filestream,&uL)!=UNZ_OK) + return 0; + + /* number of the disk with the start of the zip64 end of central directory */ + if (unz64local_getLong(pzlib_filefunc_def,filestream,&uL)!=UNZ_OK) + return 0; + if (uL != 0) + return 0; + + /* relative offset of the zip64 end of central directory record */ + if (unz64local_getLong64(pzlib_filefunc_def,filestream,&relativeOffset)!=UNZ_OK) + return 0; + + /* total number of disks */ + if (unz64local_getLong(pzlib_filefunc_def,filestream,&uL)!=UNZ_OK) + return 0; + if (uL != 1) + return 0; + + /* Goto end of central directory record */ + if (ZSEEK64(*pzlib_filefunc_def,filestream, relativeOffset,ZLIB_FILEFUNC_SEEK_SET)!=0) + return 0; + + /* the signature */ + if (unz64local_getLong(pzlib_filefunc_def,filestream,&uL)!=UNZ_OK) + return 0; + + if (uL != 0x06064b50) + return 0; + + return relativeOffset; +} + +/* + Open a Zip file. path contain the full pathname (by example, + on a Windows NT computer "c:\\test\\zlib114.zip" or on an Unix computer + "zlib/zlib114.zip". + If the zipfile cannot be opened (file doesn't exist or in not valid), the + return value is NULL. + Else, the return value is a unzFile Handle, usable with other function + of this unzip package. +*/ +local unzFile unzOpenInternal (const void *path, + zlib_filefunc64_32_def* pzlib_filefunc64_32_def, + int is64bitOpenFunction) +{ + unz64_s us; + unz64_s *s; + ZPOS64_T central_pos; + uLong uL; + + uLong number_disk; /* number of the current dist, used for + spaning ZIP, unsupported, always 0*/ + uLong number_disk_with_CD; /* number the the disk with central dir, used + for spaning ZIP, unsupported, always 0*/ + ZPOS64_T number_entry_CD; /* total number of entries in + the central dir + (same than number_entry on nospan) */ + + int err=UNZ_OK; + + if (unz_copyright[0]!=' ') + return NULL; + + us.z_filefunc.zseek32_file = NULL; + us.z_filefunc.ztell32_file = NULL; + if (pzlib_filefunc64_32_def==NULL) + fill_fopen64_filefunc(&us.z_filefunc.zfile_func64); + else + us.z_filefunc = *pzlib_filefunc64_32_def; + us.is64bitOpenFunction = is64bitOpenFunction; + + + + us.filestream = ZOPEN64(us.z_filefunc, + path, + ZLIB_FILEFUNC_MODE_READ | + ZLIB_FILEFUNC_MODE_EXISTING); + if (us.filestream==NULL) + return NULL; + + central_pos = unz64local_SearchCentralDir64(&us.z_filefunc,us.filestream); + if (central_pos) + { + uLong uS; + ZPOS64_T uL64; + + us.isZip64 = 1; + + if (ZSEEK64(us.z_filefunc, us.filestream, + central_pos,ZLIB_FILEFUNC_SEEK_SET)!=0) + err=UNZ_ERRNO; + + /* the signature, already checked */ + if (unz64local_getLong(&us.z_filefunc, us.filestream,&uL)!=UNZ_OK) + err=UNZ_ERRNO; + + /* size of zip64 end of central directory record */ + if (unz64local_getLong64(&us.z_filefunc, us.filestream,&uL64)!=UNZ_OK) + err=UNZ_ERRNO; + + /* version made by */ + if (unz64local_getShort(&us.z_filefunc, us.filestream,&uS)!=UNZ_OK) + err=UNZ_ERRNO; + + /* version needed to extract */ + if (unz64local_getShort(&us.z_filefunc, us.filestream,&uS)!=UNZ_OK) + err=UNZ_ERRNO; + + /* number of this disk */ + if (unz64local_getLong(&us.z_filefunc, us.filestream,&number_disk)!=UNZ_OK) + err=UNZ_ERRNO; + + /* number of the disk with the start of the central directory */ + if (unz64local_getLong(&us.z_filefunc, us.filestream,&number_disk_with_CD)!=UNZ_OK) + err=UNZ_ERRNO; + + /* total number of entries in the central directory on this disk */ + if (unz64local_getLong64(&us.z_filefunc, us.filestream,&us.gi.number_entry)!=UNZ_OK) + err=UNZ_ERRNO; + + /* total number of entries in the central directory */ + if (unz64local_getLong64(&us.z_filefunc, us.filestream,&number_entry_CD)!=UNZ_OK) + err=UNZ_ERRNO; + + if ((number_entry_CD!=us.gi.number_entry) || + (number_disk_with_CD!=0) || + (number_disk!=0)) + err=UNZ_BADZIPFILE; + + /* size of the central directory */ + if (unz64local_getLong64(&us.z_filefunc, us.filestream,&us.size_central_dir)!=UNZ_OK) + err=UNZ_ERRNO; + + /* offset of start of central directory with respect to the + starting disk number */ + if (unz64local_getLong64(&us.z_filefunc, us.filestream,&us.offset_central_dir)!=UNZ_OK) + err=UNZ_ERRNO; + + us.gi.size_comment = 0; + } + else + { + central_pos = unz64local_SearchCentralDir(&us.z_filefunc,us.filestream); + if (central_pos==0) + err=UNZ_ERRNO; + + us.isZip64 = 0; + + if (ZSEEK64(us.z_filefunc, us.filestream, + central_pos,ZLIB_FILEFUNC_SEEK_SET)!=0) + err=UNZ_ERRNO; + + /* the signature, already checked */ + if (unz64local_getLong(&us.z_filefunc, us.filestream,&uL)!=UNZ_OK) + err=UNZ_ERRNO; + + /* number of this disk */ + if (unz64local_getShort(&us.z_filefunc, us.filestream,&number_disk)!=UNZ_OK) + err=UNZ_ERRNO; + + /* number of the disk with the start of the central directory */ + if (unz64local_getShort(&us.z_filefunc, us.filestream,&number_disk_with_CD)!=UNZ_OK) + err=UNZ_ERRNO; + + /* total number of entries in the central dir on this disk */ + if (unz64local_getShort(&us.z_filefunc, us.filestream,&uL)!=UNZ_OK) + err=UNZ_ERRNO; + us.gi.number_entry = uL; + + /* total number of entries in the central dir */ + if (unz64local_getShort(&us.z_filefunc, us.filestream,&uL)!=UNZ_OK) + err=UNZ_ERRNO; + number_entry_CD = uL; + + if ((number_entry_CD!=us.gi.number_entry) || + (number_disk_with_CD!=0) || + (number_disk!=0)) + err=UNZ_BADZIPFILE; + + /* size of the central directory */ + if (unz64local_getLong(&us.z_filefunc, us.filestream,&uL)!=UNZ_OK) + err=UNZ_ERRNO; + us.size_central_dir = uL; + + /* offset of start of central directory with respect to the + starting disk number */ + if (unz64local_getLong(&us.z_filefunc, us.filestream,&uL)!=UNZ_OK) + err=UNZ_ERRNO; + us.offset_central_dir = uL; + + /* zipfile comment length */ + if (unz64local_getShort(&us.z_filefunc, us.filestream,&us.gi.size_comment)!=UNZ_OK) + err=UNZ_ERRNO; + } + + if ((central_pospfile_in_zip_read!=NULL) + unzCloseCurrentFile(file); + + ZCLOSE64(s->z_filefunc, s->filestream); + TRYFREE(s); + return UNZ_OK; +} + + +/* + Write info about the ZipFile in the *pglobal_info structure. + No preparation of the structure is needed + return UNZ_OK if there is no problem. */ +extern int ZEXPORT unzGetGlobalInfo64 (unzFile file, unz_global_info64* pglobal_info) +{ + unz64_s* s; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + *pglobal_info=s->gi; + return UNZ_OK; +} + +extern int ZEXPORT unzGetGlobalInfo (unzFile file, unz_global_info* pglobal_info32) +{ + unz64_s* s; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + /* to do : check if number_entry is not truncated */ + pglobal_info32->number_entry = (uLong)s->gi.number_entry; + pglobal_info32->size_comment = s->gi.size_comment; + return UNZ_OK; +} +/* + Translate date/time from Dos format to tm_unz (readable more easilty) +*/ +local void unz64local_DosDateToTmuDate (ZPOS64_T ulDosDate, tm_unz* ptm) +{ + ZPOS64_T uDate; + uDate = (ZPOS64_T)(ulDosDate>>16); + ptm->tm_mday = (uInt)(uDate&0x1f) ; + ptm->tm_mon = (uInt)((((uDate)&0x1E0)/0x20)-1) ; + ptm->tm_year = (uInt)(((uDate&0x0FE00)/0x0200)+1980) ; + + ptm->tm_hour = (uInt) ((ulDosDate &0xF800)/0x800); + ptm->tm_min = (uInt) ((ulDosDate&0x7E0)/0x20) ; + ptm->tm_sec = (uInt) (2*(ulDosDate&0x1f)) ; +} + +/* + Get Info about the current file in the zipfile, with internal only info +*/ +local int unz64local_GetCurrentFileInfoInternal OF((unzFile file, + unz_file_info64 *pfile_info, + unz_file_info64_internal + *pfile_info_internal, + char *szFileName, + uLong fileNameBufferSize, + void *extraField, + uLong extraFieldBufferSize, + char *szComment, + uLong commentBufferSize)); + +local int unz64local_GetCurrentFileInfoInternal (unzFile file, + unz_file_info64 *pfile_info, + unz_file_info64_internal + *pfile_info_internal, + char *szFileName, + uLong fileNameBufferSize, + void *extraField, + uLong extraFieldBufferSize, + char *szComment, + uLong commentBufferSize) +{ + unz64_s* s; + unz_file_info64 file_info; + unz_file_info64_internal file_info_internal; + int err=UNZ_OK; + uLong uMagic; + long lSeek=0; + uLong uL; + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + if (ZSEEK64(s->z_filefunc, s->filestream, + s->pos_in_central_dir+s->byte_before_the_zipfile, + ZLIB_FILEFUNC_SEEK_SET)!=0) + err=UNZ_ERRNO; + + + /* we check the magic */ + if (err==UNZ_OK) + { + if (unz64local_getLong(&s->z_filefunc, s->filestream,&uMagic) != UNZ_OK) + err=UNZ_ERRNO; + else if (uMagic!=0x02014b50) + err=UNZ_BADZIPFILE; + } + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&file_info.version) != UNZ_OK) + err=UNZ_ERRNO; + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&file_info.version_needed) != UNZ_OK) + err=UNZ_ERRNO; + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&file_info.flag) != UNZ_OK) + err=UNZ_ERRNO; + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&file_info.compression_method) != UNZ_OK) + err=UNZ_ERRNO; + + if (unz64local_getLong(&s->z_filefunc, s->filestream,&file_info.dosDate) != UNZ_OK) + err=UNZ_ERRNO; + + unz64local_DosDateToTmuDate(file_info.dosDate,&file_info.tmu_date); + + if (unz64local_getLong(&s->z_filefunc, s->filestream,&file_info.crc) != UNZ_OK) + err=UNZ_ERRNO; + + if (unz64local_getLong(&s->z_filefunc, s->filestream,&uL) != UNZ_OK) + err=UNZ_ERRNO; + file_info.compressed_size = uL; + + if (unz64local_getLong(&s->z_filefunc, s->filestream,&uL) != UNZ_OK) + err=UNZ_ERRNO; + file_info.uncompressed_size = uL; + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&file_info.size_filename) != UNZ_OK) + err=UNZ_ERRNO; + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&file_info.size_file_extra) != UNZ_OK) + err=UNZ_ERRNO; + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&file_info.size_file_comment) != UNZ_OK) + err=UNZ_ERRNO; + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&file_info.disk_num_start) != UNZ_OK) + err=UNZ_ERRNO; + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&file_info.internal_fa) != UNZ_OK) + err=UNZ_ERRNO; + + if (unz64local_getLong(&s->z_filefunc, s->filestream,&file_info.external_fa) != UNZ_OK) + err=UNZ_ERRNO; + + // relative offset of local header + if (unz64local_getLong(&s->z_filefunc, s->filestream,&uL) != UNZ_OK) + err=UNZ_ERRNO; + file_info_internal.offset_curfile = uL; + + lSeek+=file_info.size_filename; + if ((err==UNZ_OK) && (szFileName!=NULL)) + { + uLong uSizeRead ; + if (file_info.size_filename0) && (fileNameBufferSize>0)) + if (ZREAD64(s->z_filefunc, s->filestream,szFileName,uSizeRead)!=uSizeRead) + err=UNZ_ERRNO; + lSeek -= uSizeRead; + } + + // Read extrafield + if ((err==UNZ_OK) && (extraField!=NULL)) + { + ZPOS64_T uSizeRead ; + if (file_info.size_file_extraz_filefunc, s->filestream,lSeek,ZLIB_FILEFUNC_SEEK_CUR)==0) + lSeek=0; + else + err=UNZ_ERRNO; + } + + if ((file_info.size_file_extra>0) && (extraFieldBufferSize>0)) + if (ZREAD64(s->z_filefunc, s->filestream,extraField,(uLong)uSizeRead)!=uSizeRead) + err=UNZ_ERRNO; + + lSeek += file_info.size_file_extra - (uLong)uSizeRead; + } + else + lSeek += file_info.size_file_extra; + + + if ((err==UNZ_OK) && (file_info.size_file_extra != 0)) + { + uLong acc = 0; + + // since lSeek now points to after the extra field we need to move back + lSeek -= file_info.size_file_extra; + + if (lSeek!=0) + { + if (ZSEEK64(s->z_filefunc, s->filestream,lSeek,ZLIB_FILEFUNC_SEEK_CUR)==0) + lSeek=0; + else + err=UNZ_ERRNO; + } + + while(acc < file_info.size_file_extra) + { + uLong headerId; + uLong dataSize; + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&headerId) != UNZ_OK) + err=UNZ_ERRNO; + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&dataSize) != UNZ_OK) + err=UNZ_ERRNO; + + /* ZIP64 extra fields */ + if (headerId == 0x0001) + { + uLong uL; + + if(file_info.uncompressed_size == MAXU32) + { + if (unz64local_getLong64(&s->z_filefunc, s->filestream,&file_info.uncompressed_size) != UNZ_OK) + err=UNZ_ERRNO; + } + + if(file_info.compressed_size == MAXU32) + { + if (unz64local_getLong64(&s->z_filefunc, s->filestream,&file_info.compressed_size) != UNZ_OK) + err=UNZ_ERRNO; + } + + if(file_info_internal.offset_curfile == MAXU32) + { + /* Relative Header offset */ + if (unz64local_getLong64(&s->z_filefunc, s->filestream,&file_info_internal.offset_curfile) != UNZ_OK) + err=UNZ_ERRNO; + } + + if(file_info.disk_num_start == MAXU32) + { + /* Disk Start Number */ + if (unz64local_getLong(&s->z_filefunc, s->filestream,&uL) != UNZ_OK) + err=UNZ_ERRNO; + } + + } + else + { + if (ZSEEK64(s->z_filefunc, s->filestream,dataSize,ZLIB_FILEFUNC_SEEK_CUR)!=0) + err=UNZ_ERRNO; + } + + acc += 2 + 2 + dataSize; + } + } + + if ((err==UNZ_OK) && (szComment!=NULL)) + { + uLong uSizeRead ; + if (file_info.size_file_commentz_filefunc, s->filestream,lSeek,ZLIB_FILEFUNC_SEEK_CUR)==0) + lSeek=0; + else + err=UNZ_ERRNO; + } + + if ((file_info.size_file_comment>0) && (commentBufferSize>0)) + if (ZREAD64(s->z_filefunc, s->filestream,szComment,uSizeRead)!=uSizeRead) + err=UNZ_ERRNO; + lSeek+=file_info.size_file_comment - uSizeRead; + } + else + lSeek+=file_info.size_file_comment; + + + if ((err==UNZ_OK) && (pfile_info!=NULL)) + *pfile_info=file_info; + + if ((err==UNZ_OK) && (pfile_info_internal!=NULL)) + *pfile_info_internal=file_info_internal; + + return err; +} + + + +/* + Write info about the ZipFile in the *pglobal_info structure. + No preparation of the structure is needed + return UNZ_OK if there is no problem. +*/ +extern int ZEXPORT unzGetCurrentFileInfo64 (unzFile file, + unz_file_info64 * pfile_info, + char * szFileName, uLong fileNameBufferSize, + void *extraField, uLong extraFieldBufferSize, + char* szComment, uLong commentBufferSize) +{ + return unz64local_GetCurrentFileInfoInternal(file,pfile_info,NULL, + szFileName,fileNameBufferSize, + extraField,extraFieldBufferSize, + szComment,commentBufferSize); +} + +extern int ZEXPORT unzGetCurrentFileInfo (unzFile file, + unz_file_info * pfile_info, + char * szFileName, uLong fileNameBufferSize, + void *extraField, uLong extraFieldBufferSize, + char* szComment, uLong commentBufferSize) +{ + int err; + unz_file_info64 file_info64; + err = unz64local_GetCurrentFileInfoInternal(file,&file_info64,NULL, + szFileName,fileNameBufferSize, + extraField,extraFieldBufferSize, + szComment,commentBufferSize); + if ((err==UNZ_OK) && (pfile_info != NULL)) + { + pfile_info->version = file_info64.version; + pfile_info->version_needed = file_info64.version_needed; + pfile_info->flag = file_info64.flag; + pfile_info->compression_method = file_info64.compression_method; + pfile_info->dosDate = file_info64.dosDate; + pfile_info->crc = file_info64.crc; + + pfile_info->size_filename = file_info64.size_filename; + pfile_info->size_file_extra = file_info64.size_file_extra; + pfile_info->size_file_comment = file_info64.size_file_comment; + + pfile_info->disk_num_start = file_info64.disk_num_start; + pfile_info->internal_fa = file_info64.internal_fa; + pfile_info->external_fa = file_info64.external_fa; + + pfile_info->tmu_date = file_info64.tmu_date, + + + pfile_info->compressed_size = (uLong)file_info64.compressed_size; + pfile_info->uncompressed_size = (uLong)file_info64.uncompressed_size; + + } + return err; +} +/* + Set the current file of the zipfile to the first file. + return UNZ_OK if there is no problem +*/ +extern int ZEXPORT unzGoToFirstFile (unzFile file) +{ + int err=UNZ_OK; + unz64_s* s; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + s->pos_in_central_dir=s->offset_central_dir; + s->num_file=0; + err=unz64local_GetCurrentFileInfoInternal(file,&s->cur_file_info, + &s->cur_file_info_internal, + NULL,0,NULL,0,NULL,0); + s->current_file_ok = (err == UNZ_OK); + return err; +} + +/* + Set the current file of the zipfile to the next file. + return UNZ_OK if there is no problem + return UNZ_END_OF_LIST_OF_FILE if the actual file was the latest. +*/ +extern int ZEXPORT unzGoToNextFile (unzFile file) +{ + unz64_s* s; + int err; + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + if (!s->current_file_ok) + return UNZ_END_OF_LIST_OF_FILE; + if (s->gi.number_entry != 0xffff) /* 2^16 files overflow hack */ + if (s->num_file+1==s->gi.number_entry) + return UNZ_END_OF_LIST_OF_FILE; + + s->pos_in_central_dir += SIZECENTRALDIRITEM + s->cur_file_info.size_filename + + s->cur_file_info.size_file_extra + s->cur_file_info.size_file_comment ; + s->num_file++; + err = unz64local_GetCurrentFileInfoInternal(file,&s->cur_file_info, + &s->cur_file_info_internal, + NULL,0,NULL,0,NULL,0); + s->current_file_ok = (err == UNZ_OK); + return err; +} + + +/* + Try locate the file szFileName in the zipfile. + For the iCaseSensitivity signification, see unzipStringFileNameCompare + + return value : + UNZ_OK if the file is found. It becomes the current file. + UNZ_END_OF_LIST_OF_FILE if the file is not found +*/ +extern int ZEXPORT unzLocateFile (unzFile file, const char *szFileName, int iCaseSensitivity) +{ + unz64_s* s; + int err; + + /* We remember the 'current' position in the file so that we can jump + * back there if we fail. + */ + unz_file_info64 cur_file_infoSaved; + unz_file_info64_internal cur_file_info_internalSaved; + ZPOS64_T num_fileSaved; + ZPOS64_T pos_in_central_dirSaved; + + + if (file==NULL) + return UNZ_PARAMERROR; + + if (strlen(szFileName)>=UNZ_MAXFILENAMEINZIP) + return UNZ_PARAMERROR; + + s=(unz64_s*)file; + if (!s->current_file_ok) + return UNZ_END_OF_LIST_OF_FILE; + + /* Save the current state */ + num_fileSaved = s->num_file; + pos_in_central_dirSaved = s->pos_in_central_dir; + cur_file_infoSaved = s->cur_file_info; + cur_file_info_internalSaved = s->cur_file_info_internal; + + err = unzGoToFirstFile(file); + + while (err == UNZ_OK) + { + char szCurrentFileName[UNZ_MAXFILENAMEINZIP+1]; + err = unzGetCurrentFileInfo64(file,NULL, + szCurrentFileName,sizeof(szCurrentFileName)-1, + NULL,0,NULL,0); + if (err == UNZ_OK) + { + if (unzStringFileNameCompare(szCurrentFileName, + szFileName,iCaseSensitivity)==0) + return UNZ_OK; + err = unzGoToNextFile(file); + } + } + + /* We failed, so restore the state of the 'current file' to where we + * were. + */ + s->num_file = num_fileSaved ; + s->pos_in_central_dir = pos_in_central_dirSaved ; + s->cur_file_info = cur_file_infoSaved; + s->cur_file_info_internal = cur_file_info_internalSaved; + return err; +} + + +/* +/////////////////////////////////////////// +// Contributed by Ryan Haksi (mailto://cryogen@infoserve.net) +// I need random access +// +// Further optimization could be realized by adding an ability +// to cache the directory in memory. The goal being a single +// comprehensive file read to put the file I need in a memory. +*/ + +/* +typedef struct unz_file_pos_s +{ + ZPOS64_T pos_in_zip_directory; // offset in file + ZPOS64_T num_of_file; // # of file +} unz_file_pos; +*/ + +extern int ZEXPORT unzGetFilePos64(unzFile file, unz64_file_pos* file_pos) +{ + unz64_s* s; + + if (file==NULL || file_pos==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + if (!s->current_file_ok) + return UNZ_END_OF_LIST_OF_FILE; + + file_pos->pos_in_zip_directory = s->pos_in_central_dir; + file_pos->num_of_file = s->num_file; + + return UNZ_OK; +} + +extern int ZEXPORT unzGetFilePos( + unzFile file, + unz_file_pos* file_pos) +{ + unz64_file_pos file_pos64; + int err = unzGetFilePos64(file,&file_pos64); + if (err==UNZ_OK) + { + file_pos->pos_in_zip_directory = (uLong)file_pos64.pos_in_zip_directory; + file_pos->num_of_file = (uLong)file_pos64.num_of_file; + } + return err; +} + +extern int ZEXPORT unzGoToFilePos64(unzFile file, const unz64_file_pos* file_pos) +{ + unz64_s* s; + int err; + + if (file==NULL || file_pos==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + + /* jump to the right spot */ + s->pos_in_central_dir = file_pos->pos_in_zip_directory; + s->num_file = file_pos->num_of_file; + + /* set the current file */ + err = unz64local_GetCurrentFileInfoInternal(file,&s->cur_file_info, + &s->cur_file_info_internal, + NULL,0,NULL,0,NULL,0); + /* return results */ + s->current_file_ok = (err == UNZ_OK); + return err; +} + +extern int ZEXPORT unzGoToFilePos( + unzFile file, + unz_file_pos* file_pos) +{ + unz64_file_pos file_pos64; + if (file_pos == NULL) + return UNZ_PARAMERROR; + + file_pos64.pos_in_zip_directory = file_pos->pos_in_zip_directory; + file_pos64.num_of_file = file_pos->num_of_file; + return unzGoToFilePos64(file,&file_pos64); +} + +/* +// Unzip Helper Functions - should be here? +/////////////////////////////////////////// +*/ + +/* + Read the local header of the current zipfile + Check the coherency of the local header and info in the end of central + directory about this file + store in *piSizeVar the size of extra info in local header + (filename and size of extra field data) +*/ +local int unz64local_CheckCurrentFileCoherencyHeader (unz64_s* s, uInt* piSizeVar, + ZPOS64_T * poffset_local_extrafield, + uInt * psize_local_extrafield) +{ + uLong uMagic,uData,uFlags; + uLong size_filename; + uLong size_extra_field; + int err=UNZ_OK; + + *piSizeVar = 0; + *poffset_local_extrafield = 0; + *psize_local_extrafield = 0; + + if (ZSEEK64(s->z_filefunc, s->filestream,s->cur_file_info_internal.offset_curfile + + s->byte_before_the_zipfile,ZLIB_FILEFUNC_SEEK_SET)!=0) + return UNZ_ERRNO; + + + if (err==UNZ_OK) + { + if (unz64local_getLong(&s->z_filefunc, s->filestream,&uMagic) != UNZ_OK) + err=UNZ_ERRNO; + else if (uMagic!=0x04034b50) + err=UNZ_BADZIPFILE; + } + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) + err=UNZ_ERRNO; +/* + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.wVersion)) + err=UNZ_BADZIPFILE; +*/ + if (unz64local_getShort(&s->z_filefunc, s->filestream,&uFlags) != UNZ_OK) + err=UNZ_ERRNO; + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.compression_method)) + err=UNZ_BADZIPFILE; + + if ((err==UNZ_OK) && (s->cur_file_info.compression_method!=0) && +/* #ifdef HAVE_BZIP2 */ + (s->cur_file_info.compression_method!=Z_BZIP2ED) && +/* #endif */ + (s->cur_file_info.compression_method!=Z_DEFLATED)) + err=UNZ_BADZIPFILE; + + if (unz64local_getLong(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) /* date/time */ + err=UNZ_ERRNO; + + if (unz64local_getLong(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) /* crc */ + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (uData!=s->cur_file_info.crc) && ((uFlags & 8)==0)) + err=UNZ_BADZIPFILE; + + if (unz64local_getLong(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) /* size compr */ + err=UNZ_ERRNO; + else if (uData != 0xFFFFFFFF && (err==UNZ_OK) && (uData!=s->cur_file_info.compressed_size) && ((uFlags & 8)==0)) + err=UNZ_BADZIPFILE; + + if (unz64local_getLong(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) /* size uncompr */ + err=UNZ_ERRNO; + else if (uData != 0xFFFFFFFF && (err==UNZ_OK) && (uData!=s->cur_file_info.uncompressed_size) && ((uFlags & 8)==0)) + err=UNZ_BADZIPFILE; + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&size_filename) != UNZ_OK) + err=UNZ_ERRNO; + else if ((err==UNZ_OK) && (size_filename!=s->cur_file_info.size_filename)) + err=UNZ_BADZIPFILE; + + *piSizeVar += (uInt)size_filename; + + if (unz64local_getShort(&s->z_filefunc, s->filestream,&size_extra_field) != UNZ_OK) + err=UNZ_ERRNO; + *poffset_local_extrafield= s->cur_file_info_internal.offset_curfile + + SIZEZIPLOCALHEADER + size_filename; + *psize_local_extrafield = (uInt)size_extra_field; + + *piSizeVar += (uInt)size_extra_field; + + return err; +} + +/* + Open for reading data the current file in the zipfile. + If there is no error and the file is opened, the return value is UNZ_OK. +*/ +extern int ZEXPORT unzOpenCurrentFile3 (unzFile file, int* method, + int* level, int raw, const char* password) +{ + int err=UNZ_OK; + uInt iSizeVar; + unz64_s* s; + file_in_zip64_read_info_s* pfile_in_zip_read_info; + ZPOS64_T offset_local_extrafield; /* offset of the local extra field */ + uInt size_local_extrafield; /* size of the local extra field */ +# ifndef NOUNCRYPT + char source[12]; +# else + if (password != NULL) + return UNZ_PARAMERROR; +# endif + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + if (!s->current_file_ok) + return UNZ_PARAMERROR; + + if (s->pfile_in_zip_read != NULL) + unzCloseCurrentFile(file); + + if (unz64local_CheckCurrentFileCoherencyHeader(s,&iSizeVar, &offset_local_extrafield,&size_local_extrafield)!=UNZ_OK) + return UNZ_BADZIPFILE; + + pfile_in_zip_read_info = (file_in_zip64_read_info_s*)ALLOC(sizeof(file_in_zip64_read_info_s)); + if (pfile_in_zip_read_info==NULL) + return UNZ_INTERNALERROR; + + pfile_in_zip_read_info->read_buffer=(char*)ALLOC(UNZ_BUFSIZE); + pfile_in_zip_read_info->offset_local_extrafield = offset_local_extrafield; + pfile_in_zip_read_info->size_local_extrafield = size_local_extrafield; + pfile_in_zip_read_info->pos_local_extrafield=0; + pfile_in_zip_read_info->raw=raw; + + if (pfile_in_zip_read_info->read_buffer==NULL) + { + TRYFREE(pfile_in_zip_read_info); + return UNZ_INTERNALERROR; + } + + pfile_in_zip_read_info->stream_initialised=0; + + if (method!=NULL) + *method = (int)s->cur_file_info.compression_method; + + if (level!=NULL) + { + *level = 6; + switch (s->cur_file_info.flag & 0x06) + { + case 6 : *level = 1; break; + case 4 : *level = 2; break; + case 2 : *level = 9; break; + } + } + + if ((s->cur_file_info.compression_method!=0) && +/* #ifdef HAVE_BZIP2 */ + (s->cur_file_info.compression_method!=Z_BZIP2ED) && +/* #endif */ + (s->cur_file_info.compression_method!=Z_DEFLATED)) + + err=UNZ_BADZIPFILE; + + pfile_in_zip_read_info->crc32_wait=s->cur_file_info.crc; + pfile_in_zip_read_info->crc32=0; + pfile_in_zip_read_info->total_out_64=0; + pfile_in_zip_read_info->compression_method = s->cur_file_info.compression_method; + pfile_in_zip_read_info->filestream=s->filestream; + pfile_in_zip_read_info->z_filefunc=s->z_filefunc; + pfile_in_zip_read_info->byte_before_the_zipfile=s->byte_before_the_zipfile; + + pfile_in_zip_read_info->stream.total_out = 0; + + if ((s->cur_file_info.compression_method==Z_BZIP2ED) && (!raw)) + { +#ifdef HAVE_BZIP2 + pfile_in_zip_read_info->bstream.bzalloc = (void *(*) (void *, int, int))0; + pfile_in_zip_read_info->bstream.bzfree = (free_func)0; + pfile_in_zip_read_info->bstream.opaque = (voidpf)0; + pfile_in_zip_read_info->bstream.state = (voidpf)0; + + pfile_in_zip_read_info->stream.zalloc = (alloc_func)0; + pfile_in_zip_read_info->stream.zfree = (free_func)0; + pfile_in_zip_read_info->stream.opaque = (voidpf)0; + pfile_in_zip_read_info->stream.next_in = (voidpf)0; + pfile_in_zip_read_info->stream.avail_in = 0; + + err=BZ2_bzDecompressInit(&pfile_in_zip_read_info->bstream, 0, 0); + if (err == Z_OK) + pfile_in_zip_read_info->stream_initialised=Z_BZIP2ED; + else + { + TRYFREE(pfile_in_zip_read_info); + return err; + } +#else + pfile_in_zip_read_info->raw=1; +#endif + } + else if ((s->cur_file_info.compression_method==Z_DEFLATED) && (!raw)) + { + pfile_in_zip_read_info->stream.zalloc = (alloc_func)0; + pfile_in_zip_read_info->stream.zfree = (free_func)0; + pfile_in_zip_read_info->stream.opaque = (voidpf)0; + pfile_in_zip_read_info->stream.next_in = 0; + pfile_in_zip_read_info->stream.avail_in = 0; + + err=inflateInit2(&pfile_in_zip_read_info->stream, -MAX_WBITS); + if (err == Z_OK) + pfile_in_zip_read_info->stream_initialised=Z_DEFLATED; + else + { + TRYFREE(pfile_in_zip_read_info); + return err; + } + /* windowBits is passed < 0 to tell that there is no zlib header. + * Note that in this case inflate *requires* an extra "dummy" byte + * after the compressed stream in order to complete decompression and + * return Z_STREAM_END. + * In unzip, i don't wait absolutely Z_STREAM_END because I known the + * size of both compressed and uncompressed data + */ + } + pfile_in_zip_read_info->rest_read_compressed = + s->cur_file_info.compressed_size ; + pfile_in_zip_read_info->rest_read_uncompressed = + s->cur_file_info.uncompressed_size ; + + + pfile_in_zip_read_info->pos_in_zipfile = + s->cur_file_info_internal.offset_curfile + SIZEZIPLOCALHEADER + + iSizeVar; + + pfile_in_zip_read_info->stream.avail_in = (uInt)0; + + s->pfile_in_zip_read = pfile_in_zip_read_info; + s->encrypted = 0; + +# ifndef NOUNCRYPT + if (password != NULL) + { + int i; + s->pcrc_32_tab = get_crc_table(); + init_keys(password,s->keys,s->pcrc_32_tab); + if (ZSEEK64(s->z_filefunc, s->filestream, + s->pfile_in_zip_read->pos_in_zipfile + + s->pfile_in_zip_read->byte_before_the_zipfile, + SEEK_SET)!=0) + return UNZ_INTERNALERROR; + if(ZREAD64(s->z_filefunc, s->filestream,source, 12)<12) + return UNZ_INTERNALERROR; + + for (i = 0; i<12; i++) + zdecode(s->keys,s->pcrc_32_tab,source[i]); + + s->pfile_in_zip_read->pos_in_zipfile+=12; + s->encrypted=1; + } +# endif + + + return UNZ_OK; +} + +extern int ZEXPORT unzOpenCurrentFile (unzFile file) +{ + return unzOpenCurrentFile3(file, NULL, NULL, 0, NULL); +} + +extern int ZEXPORT unzOpenCurrentFilePassword (unzFile file, const char* password) +{ + return unzOpenCurrentFile3(file, NULL, NULL, 0, password); +} + +extern int ZEXPORT unzOpenCurrentFile2 (unzFile file, int* method, int* level, int raw) +{ + return unzOpenCurrentFile3(file, method, level, raw, NULL); +} + +/** Addition for GDAL : START */ + +extern ZPOS64_T ZEXPORT unzGetCurrentFileZStreamPos64( unzFile file) +{ + unz64_s* s; + file_in_zip64_read_info_s* pfile_in_zip_read_info; + s=(unz64_s*)file; + if (file==NULL) + return 0; //UNZ_PARAMERROR; + pfile_in_zip_read_info=s->pfile_in_zip_read; + if (pfile_in_zip_read_info==NULL) + return 0; //UNZ_PARAMERROR; + return pfile_in_zip_read_info->pos_in_zipfile + + pfile_in_zip_read_info->byte_before_the_zipfile; +} + +/** Addition for GDAL : END */ + +/* + Read bytes from the current file. + buf contain buffer where data must be copied + len the size of buf. + + return the number of byte copied if somes bytes are copied + return 0 if the end of file was reached + return <0 with error code if there is an error + (UNZ_ERRNO for IO error, or zLib error for uncompress error) +*/ +extern int ZEXPORT unzReadCurrentFile (unzFile file, voidp buf, unsigned len) +{ + int err=UNZ_OK; + uInt iRead = 0; + unz64_s* s; + file_in_zip64_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + + if (pfile_in_zip_read_info->read_buffer == NULL) + return UNZ_END_OF_LIST_OF_FILE; + if (len==0) + return 0; + + pfile_in_zip_read_info->stream.next_out = (Bytef*)buf; + + pfile_in_zip_read_info->stream.avail_out = (uInt)len; + + if ((len>pfile_in_zip_read_info->rest_read_uncompressed) && + (!(pfile_in_zip_read_info->raw))) + pfile_in_zip_read_info->stream.avail_out = + (uInt)pfile_in_zip_read_info->rest_read_uncompressed; + + if ((len>pfile_in_zip_read_info->rest_read_compressed+ + pfile_in_zip_read_info->stream.avail_in) && + (pfile_in_zip_read_info->raw)) + pfile_in_zip_read_info->stream.avail_out = + (uInt)pfile_in_zip_read_info->rest_read_compressed+ + pfile_in_zip_read_info->stream.avail_in; + + while (pfile_in_zip_read_info->stream.avail_out>0) + { + if ((pfile_in_zip_read_info->stream.avail_in==0) && + (pfile_in_zip_read_info->rest_read_compressed>0)) + { + uInt uReadThis = UNZ_BUFSIZE; + if (pfile_in_zip_read_info->rest_read_compressedrest_read_compressed; + if (uReadThis == 0) + return UNZ_EOF; + if (ZSEEK64(pfile_in_zip_read_info->z_filefunc, + pfile_in_zip_read_info->filestream, + pfile_in_zip_read_info->pos_in_zipfile + + pfile_in_zip_read_info->byte_before_the_zipfile, + ZLIB_FILEFUNC_SEEK_SET)!=0) + return UNZ_ERRNO; + if (ZREAD64(pfile_in_zip_read_info->z_filefunc, + pfile_in_zip_read_info->filestream, + pfile_in_zip_read_info->read_buffer, + uReadThis)!=uReadThis) + return UNZ_ERRNO; + + +# ifndef NOUNCRYPT + if(s->encrypted) + { + uInt i; + for(i=0;iread_buffer[i] = + zdecode(s->keys,s->pcrc_32_tab, + pfile_in_zip_read_info->read_buffer[i]); + } +# endif + + + pfile_in_zip_read_info->pos_in_zipfile += uReadThis; + + pfile_in_zip_read_info->rest_read_compressed-=uReadThis; + + pfile_in_zip_read_info->stream.next_in = + (Bytef*)pfile_in_zip_read_info->read_buffer; + pfile_in_zip_read_info->stream.avail_in = (uInt)uReadThis; + } + + if ((pfile_in_zip_read_info->compression_method==0) || (pfile_in_zip_read_info->raw)) + { + uInt uDoCopy,i ; + + if ((pfile_in_zip_read_info->stream.avail_in == 0) && + (pfile_in_zip_read_info->rest_read_compressed == 0)) + return (iRead==0) ? UNZ_EOF : iRead; + + if (pfile_in_zip_read_info->stream.avail_out < + pfile_in_zip_read_info->stream.avail_in) + uDoCopy = pfile_in_zip_read_info->stream.avail_out ; + else + uDoCopy = pfile_in_zip_read_info->stream.avail_in ; + + for (i=0;istream.next_out+i) = + *(pfile_in_zip_read_info->stream.next_in+i); + + pfile_in_zip_read_info->total_out_64 = pfile_in_zip_read_info->total_out_64 + uDoCopy; + + pfile_in_zip_read_info->crc32 = crc32(pfile_in_zip_read_info->crc32, + pfile_in_zip_read_info->stream.next_out, + uDoCopy); + pfile_in_zip_read_info->rest_read_uncompressed-=uDoCopy; + pfile_in_zip_read_info->stream.avail_in -= uDoCopy; + pfile_in_zip_read_info->stream.avail_out -= uDoCopy; + pfile_in_zip_read_info->stream.next_out += uDoCopy; + pfile_in_zip_read_info->stream.next_in += uDoCopy; + pfile_in_zip_read_info->stream.total_out += uDoCopy; + iRead += uDoCopy; + } + else if (pfile_in_zip_read_info->compression_method==Z_BZIP2ED) + { +#ifdef HAVE_BZIP2 + uLong uTotalOutBefore,uTotalOutAfter; + const Bytef *bufBefore; + uLong uOutThis; + + pfile_in_zip_read_info->bstream.next_in = (char*)pfile_in_zip_read_info->stream.next_in; + pfile_in_zip_read_info->bstream.avail_in = pfile_in_zip_read_info->stream.avail_in; + pfile_in_zip_read_info->bstream.total_in_lo32 = pfile_in_zip_read_info->stream.total_in; + pfile_in_zip_read_info->bstream.total_in_hi32 = 0; + pfile_in_zip_read_info->bstream.next_out = (char*)pfile_in_zip_read_info->stream.next_out; + pfile_in_zip_read_info->bstream.avail_out = pfile_in_zip_read_info->stream.avail_out; + pfile_in_zip_read_info->bstream.total_out_lo32 = pfile_in_zip_read_info->stream.total_out; + pfile_in_zip_read_info->bstream.total_out_hi32 = 0; + + uTotalOutBefore = pfile_in_zip_read_info->bstream.total_out_lo32; + bufBefore = (const Bytef *)pfile_in_zip_read_info->bstream.next_out; + + err=BZ2_bzDecompress(&pfile_in_zip_read_info->bstream); + + uTotalOutAfter = pfile_in_zip_read_info->bstream.total_out_lo32; + uOutThis = uTotalOutAfter-uTotalOutBefore; + + pfile_in_zip_read_info->total_out_64 = pfile_in_zip_read_info->total_out_64 + uOutThis; + + pfile_in_zip_read_info->crc32 = crc32(pfile_in_zip_read_info->crc32,bufBefore, (uInt)(uOutThis)); + pfile_in_zip_read_info->rest_read_uncompressed -= uOutThis; + iRead += (uInt)(uTotalOutAfter - uTotalOutBefore); + + pfile_in_zip_read_info->stream.next_in = (Bytef*)pfile_in_zip_read_info->bstream.next_in; + pfile_in_zip_read_info->stream.avail_in = pfile_in_zip_read_info->bstream.avail_in; + pfile_in_zip_read_info->stream.total_in = pfile_in_zip_read_info->bstream.total_in_lo32; + pfile_in_zip_read_info->stream.next_out = (Bytef*)pfile_in_zip_read_info->bstream.next_out; + pfile_in_zip_read_info->stream.avail_out = pfile_in_zip_read_info->bstream.avail_out; + pfile_in_zip_read_info->stream.total_out = pfile_in_zip_read_info->bstream.total_out_lo32; + + if (err==BZ_STREAM_END) + return (iRead==0) ? UNZ_EOF : iRead; + if (err!=BZ_OK) + break; +#endif + } // end Z_BZIP2ED + else + { + ZPOS64_T uTotalOutBefore,uTotalOutAfter; + const Bytef *bufBefore; + ZPOS64_T uOutThis; + int flush=Z_SYNC_FLUSH; + + uTotalOutBefore = pfile_in_zip_read_info->stream.total_out; + bufBefore = pfile_in_zip_read_info->stream.next_out; + + /* + if ((pfile_in_zip_read_info->rest_read_uncompressed == + pfile_in_zip_read_info->stream.avail_out) && + (pfile_in_zip_read_info->rest_read_compressed == 0)) + flush = Z_FINISH; + */ + err=inflate(&pfile_in_zip_read_info->stream,flush); + + if ((err>=0) && (pfile_in_zip_read_info->stream.msg!=NULL)) + err = Z_DATA_ERROR; + + uTotalOutAfter = pfile_in_zip_read_info->stream.total_out; + uOutThis = uTotalOutAfter-uTotalOutBefore; + + pfile_in_zip_read_info->total_out_64 = pfile_in_zip_read_info->total_out_64 + uOutThis; + + pfile_in_zip_read_info->crc32 = + crc32(pfile_in_zip_read_info->crc32,bufBefore, + (uInt)(uOutThis)); + + pfile_in_zip_read_info->rest_read_uncompressed -= + uOutThis; + + iRead += (uInt)(uTotalOutAfter - uTotalOutBefore); + + if (err==Z_STREAM_END) + return (iRead==0) ? UNZ_EOF : iRead; + if (err!=Z_OK) + break; + } + } + + if (err==Z_OK) + return iRead; + return err; +} + + +/* + Give the current position in uncompressed data +*/ +extern z_off_t ZEXPORT unztell (unzFile file) +{ + unz64_s* s; + file_in_zip64_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + return (z_off_t)pfile_in_zip_read_info->stream.total_out; +} + +extern ZPOS64_T ZEXPORT unztell64 (unzFile file) +{ + + unz64_s* s; + file_in_zip64_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return (ZPOS64_T)-1; + s=(unz64_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return (ZPOS64_T)-1; + + return pfile_in_zip_read_info->total_out_64; +} + + +/* + return 1 if the end of file was reached, 0 elsewhere +*/ +extern int ZEXPORT unzeof (unzFile file) +{ + unz64_s* s; + file_in_zip64_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + if (pfile_in_zip_read_info->rest_read_uncompressed == 0) + return 1; + else + return 0; +} + + + +/* +Read extra field from the current file (opened by unzOpenCurrentFile) +This is the local-header version of the extra field (sometimes, there is +more info in the local-header version than in the central-header) + + if buf==NULL, it return the size of the local extra field that can be read + + if buf!=NULL, len is the size of the buffer, the extra header is copied in + buf. + the return value is the number of bytes copied in buf, or (if <0) + the error code +*/ +extern int ZEXPORT unzGetLocalExtrafield (unzFile file, voidp buf, unsigned len) +{ + unz64_s* s; + file_in_zip64_read_info_s* pfile_in_zip_read_info; + uInt read_now; + ZPOS64_T size_to_read; + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + size_to_read = (pfile_in_zip_read_info->size_local_extrafield - + pfile_in_zip_read_info->pos_local_extrafield); + + if (buf==NULL) + return (int)size_to_read; + + if (len>size_to_read) + read_now = (uInt)size_to_read; + else + read_now = (uInt)len ; + + if (read_now==0) + return 0; + + if (ZSEEK64(pfile_in_zip_read_info->z_filefunc, + pfile_in_zip_read_info->filestream, + pfile_in_zip_read_info->offset_local_extrafield + + pfile_in_zip_read_info->pos_local_extrafield, + ZLIB_FILEFUNC_SEEK_SET)!=0) + return UNZ_ERRNO; + + if (ZREAD64(pfile_in_zip_read_info->z_filefunc, + pfile_in_zip_read_info->filestream, + buf,read_now)!=read_now) + return UNZ_ERRNO; + + return (int)read_now; +} + +/* + Close the file in zip opened with unzipOpenCurrentFile + Return UNZ_CRCERROR if all the file was read but the CRC is not good +*/ +extern int ZEXPORT unzCloseCurrentFile (unzFile file) +{ + int err=UNZ_OK; + + unz64_s* s; + file_in_zip64_read_info_s* pfile_in_zip_read_info; + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + pfile_in_zip_read_info=s->pfile_in_zip_read; + + if (pfile_in_zip_read_info==NULL) + return UNZ_PARAMERROR; + + + if ((pfile_in_zip_read_info->rest_read_uncompressed == 0) && + (!pfile_in_zip_read_info->raw)) + { + if (pfile_in_zip_read_info->crc32 != pfile_in_zip_read_info->crc32_wait) + err=UNZ_CRCERROR; + } + + + TRYFREE(pfile_in_zip_read_info->read_buffer); + pfile_in_zip_read_info->read_buffer = NULL; + if (pfile_in_zip_read_info->stream_initialised == Z_DEFLATED) + inflateEnd(&pfile_in_zip_read_info->stream); +#ifdef HAVE_BZIP2 + else if (pfile_in_zip_read_info->stream_initialised == Z_BZIP2ED) + BZ2_bzDecompressEnd(&pfile_in_zip_read_info->bstream); +#endif + + + pfile_in_zip_read_info->stream_initialised = 0; + TRYFREE(pfile_in_zip_read_info); + + s->pfile_in_zip_read=NULL; + + return err; +} + + +/* + Get the global comment string of the ZipFile, in the szComment buffer. + uSizeBuf is the size of the szComment buffer. + return the number of byte copied or an error code <0 +*/ +extern int ZEXPORT unzGetGlobalComment (unzFile file, char * szComment, uLong uSizeBuf) +{ + unz64_s* s; + uLong uReadThis ; + if (file==NULL) + return (int)UNZ_PARAMERROR; + s=(unz64_s*)file; + + uReadThis = uSizeBuf; + if (uReadThis>s->gi.size_comment) + uReadThis = s->gi.size_comment; + + if (ZSEEK64(s->z_filefunc,s->filestream,s->central_pos+22,ZLIB_FILEFUNC_SEEK_SET)!=0) + return UNZ_ERRNO; + + if (uReadThis>0) + { + *szComment='\0'; + if (ZREAD64(s->z_filefunc,s->filestream,szComment,uReadThis)!=uReadThis) + return UNZ_ERRNO; + } + + if ((szComment != NULL) && (uSizeBuf > s->gi.size_comment)) + *(szComment+s->gi.size_comment)='\0'; + return (int)uReadThis; +} + +/* Additions by RX '2004 */ +extern ZPOS64_T ZEXPORT unzGetOffset64(unzFile file) +{ + unz64_s* s; + + if (file==NULL) + return 0; //UNZ_PARAMERROR; + s=(unz64_s*)file; + if (!s->current_file_ok) + return 0; + if (s->gi.number_entry != 0 && s->gi.number_entry != 0xffff) + if (s->num_file==s->gi.number_entry) + return 0; + return s->pos_in_central_dir; +} + +extern uLong ZEXPORT unzGetOffset (unzFile file) +{ + ZPOS64_T offset64; + + if (file==NULL) + return 0; //UNZ_PARAMERROR; + offset64 = unzGetOffset64(file); + return (uLong)offset64; +} + +extern int ZEXPORT unzSetOffset64(unzFile file, ZPOS64_T pos) +{ + unz64_s* s; + int err; + + if (file==NULL) + return UNZ_PARAMERROR; + s=(unz64_s*)file; + + s->pos_in_central_dir = pos; + s->num_file = s->gi.number_entry; /* hack */ + err = unz64local_GetCurrentFileInfoInternal(file,&s->cur_file_info, + &s->cur_file_info_internal, + NULL,0,NULL,0,NULL,0); + s->current_file_ok = (err == UNZ_OK); + return err; +} + +extern int ZEXPORT unzSetOffset (unzFile file, uLong pos) +{ + return unzSetOffset64(file,pos); +} diff --git a/Samples/3rdParty/minizip/src/unzip.h b/Samples/3rdParty/minizip/src/unzip.h new file mode 100755 index 0000000..3183968 --- /dev/null +++ b/Samples/3rdParty/minizip/src/unzip.h @@ -0,0 +1,437 @@ +/* unzip.h -- IO for uncompress .zip files using zlib + Version 1.1, February 14h, 2010 + part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html ) + + Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html ) + + Modifications of Unzip for Zip64 + Copyright (C) 2007-2008 Even Rouault + + Modifications for Zip64 support on both zip and unzip + Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com ) + + For more info read MiniZip_info.txt + + --------------------------------------------------------------------------------- + + Condition of use and distribution are the same than zlib : + + 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. + + --------------------------------------------------------------------------------- + + Changes + + See header of unzip64.c + +*/ + +#ifndef _unz64_H +#define _unz64_H + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef _ZLIB_H +#include "zlib.h" +#endif + +#ifndef _ZLIBIOAPI_H +#include "ioapi.h" +#endif + +#ifdef HAVE_BZIP2 +#include "bzlib.h" +#endif + +#define Z_BZIP2ED 12 + +#if defined(STRICTUNZIP) || defined(STRICTZIPUNZIP) +/* like the STRICT of WIN32, we define a pointer that cannot be converted + from (void*) without cast */ +typedef struct TagunzFile__ { int unused; } unzFile__; +typedef unzFile__ *unzFile; +#else +typedef voidp unzFile; +#endif + + +#define UNZ_OK (0) +#define UNZ_END_OF_LIST_OF_FILE (-100) +#define UNZ_ERRNO (Z_ERRNO) +#define UNZ_EOF (0) +#define UNZ_PARAMERROR (-102) +#define UNZ_BADZIPFILE (-103) +#define UNZ_INTERNALERROR (-104) +#define UNZ_CRCERROR (-105) + +/* tm_unz contain date/time info */ +typedef struct tm_unz_s +{ + uInt tm_sec; /* seconds after the minute - [0,59] */ + uInt tm_min; /* minutes after the hour - [0,59] */ + uInt tm_hour; /* hours since midnight - [0,23] */ + uInt tm_mday; /* day of the month - [1,31] */ + uInt tm_mon; /* months since January - [0,11] */ + uInt tm_year; /* years - [1980..2044] */ +} tm_unz; + +/* unz_global_info structure contain global data about the ZIPfile + These data comes from the end of central dir */ +typedef struct unz_global_info64_s +{ + ZPOS64_T number_entry; /* total number of entries in + the central dir on this disk */ + uLong size_comment; /* size of the global comment of the zipfile */ +} unz_global_info64; + +typedef struct unz_global_info_s +{ + uLong number_entry; /* total number of entries in + the central dir on this disk */ + uLong size_comment; /* size of the global comment of the zipfile */ +} unz_global_info; + +/* unz_file_info contain information about a file in the zipfile */ +typedef struct unz_file_info64_s +{ + uLong version; /* version made by 2 bytes */ + uLong version_needed; /* version needed to extract 2 bytes */ + uLong flag; /* general purpose bit flag 2 bytes */ + uLong compression_method; /* compression method 2 bytes */ + uLong dosDate; /* last mod file date in Dos fmt 4 bytes */ + uLong crc; /* crc-32 4 bytes */ + ZPOS64_T compressed_size; /* compressed size 8 bytes */ + ZPOS64_T uncompressed_size; /* uncompressed size 8 bytes */ + uLong size_filename; /* filename length 2 bytes */ + uLong size_file_extra; /* extra field length 2 bytes */ + uLong size_file_comment; /* file comment length 2 bytes */ + + uLong disk_num_start; /* disk number start 2 bytes */ + uLong internal_fa; /* internal file attributes 2 bytes */ + uLong external_fa; /* external file attributes 4 bytes */ + + tm_unz tmu_date; +} unz_file_info64; + +typedef struct unz_file_info_s +{ + uLong version; /* version made by 2 bytes */ + uLong version_needed; /* version needed to extract 2 bytes */ + uLong flag; /* general purpose bit flag 2 bytes */ + uLong compression_method; /* compression method 2 bytes */ + uLong dosDate; /* last mod file date in Dos fmt 4 bytes */ + uLong crc; /* crc-32 4 bytes */ + uLong compressed_size; /* compressed size 4 bytes */ + uLong uncompressed_size; /* uncompressed size 4 bytes */ + uLong size_filename; /* filename length 2 bytes */ + uLong size_file_extra; /* extra field length 2 bytes */ + uLong size_file_comment; /* file comment length 2 bytes */ + + uLong disk_num_start; /* disk number start 2 bytes */ + uLong internal_fa; /* internal file attributes 2 bytes */ + uLong external_fa; /* external file attributes 4 bytes */ + + tm_unz tmu_date; +} unz_file_info; + +extern int ZEXPORT unzStringFileNameCompare OF ((const char* fileName1, + const char* fileName2, + int iCaseSensitivity)); +/* + Compare two filename (fileName1,fileName2). + If iCaseSenisivity = 1, comparision is case sensitivity (like strcmp) + If iCaseSenisivity = 2, comparision is not case sensitivity (like strcmpi + or strcasecmp) + If iCaseSenisivity = 0, case sensitivity is defaut of your operating system + (like 1 on Unix, 2 on Windows) +*/ + + +extern unzFile ZEXPORT unzOpen OF((const char *path)); +extern unzFile ZEXPORT unzOpen64 OF((const void *path)); +/* + Open a Zip file. path contain the full pathname (by example, + on a Windows XP computer "c:\\zlib\\zlib113.zip" or on an Unix computer + "zlib/zlib113.zip". + If the zipfile cannot be opened (file don't exist or in not valid), the + return value is NULL. + Else, the return value is a unzFile Handle, usable with other function + of this unzip package. + the "64" function take a const void* pointer, because the path is just the + value passed to the open64_file_func callback. + Under Windows, if UNICODE is defined, using fill_fopen64_filefunc, the path + is a pointer to a wide unicode string (LPCTSTR is LPCWSTR), so const char* + does not describe the reality +*/ + + +extern unzFile ZEXPORT unzOpen2 OF((const char *path, + zlib_filefunc_def* pzlib_filefunc_def)); +/* + Open a Zip file, like unzOpen, but provide a set of file low level API + for read/write the zip file (see ioapi.h) +*/ + +extern unzFile ZEXPORT unzOpen2_64 OF((const void *path, + zlib_filefunc64_def* pzlib_filefunc_def)); +/* + Open a Zip file, like unz64Open, but provide a set of file low level API + for read/write the zip file (see ioapi.h) +*/ + +extern int ZEXPORT unzClose OF((unzFile file)); +/* + Close a ZipFile opened with unzipOpen. + If there is files inside the .Zip opened with unzOpenCurrentFile (see later), + these files MUST be closed with unzipCloseCurrentFile before call unzipClose. + return UNZ_OK if there is no problem. */ + +extern int ZEXPORT unzGetGlobalInfo OF((unzFile file, + unz_global_info *pglobal_info)); + +extern int ZEXPORT unzGetGlobalInfo64 OF((unzFile file, + unz_global_info64 *pglobal_info)); +/* + Write info about the ZipFile in the *pglobal_info structure. + No preparation of the structure is needed + return UNZ_OK if there is no problem. */ + + +extern int ZEXPORT unzGetGlobalComment OF((unzFile file, + char *szComment, + uLong uSizeBuf)); +/* + Get the global comment string of the ZipFile, in the szComment buffer. + uSizeBuf is the size of the szComment buffer. + return the number of byte copied or an error code <0 +*/ + + +/***************************************************************************/ +/* Unzip package allow you browse the directory of the zipfile */ + +extern int ZEXPORT unzGoToFirstFile OF((unzFile file)); +/* + Set the current file of the zipfile to the first file. + return UNZ_OK if there is no problem +*/ + +extern int ZEXPORT unzGoToNextFile OF((unzFile file)); +/* + Set the current file of the zipfile to the next file. + return UNZ_OK if there is no problem + return UNZ_END_OF_LIST_OF_FILE if the actual file was the latest. +*/ + +extern int ZEXPORT unzLocateFile OF((unzFile file, + const char *szFileName, + int iCaseSensitivity)); +/* + Try locate the file szFileName in the zipfile. + For the iCaseSensitivity signification, see unzStringFileNameCompare + + return value : + UNZ_OK if the file is found. It becomes the current file. + UNZ_END_OF_LIST_OF_FILE if the file is not found +*/ + + +/* ****************************************** */ +/* Ryan supplied functions */ +/* unz_file_info contain information about a file in the zipfile */ +typedef struct unz_file_pos_s +{ + uLong pos_in_zip_directory; /* offset in zip file directory */ + uLong num_of_file; /* # of file */ +} unz_file_pos; + +extern int ZEXPORT unzGetFilePos( + unzFile file, + unz_file_pos* file_pos); + +extern int ZEXPORT unzGoToFilePos( + unzFile file, + unz_file_pos* file_pos); + +typedef struct unz64_file_pos_s +{ + ZPOS64_T pos_in_zip_directory; /* offset in zip file directory */ + ZPOS64_T num_of_file; /* # of file */ +} unz64_file_pos; + +extern int ZEXPORT unzGetFilePos64( + unzFile file, + unz64_file_pos* file_pos); + +extern int ZEXPORT unzGoToFilePos64( + unzFile file, + const unz64_file_pos* file_pos); + +/* ****************************************** */ + +extern int ZEXPORT unzGetCurrentFileInfo64 OF((unzFile file, + unz_file_info64 *pfile_info, + char *szFileName, + uLong fileNameBufferSize, + void *extraField, + uLong extraFieldBufferSize, + char *szComment, + uLong commentBufferSize)); + +extern int ZEXPORT unzGetCurrentFileInfo OF((unzFile file, + unz_file_info *pfile_info, + char *szFileName, + uLong fileNameBufferSize, + void *extraField, + uLong extraFieldBufferSize, + char *szComment, + uLong commentBufferSize)); +/* + Get Info about the current file + if pfile_info!=NULL, the *pfile_info structure will contain somes info about + the current file + if szFileName!=NULL, the filemane string will be copied in szFileName + (fileNameBufferSize is the size of the buffer) + if extraField!=NULL, the extra field information will be copied in extraField + (extraFieldBufferSize is the size of the buffer). + This is the Central-header version of the extra field + if szComment!=NULL, the comment string of the file will be copied in szComment + (commentBufferSize is the size of the buffer) +*/ + + +/** Addition for GDAL : START */ + +extern ZPOS64_T ZEXPORT unzGetCurrentFileZStreamPos64 OF((unzFile file)); + +/** Addition for GDAL : END */ + + +/***************************************************************************/ +/* for reading the content of the current zipfile, you can open it, read data + from it, and close it (you can close it before reading all the file) + */ + +extern int ZEXPORT unzOpenCurrentFile OF((unzFile file)); +/* + Open for reading data the current file in the zipfile. + If there is no error, the return value is UNZ_OK. +*/ + +extern int ZEXPORT unzOpenCurrentFilePassword OF((unzFile file, + const char* password)); +/* + Open for reading data the current file in the zipfile. + password is a crypting password + If there is no error, the return value is UNZ_OK. +*/ + +extern int ZEXPORT unzOpenCurrentFile2 OF((unzFile file, + int* method, + int* level, + int raw)); +/* + Same than unzOpenCurrentFile, but open for read raw the file (not uncompress) + if raw==1 + *method will receive method of compression, *level will receive level of + compression + note : you can set level parameter as NULL (if you did not want known level, + but you CANNOT set method parameter as NULL +*/ + +extern int ZEXPORT unzOpenCurrentFile3 OF((unzFile file, + int* method, + int* level, + int raw, + const char* password)); +/* + Same than unzOpenCurrentFile, but open for read raw the file (not uncompress) + if raw==1 + *method will receive method of compression, *level will receive level of + compression + note : you can set level parameter as NULL (if you did not want known level, + but you CANNOT set method parameter as NULL +*/ + + +extern int ZEXPORT unzCloseCurrentFile OF((unzFile file)); +/* + Close the file in zip opened with unzOpenCurrentFile + Return UNZ_CRCERROR if all the file was read but the CRC is not good +*/ + +extern int ZEXPORT unzReadCurrentFile OF((unzFile file, + voidp buf, + unsigned len)); +/* + Read bytes from the current file (opened by unzOpenCurrentFile) + buf contain buffer where data must be copied + len the size of buf. + + return the number of byte copied if somes bytes are copied + return 0 if the end of file was reached + return <0 with error code if there is an error + (UNZ_ERRNO for IO error, or zLib error for uncompress error) +*/ + +extern z_off_t ZEXPORT unztell OF((unzFile file)); + +extern ZPOS64_T ZEXPORT unztell64 OF((unzFile file)); +/* + Give the current position in uncompressed data +*/ + +extern int ZEXPORT unzeof OF((unzFile file)); +/* + return 1 if the end of file was reached, 0 elsewhere +*/ + +extern int ZEXPORT unzGetLocalExtrafield OF((unzFile file, + voidp buf, + unsigned len)); +/* + Read extra field from the current file (opened by unzOpenCurrentFile) + This is the local-header version of the extra field (sometimes, there is + more info in the local-header version than in the central-header) + + if buf==NULL, it return the size of the local extra field + + if buf!=NULL, len is the size of the buffer, the extra header is copied in + buf. + the return value is the number of bytes copied in buf, or (if <0) + the error code +*/ + +/***************************************************************************/ + +/* Get the current file offset */ +extern ZPOS64_T ZEXPORT unzGetOffset64 (unzFile file); +extern uLong ZEXPORT unzGetOffset (unzFile file); + +/* Set the current file offset */ +extern int ZEXPORT unzSetOffset64 (unzFile file, ZPOS64_T pos); +extern int ZEXPORT unzSetOffset (unzFile file, uLong pos); + + + +#ifdef __cplusplus +} +#endif + +#endif /* _unz64_H */ diff --git a/Samples/3rdParty/minizip/src/zip.c b/Samples/3rdParty/minizip/src/zip.c new file mode 100755 index 0000000..9a4da9b --- /dev/null +++ b/Samples/3rdParty/minizip/src/zip.c @@ -0,0 +1,2007 @@ +/* zip.c -- IO on .zip files using zlib + Version 1.1, February 14h, 2010 + part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html ) + + Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html ) + + Modifications for Zip64 support + Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com ) + + For more info read MiniZip_info.txt + + Changes + Oct-2009 - Mathias Svensson - Remove old C style function prototypes + Oct-2009 - Mathias Svensson - Added Zip64 Support when creating new file archives + Oct-2009 - Mathias Svensson - Did some code cleanup and refactoring to get better overview of some functions. + Oct-2009 - Mathias Svensson - Added zipRemoveExtraInfoBlock to strip extra field data from its ZIP64 data + It is used when recreting zip archive with RAW when deleting items from a zip. + ZIP64 data is automaticly added to items that needs it, and existing ZIP64 data need to be removed. + Oct-2009 - Mathias Svensson - Added support for BZIP2 as compression mode (bzip2 lib is required) + Jan-2010 - back to unzip and minizip 1.0 name scheme, with compatibility layer + +*/ + + +#include +#include +#include +#include +#include "zlib.h" +#include "zip.h" + +#ifdef STDC +# include +# include +# include +#endif +#ifdef NO_ERRNO_H + extern int errno; +#else +# include +#endif + + +#ifndef local +# define local static +#endif +/* compile with -Dlocal if your debugger can't find static symbols */ + +#ifndef VERSIONMADEBY +# define VERSIONMADEBY (0x0) /* platform depedent */ +#endif + +#ifndef Z_BUFSIZE +#define Z_BUFSIZE (64*1024) //(16384) +#endif + +#ifndef Z_MAXFILENAMEINZIP +#define Z_MAXFILENAMEINZIP (256) +#endif + +#ifndef ALLOC +# define ALLOC(size) (malloc(size)) +#endif +#ifndef TRYFREE +# define TRYFREE(p) {if (p) free(p);} +#endif + +/* +#define SIZECENTRALDIRITEM (0x2e) +#define SIZEZIPLOCALHEADER (0x1e) +*/ + +/* I've found an old Unix (a SunOS 4.1.3_U1) without all SEEK_* defined.... */ + + +// NOT sure that this work on ALL platform +#define MAKEULONG64(a, b) ((ZPOS64_T)(((unsigned long)(a)) | ((ZPOS64_T)((unsigned long)(b))) << 32)) + +#ifndef SEEK_CUR +#define SEEK_CUR 1 +#endif + +#ifndef SEEK_END +#define SEEK_END 2 +#endif + +#ifndef SEEK_SET +#define SEEK_SET 0 +#endif + +#ifndef DEF_MEM_LEVEL +#if MAX_MEM_LEVEL >= 8 +# define DEF_MEM_LEVEL 8 +#else +# define DEF_MEM_LEVEL MAX_MEM_LEVEL +#endif +#endif +const char zip_copyright[] =" zip 1.01 Copyright 1998-2004 Gilles Vollant - http://www.winimage.com/zLibDll"; + + +#define SIZEDATA_INDATABLOCK (4096-(4*4)) + +#define LOCALHEADERMAGIC (0x04034b50) +#define CENTRALHEADERMAGIC (0x02014b50) +#define ENDHEADERMAGIC (0x06054b50) +#define ZIP64ENDHEADERMAGIC (0x6064b50) +#define ZIP64ENDLOCHEADERMAGIC (0x7064b50) + +#define FLAG_LOCALHEADER_OFFSET (0x06) +#define CRC_LOCALHEADER_OFFSET (0x0e) + +#define SIZECENTRALHEADER (0x2e) /* 46 */ + +typedef struct linkedlist_datablock_internal_s +{ + struct linkedlist_datablock_internal_s* next_datablock; + uLong avail_in_this_block; + uLong filled_in_this_block; + uLong unused; /* for future use and alignement */ + unsigned char data[SIZEDATA_INDATABLOCK]; +} linkedlist_datablock_internal; + +typedef struct linkedlist_data_s +{ + linkedlist_datablock_internal* first_block; + linkedlist_datablock_internal* last_block; +} linkedlist_data; + + +typedef struct +{ + z_stream stream; /* zLib stream structure for inflate */ +#ifdef HAVE_BZIP2 + bz_stream bstream; /* bzLib stream structure for bziped */ +#endif + + int stream_initialised; /* 1 is stream is initialised */ + uInt pos_in_buffered_data; /* last written byte in buffered_data */ + + ZPOS64_T pos_local_header; /* offset of the local header of the file + currenty writing */ + char* central_header; /* central header data for the current file */ + uLong size_centralExtra; + uLong size_centralheader; /* size of the central header for cur file */ + uLong size_centralExtraFree; /* Extra bytes allocated to the centralheader but that are not used */ + uLong flag; /* flag of the file currently writing */ + + int method; /* compression method of file currenty wr.*/ + int raw; /* 1 for directly writing raw data */ + Byte buffered_data[Z_BUFSIZE];/* buffer contain compressed data to be writ*/ + uLong dosDate; + uLong crc32; + int encrypt; + int zip64; /* Add ZIP64 extened information in the extra field */ + ZPOS64_T pos_zip64extrainfo; + ZPOS64_T totalCompressedData; + ZPOS64_T totalUncompressedData; +#ifndef NOCRYPT + unsigned long keys[3]; /* keys defining the pseudo-random sequence */ + const unsigned long* pcrc_32_tab; + int crypt_header_size; +#endif +} curfile64_info; + +typedef struct +{ + zlib_filefunc64_32_def z_filefunc; + voidpf filestream; /* io structore of the zipfile */ + linkedlist_data central_dir;/* datablock with central dir in construction*/ + int in_opened_file_inzip; /* 1 if a file in the zip is currently writ.*/ + curfile64_info ci; /* info on the file curretly writing */ + + ZPOS64_T begin_pos; /* position of the beginning of the zipfile */ + ZPOS64_T add_position_when_writting_offset; + ZPOS64_T number_entry; + +#ifndef NO_ADDFILEINEXISTINGZIP + char *globalcomment; +#endif + +} zip64_internal; + + +#ifndef NOCRYPT +#define INCLUDECRYPTINGCODE_IFCRYPTALLOWED +#include "crypt.h" +#endif + +local linkedlist_datablock_internal* allocate_new_datablock() +{ + linkedlist_datablock_internal* ldi; + ldi = (linkedlist_datablock_internal*) + ALLOC(sizeof(linkedlist_datablock_internal)); + if (ldi!=NULL) + { + ldi->next_datablock = NULL ; + ldi->filled_in_this_block = 0 ; + ldi->avail_in_this_block = SIZEDATA_INDATABLOCK ; + } + return ldi; +} + +local void free_datablock(linkedlist_datablock_internal* ldi) +{ + while (ldi!=NULL) + { + linkedlist_datablock_internal* ldinext = ldi->next_datablock; + TRYFREE(ldi); + ldi = ldinext; + } +} + +local void init_linkedlist(linkedlist_data* ll) +{ + ll->first_block = ll->last_block = NULL; +} + +local void free_linkedlist(linkedlist_data* ll) +{ + free_datablock(ll->first_block); + ll->first_block = ll->last_block = NULL; +} + + +local int add_data_in_datablock(linkedlist_data* ll, const void* buf, uLong len) +{ + linkedlist_datablock_internal* ldi; + const unsigned char* from_copy; + + if (ll==NULL) + return ZIP_INTERNALERROR; + + if (ll->last_block == NULL) + { + ll->first_block = ll->last_block = allocate_new_datablock(); + if (ll->first_block == NULL) + return ZIP_INTERNALERROR; + } + + ldi = ll->last_block; + from_copy = (unsigned char*)buf; + + while (len>0) + { + uInt copy_this; + uInt i; + unsigned char* to_copy; + + if (ldi->avail_in_this_block==0) + { + ldi->next_datablock = allocate_new_datablock(); + if (ldi->next_datablock == NULL) + return ZIP_INTERNALERROR; + ldi = ldi->next_datablock ; + ll->last_block = ldi; + } + + if (ldi->avail_in_this_block < len) + copy_this = (uInt)ldi->avail_in_this_block; + else + copy_this = (uInt)len; + + to_copy = &(ldi->data[ldi->filled_in_this_block]); + + for (i=0;ifilled_in_this_block += copy_this; + ldi->avail_in_this_block -= copy_this; + from_copy += copy_this ; + len -= copy_this; + } + return ZIP_OK; +} + + + +/****************************************************************************/ + +#ifndef NO_ADDFILEINEXISTINGZIP +/* =========================================================================== + Inputs a long in LSB order to the given file + nbByte == 1, 2 ,4 or 8 (byte, short or long, ZPOS64_T) +*/ + +local int zip64local_putValue OF((const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, ZPOS64_T x, int nbByte)); +local int zip64local_putValue (const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, ZPOS64_T x, int nbByte) +{ + unsigned char buf[8]; + int n; + for (n = 0; n < nbByte; n++) + { + buf[n] = (unsigned char)(x & 0xff); + x >>= 8; + } + if (x != 0) + { /* data overflow - hack for ZIP64 (X Roche) */ + for (n = 0; n < nbByte; n++) + { + buf[n] = 0xff; + } + } + + if (ZWRITE64(*pzlib_filefunc_def,filestream,buf,nbByte)!=(uLong)nbByte) + return ZIP_ERRNO; + else + return ZIP_OK; +} + +local void zip64local_putValue_inmemory OF((void* dest, ZPOS64_T x, int nbByte)); +local void zip64local_putValue_inmemory (void* dest, ZPOS64_T x, int nbByte) +{ + unsigned char* buf=(unsigned char*)dest; + int n; + for (n = 0; n < nbByte; n++) { + buf[n] = (unsigned char)(x & 0xff); + x >>= 8; + } + + if (x != 0) + { /* data overflow - hack for ZIP64 */ + for (n = 0; n < nbByte; n++) + { + buf[n] = 0xff; + } + } +} + +/****************************************************************************/ + + +local uLong zip64local_TmzDateToDosDate(const tm_zip* ptm) +{ + uLong year = (uLong)ptm->tm_year; + if (year>=1980) + year-=1980; + else if (year>=80) + year-=80; + return + (uLong) (((ptm->tm_mday) + (32 * (ptm->tm_mon+1)) + (512 * year)) << 16) | + ((ptm->tm_sec/2) + (32* ptm->tm_min) + (2048 * (uLong)ptm->tm_hour)); +} + + +/****************************************************************************/ + +local int zip64local_getByte OF((const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, int *pi)); + +local int zip64local_getByte(const zlib_filefunc64_32_def* pzlib_filefunc_def,voidpf filestream,int* pi) +{ + unsigned char c; + int err = (int)ZREAD64(*pzlib_filefunc_def,filestream,&c,1); + if (err==1) + { + *pi = (int)c; + return ZIP_OK; + } + else + { + if (ZERROR64(*pzlib_filefunc_def,filestream)) + return ZIP_ERRNO; + else + return ZIP_EOF; + } +} + + +/* =========================================================================== + Reads a long in LSB order from the given gz_stream. Sets +*/ +local int zip64local_getShort OF((const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, uLong *pX)); + +local int zip64local_getShort (const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, uLong* pX) +{ + uLong x ; + int i = 0; + int err; + + err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); + x = (uLong)i; + + if (err==ZIP_OK) + err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); + x += ((uLong)i)<<8; + + if (err==ZIP_OK) + *pX = x; + else + *pX = 0; + return err; +} + +local int zip64local_getLong OF((const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, uLong *pX)); + +local int zip64local_getLong (const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, uLong* pX) +{ + uLong x ; + int i = 0; + int err; + + err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); + x = (uLong)i; + + if (err==ZIP_OK) + err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); + x += ((uLong)i)<<8; + + if (err==ZIP_OK) + err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); + x += ((uLong)i)<<16; + + if (err==ZIP_OK) + err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); + x += ((uLong)i)<<24; + + if (err==ZIP_OK) + *pX = x; + else + *pX = 0; + return err; +} + +local int zip64local_getLong64 OF((const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, ZPOS64_T *pX)); + + +local int zip64local_getLong64 (const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream, ZPOS64_T *pX) +{ + ZPOS64_T x; + int i = 0; + int err; + + err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); + x = (ZPOS64_T)i; + + if (err==ZIP_OK) + err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); + x += ((ZPOS64_T)i)<<8; + + if (err==ZIP_OK) + err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); + x += ((ZPOS64_T)i)<<16; + + if (err==ZIP_OK) + err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); + x += ((ZPOS64_T)i)<<24; + + if (err==ZIP_OK) + err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); + x += ((ZPOS64_T)i)<<32; + + if (err==ZIP_OK) + err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); + x += ((ZPOS64_T)i)<<40; + + if (err==ZIP_OK) + err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); + x += ((ZPOS64_T)i)<<48; + + if (err==ZIP_OK) + err = zip64local_getByte(pzlib_filefunc_def,filestream,&i); + x += ((ZPOS64_T)i)<<56; + + if (err==ZIP_OK) + *pX = x; + else + *pX = 0; + + return err; +} + +#ifndef BUFREADCOMMENT +#define BUFREADCOMMENT (0x400) +#endif +/* + Locate the Central directory of a zipfile (at the end, just before + the global comment) +*/ +local ZPOS64_T zip64local_SearchCentralDir OF((const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream)); + +local ZPOS64_T zip64local_SearchCentralDir(const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream) +{ + unsigned char* buf; + ZPOS64_T uSizeFile; + ZPOS64_T uBackRead; + ZPOS64_T uMaxBack=0xffff; /* maximum size of global comment */ + ZPOS64_T uPosFound=0; + + if (ZSEEK64(*pzlib_filefunc_def,filestream,0,ZLIB_FILEFUNC_SEEK_END) != 0) + return 0; + + + uSizeFile = ZTELL64(*pzlib_filefunc_def,filestream); + + if (uMaxBack>uSizeFile) + uMaxBack = uSizeFile; + + buf = (unsigned char*)ALLOC(BUFREADCOMMENT+4); + if (buf==NULL) + return 0; + + uBackRead = 4; + while (uBackReaduMaxBack) + uBackRead = uMaxBack; + else + uBackRead+=BUFREADCOMMENT; + uReadPos = uSizeFile-uBackRead ; + + uReadSize = ((BUFREADCOMMENT+4) < (uSizeFile-uReadPos)) ? + (BUFREADCOMMENT+4) : (uLong)(uSizeFile-uReadPos); + if (ZSEEK64(*pzlib_filefunc_def,filestream,uReadPos,ZLIB_FILEFUNC_SEEK_SET)!=0) + break; + + if (ZREAD64(*pzlib_filefunc_def,filestream,buf,uReadSize)!=uReadSize) + break; + + for (i=(int)uReadSize-3; (i--)>0;) + if (((*(buf+i))==0x50) && ((*(buf+i+1))==0x4b) && + ((*(buf+i+2))==0x05) && ((*(buf+i+3))==0x06)) + { + uPosFound = uReadPos+i; + break; + } + + if (uPosFound!=0) + break; + } + TRYFREE(buf); + return uPosFound; +} + +/* +Locate the End of Zip64 Central directory locator and from there find the CD of a zipfile (at the end, just before +the global comment) +*/ +local ZPOS64_T zip64local_SearchCentralDir64 OF((const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream)); + +local ZPOS64_T zip64local_SearchCentralDir64(const zlib_filefunc64_32_def* pzlib_filefunc_def, voidpf filestream) +{ + unsigned char* buf; + ZPOS64_T uSizeFile; + ZPOS64_T uBackRead; + ZPOS64_T uMaxBack=0xffff; /* maximum size of global comment */ + ZPOS64_T uPosFound=0; + uLong uL; + ZPOS64_T relativeOffset; + + if (ZSEEK64(*pzlib_filefunc_def,filestream,0,ZLIB_FILEFUNC_SEEK_END) != 0) + return 0; + + uSizeFile = ZTELL64(*pzlib_filefunc_def,filestream); + + if (uMaxBack>uSizeFile) + uMaxBack = uSizeFile; + + buf = (unsigned char*)ALLOC(BUFREADCOMMENT+4); + if (buf==NULL) + return 0; + + uBackRead = 4; + while (uBackReaduMaxBack) + uBackRead = uMaxBack; + else + uBackRead+=BUFREADCOMMENT; + uReadPos = uSizeFile-uBackRead ; + + uReadSize = ((BUFREADCOMMENT+4) < (uSizeFile-uReadPos)) ? + (BUFREADCOMMENT+4) : (uLong)(uSizeFile-uReadPos); + if (ZSEEK64(*pzlib_filefunc_def,filestream,uReadPos,ZLIB_FILEFUNC_SEEK_SET)!=0) + break; + + if (ZREAD64(*pzlib_filefunc_def,filestream,buf,uReadSize)!=uReadSize) + break; + + for (i=(int)uReadSize-3; (i--)>0;) + { + // Signature "0x07064b50" Zip64 end of central directory locater + if (((*(buf+i))==0x50) && ((*(buf+i+1))==0x4b) && ((*(buf+i+2))==0x06) && ((*(buf+i+3))==0x07)) + { + uPosFound = uReadPos+i; + break; + } + } + + if (uPosFound!=0) + break; + } + + TRYFREE(buf); + if (uPosFound == 0) + return 0; + + /* Zip64 end of central directory locator */ + if (ZSEEK64(*pzlib_filefunc_def,filestream, uPosFound,ZLIB_FILEFUNC_SEEK_SET)!=0) + return 0; + + /* the signature, already checked */ + if (zip64local_getLong(pzlib_filefunc_def,filestream,&uL)!=ZIP_OK) + return 0; + + /* number of the disk with the start of the zip64 end of central directory */ + if (zip64local_getLong(pzlib_filefunc_def,filestream,&uL)!=ZIP_OK) + return 0; + if (uL != 0) + return 0; + + /* relative offset of the zip64 end of central directory record */ + if (zip64local_getLong64(pzlib_filefunc_def,filestream,&relativeOffset)!=ZIP_OK) + return 0; + + /* total number of disks */ + if (zip64local_getLong(pzlib_filefunc_def,filestream,&uL)!=ZIP_OK) + return 0; + if (uL != 1) + return 0; + + /* Goto Zip64 end of central directory record */ + if (ZSEEK64(*pzlib_filefunc_def,filestream, relativeOffset,ZLIB_FILEFUNC_SEEK_SET)!=0) + return 0; + + /* the signature */ + if (zip64local_getLong(pzlib_filefunc_def,filestream,&uL)!=ZIP_OK) + return 0; + + if (uL != 0x06064b50) // signature of 'Zip64 end of central directory' + return 0; + + return relativeOffset; +} + +int LoadCentralDirectoryRecord(zip64_internal* pziinit) +{ + int err=ZIP_OK; + ZPOS64_T byte_before_the_zipfile;/* byte before the zipfile, (>0 for sfx)*/ + + ZPOS64_T size_central_dir; /* size of the central directory */ + ZPOS64_T offset_central_dir; /* offset of start of central directory */ + ZPOS64_T central_pos; + uLong uL; + + uLong number_disk; /* number of the current dist, used for + spaning ZIP, unsupported, always 0*/ + uLong number_disk_with_CD; /* number the the disk with central dir, used + for spaning ZIP, unsupported, always 0*/ + ZPOS64_T number_entry; + ZPOS64_T number_entry_CD; /* total number of entries in + the central dir + (same than number_entry on nospan) */ + uLong VersionMadeBy; + uLong VersionNeeded; + uLong size_comment; + + int hasZIP64Record = 0; + + // check first if we find a ZIP64 record + central_pos = zip64local_SearchCentralDir64(&pziinit->z_filefunc,pziinit->filestream); + if(central_pos > 0) + { + hasZIP64Record = 1; + } + else if(central_pos == 0) + { + central_pos = zip64local_SearchCentralDir(&pziinit->z_filefunc,pziinit->filestream); + } + +/* disable to allow appending to empty ZIP archive + if (central_pos==0) + err=ZIP_ERRNO; +*/ + + if(hasZIP64Record) + { + ZPOS64_T sizeEndOfCentralDirectory; + if (ZSEEK64(pziinit->z_filefunc, pziinit->filestream, central_pos, ZLIB_FILEFUNC_SEEK_SET) != 0) + err=ZIP_ERRNO; + + /* the signature, already checked */ + if (zip64local_getLong(&pziinit->z_filefunc, pziinit->filestream,&uL)!=ZIP_OK) + err=ZIP_ERRNO; + + /* size of zip64 end of central directory record */ + if (zip64local_getLong64(&pziinit->z_filefunc, pziinit->filestream, &sizeEndOfCentralDirectory)!=ZIP_OK) + err=ZIP_ERRNO; + + /* version made by */ + if (zip64local_getShort(&pziinit->z_filefunc, pziinit->filestream, &VersionMadeBy)!=ZIP_OK) + err=ZIP_ERRNO; + + /* version needed to extract */ + if (zip64local_getShort(&pziinit->z_filefunc, pziinit->filestream, &VersionNeeded)!=ZIP_OK) + err=ZIP_ERRNO; + + /* number of this disk */ + if (zip64local_getLong(&pziinit->z_filefunc, pziinit->filestream,&number_disk)!=ZIP_OK) + err=ZIP_ERRNO; + + /* number of the disk with the start of the central directory */ + if (zip64local_getLong(&pziinit->z_filefunc, pziinit->filestream,&number_disk_with_CD)!=ZIP_OK) + err=ZIP_ERRNO; + + /* total number of entries in the central directory on this disk */ + if (zip64local_getLong64(&pziinit->z_filefunc, pziinit->filestream, &number_entry)!=ZIP_OK) + err=ZIP_ERRNO; + + /* total number of entries in the central directory */ + if (zip64local_getLong64(&pziinit->z_filefunc, pziinit->filestream,&number_entry_CD)!=ZIP_OK) + err=ZIP_ERRNO; + + if ((number_entry_CD!=number_entry) || (number_disk_with_CD!=0) || (number_disk!=0)) + err=ZIP_BADZIPFILE; + + /* size of the central directory */ + if (zip64local_getLong64(&pziinit->z_filefunc, pziinit->filestream,&size_central_dir)!=ZIP_OK) + err=ZIP_ERRNO; + + /* offset of start of central directory with respect to the + starting disk number */ + if (zip64local_getLong64(&pziinit->z_filefunc, pziinit->filestream,&offset_central_dir)!=ZIP_OK) + err=ZIP_ERRNO; + + // TODO.. + // read the comment from the standard central header. + size_comment = 0; + } + else + { + // Read End of central Directory info + if (ZSEEK64(pziinit->z_filefunc, pziinit->filestream, central_pos,ZLIB_FILEFUNC_SEEK_SET)!=0) + err=ZIP_ERRNO; + + /* the signature, already checked */ + if (zip64local_getLong(&pziinit->z_filefunc, pziinit->filestream,&uL)!=ZIP_OK) + err=ZIP_ERRNO; + + /* number of this disk */ + if (zip64local_getShort(&pziinit->z_filefunc, pziinit->filestream,&number_disk)!=ZIP_OK) + err=ZIP_ERRNO; + + /* number of the disk with the start of the central directory */ + if (zip64local_getShort(&pziinit->z_filefunc, pziinit->filestream,&number_disk_with_CD)!=ZIP_OK) + err=ZIP_ERRNO; + + /* total number of entries in the central dir on this disk */ + number_entry = 0; + if (zip64local_getShort(&pziinit->z_filefunc, pziinit->filestream, &uL)!=ZIP_OK) + err=ZIP_ERRNO; + else + number_entry = uL; + + /* total number of entries in the central dir */ + number_entry_CD = 0; + if (zip64local_getShort(&pziinit->z_filefunc, pziinit->filestream, &uL)!=ZIP_OK) + err=ZIP_ERRNO; + else + number_entry_CD = uL; + + if ((number_entry_CD!=number_entry) || (number_disk_with_CD!=0) || (number_disk!=0)) + err=ZIP_BADZIPFILE; + + /* size of the central directory */ + size_central_dir = 0; + if (zip64local_getLong(&pziinit->z_filefunc, pziinit->filestream, &uL)!=ZIP_OK) + err=ZIP_ERRNO; + else + size_central_dir = uL; + + /* offset of start of central directory with respect to the starting disk number */ + offset_central_dir = 0; + if (zip64local_getLong(&pziinit->z_filefunc, pziinit->filestream, &uL)!=ZIP_OK) + err=ZIP_ERRNO; + else + offset_central_dir = uL; + + + /* zipfile global comment length */ + if (zip64local_getShort(&pziinit->z_filefunc, pziinit->filestream, &size_comment)!=ZIP_OK) + err=ZIP_ERRNO; + } + + if ((central_posz_filefunc, pziinit->filestream); + return ZIP_ERRNO; + } + + if (size_comment>0) + { + pziinit->globalcomment = (char*)ALLOC(size_comment+1); + if (pziinit->globalcomment) + { + size_comment = ZREAD64(pziinit->z_filefunc, pziinit->filestream, pziinit->globalcomment,size_comment); + pziinit->globalcomment[size_comment]=0; + } + } + + byte_before_the_zipfile = central_pos - (offset_central_dir+size_central_dir); + pziinit->add_position_when_writting_offset = byte_before_the_zipfile; + + { + ZPOS64_T size_central_dir_to_read = size_central_dir; + size_t buf_size = SIZEDATA_INDATABLOCK; + void* buf_read = (void*)ALLOC(buf_size); + if (ZSEEK64(pziinit->z_filefunc, pziinit->filestream, offset_central_dir + byte_before_the_zipfile, ZLIB_FILEFUNC_SEEK_SET) != 0) + err=ZIP_ERRNO; + + while ((size_central_dir_to_read>0) && (err==ZIP_OK)) + { + ZPOS64_T read_this = SIZEDATA_INDATABLOCK; + if (read_this > size_central_dir_to_read) + read_this = size_central_dir_to_read; + + if (ZREAD64(pziinit->z_filefunc, pziinit->filestream,buf_read,(uLong)read_this) != read_this) + err=ZIP_ERRNO; + + if (err==ZIP_OK) + err = add_data_in_datablock(&pziinit->central_dir,buf_read, (uLong)read_this); + + size_central_dir_to_read-=read_this; + } + TRYFREE(buf_read); + } + pziinit->begin_pos = byte_before_the_zipfile; + pziinit->number_entry = number_entry_CD; + + if (ZSEEK64(pziinit->z_filefunc, pziinit->filestream, offset_central_dir+byte_before_the_zipfile,ZLIB_FILEFUNC_SEEK_SET) != 0) + err=ZIP_ERRNO; + + return err; +} + + +#endif /* !NO_ADDFILEINEXISTINGZIP*/ + + +/************************************************************/ +extern zipFile ZEXPORT zipOpen3 (const void *pathname, int append, zipcharpc* globalcomment, zlib_filefunc64_32_def* pzlib_filefunc64_32_def) +{ + zip64_internal ziinit; + zip64_internal* zi; + int err=ZIP_OK; + + ziinit.z_filefunc.zseek32_file = NULL; + ziinit.z_filefunc.ztell32_file = NULL; + if (pzlib_filefunc64_32_def==NULL) + fill_fopen64_filefunc(&ziinit.z_filefunc.zfile_func64); + else + ziinit.z_filefunc = *pzlib_filefunc64_32_def; + + ziinit.filestream = ZOPEN64(ziinit.z_filefunc, + pathname, + (append == APPEND_STATUS_CREATE) ? + (ZLIB_FILEFUNC_MODE_READ | ZLIB_FILEFUNC_MODE_WRITE | ZLIB_FILEFUNC_MODE_CREATE) : + (ZLIB_FILEFUNC_MODE_READ | ZLIB_FILEFUNC_MODE_WRITE | ZLIB_FILEFUNC_MODE_EXISTING)); + + if (ziinit.filestream == NULL) + return NULL; + + if (append == APPEND_STATUS_CREATEAFTER) + ZSEEK64(ziinit.z_filefunc,ziinit.filestream,0,SEEK_END); + + ziinit.begin_pos = ZTELL64(ziinit.z_filefunc,ziinit.filestream); + ziinit.in_opened_file_inzip = 0; + ziinit.ci.stream_initialised = 0; + ziinit.number_entry = 0; + ziinit.add_position_when_writting_offset = 0; + init_linkedlist(&(ziinit.central_dir)); + + + + zi = (zip64_internal*)ALLOC(sizeof(zip64_internal)); + if (zi==NULL) + { + ZCLOSE64(ziinit.z_filefunc,ziinit.filestream); + return NULL; + } + + /* now we add file in a zipfile */ +# ifndef NO_ADDFILEINEXISTINGZIP + ziinit.globalcomment = NULL; + if (append == APPEND_STATUS_ADDINZIP) + { + // Read and Cache Central Directory Records + err = LoadCentralDirectoryRecord(&ziinit); + } + + if (globalcomment) + { + *globalcomment = ziinit.globalcomment; + } +# endif /* !NO_ADDFILEINEXISTINGZIP*/ + + if (err != ZIP_OK) + { +# ifndef NO_ADDFILEINEXISTINGZIP + TRYFREE(ziinit.globalcomment); +# endif /* !NO_ADDFILEINEXISTINGZIP*/ + TRYFREE(zi); + return NULL; + } + else + { + *zi = ziinit; + return (zipFile)zi; + } +} + +extern zipFile ZEXPORT zipOpen2 (const char *pathname, int append, zipcharpc* globalcomment, zlib_filefunc_def* pzlib_filefunc32_def) +{ + if (pzlib_filefunc32_def != NULL) + { + zlib_filefunc64_32_def zlib_filefunc64_32_def_fill; + fill_zlib_filefunc64_32_def_from_filefunc32(&zlib_filefunc64_32_def_fill,pzlib_filefunc32_def); + return zipOpen3(pathname, append, globalcomment, &zlib_filefunc64_32_def_fill); + } + else + return zipOpen3(pathname, append, globalcomment, NULL); +} + +extern zipFile ZEXPORT zipOpen2_64 (const void *pathname, int append, zipcharpc* globalcomment, zlib_filefunc64_def* pzlib_filefunc_def) +{ + if (pzlib_filefunc_def != NULL) + { + zlib_filefunc64_32_def zlib_filefunc64_32_def_fill; + zlib_filefunc64_32_def_fill.zfile_func64 = *pzlib_filefunc_def; + zlib_filefunc64_32_def_fill.ztell32_file = NULL; + zlib_filefunc64_32_def_fill.zseek32_file = NULL; + return zipOpen3(pathname, append, globalcomment, &zlib_filefunc64_32_def_fill); + } + else + return zipOpen3(pathname, append, globalcomment, NULL); +} + + + +extern zipFile ZEXPORT zipOpen (const char* pathname, int append) +{ + return zipOpen3((const void*)pathname,append,NULL,NULL); +} + +extern zipFile ZEXPORT zipOpen64 (const void* pathname, int append) +{ + return zipOpen3(pathname,append,NULL,NULL); +} + +int Write_LocalFileHeader(zip64_internal* zi, const char* filename, uInt size_extrafield_local, const void* extrafield_local) +{ + /* write the local header */ + int err; + uInt size_filename = (uInt)strlen(filename); + uInt size_extrafield = size_extrafield_local; + + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)LOCALHEADERMAGIC, 4); + + if (err==ZIP_OK) + { + if(zi->ci.zip64) + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)45,2);/* version needed to extract */ + else + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)20,2);/* version needed to extract */ + } + + if (err==ZIP_OK) + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)zi->ci.flag,2); + + if (err==ZIP_OK) + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)zi->ci.method,2); + + if (err==ZIP_OK) + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)zi->ci.dosDate,4); + + // CRC / Compressed size / Uncompressed size will be filled in later and rewritten later + if (err==ZIP_OK) + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,4); /* crc 32, unknown */ + if (err==ZIP_OK) + { + if(zi->ci.zip64) + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0xFFFFFFFF,4); /* compressed size, unknown */ + else + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,4); /* compressed size, unknown */ + } + if (err==ZIP_OK) + { + if(zi->ci.zip64) + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0xFFFFFFFF,4); /* uncompressed size, unknown */ + else + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,4); /* uncompressed size, unknown */ + } + + if (err==ZIP_OK) + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)size_filename,2); + + if(zi->ci.zip64) + { + size_extrafield += 20; + } + + if (err==ZIP_OK) + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)size_extrafield,2); + + if ((err==ZIP_OK) && (size_filename > 0)) + { + if (ZWRITE64(zi->z_filefunc,zi->filestream,filename,size_filename)!=size_filename) + err = ZIP_ERRNO; + } + + if ((err==ZIP_OK) && (size_extrafield_local > 0)) + { + if (ZWRITE64(zi->z_filefunc, zi->filestream, extrafield_local, size_extrafield_local) != size_extrafield_local) + err = ZIP_ERRNO; + } + + + if ((err==ZIP_OK) && (zi->ci.zip64)) + { + // write the Zip64 extended info + short HeaderID = 1; + short DataSize = 16; + ZPOS64_T CompressedSize = 0; + ZPOS64_T UncompressedSize = 0; + + // Remember position of Zip64 extended info for the local file header. (needed when we update size after done with file) + zi->ci.pos_zip64extrainfo = ZTELL64(zi->z_filefunc,zi->filestream); + + err = zip64local_putValue(&zi->z_filefunc, zi->filestream, (short)HeaderID,2); + err = zip64local_putValue(&zi->z_filefunc, zi->filestream, (short)DataSize,2); + + err = zip64local_putValue(&zi->z_filefunc, zi->filestream, (ZPOS64_T)UncompressedSize,8); + err = zip64local_putValue(&zi->z_filefunc, zi->filestream, (ZPOS64_T)CompressedSize,8); + } + + return err; +} + +/* + NOTE. + When writing RAW the ZIP64 extended information in extrafield_local and extrafield_global needs to be stripped + before calling this function it can be done with zipRemoveExtraInfoBlock + + It is not done here because then we need to realloc a new buffer since parameters are 'const' and I want to minimize + unnecessary allocations. + */ +extern int ZEXPORT zipOpenNewFileInZip4_64 (zipFile file, const char* filename, const zip_fileinfo* zipfi, + const void* extrafield_local, uInt size_extrafield_local, + const void* extrafield_global, uInt size_extrafield_global, + const char* comment, int method, int level, int raw, + int windowBits,int memLevel, int strategy, + const char* password, uLong crcForCrypting, + uLong versionMadeBy, uLong flagBase, int zip64) +{ + zip64_internal* zi; + uInt size_filename; + uInt size_comment; + uInt i; + int err = ZIP_OK; + +# ifdef NOCRYPT + (crcForCrypting); + if (password != NULL) + return ZIP_PARAMERROR; +# endif + + if (file == NULL) + return ZIP_PARAMERROR; + +#ifdef HAVE_BZIP2 + if ((method!=0) && (method!=Z_DEFLATED) && (method!=Z_BZIP2ED)) + return ZIP_PARAMERROR; +#else + if ((method!=0) && (method!=Z_DEFLATED)) + return ZIP_PARAMERROR; +#endif + + zi = (zip64_internal*)file; + + if (zi->in_opened_file_inzip == 1) + { + err = zipCloseFileInZip (file); + if (err != ZIP_OK) + return err; + } + + if (filename==NULL) + filename="-"; + + if (comment==NULL) + size_comment = 0; + else + size_comment = (uInt)strlen(comment); + + size_filename = (uInt)strlen(filename); + + if (zipfi == NULL) + zi->ci.dosDate = 0; + else + { + if (zipfi->dosDate != 0) + zi->ci.dosDate = zipfi->dosDate; + else + zi->ci.dosDate = zip64local_TmzDateToDosDate(&zipfi->tmz_date); + } + + zi->ci.flag = flagBase; + if ((level==8) || (level==9)) + zi->ci.flag |= 2; + if (level==2) + zi->ci.flag |= 4; + if (level==1) + zi->ci.flag |= 6; + if (password != NULL) + zi->ci.flag |= 1; + + zi->ci.crc32 = 0; + zi->ci.method = method; + zi->ci.encrypt = 0; + zi->ci.stream_initialised = 0; + zi->ci.pos_in_buffered_data = 0; + zi->ci.raw = raw; + zi->ci.pos_local_header = ZTELL64(zi->z_filefunc,zi->filestream); + + zi->ci.size_centralheader = SIZECENTRALHEADER + size_filename + size_extrafield_global + size_comment; + zi->ci.size_centralExtraFree = 32; // Extra space we have reserved in case we need to add ZIP64 extra info data + + zi->ci.central_header = (char*)ALLOC((uInt)zi->ci.size_centralheader + zi->ci.size_centralExtraFree); + + zi->ci.size_centralExtra = size_extrafield_global; + zip64local_putValue_inmemory(zi->ci.central_header,(uLong)CENTRALHEADERMAGIC,4); + /* version info */ + zip64local_putValue_inmemory(zi->ci.central_header+4,(uLong)versionMadeBy,2); + zip64local_putValue_inmemory(zi->ci.central_header+6,(uLong)20,2); + zip64local_putValue_inmemory(zi->ci.central_header+8,(uLong)zi->ci.flag,2); + zip64local_putValue_inmemory(zi->ci.central_header+10,(uLong)zi->ci.method,2); + zip64local_putValue_inmemory(zi->ci.central_header+12,(uLong)zi->ci.dosDate,4); + zip64local_putValue_inmemory(zi->ci.central_header+16,(uLong)0,4); /*crc*/ + zip64local_putValue_inmemory(zi->ci.central_header+20,(uLong)0,4); /*compr size*/ + zip64local_putValue_inmemory(zi->ci.central_header+24,(uLong)0,4); /*uncompr size*/ + zip64local_putValue_inmemory(zi->ci.central_header+28,(uLong)size_filename,2); + zip64local_putValue_inmemory(zi->ci.central_header+30,(uLong)size_extrafield_global,2); + zip64local_putValue_inmemory(zi->ci.central_header+32,(uLong)size_comment,2); + zip64local_putValue_inmemory(zi->ci.central_header+34,(uLong)0,2); /*disk nm start*/ + + if (zipfi==NULL) + zip64local_putValue_inmemory(zi->ci.central_header+36,(uLong)0,2); + else + zip64local_putValue_inmemory(zi->ci.central_header+36,(uLong)zipfi->internal_fa,2); + + if (zipfi==NULL) + zip64local_putValue_inmemory(zi->ci.central_header+38,(uLong)0,4); + else + zip64local_putValue_inmemory(zi->ci.central_header+38,(uLong)zipfi->external_fa,4); + + if(zi->ci.pos_local_header >= 0xffffffff) + zip64local_putValue_inmemory(zi->ci.central_header+42,(uLong)0xffffffff,4); + else + zip64local_putValue_inmemory(zi->ci.central_header+42,(uLong)zi->ci.pos_local_header - zi->add_position_when_writting_offset,4); + + for (i=0;ici.central_header+SIZECENTRALHEADER+i) = *(filename+i); + + for (i=0;ici.central_header+SIZECENTRALHEADER+size_filename+i) = + *(((const char*)extrafield_global)+i); + + for (i=0;ici.central_header+SIZECENTRALHEADER+size_filename+ + size_extrafield_global+i) = *(comment+i); + if (zi->ci.central_header == NULL) + return ZIP_INTERNALERROR; + + zi->ci.zip64 = zip64; + zi->ci.totalCompressedData = 0; + zi->ci.totalUncompressedData = 0; + zi->ci.pos_zip64extrainfo = 0; + + err = Write_LocalFileHeader(zi, filename, size_extrafield_local, extrafield_local); + +#ifdef HAVE_BZIP2 + zi->ci.bstream.avail_in = (uInt)0; + zi->ci.bstream.avail_out = (uInt)Z_BUFSIZE; + zi->ci.bstream.next_out = (char*)zi->ci.buffered_data; + zi->ci.bstream.total_in_hi32 = 0; + zi->ci.bstream.total_in_lo32 = 0; + zi->ci.bstream.total_out_hi32 = 0; + zi->ci.bstream.total_out_lo32 = 0; +#endif + + zi->ci.stream.avail_in = (uInt)0; + zi->ci.stream.avail_out = (uInt)Z_BUFSIZE; + zi->ci.stream.next_out = zi->ci.buffered_data; + zi->ci.stream.total_in = 0; + zi->ci.stream.total_out = 0; + zi->ci.stream.data_type = Z_BINARY; + +#ifdef HAVE_BZIP2 + if ((err==ZIP_OK) && (zi->ci.method == Z_DEFLATED || zi->ci.method == Z_BZIP2ED) && (!zi->ci.raw)) +#else + if ((err==ZIP_OK) && (zi->ci.method == Z_DEFLATED) && (!zi->ci.raw)) +#endif + { + if(zi->ci.method == Z_DEFLATED) + { + zi->ci.stream.zalloc = (alloc_func)0; + zi->ci.stream.zfree = (free_func)0; + zi->ci.stream.opaque = (voidpf)0; + + if (windowBits>0) + windowBits = -windowBits; + + err = deflateInit2(&zi->ci.stream, level, Z_DEFLATED, windowBits, memLevel, strategy); + + if (err==Z_OK) + zi->ci.stream_initialised = Z_DEFLATED; + } + else if(zi->ci.method == Z_BZIP2ED) + { +#ifdef HAVE_BZIP2 + // Init BZip stuff here + zi->ci.bstream.bzalloc = 0; + zi->ci.bstream.bzfree = 0; + zi->ci.bstream.opaque = (voidpf)0; + + err = BZ2_bzCompressInit(&zi->ci.bstream, level, 0,35); + if(err == BZ_OK) + zi->ci.stream_initialised = Z_BZIP2ED; +#endif + } + + } + +# ifndef NOCRYPT + zi->ci.crypt_header_size = 0; + if ((err==Z_OK) && (password != NULL)) + { + unsigned char bufHead[RAND_HEAD_LEN]; + unsigned int sizeHead; + zi->ci.encrypt = 1; + zi->ci.pcrc_32_tab = (const unsigned long *)get_crc_table(); + /*init_keys(password,zi->ci.keys,zi->ci.pcrc_32_tab);*/ + + sizeHead=crypthead(password,bufHead,RAND_HEAD_LEN,zi->ci.keys,zi->ci.pcrc_32_tab,crcForCrypting); + zi->ci.crypt_header_size = sizeHead; + + if (ZWRITE64(zi->z_filefunc,zi->filestream,bufHead,sizeHead) != sizeHead) + err = ZIP_ERRNO; + } +# endif + + if (err==Z_OK) + zi->in_opened_file_inzip = 1; + return err; +} + +extern int ZEXPORT zipOpenNewFileInZip4 (zipFile file, const char* filename, const zip_fileinfo* zipfi, + const void* extrafield_local, uInt size_extrafield_local, + const void* extrafield_global, uInt size_extrafield_global, + const char* comment, int method, int level, int raw, + int windowBits,int memLevel, int strategy, + const char* password, uLong crcForCrypting, + uLong versionMadeBy, uLong flagBase) +{ + return zipOpenNewFileInZip4_64 (file, filename, zipfi, + extrafield_local, size_extrafield_local, + extrafield_global, size_extrafield_global, + comment, method, level, raw, + windowBits, memLevel, strategy, + password, crcForCrypting, versionMadeBy, flagBase, 0); +} + +extern int ZEXPORT zipOpenNewFileInZip3 (zipFile file, const char* filename, const zip_fileinfo* zipfi, + const void* extrafield_local, uInt size_extrafield_local, + const void* extrafield_global, uInt size_extrafield_global, + const char* comment, int method, int level, int raw, + int windowBits,int memLevel, int strategy, + const char* password, uLong crcForCrypting) +{ + return zipOpenNewFileInZip4_64 (file, filename, zipfi, + extrafield_local, size_extrafield_local, + extrafield_global, size_extrafield_global, + comment, method, level, raw, + windowBits, memLevel, strategy, + password, crcForCrypting, VERSIONMADEBY, 0, 0); +} + +extern int ZEXPORT zipOpenNewFileInZip3_64(zipFile file, const char* filename, const zip_fileinfo* zipfi, + const void* extrafield_local, uInt size_extrafield_local, + const void* extrafield_global, uInt size_extrafield_global, + const char* comment, int method, int level, int raw, + int windowBits,int memLevel, int strategy, + const char* password, uLong crcForCrypting, int zip64) +{ + return zipOpenNewFileInZip4_64 (file, filename, zipfi, + extrafield_local, size_extrafield_local, + extrafield_global, size_extrafield_global, + comment, method, level, raw, + windowBits, memLevel, strategy, + password, crcForCrypting, VERSIONMADEBY, 0, zip64); +} + +extern int ZEXPORT zipOpenNewFileInZip2(zipFile file, const char* filename, const zip_fileinfo* zipfi, + const void* extrafield_local, uInt size_extrafield_local, + const void* extrafield_global, uInt size_extrafield_global, + const char* comment, int method, int level, int raw) +{ + return zipOpenNewFileInZip4_64 (file, filename, zipfi, + extrafield_local, size_extrafield_local, + extrafield_global, size_extrafield_global, + comment, method, level, raw, + -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, + NULL, 0, VERSIONMADEBY, 0, 0); +} + +extern int ZEXPORT zipOpenNewFileInZip2_64(zipFile file, const char* filename, const zip_fileinfo* zipfi, + const void* extrafield_local, uInt size_extrafield_local, + const void* extrafield_global, uInt size_extrafield_global, + const char* comment, int method, int level, int raw, int zip64) +{ + return zipOpenNewFileInZip4_64 (file, filename, zipfi, + extrafield_local, size_extrafield_local, + extrafield_global, size_extrafield_global, + comment, method, level, raw, + -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, + NULL, 0, VERSIONMADEBY, 0, zip64); +} + +extern int ZEXPORT zipOpenNewFileInZip64 (zipFile file, const char* filename, const zip_fileinfo* zipfi, + const void* extrafield_local, uInt size_extrafield_local, + const void*extrafield_global, uInt size_extrafield_global, + const char* comment, int method, int level, int zip64) +{ + return zipOpenNewFileInZip4_64 (file, filename, zipfi, + extrafield_local, size_extrafield_local, + extrafield_global, size_extrafield_global, + comment, method, level, 0, + -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, + NULL, 0, VERSIONMADEBY, 0, zip64); +} + +extern int ZEXPORT zipOpenNewFileInZip (zipFile file, const char* filename, const zip_fileinfo* zipfi, + const void* extrafield_local, uInt size_extrafield_local, + const void*extrafield_global, uInt size_extrafield_global, + const char* comment, int method, int level) +{ + return zipOpenNewFileInZip4_64 (file, filename, zipfi, + extrafield_local, size_extrafield_local, + extrafield_global, size_extrafield_global, + comment, method, level, 0, + -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, + NULL, 0, VERSIONMADEBY, 0, 0); +} + +local int zip64FlushWriteBuffer(zip64_internal* zi) +{ + int err=ZIP_OK; + + if (zi->ci.encrypt != 0) + { +#ifndef NOCRYPT + uInt i; + int t; + for (i=0;ici.pos_in_buffered_data;i++) + zi->ci.buffered_data[i] = zencode(zi->ci.keys, zi->ci.pcrc_32_tab, zi->ci.buffered_data[i],t); +#endif + } + + if (ZWRITE64(zi->z_filefunc,zi->filestream,zi->ci.buffered_data,zi->ci.pos_in_buffered_data) != zi->ci.pos_in_buffered_data) + err = ZIP_ERRNO; + + zi->ci.totalCompressedData += zi->ci.pos_in_buffered_data; + +#ifdef HAVE_BZIP2 + if(zi->ci.method == Z_BZIP2ED) + { + zi->ci.totalUncompressedData += zi->ci.bstream.total_in_lo32; + zi->ci.bstream.total_in_lo32 = 0; + zi->ci.bstream.total_in_hi32 = 0; + } + else +#endif + { + zi->ci.totalUncompressedData += zi->ci.stream.total_in; + zi->ci.stream.total_in = 0; + } + + + zi->ci.pos_in_buffered_data = 0; + + return err; +} + +extern int ZEXPORT zipWriteInFileInZip (zipFile file,const void* buf,unsigned int len) +{ + zip64_internal* zi; + int err=ZIP_OK; + + if (file == NULL) + return ZIP_PARAMERROR; + zi = (zip64_internal*)file; + + if (zi->in_opened_file_inzip == 0) + return ZIP_PARAMERROR; + + zi->ci.crc32 = crc32(zi->ci.crc32,buf,(uInt)len); + +#ifdef HAVE_BZIP2 + if(zi->ci.method == Z_BZIP2ED && (!zi->ci.raw)) + { + zi->ci.bstream.next_in = (void*)buf; + zi->ci.bstream.avail_in = len; + err = BZ_RUN_OK; + + while ((err==BZ_RUN_OK) && (zi->ci.bstream.avail_in>0)) + { + if (zi->ci.bstream.avail_out == 0) + { + if (zip64FlushWriteBuffer(zi) == ZIP_ERRNO) + err = ZIP_ERRNO; + zi->ci.bstream.avail_out = (uInt)Z_BUFSIZE; + zi->ci.bstream.next_out = (char*)zi->ci.buffered_data; + } + + + if(err != BZ_RUN_OK) + break; + + if ((zi->ci.method == Z_BZIP2ED) && (!zi->ci.raw)) + { + uLong uTotalOutBefore_lo = zi->ci.bstream.total_out_lo32; +// uLong uTotalOutBefore_hi = zi->ci.bstream.total_out_hi32; + err=BZ2_bzCompress(&zi->ci.bstream, BZ_RUN); + + zi->ci.pos_in_buffered_data += (uInt)(zi->ci.bstream.total_out_lo32 - uTotalOutBefore_lo) ; + } + } + + if(err == BZ_RUN_OK) + err = ZIP_OK; + } + else +#endif + { + zi->ci.stream.next_in = (Bytef*)buf; + zi->ci.stream.avail_in = len; + + while ((err==ZIP_OK) && (zi->ci.stream.avail_in>0)) + { + if (zi->ci.stream.avail_out == 0) + { + if (zip64FlushWriteBuffer(zi) == ZIP_ERRNO) + err = ZIP_ERRNO; + zi->ci.stream.avail_out = (uInt)Z_BUFSIZE; + zi->ci.stream.next_out = zi->ci.buffered_data; + } + + + if(err != ZIP_OK) + break; + + if ((zi->ci.method == Z_DEFLATED) && (!zi->ci.raw)) + { + uLong uTotalOutBefore = zi->ci.stream.total_out; + err=deflate(&zi->ci.stream, Z_NO_FLUSH); + if(uTotalOutBefore > zi->ci.stream.total_out) + { + int bBreak = 0; + bBreak++; + } + + zi->ci.pos_in_buffered_data += (uInt)(zi->ci.stream.total_out - uTotalOutBefore) ; + } + else + { + uInt copy_this,i; + if (zi->ci.stream.avail_in < zi->ci.stream.avail_out) + copy_this = zi->ci.stream.avail_in; + else + copy_this = zi->ci.stream.avail_out; + + for (i = 0; i < copy_this; i++) + *(((char*)zi->ci.stream.next_out)+i) = + *(((const char*)zi->ci.stream.next_in)+i); + { + zi->ci.stream.avail_in -= copy_this; + zi->ci.stream.avail_out-= copy_this; + zi->ci.stream.next_in+= copy_this; + zi->ci.stream.next_out+= copy_this; + zi->ci.stream.total_in+= copy_this; + zi->ci.stream.total_out+= copy_this; + zi->ci.pos_in_buffered_data += copy_this; + } + } + }// while(...) + } + + return err; +} + +extern int ZEXPORT zipCloseFileInZipRaw (zipFile file, uLong uncompressed_size, uLong crc32) +{ + return zipCloseFileInZipRaw64 (file, uncompressed_size, crc32); +} + +extern int ZEXPORT zipCloseFileInZipRaw64 (zipFile file, ZPOS64_T uncompressed_size, uLong crc32) +{ + zip64_internal* zi; + ZPOS64_T compressed_size; + uLong invalidValue = 0xffffffff; + short datasize = 0; + int err=ZIP_OK; + + if (file == NULL) + return ZIP_PARAMERROR; + zi = (zip64_internal*)file; + + if (zi->in_opened_file_inzip == 0) + return ZIP_PARAMERROR; + zi->ci.stream.avail_in = 0; + + if ((zi->ci.method == Z_DEFLATED) && (!zi->ci.raw)) + { + while (err==ZIP_OK) + { + uLong uTotalOutBefore; + if (zi->ci.stream.avail_out == 0) + { + if (zip64FlushWriteBuffer(zi) == ZIP_ERRNO) + err = ZIP_ERRNO; + zi->ci.stream.avail_out = (uInt)Z_BUFSIZE; + zi->ci.stream.next_out = zi->ci.buffered_data; + } + uTotalOutBefore = zi->ci.stream.total_out; + err=deflate(&zi->ci.stream, Z_FINISH); + zi->ci.pos_in_buffered_data += (uInt)(zi->ci.stream.total_out - uTotalOutBefore) ; + } + } + else if ((zi->ci.method == Z_BZIP2ED) && (!zi->ci.raw)) + { +#ifdef HAVE_BZIP2 + err = BZ_FINISH_OK; + while (err==BZ_FINISH_OK) + { + uLong uTotalOutBefore; + if (zi->ci.bstream.avail_out == 0) + { + if (zip64FlushWriteBuffer(zi) == ZIP_ERRNO) + err = ZIP_ERRNO; + zi->ci.bstream.avail_out = (uInt)Z_BUFSIZE; + zi->ci.bstream.next_out = (char*)zi->ci.buffered_data; + } + uTotalOutBefore = zi->ci.bstream.total_out_lo32; + err=BZ2_bzCompress(&zi->ci.bstream, BZ_FINISH); + if(err == BZ_STREAM_END) + err = Z_STREAM_END; + + zi->ci.pos_in_buffered_data += (uInt)(zi->ci.bstream.total_out_lo32 - uTotalOutBefore); + } + + if(err == BZ_FINISH_OK) + err = ZIP_OK; +#endif + } + + if (err==Z_STREAM_END) + err=ZIP_OK; /* this is normal */ + + if ((zi->ci.pos_in_buffered_data>0) && (err==ZIP_OK)) + { + if (zip64FlushWriteBuffer(zi)==ZIP_ERRNO) + err = ZIP_ERRNO; + } + + if ((zi->ci.method == Z_DEFLATED) && (!zi->ci.raw)) + { + int tmp_err = deflateEnd(&zi->ci.stream); + if (err == ZIP_OK) + err = tmp_err; + zi->ci.stream_initialised = 0; + } +#ifdef HAVE_BZIP2 + else if((zi->ci.method == Z_BZIP2ED) && (!zi->ci.raw)) + { + int tmperr = BZ2_bzCompressEnd(&zi->ci.bstream); + if (err==ZIP_OK) + err = tmperr; + zi->ci.stream_initialised = 0; + } +#endif + + if (!zi->ci.raw) + { + crc32 = (uLong)zi->ci.crc32; + uncompressed_size = zi->ci.totalUncompressedData; + } + compressed_size = zi->ci.totalCompressedData; + +# ifndef NOCRYPT + compressed_size += zi->ci.crypt_header_size; +# endif + + // update Current Item crc and sizes, + if(compressed_size >= 0xffffffff || uncompressed_size >= 0xffffffff || zi->ci.pos_local_header >= 0xffffffff) + { + /*version Made by*/ + zip64local_putValue_inmemory(zi->ci.central_header+4,(uLong)45,2); + /*version needed*/ + zip64local_putValue_inmemory(zi->ci.central_header+6,(uLong)45,2); + + } + + zip64local_putValue_inmemory(zi->ci.central_header+16,crc32,4); /*crc*/ + + + if(compressed_size >= 0xffffffff) + zip64local_putValue_inmemory(zi->ci.central_header+20, invalidValue,4); /*compr size*/ + else + zip64local_putValue_inmemory(zi->ci.central_header+20, compressed_size,4); /*compr size*/ + + /// set internal file attributes field + if (zi->ci.stream.data_type == Z_ASCII) + zip64local_putValue_inmemory(zi->ci.central_header+36,(uLong)Z_ASCII,2); + + if(uncompressed_size >= 0xffffffff) + zip64local_putValue_inmemory(zi->ci.central_header+24, invalidValue,4); /*uncompr size*/ + else + zip64local_putValue_inmemory(zi->ci.central_header+24, uncompressed_size,4); /*uncompr size*/ + + // Add ZIP64 extra info field for uncompressed size + if(uncompressed_size >= 0xffffffff) + datasize += 8; + + // Add ZIP64 extra info field for compressed size + if(compressed_size >= 0xffffffff) + datasize += 8; + + // Add ZIP64 extra info field for relative offset to local file header of current file + if(zi->ci.pos_local_header >= 0xffffffff) + datasize += 8; + + if(datasize > 0) + { + char* p = NULL; + + if((uLong)(datasize + 4) > zi->ci.size_centralExtraFree) + { + // we can not write more data to the buffer that we have room for. + return ZIP_BADZIPFILE; + } + + p = zi->ci.central_header + zi->ci.size_centralheader; + + // Add Extra Information Header for 'ZIP64 information' + zip64local_putValue_inmemory(p, 0x0001, 2); // HeaderID + p += 2; + zip64local_putValue_inmemory(p, datasize, 2); // DataSize + p += 2; + + if(uncompressed_size >= 0xffffffff) + { + zip64local_putValue_inmemory(p, uncompressed_size, 8); + p += 8; + } + + if(compressed_size >= 0xffffffff) + { + zip64local_putValue_inmemory(p, compressed_size, 8); + p += 8; + } + + if(zi->ci.pos_local_header >= 0xffffffff) + { + zip64local_putValue_inmemory(p, zi->ci.pos_local_header, 8); + p += 8; + } + + // Update how much extra free space we got in the memory buffer + // and increase the centralheader size so the new ZIP64 fields are included + // ( 4 below is the size of HeaderID and DataSize field ) + zi->ci.size_centralExtraFree -= datasize + 4; + zi->ci.size_centralheader += datasize + 4; + + // Update the extra info size field + zi->ci.size_centralExtra += datasize + 4; + zip64local_putValue_inmemory(zi->ci.central_header+30,(uLong)zi->ci.size_centralExtra,2); + } + + if (err==ZIP_OK) + err = add_data_in_datablock(&zi->central_dir, zi->ci.central_header, (uLong)zi->ci.size_centralheader); + + free(zi->ci.central_header); + + if (err==ZIP_OK) + { + // Update the LocalFileHeader with the new values. + + ZPOS64_T cur_pos_inzip = ZTELL64(zi->z_filefunc,zi->filestream); + + if (ZSEEK64(zi->z_filefunc,zi->filestream, zi->ci.pos_local_header + 14,ZLIB_FILEFUNC_SEEK_SET)!=0) + err = ZIP_ERRNO; + + if (err==ZIP_OK) + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,crc32,4); /* crc 32, unknown */ + + if(uncompressed_size >= 0xffffffff || compressed_size >= 0xffffffff ) + { + if(zi->ci.pos_zip64extrainfo > 0) + { + // Update the size in the ZIP64 extended field. + if (ZSEEK64(zi->z_filefunc,zi->filestream, zi->ci.pos_zip64extrainfo + 4,ZLIB_FILEFUNC_SEEK_SET)!=0) + err = ZIP_ERRNO; + + if (err==ZIP_OK) /* compressed size, unknown */ + err = zip64local_putValue(&zi->z_filefunc, zi->filestream, uncompressed_size, 8); + + if (err==ZIP_OK) /* uncompressed size, unknown */ + err = zip64local_putValue(&zi->z_filefunc, zi->filestream, compressed_size, 8); + } + else + err = ZIP_BADZIPFILE; // Caller passed zip64 = 0, so no room for zip64 info -> fatal + } + else + { + if (err==ZIP_OK) /* compressed size, unknown */ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,compressed_size,4); + + if (err==ZIP_OK) /* uncompressed size, unknown */ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,uncompressed_size,4); + } + + if (ZSEEK64(zi->z_filefunc,zi->filestream, cur_pos_inzip,ZLIB_FILEFUNC_SEEK_SET)!=0) + err = ZIP_ERRNO; + } + + zi->number_entry ++; + zi->in_opened_file_inzip = 0; + + return err; +} + +extern int ZEXPORT zipCloseFileInZip (zipFile file) +{ + return zipCloseFileInZipRaw (file,0,0); +} + +int Write_Zip64EndOfCentralDirectoryLocator(zip64_internal* zi, ZPOS64_T zip64eocd_pos_inzip) +{ + int err = ZIP_OK; + ZPOS64_T pos = zip64eocd_pos_inzip - zi->add_position_when_writting_offset; + + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)ZIP64ENDLOCHEADERMAGIC,4); + + /*num disks*/ + if (err==ZIP_OK) /* number of the disk with the start of the central directory */ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,4); + + /*relative offset*/ + if (err==ZIP_OK) /* Relative offset to the Zip64EndOfCentralDirectory */ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream, pos,8); + + /*total disks*/ /* Do not support spawning of disk so always say 1 here*/ + if (err==ZIP_OK) /* number of the disk with the start of the central directory */ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)1,4); + + return err; +} + +int Write_Zip64EndOfCentralDirectoryRecord(zip64_internal* zi, uLong size_centraldir, ZPOS64_T centraldir_pos_inzip) +{ + int err = ZIP_OK; + + uLong Zip64DataSize = 44; + + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)ZIP64ENDHEADERMAGIC,4); + + if (err==ZIP_OK) /* size of this 'zip64 end of central directory' */ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(ZPOS64_T)Zip64DataSize,8); // why ZPOS64_T of this ? + + if (err==ZIP_OK) /* version made by */ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)45,2); + + if (err==ZIP_OK) /* version needed */ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)45,2); + + if (err==ZIP_OK) /* number of this disk */ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,4); + + if (err==ZIP_OK) /* number of the disk with the start of the central directory */ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,4); + + if (err==ZIP_OK) /* total number of entries in the central dir on this disk */ + err = zip64local_putValue(&zi->z_filefunc, zi->filestream, zi->number_entry, 8); + + if (err==ZIP_OK) /* total number of entries in the central dir */ + err = zip64local_putValue(&zi->z_filefunc, zi->filestream, zi->number_entry, 8); + + if (err==ZIP_OK) /* size of the central directory */ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(ZPOS64_T)size_centraldir,8); + + if (err==ZIP_OK) /* offset of start of central directory with respect to the starting disk number */ + { + ZPOS64_T pos = centraldir_pos_inzip - zi->add_position_when_writting_offset; + err = zip64local_putValue(&zi->z_filefunc,zi->filestream, (ZPOS64_T)pos,8); + } + return err; +} +int Write_EndOfCentralDirectoryRecord(zip64_internal* zi, uLong size_centraldir, ZPOS64_T centraldir_pos_inzip) +{ + int err = ZIP_OK; + + /*signature*/ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)ENDHEADERMAGIC,4); + + if (err==ZIP_OK) /* number of this disk */ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,2); + + if (err==ZIP_OK) /* number of the disk with the start of the central directory */ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,2); + + if (err==ZIP_OK) /* total number of entries in the central dir on this disk */ + { + { + if(zi->number_entry >= 0xFFFF) + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0xffff,2); // use value in ZIP64 record + else + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)zi->number_entry,2); + } + } + + if (err==ZIP_OK) /* total number of entries in the central dir */ + { + if(zi->number_entry >= 0xFFFF) + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)0xffff,2); // use value in ZIP64 record + else + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)zi->number_entry,2); + } + + if (err==ZIP_OK) /* size of the central directory */ + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)size_centraldir,4); + + if (err==ZIP_OK) /* offset of start of central directory with respect to the starting disk number */ + { + ZPOS64_T pos = centraldir_pos_inzip - zi->add_position_when_writting_offset; + if(pos >= 0xffffffff) + { + err = zip64local_putValue(&zi->z_filefunc,zi->filestream, (uLong)0xffffffff,4); + } + else + err = zip64local_putValue(&zi->z_filefunc,zi->filestream, (uLong)(centraldir_pos_inzip - zi->add_position_when_writting_offset),4); + } + + return err; +} + +int Write_GlobalComment(zip64_internal* zi, const char* global_comment) +{ + int err = ZIP_OK; + uInt size_global_comment = 0; + + if(global_comment != NULL) + size_global_comment = (uInt)strlen(global_comment); + + err = zip64local_putValue(&zi->z_filefunc,zi->filestream,(uLong)size_global_comment,2); + + if (err == ZIP_OK && size_global_comment > 0) + { + if (ZWRITE64(zi->z_filefunc,zi->filestream, global_comment, size_global_comment) != size_global_comment) + err = ZIP_ERRNO; + } + return err; +} + +extern int ZEXPORT zipClose (zipFile file, const char* global_comment) +{ + zip64_internal* zi; + int err = 0; + uLong size_centraldir = 0; + ZPOS64_T centraldir_pos_inzip; + ZPOS64_T pos; + + if (file == NULL) + return ZIP_PARAMERROR; + + zi = (zip64_internal*)file; + + if (zi->in_opened_file_inzip == 1) + { + err = zipCloseFileInZip (file); + } + +#ifndef NO_ADDFILEINEXISTINGZIP + if (global_comment==NULL) + global_comment = zi->globalcomment; +#endif + + centraldir_pos_inzip = ZTELL64(zi->z_filefunc,zi->filestream); + + if (err==ZIP_OK) + { + linkedlist_datablock_internal* ldi = zi->central_dir.first_block; + while (ldi!=NULL) + { + if ((err==ZIP_OK) && (ldi->filled_in_this_block>0)) + { + if (ZWRITE64(zi->z_filefunc,zi->filestream, ldi->data, ldi->filled_in_this_block) != ldi->filled_in_this_block) + err = ZIP_ERRNO; + } + + size_centraldir += ldi->filled_in_this_block; + ldi = ldi->next_datablock; + } + } + free_linkedlist(&(zi->central_dir)); + + pos = centraldir_pos_inzip - zi->add_position_when_writting_offset; + if(pos >= 0xffffffff || zi->number_entry > 0xFFFF) + { + ZPOS64_T Zip64EOCDpos = ZTELL64(zi->z_filefunc,zi->filestream); + Write_Zip64EndOfCentralDirectoryRecord(zi, size_centraldir, centraldir_pos_inzip); + + Write_Zip64EndOfCentralDirectoryLocator(zi, Zip64EOCDpos); + } + + if (err==ZIP_OK) + err = Write_EndOfCentralDirectoryRecord(zi, size_centraldir, centraldir_pos_inzip); + + if(err == ZIP_OK) + err = Write_GlobalComment(zi, global_comment); + + if (ZCLOSE64(zi->z_filefunc,zi->filestream) != 0) + if (err == ZIP_OK) + err = ZIP_ERRNO; + +#ifndef NO_ADDFILEINEXISTINGZIP + TRYFREE(zi->globalcomment); +#endif + TRYFREE(zi); + + return err; +} + +extern int ZEXPORT zipRemoveExtraInfoBlock (char* pData, int* dataLen, short sHeader) +{ + char* p = pData; + int size = 0; + char* pNewHeader; + char* pTmp; + short header; + short dataSize; + + int retVal = ZIP_OK; + + if(pData == NULL || *dataLen < 4) + return ZIP_PARAMERROR; + + pNewHeader = (char*)ALLOC(*dataLen); + pTmp = pNewHeader; + + while(p < (pData + *dataLen)) + { + header = *(short*)p; + dataSize = *(((short*)p)+1); + + if( header == sHeader ) // Header found. + { + p += dataSize + 4; // skip it. do not copy to temp buffer + } + else + { + // Extra Info block should not be removed, So copy it to the temp buffer. + memcpy(pTmp, p, dataSize + 4); + p += dataSize + 4; + size += dataSize + 4; + } + + } + + if(size < *dataLen) + { + // clean old extra info block. + memset(pData,0, *dataLen); + + // copy the new extra info block over the old + if(size > 0) + memcpy(pData, pNewHeader, size); + + // set the new extra info size + *dataLen = size; + + retVal = ZIP_OK; + } + else + retVal = ZIP_ERRNO; + + TRYFREE(pNewHeader); + + return retVal; +} diff --git a/Samples/3rdParty/minizip/src/zip.h b/Samples/3rdParty/minizip/src/zip.h new file mode 100755 index 0000000..8aaebb6 --- /dev/null +++ b/Samples/3rdParty/minizip/src/zip.h @@ -0,0 +1,362 @@ +/* zip.h -- IO on .zip files using zlib + Version 1.1, February 14h, 2010 + part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html ) + + Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html ) + + Modifications for Zip64 support + Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com ) + + For more info read MiniZip_info.txt + + --------------------------------------------------------------------------- + + Condition of use and distribution are the same than zlib : + + 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. + + --------------------------------------------------------------------------- + + Changes + + See header of zip.h + +*/ + +#ifndef _zip12_H +#define _zip12_H + +#ifdef __cplusplus +extern "C" { +#endif + +//#define HAVE_BZIP2 + +#ifndef _ZLIB_H +#include "zlib.h" +#endif + +#ifndef _ZLIBIOAPI_H +#include "ioapi.h" +#endif + +#ifdef HAVE_BZIP2 +#include "bzlib.h" +#endif + +#define Z_BZIP2ED 12 + +#if defined(STRICTZIP) || defined(STRICTZIPUNZIP) +/* like the STRICT of WIN32, we define a pointer that cannot be converted + from (void*) without cast */ +typedef struct TagzipFile__ { int unused; } zipFile__; +typedef zipFile__ *zipFile; +#else +typedef voidp zipFile; +#endif + +#define ZIP_OK (0) +#define ZIP_EOF (0) +#define ZIP_ERRNO (Z_ERRNO) +#define ZIP_PARAMERROR (-102) +#define ZIP_BADZIPFILE (-103) +#define ZIP_INTERNALERROR (-104) + +#ifndef DEF_MEM_LEVEL +# if MAX_MEM_LEVEL >= 8 +# define DEF_MEM_LEVEL 8 +# else +# define DEF_MEM_LEVEL MAX_MEM_LEVEL +# endif +#endif +/* default memLevel */ + +/* tm_zip contain date/time info */ +typedef struct tm_zip_s +{ + uInt tm_sec; /* seconds after the minute - [0,59] */ + uInt tm_min; /* minutes after the hour - [0,59] */ + uInt tm_hour; /* hours since midnight - [0,23] */ + uInt tm_mday; /* day of the month - [1,31] */ + uInt tm_mon; /* months since January - [0,11] */ + uInt tm_year; /* years - [1980..2044] */ +} tm_zip; + +typedef struct +{ + tm_zip tmz_date; /* date in understandable format */ + uLong dosDate; /* if dos_date == 0, tmu_date is used */ +/* uLong flag; */ /* general purpose bit flag 2 bytes */ + + uLong internal_fa; /* internal file attributes 2 bytes */ + uLong external_fa; /* external file attributes 4 bytes */ +} zip_fileinfo; + +typedef const char* zipcharpc; + + +#define APPEND_STATUS_CREATE (0) +#define APPEND_STATUS_CREATEAFTER (1) +#define APPEND_STATUS_ADDINZIP (2) + +extern zipFile ZEXPORT zipOpen OF((const char *pathname, int append)); +extern zipFile ZEXPORT zipOpen64 OF((const void *pathname, int append)); +/* + Create a zipfile. + pathname contain on Windows XP a filename like "c:\\zlib\\zlib113.zip" or on + an Unix computer "zlib/zlib113.zip". + if the file pathname exist and append==APPEND_STATUS_CREATEAFTER, the zip + will be created at the end of the file. + (useful if the file contain a self extractor code) + if the file pathname exist and append==APPEND_STATUS_ADDINZIP, we will + add files in existing zip (be sure you don't add file that doesn't exist) + If the zipfile cannot be opened, the return value is NULL. + Else, the return value is a zipFile Handle, usable with other function + of this zip package. +*/ + +/* Note : there is no delete function into a zipfile. + If you want delete file into a zipfile, you must open a zipfile, and create another + Of couse, you can use RAW reading and writing to copy the file you did not want delte +*/ + +extern zipFile ZEXPORT zipOpen2 OF((const char *pathname, + int append, + zipcharpc* globalcomment, + zlib_filefunc_def* pzlib_filefunc_def)); + +extern zipFile ZEXPORT zipOpen2_64 OF((const void *pathname, + int append, + zipcharpc* globalcomment, + zlib_filefunc64_def* pzlib_filefunc_def)); + +extern int ZEXPORT zipOpenNewFileInZip OF((zipFile file, + const char* filename, + const zip_fileinfo* zipfi, + const void* extrafield_local, + uInt size_extrafield_local, + const void* extrafield_global, + uInt size_extrafield_global, + const char* comment, + int method, + int level)); + +extern int ZEXPORT zipOpenNewFileInZip64 OF((zipFile file, + const char* filename, + const zip_fileinfo* zipfi, + const void* extrafield_local, + uInt size_extrafield_local, + const void* extrafield_global, + uInt size_extrafield_global, + const char* comment, + int method, + int level, + int zip64)); + +/* + Open a file in the ZIP for writing. + filename : the filename in zip (if NULL, '-' without quote will be used + *zipfi contain supplemental information + if extrafield_local!=NULL and size_extrafield_local>0, extrafield_local + contains the extrafield data the the local header + if extrafield_global!=NULL and size_extrafield_global>0, extrafield_global + contains the extrafield data the the local header + if comment != NULL, comment contain the comment string + method contain the compression method (0 for store, Z_DEFLATED for deflate) + level contain the level of compression (can be Z_DEFAULT_COMPRESSION) + zip64 is set to 1 if a zip64 extended information block should be added to the local file header. + this MUST be '1' if the uncompressed size is >= 0xffffffff. + +*/ + + +extern int ZEXPORT zipOpenNewFileInZip2 OF((zipFile file, + const char* filename, + const zip_fileinfo* zipfi, + const void* extrafield_local, + uInt size_extrafield_local, + const void* extrafield_global, + uInt size_extrafield_global, + const char* comment, + int method, + int level, + int raw)); + + +extern int ZEXPORT zipOpenNewFileInZip2_64 OF((zipFile file, + const char* filename, + const zip_fileinfo* zipfi, + const void* extrafield_local, + uInt size_extrafield_local, + const void* extrafield_global, + uInt size_extrafield_global, + const char* comment, + int method, + int level, + int raw, + int zip64)); +/* + Same than zipOpenNewFileInZip, except if raw=1, we write raw file + */ + +extern int ZEXPORT zipOpenNewFileInZip3 OF((zipFile file, + const char* filename, + const zip_fileinfo* zipfi, + const void* extrafield_local, + uInt size_extrafield_local, + const void* extrafield_global, + uInt size_extrafield_global, + const char* comment, + int method, + int level, + int raw, + int windowBits, + int memLevel, + int strategy, + const char* password, + uLong crcForCrypting)); + +extern int ZEXPORT zipOpenNewFileInZip3_64 OF((zipFile file, + const char* filename, + const zip_fileinfo* zipfi, + const void* extrafield_local, + uInt size_extrafield_local, + const void* extrafield_global, + uInt size_extrafield_global, + const char* comment, + int method, + int level, + int raw, + int windowBits, + int memLevel, + int strategy, + const char* password, + uLong crcForCrypting, + int zip64 + )); + +/* + Same than zipOpenNewFileInZip2, except + windowBits,memLevel,,strategy : see parameter strategy in deflateInit2 + password : crypting password (NULL for no crypting) + crcForCrypting : crc of file to compress (needed for crypting) + */ + +extern int ZEXPORT zipOpenNewFileInZip4 OF((zipFile file, + const char* filename, + const zip_fileinfo* zipfi, + const void* extrafield_local, + uInt size_extrafield_local, + const void* extrafield_global, + uInt size_extrafield_global, + const char* comment, + int method, + int level, + int raw, + int windowBits, + int memLevel, + int strategy, + const char* password, + uLong crcForCrypting, + uLong versionMadeBy, + uLong flagBase + )); + + +extern int ZEXPORT zipOpenNewFileInZip4_64 OF((zipFile file, + const char* filename, + const zip_fileinfo* zipfi, + const void* extrafield_local, + uInt size_extrafield_local, + const void* extrafield_global, + uInt size_extrafield_global, + const char* comment, + int method, + int level, + int raw, + int windowBits, + int memLevel, + int strategy, + const char* password, + uLong crcForCrypting, + uLong versionMadeBy, + uLong flagBase, + int zip64 + )); +/* + Same than zipOpenNewFileInZip4, except + versionMadeBy : value for Version made by field + flag : value for flag field (compression level info will be added) + */ + + +extern int ZEXPORT zipWriteInFileInZip OF((zipFile file, + const void* buf, + unsigned len)); +/* + Write data in the zipfile +*/ + +extern int ZEXPORT zipCloseFileInZip OF((zipFile file)); +/* + Close the current file in the zipfile +*/ + +extern int ZEXPORT zipCloseFileInZipRaw OF((zipFile file, + uLong uncompressed_size, + uLong crc32)); + +extern int ZEXPORT zipCloseFileInZipRaw64 OF((zipFile file, + ZPOS64_T uncompressed_size, + uLong crc32)); + +/* + Close the current file in the zipfile, for file opened with + parameter raw=1 in zipOpenNewFileInZip2 + uncompressed_size and crc32 are value for the uncompressed size +*/ + +extern int ZEXPORT zipClose OF((zipFile file, + const char* global_comment)); +/* + Close the zipfile +*/ + + +extern int ZEXPORT zipRemoveExtraInfoBlock OF((char* pData, int* dataLen, short sHeader)); +/* + zipRemoveExtraInfoBlock - Added by Mathias Svensson + + Remove extra information block from a extra information data for the local file header or central directory header + + It is needed to remove ZIP64 extra information blocks when before data is written if using RAW mode. + + 0x0001 is the signature header for the ZIP64 extra information blocks + + usage. + Remove ZIP64 Extra information from a central director extra field data + zipRemoveExtraInfoBlock(pCenDirExtraFieldData, &nCenDirExtraFieldDataLen, 0x0001); + + Remove ZIP64 Extra information from a Local File Header extra field data + zipRemoveExtraInfoBlock(pLocalHeaderExtraFieldData, &nLocalHeaderExtraFieldDataLen, 0x0001); +*/ + +#ifdef __cplusplus +} +#endif + +#endif /* _zip64_H */ diff --git a/Samples/3rdParty/stb/src/stb_image.c b/Samples/3rdParty/stb/src/stb_image.c new file mode 100755 index 0000000..9032264 --- /dev/null +++ b/Samples/3rdParty/stb/src/stb_image.c @@ -0,0 +1,6455 @@ +/* stb_image - v2.12 - public domain image loader - http://nothings.org/stb_image.h + no warranty implied; use at your own risk + + Do this: + #define STB_IMAGE_IMPLEMENTATION + before you include this file in *one* C or C++ file to create the implementation. + + // i.e. it should look like this: + #include ... + #include ... + #include ... + #define STB_IMAGE_IMPLEMENTATION + #include "stb_image.h" + + You can #define STBI_ASSERT(x) before the #include to avoid using assert.h. + And #define STBI_MALLOC, STBI_REALLOC, and STBI_FREE to avoid using malloc,realloc,free + + + QUICK NOTES: + Primarily of interest to game developers and other people who can + avoid problematic images and only need the trivial interface + + JPEG baseline & progressive (12 bpc/arithmetic not supported, same as stock IJG lib) + PNG 1/2/4/8-bit-per-channel (16 bpc not supported) + + TGA (not sure what subset, if a subset) + BMP non-1bpp, non-RLE + PSD (composited view only, no extra channels, 8/16 bit-per-channel) + + GIF (*comp always reports as 4-channel) + HDR (radiance rgbE format) + PIC (Softimage PIC) + PNM (PPM and PGM binary only) + + Animated GIF still needs a proper API, but here's one way to do it: + http://gist.github.com/urraka/685d9a6340b26b830d49 + + - decode from memory or through FILE (define STBI_NO_STDIO to remove code) + - decode from arbitrary I/O callbacks + - SIMD acceleration on x86/x64 (SSE2) and ARM (NEON) + + Full documentation under "DOCUMENTATION" below. + + + Revision 2.00 release notes: + + - Progressive JPEG is now supported. + + - PPM and PGM binary formats are now supported, thanks to Ken Miller. + + - x86 platforms now make use of SSE2 SIMD instructions for + JPEG decoding, and ARM platforms can use NEON SIMD if requested. + This work was done by Fabian "ryg" Giesen. SSE2 is used by + default, but NEON must be enabled explicitly; see docs. + + With other JPEG optimizations included in this version, we see + 2x speedup on a JPEG on an x86 machine, and a 1.5x speedup + on a JPEG on an ARM machine, relative to previous versions of this + library. The same results will not obtain for all JPGs and for all + x86/ARM machines. (Note that progressive JPEGs are significantly + slower to decode than regular JPEGs.) This doesn't mean that this + is the fastest JPEG decoder in the land; rather, it brings it + closer to parity with standard libraries. If you want the fastest + decode, look elsewhere. (See "Philosophy" section of docs below.) + + See final bullet items below for more info on SIMD. + + - Added STBI_MALLOC, STBI_REALLOC, and STBI_FREE macros for replacing + the memory allocator. Unlike other STBI libraries, these macros don't + support a context parameter, so if you need to pass a context in to + the allocator, you'll have to store it in a global or a thread-local + variable. + + - Split existing STBI_NO_HDR flag into two flags, STBI_NO_HDR and + STBI_NO_LINEAR. + STBI_NO_HDR: suppress implementation of .hdr reader format + STBI_NO_LINEAR: suppress high-dynamic-range light-linear float API + + - You can suppress implementation of any of the decoders to reduce + your code footprint by #defining one or more of the following + symbols before creating the implementation. + + STBI_NO_JPEG + STBI_NO_PNG + STBI_NO_BMP + STBI_NO_PSD + STBI_NO_TGA + STBI_NO_GIF + STBI_NO_HDR + STBI_NO_PIC + STBI_NO_PNM (.ppm and .pgm) + + - You can request *only* certain decoders and suppress all other ones + (this will be more forward-compatible, as addition of new decoders + doesn't require you to disable them explicitly): + + STBI_ONLY_JPEG + STBI_ONLY_PNG + STBI_ONLY_BMP + STBI_ONLY_PSD + STBI_ONLY_TGA + STBI_ONLY_GIF + STBI_ONLY_HDR + STBI_ONLY_PIC + STBI_ONLY_PNM (.ppm and .pgm) + + Note that you can define multiples of these, and you will get all + of them ("only x" and "only y" is interpreted to mean "only x&y"). + + - If you use STBI_NO_PNG (or _ONLY_ without PNG), and you still + want the zlib decoder to be available, #define STBI_SUPPORT_ZLIB + + - Compilation of all SIMD code can be suppressed with + #define STBI_NO_SIMD + It should not be necessary to disable SIMD unless you have issues + compiling (e.g. using an x86 compiler which doesn't support SSE + intrinsics or that doesn't support the method used to detect + SSE2 support at run-time), and even those can be reported as + bugs so I can refine the built-in compile-time checking to be + smarter. + + - The old STBI_SIMD system which allowed installing a user-defined + IDCT etc. has been removed. If you need this, don't upgrade. My + assumption is that almost nobody was doing this, and those who + were will find the built-in SIMD more satisfactory anyway. + + - RGB values computed for JPEG images are slightly different from + previous versions of stb_image. (This is due to using less + integer precision in SIMD.) The C code has been adjusted so + that the same RGB values will be computed regardless of whether + SIMD support is available, so your app should always produce + consistent results. But these results are slightly different from + previous versions. (Specifically, about 3% of available YCbCr values + will compute different RGB results from pre-1.49 versions by +-1; + most of the deviating values are one smaller in the G channel.) + + - If you must produce consistent results with previous versions of + stb_image, #define STBI_JPEG_OLD and you will get the same results + you used to; however, you will not get the SIMD speedups for + the YCbCr-to-RGB conversion step (although you should still see + significant JPEG speedup from the other changes). + + Please note that STBI_JPEG_OLD is a temporary feature; it will be + removed in future versions of the library. It is only intended for + near-term back-compatibility use. + + + Latest revision history: + 2.12 (2016-04-02) fix typo in 2.11 PSD fix that caused crashes + 2.11 (2016-04-02) 16-bit PNGS; enable SSE2 in non-gcc x64 + RGB-format JPEG; remove white matting in PSD; + allocate large structures on the stack; + correct channel count for PNG & BMP + 2.10 (2016-01-22) avoid warning introduced in 2.09 + 2.09 (2016-01-16) 16-bit TGA; comments in PNM files; STBI_REALLOC_SIZED + 2.08 (2015-09-13) fix to 2.07 cleanup, reading RGB PSD as RGBA + 2.07 (2015-09-13) partial animated GIF support + limited 16-bit PSD support + minor bugs, code cleanup, and compiler warnings + 2.06 (2015-04-19) fix bug where PSD returns wrong '*comp' value + 2.05 (2015-04-19) fix bug in progressive JPEG handling, fix warning + 2.04 (2015-04-15) try to re-enable SIMD on MinGW 64-bit + 2.03 (2015-04-12) additional corruption checking + stbi_set_flip_vertically_on_load + fix NEON support; fix mingw support + 2.02 (2015-01-19) fix incorrect assert, fix warning + 2.01 (2015-01-17) fix various warnings + 2.00b (2014-12-25) fix STBI_MALLOC in progressive JPEG + 2.00 (2014-12-25) optimize JPEG, including x86 SSE2 & ARM NEON SIMD + progressive JPEG + PGM/PPM support + STBI_MALLOC,STBI_REALLOC,STBI_FREE + STBI_NO_*, STBI_ONLY_* + GIF bugfix + + See end of file for full revision history. + + + ============================ Contributors ========================= + + Image formats Extensions, features + Sean Barrett (jpeg, png, bmp) Jetro Lauha (stbi_info) + Nicolas Schulz (hdr, psd) Martin "SpartanJ" Golini (stbi_info) + Jonathan Dummer (tga) James "moose2000" Brown (iPhone PNG) + Jean-Marc Lienher (gif) Ben "Disch" Wenger (io callbacks) + Tom Seddon (pic) Omar Cornut (1/2/4-bit PNG) + Thatcher Ulrich (psd) Nicolas Guillemot (vertical flip) + Ken Miller (pgm, ppm) Richard Mitton (16-bit PSD) + urraka@github (animated gif) Junggon Kim (PNM comments) + Daniel Gibson (16-bit TGA) + + Optimizations & bugfixes + Fabian "ryg" Giesen + Arseny Kapoulkine + + Bug & warning fixes + Marc LeBlanc David Woo Guillaume George Martins Mozeiko + Christpher Lloyd Martin Golini Jerry Jansson Joseph Thomson + Dave Moore Roy Eltham Hayaki Saito Phil Jordan + Won Chun Luke Graham Johan Duparc Nathan Reed + the Horde3D community Thomas Ruf Ronny Chevalier Nick Verigakis + Janez Zemva John Bartholomew Michal Cichon svdijk@github + Jonathan Blow Ken Hamada Tero Hanninen Baldur Karlsson + Laurent Gomila Cort Stratton Sergio Gonzalez romigrou@github + Aruelien Pocheville Thibault Reuille Cass Everitt Matthew Gregan + Ryamond Barbiero Paul Du Bois Engin Manap snagar@github + Michaelangel007@github Oriol Ferrer Mesia socks-the-fox + Blazej Dariusz Roszkowski + + +LICENSE + +This software is dual-licensed to the public domain and under the following +license: you are granted a perpetual, irrevocable license to copy, modify, +publish, and distribute this file as you see fit. + +*/ + +#include "stb_image.h" + +#define STB_IMAGE_IMPLEMENTATION + +#ifdef STB_IMAGE_IMPLEMENTATION + +#if defined(STBI_ONLY_JPEG) || defined(STBI_ONLY_PNG) || defined(STBI_ONLY_BMP) \ + || defined(STBI_ONLY_TGA) || defined(STBI_ONLY_GIF) || defined(STBI_ONLY_PSD) \ + || defined(STBI_ONLY_HDR) || defined(STBI_ONLY_PIC) || defined(STBI_ONLY_PNM) \ + || defined(STBI_ONLY_ZLIB) + #ifndef STBI_ONLY_JPEG + #define STBI_NO_JPEG + #endif + #ifndef STBI_ONLY_PNG + #define STBI_NO_PNG + #endif + #ifndef STBI_ONLY_BMP + #define STBI_NO_BMP + #endif + #ifndef STBI_ONLY_PSD + #define STBI_NO_PSD + #endif + #ifndef STBI_ONLY_TGA + #define STBI_NO_TGA + #endif + #ifndef STBI_ONLY_GIF + #define STBI_NO_GIF + #endif + #ifndef STBI_ONLY_HDR + #define STBI_NO_HDR + #endif + #ifndef STBI_ONLY_PIC + #define STBI_NO_PIC + #endif + #ifndef STBI_ONLY_PNM + #define STBI_NO_PNM + #endif +#endif + +#if defined(STBI_NO_PNG) && !defined(STBI_SUPPORT_ZLIB) && !defined(STBI_NO_ZLIB) +#define STBI_NO_ZLIB +#endif + + +#include +#include // ptrdiff_t on osx +#include +#include + +#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) +#include // ldexp +#endif + +#ifndef STBI_NO_STDIO +#include +#endif + +#ifndef STBI_ASSERT +#include +#define STBI_ASSERT(x) assert(x) +#endif + + +#ifndef _MSC_VER + #ifdef __cplusplus + #define stbi_inline inline + #else + #define stbi_inline + #endif +#else + #define stbi_inline __forceinline +#endif + + +#ifdef _MSC_VER +typedef unsigned short stbi__uint16; +typedef signed short stbi__int16; +typedef unsigned int stbi__uint32; +typedef signed int stbi__int32; +#else +#include +typedef uint16_t stbi__uint16; +typedef int16_t stbi__int16; +typedef uint32_t stbi__uint32; +typedef int32_t stbi__int32; +#endif + +// should produce compiler error if size is wrong +typedef unsigned char validate_uint32[sizeof(stbi__uint32)==4 ? 1 : -1]; + +#ifdef _MSC_VER +#define STBI_NOTUSED(v) (void)(v) +#else +#define STBI_NOTUSED(v) (void)sizeof(v) +#endif + +#ifdef _MSC_VER +#define STBI_HAS_LROTL +#endif + +#ifdef STBI_HAS_LROTL + #define stbi_lrot(x,y) _lrotl(x,y) +#else + #define stbi_lrot(x,y) (((x) << (y)) | ((x) >> (32 - (y)))) +#endif + +#if defined(STBI_MALLOC) && defined(STBI_FREE) && (defined(STBI_REALLOC) || defined(STBI_REALLOC_SIZED)) +// ok +#elif !defined(STBI_MALLOC) && !defined(STBI_FREE) && !defined(STBI_REALLOC) && !defined(STBI_REALLOC_SIZED) +// ok +#else +#error "Must define all or none of STBI_MALLOC, STBI_FREE, and STBI_REALLOC (or STBI_REALLOC_SIZED)." +#endif + +#ifndef STBI_MALLOC +#define STBI_MALLOC(sz) malloc(sz) +#define STBI_REALLOC(p,newsz) realloc(p,newsz) +#define STBI_FREE(p) free(p) +#endif + +#ifndef STBI_REALLOC_SIZED +#define STBI_REALLOC_SIZED(p,oldsz,newsz) STBI_REALLOC(p,newsz) +#endif + +// x86/x64 detection +#if defined(__x86_64__) || defined(_M_X64) +#define STBI__X64_TARGET +#elif defined(__i386) || defined(_M_IX86) +#define STBI__X86_TARGET +#endif + +#if defined(__GNUC__) && (defined(STBI__X86_TARGET) || defined(STBI__X64_TARGET)) && !defined(__SSE2__) && !defined(STBI_NO_SIMD) +// NOTE: not clear do we actually need this for the 64-bit path? +// gcc doesn't support sse2 intrinsics unless you compile with -msse2, +// (but compiling with -msse2 allows the compiler to use SSE2 everywhere; +// this is just broken and gcc are jerks for not fixing it properly +// http://www.virtualdub.org/blog/pivot/entry.php?id=363 ) +#define STBI_NO_SIMD +#endif + +#if defined(__MINGW32__) && defined(STBI__X86_TARGET) && !defined(STBI_MINGW_ENABLE_SSE2) && !defined(STBI_NO_SIMD) +// Note that __MINGW32__ doesn't actually mean 32-bit, so we have to avoid STBI__X64_TARGET +// +// 32-bit MinGW wants ESP to be 16-byte aligned, but this is not in the +// Windows ABI and VC++ as well as Windows DLLs don't maintain that invariant. +// As a result, enabling SSE2 on 32-bit MinGW is dangerous when not +// simultaneously enabling "-mstackrealign". +// +// See https://github.com/nothings/stb/issues/81 for more information. +// +// So default to no SSE2 on 32-bit MinGW. If you've read this far and added +// -mstackrealign to your build settings, feel free to #define STBI_MINGW_ENABLE_SSE2. +#define STBI_NO_SIMD +#endif + +#if !defined(STBI_NO_SIMD) && (defined(STBI__X86_TARGET) || defined(STBI__X64_TARGET)) +#define STBI_SSE2 +#include + +#ifdef _MSC_VER + +#if _MSC_VER >= 1400 // not VC6 +#include // __cpuid +static int stbi__cpuid3(void) +{ + int info[4]; + __cpuid(info,1); + return info[3]; +} +#else +static int stbi__cpuid3(void) +{ + int res; + __asm { + mov eax,1 + cpuid + mov res,edx + } + return res; +} +#endif + +#define STBI_SIMD_ALIGN(type, name) __declspec(align(16)) type name + +static int stbi__sse2_available() +{ + int info3 = stbi__cpuid3(); + return ((info3 >> 26) & 1) != 0; +} +#else // assume GCC-style if not VC++ +#define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16))) + +static int stbi__sse2_available() +{ +#if defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__) >= 408 // GCC 4.8 or later + // GCC 4.8+ has a nice way to do this + return __builtin_cpu_supports("sse2"); +#else + // portable way to do this, preferably without using GCC inline ASM? + // just bail for now. + return 0; +#endif +} +#endif +#endif + +// ARM NEON +#if defined(STBI_NO_SIMD) && defined(STBI_NEON) +#undef STBI_NEON +#endif + +#ifdef STBI_NEON +#include +// assume GCC or Clang on ARM targets +#define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16))) +#endif + +#ifndef STBI_SIMD_ALIGN +#define STBI_SIMD_ALIGN(type, name) type name +#endif + +/////////////////////////////////////////////// +// +// stbi__context struct and start_xxx functions + +// stbi__context structure is our basic context used by all images, so it +// contains all the IO context, plus some basic image information +typedef struct +{ + stbi__uint32 img_x, img_y; + int img_n, img_out_n; + + stbi_io_callbacks io; + void *io_user_data; + + int read_from_callbacks; + int buflen; + stbi_uc buffer_start[128]; + + stbi_uc *img_buffer, *img_buffer_end; + stbi_uc *img_buffer_original, *img_buffer_original_end; +} stbi__context; + + +static void stbi__refill_buffer(stbi__context *s); + +// initialize a memory-decode context +static void stbi__start_mem(stbi__context *s, stbi_uc const *buffer, int len) +{ + s->io.read = NULL; + s->read_from_callbacks = 0; + s->img_buffer = s->img_buffer_original = (stbi_uc *) buffer; + s->img_buffer_end = s->img_buffer_original_end = (stbi_uc *) buffer+len; +} + +// initialize a callback-based context +static void stbi__start_callbacks(stbi__context *s, stbi_io_callbacks *c, void *user) +{ + s->io = *c; + s->io_user_data = user; + s->buflen = sizeof(s->buffer_start); + s->read_from_callbacks = 1; + s->img_buffer_original = s->buffer_start; + stbi__refill_buffer(s); + s->img_buffer_original_end = s->img_buffer_end; +} + +#ifndef STBI_NO_STDIO + +static int stbi__stdio_read(void *user, char *data, int size) +{ + return (int) fread(data,1,size,(FILE*) user); +} + +static void stbi__stdio_skip(void *user, int n) +{ + fseek((FILE*) user, n, SEEK_CUR); +} + +static int stbi__stdio_eof(void *user) +{ + return feof((FILE*) user); +} + +static stbi_io_callbacks stbi__stdio_callbacks = +{ + stbi__stdio_read, + stbi__stdio_skip, + stbi__stdio_eof, +}; + +static void stbi__start_file(stbi__context *s, FILE *f) +{ + stbi__start_callbacks(s, &stbi__stdio_callbacks, (void *) f); +} + +//static void stop_file(stbi__context *s) { } + +#endif // !STBI_NO_STDIO + +static void stbi__rewind(stbi__context *s) +{ + // conceptually rewind SHOULD rewind to the beginning of the stream, + // but we just rewind to the beginning of the initial buffer, because + // we only use it after doing 'test', which only ever looks at at most 92 bytes + s->img_buffer = s->img_buffer_original; + s->img_buffer_end = s->img_buffer_original_end; +} + +#ifndef STBI_NO_JPEG +static int stbi__jpeg_test(stbi__context *s); +static stbi_uc *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); +static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_PNG +static int stbi__png_test(stbi__context *s); +static stbi_uc *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); +static int stbi__png_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_BMP +static int stbi__bmp_test(stbi__context *s); +static stbi_uc *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); +static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_TGA +static int stbi__tga_test(stbi__context *s); +static stbi_uc *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); +static int stbi__tga_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_PSD +static int stbi__psd_test(stbi__context *s); +static stbi_uc *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); +static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_HDR +static int stbi__hdr_test(stbi__context *s); +static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); +static int stbi__hdr_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_PIC +static int stbi__pic_test(stbi__context *s); +static stbi_uc *stbi__pic_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); +static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_GIF +static int stbi__gif_test(stbi__context *s); +static stbi_uc *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); +static int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_PNM +static int stbi__pnm_test(stbi__context *s); +static stbi_uc *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp); +static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +// this is not threadsafe +static const char *stbi__g_failure_reason; + +STBIDEF const char *stbi_failure_reason(void) +{ + return stbi__g_failure_reason; +} + +static int stbi__err(const char *str) +{ + stbi__g_failure_reason = str; + return 0; +} + +static void *stbi__malloc(size_t size) +{ + return STBI_MALLOC(size); +} + +// stbi__err - error +// stbi__errpf - error returning pointer to float +// stbi__errpuc - error returning pointer to unsigned char + +#ifdef STBI_NO_FAILURE_STRINGS + #define stbi__err(x,y) 0 +#elif defined(STBI_FAILURE_USERMSG) + #define stbi__err(x,y) stbi__err(y) +#else + #define stbi__err(x,y) stbi__err(x) +#endif + +#define stbi__errpf(x,y) ((float *)(size_t) (stbi__err(x,y)?NULL:NULL)) +#define stbi__errpuc(x,y) ((unsigned char *)(size_t) (stbi__err(x,y)?NULL:NULL)) + +STBIDEF void stbi_image_free(void *retval_from_stbi_load) +{ + STBI_FREE(retval_from_stbi_load); +} + +#ifndef STBI_NO_LINEAR +static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp); +#endif + +#ifndef STBI_NO_HDR +static stbi_uc *stbi__hdr_to_ldr(float *data, int x, int y, int comp); +#endif + +static int stbi__vertically_flip_on_load = 0; + +STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip) +{ + stbi__vertically_flip_on_load = flag_true_if_should_flip; +} + +static unsigned char *stbi__load_main(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + #ifndef STBI_NO_JPEG + if (stbi__jpeg_test(s)) return stbi__jpeg_load(s,x,y,comp,req_comp); + #endif + #ifndef STBI_NO_PNG + if (stbi__png_test(s)) return stbi__png_load(s,x,y,comp,req_comp); + #endif + #ifndef STBI_NO_BMP + if (stbi__bmp_test(s)) return stbi__bmp_load(s,x,y,comp,req_comp); + #endif + #ifndef STBI_NO_GIF + if (stbi__gif_test(s)) return stbi__gif_load(s,x,y,comp,req_comp); + #endif + #ifndef STBI_NO_PSD + if (stbi__psd_test(s)) return stbi__psd_load(s,x,y,comp,req_comp); + #endif + #ifndef STBI_NO_PIC + if (stbi__pic_test(s)) return stbi__pic_load(s,x,y,comp,req_comp); + #endif + #ifndef STBI_NO_PNM + if (stbi__pnm_test(s)) return stbi__pnm_load(s,x,y,comp,req_comp); + #endif + + #ifndef STBI_NO_HDR + if (stbi__hdr_test(s)) { + float *hdr = stbi__hdr_load(s, x,y,comp,req_comp); + return stbi__hdr_to_ldr(hdr, *x, *y, req_comp ? req_comp : *comp); + } + #endif + + #ifndef STBI_NO_TGA + // test tga last because it's a crappy test! + if (stbi__tga_test(s)) + return stbi__tga_load(s,x,y,comp,req_comp); + #endif + + return stbi__errpuc("unknown image type", "Image not of any known type, or corrupt"); +} + +static unsigned char *stbi__load_flip(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + unsigned char *result = stbi__load_main(s, x, y, comp, req_comp); + + if (stbi__vertically_flip_on_load && result != NULL) { + int w = *x, h = *y; + int depth = req_comp ? req_comp : *comp; + int row,col,z; + stbi_uc temp; + + // @OPTIMIZE: use a bigger temp buffer and memcpy multiple pixels at once + for (row = 0; row < (h>>1); row++) { + for (col = 0; col < w; col++) { + for (z = 0; z < depth; z++) { + temp = result[(row * w + col) * depth + z]; + result[(row * w + col) * depth + z] = result[((h - row - 1) * w + col) * depth + z]; + result[((h - row - 1) * w + col) * depth + z] = temp; + } + } + } + } + + return result; +} + +#ifndef STBI_NO_HDR +static void stbi__float_postprocess(float *result, int *x, int *y, int *comp, int req_comp) +{ + if (stbi__vertically_flip_on_load && result != NULL) { + int w = *x, h = *y; + int depth = req_comp ? req_comp : *comp; + int row,col,z; + float temp; + + // @OPTIMIZE: use a bigger temp buffer and memcpy multiple pixels at once + for (row = 0; row < (h>>1); row++) { + for (col = 0; col < w; col++) { + for (z = 0; z < depth; z++) { + temp = result[(row * w + col) * depth + z]; + result[(row * w + col) * depth + z] = result[((h - row - 1) * w + col) * depth + z]; + result[((h - row - 1) * w + col) * depth + z] = temp; + } + } + } + } +} +#endif + +#ifndef STBI_NO_STDIO + +static FILE *stbi__fopen(char const *filename, char const *mode) +{ + FILE *f; +#if defined(_MSC_VER) && _MSC_VER >= 1400 + if (0 != fopen_s(&f, filename, mode)) + f=0; +#else + f = fopen(filename, mode); +#endif + return f; +} + + +STBIDEF stbi_uc *stbi_load(char const *filename, int *x, int *y, int *comp, int req_comp) +{ + FILE *f = stbi__fopen(filename, "rb"); + unsigned char *result; + if (!f) return stbi__errpuc("can't fopen", "Unable to open file"); + result = stbi_load_from_file(f,x,y,comp,req_comp); + fclose(f); + return result; +} + +STBIDEF stbi_uc *stbi_load_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) +{ + unsigned char *result; + stbi__context s; + stbi__start_file(&s,f); + result = stbi__load_flip(&s,x,y,comp,req_comp); + if (result) { + // need to 'unget' all the characters in the IO buffer + fseek(f, - (int) (s.img_buffer_end - s.img_buffer), SEEK_CUR); + } + return result; +} +#endif //!STBI_NO_STDIO + +STBIDEF stbi_uc *stbi_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__load_flip(&s,x,y,comp,req_comp); +} + +STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); + return stbi__load_flip(&s,x,y,comp,req_comp); +} + +#ifndef STBI_NO_LINEAR +static float *stbi__loadf_main(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + unsigned char *data; + #ifndef STBI_NO_HDR + if (stbi__hdr_test(s)) { + float *hdr_data = stbi__hdr_load(s,x,y,comp,req_comp); + if (hdr_data) + stbi__float_postprocess(hdr_data,x,y,comp,req_comp); + return hdr_data; + } + #endif + data = stbi__load_flip(s, x, y, comp, req_comp); + if (data) + return stbi__ldr_to_hdr(data, *x, *y, req_comp ? req_comp : *comp); + return stbi__errpf("unknown image type", "Image not of any known type, or corrupt"); +} + +STBIDEF float *stbi_loadf_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__loadf_main(&s,x,y,comp,req_comp); +} + +STBIDEF float *stbi_loadf_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); + return stbi__loadf_main(&s,x,y,comp,req_comp); +} + +#ifndef STBI_NO_STDIO +STBIDEF float *stbi_loadf(char const *filename, int *x, int *y, int *comp, int req_comp) +{ + float *result; + FILE *f = stbi__fopen(filename, "rb"); + if (!f) return stbi__errpf("can't fopen", "Unable to open file"); + result = stbi_loadf_from_file(f,x,y,comp,req_comp); + fclose(f); + return result; +} + +STBIDEF float *stbi_loadf_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_file(&s,f); + return stbi__loadf_main(&s,x,y,comp,req_comp); +} +#endif // !STBI_NO_STDIO + +#endif // !STBI_NO_LINEAR + +// these is-hdr-or-not is defined independent of whether STBI_NO_LINEAR is +// defined, for API simplicity; if STBI_NO_LINEAR is defined, it always +// reports false! + +STBIDEF int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len) +{ + #ifndef STBI_NO_HDR + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__hdr_test(&s); + #else + STBI_NOTUSED(buffer); + STBI_NOTUSED(len); + return 0; + #endif +} + +#ifndef STBI_NO_STDIO +STBIDEF int stbi_is_hdr (char const *filename) +{ + FILE *f = stbi__fopen(filename, "rb"); + int result=0; + if (f) { + result = stbi_is_hdr_from_file(f); + fclose(f); + } + return result; +} + +STBIDEF int stbi_is_hdr_from_file(FILE *f) +{ + #ifndef STBI_NO_HDR + stbi__context s; + stbi__start_file(&s,f); + return stbi__hdr_test(&s); + #else + STBI_NOTUSED(f); + return 0; + #endif +} +#endif // !STBI_NO_STDIO + +STBIDEF int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user) +{ + #ifndef STBI_NO_HDR + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); + return stbi__hdr_test(&s); + #else + STBI_NOTUSED(clbk); + STBI_NOTUSED(user); + return 0; + #endif +} + +#ifndef STBI_NO_LINEAR +static float stbi__l2h_gamma=2.2f, stbi__l2h_scale=1.0f; + +STBIDEF void stbi_ldr_to_hdr_gamma(float gamma) { stbi__l2h_gamma = gamma; } +STBIDEF void stbi_ldr_to_hdr_scale(float scale) { stbi__l2h_scale = scale; } +#endif + +static float stbi__h2l_gamma_i=1.0f/2.2f, stbi__h2l_scale_i=1.0f; + +STBIDEF void stbi_hdr_to_ldr_gamma(float gamma) { stbi__h2l_gamma_i = 1/gamma; } +STBIDEF void stbi_hdr_to_ldr_scale(float scale) { stbi__h2l_scale_i = 1/scale; } + + +////////////////////////////////////////////////////////////////////////////// +// +// Common code used by all image loaders +// + +enum +{ + STBI__SCAN_load=0, + STBI__SCAN_type, + STBI__SCAN_header +}; + +static void stbi__refill_buffer(stbi__context *s) +{ + int n = (s->io.read)(s->io_user_data,(char*)s->buffer_start,s->buflen); + if (n == 0) { + // at end of file, treat same as if from memory, but need to handle case + // where s->img_buffer isn't pointing to safe memory, e.g. 0-byte file + s->read_from_callbacks = 0; + s->img_buffer = s->buffer_start; + s->img_buffer_end = s->buffer_start+1; + *s->img_buffer = 0; + } else { + s->img_buffer = s->buffer_start; + s->img_buffer_end = s->buffer_start + n; + } +} + +stbi_inline static stbi_uc stbi__get8(stbi__context *s) +{ + if (s->img_buffer < s->img_buffer_end) + return *s->img_buffer++; + if (s->read_from_callbacks) { + stbi__refill_buffer(s); + return *s->img_buffer++; + } + return 0; +} + +stbi_inline static int stbi__at_eof(stbi__context *s) +{ + if (s->io.read) { + if (!(s->io.eof)(s->io_user_data)) return 0; + // if feof() is true, check if buffer = end + // special case: we've only got the special 0 character at the end + if (s->read_from_callbacks == 0) return 1; + } + + return s->img_buffer >= s->img_buffer_end; +} + +static void stbi__skip(stbi__context *s, int n) +{ + if (n < 0) { + s->img_buffer = s->img_buffer_end; + return; + } + if (s->io.read) { + int blen = (int) (s->img_buffer_end - s->img_buffer); + if (blen < n) { + s->img_buffer = s->img_buffer_end; + (s->io.skip)(s->io_user_data, n - blen); + return; + } + } + s->img_buffer += n; +} + +static int stbi__getn(stbi__context *s, stbi_uc *buffer, int n) +{ + if (s->io.read) { + int blen = (int) (s->img_buffer_end - s->img_buffer); + if (blen < n) { + int res, count; + + memcpy(buffer, s->img_buffer, blen); + + count = (s->io.read)(s->io_user_data, (char*) buffer + blen, n - blen); + res = (count == (n-blen)); + s->img_buffer = s->img_buffer_end; + return res; + } + } + + if (s->img_buffer+n <= s->img_buffer_end) { + memcpy(buffer, s->img_buffer, n); + s->img_buffer += n; + return 1; + } else + return 0; +} + +static int stbi__get16be(stbi__context *s) +{ + int z = stbi__get8(s); + return (z << 8) + stbi__get8(s); +} + +static stbi__uint32 stbi__get32be(stbi__context *s) +{ + stbi__uint32 z = stbi__get16be(s); + return (z << 16) + stbi__get16be(s); +} + +#if defined(STBI_NO_BMP) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) +// nothing +#else +static int stbi__get16le(stbi__context *s) +{ + int z = stbi__get8(s); + return z + (stbi__get8(s) << 8); +} +#endif + +#ifndef STBI_NO_BMP +static stbi__uint32 stbi__get32le(stbi__context *s) +{ + stbi__uint32 z = stbi__get16le(s); + return z + (stbi__get16le(s) << 16); +} +#endif + +#define STBI__BYTECAST(x) ((stbi_uc) ((x) & 255)) // truncate int to byte without warnings + + +////////////////////////////////////////////////////////////////////////////// +// +// generic converter from built-in img_n to req_comp +// individual types do this automatically as much as possible (e.g. jpeg +// does all cases internally since it needs to colorspace convert anyway, +// and it never has alpha, so very few cases ). png can automatically +// interleave an alpha=255 channel, but falls back to this for other cases +// +// assume data buffer is malloced, so malloc a new one and free that one +// only failure mode is malloc failing + +static stbi_uc stbi__compute_y(int r, int g, int b) +{ + return (stbi_uc) (((r*77) + (g*150) + (29*b)) >> 8); +} + +static unsigned char *stbi__convert_format(unsigned char *data, int img_n, int req_comp, unsigned int x, unsigned int y) +{ + int i,j; + unsigned char *good; + + if (req_comp == img_n) return data; + STBI_ASSERT(req_comp >= 1 && req_comp <= 4); + + good = (unsigned char *) stbi__malloc(req_comp * x * y); + if (good == NULL) { + STBI_FREE(data); + return stbi__errpuc("outofmem", "Out of memory"); + } + + for (j=0; j < (int) y; ++j) { + unsigned char *src = data + j * x * img_n ; + unsigned char *dest = good + j * x * req_comp; + + #define COMBO(a,b) ((a)*8+(b)) + #define CASE(a,b) case COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b) + // convert source image with img_n components to one with req_comp components; + // avoid switch per pixel, so use switch per scanline and massive macros + switch (COMBO(img_n, req_comp)) { + CASE(1,2) dest[0]=src[0], dest[1]=255; break; + CASE(1,3) dest[0]=dest[1]=dest[2]=src[0]; break; + CASE(1,4) dest[0]=dest[1]=dest[2]=src[0], dest[3]=255; break; + CASE(2,1) dest[0]=src[0]; break; + CASE(2,3) dest[0]=dest[1]=dest[2]=src[0]; break; + CASE(2,4) dest[0]=dest[1]=dest[2]=src[0], dest[3]=src[1]; break; + CASE(3,4) dest[0]=src[0],dest[1]=src[1],dest[2]=src[2],dest[3]=255; break; + CASE(3,1) dest[0]=stbi__compute_y(src[0],src[1],src[2]); break; + CASE(3,2) dest[0]=stbi__compute_y(src[0],src[1],src[2]), dest[1] = 255; break; + CASE(4,1) dest[0]=stbi__compute_y(src[0],src[1],src[2]); break; + CASE(4,2) dest[0]=stbi__compute_y(src[0],src[1],src[2]), dest[1] = src[3]; break; + CASE(4,3) dest[0]=src[0],dest[1]=src[1],dest[2]=src[2]; break; + default: STBI_ASSERT(0); + } + #undef CASE + } + + STBI_FREE(data); + return good; +} + +#ifndef STBI_NO_LINEAR +static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp) +{ + int i,k,n; + float *output = (float *) stbi__malloc(x * y * comp * sizeof(float)); + if (output == NULL) { STBI_FREE(data); return stbi__errpf("outofmem", "Out of memory"); } + // compute number of non-alpha components + if (comp & 1) n = comp; else n = comp-1; + for (i=0; i < x*y; ++i) { + for (k=0; k < n; ++k) { + output[i*comp + k] = (float) (pow(data[i*comp+k]/255.0f, stbi__l2h_gamma) * stbi__l2h_scale); + } + if (k < comp) output[i*comp + k] = data[i*comp+k]/255.0f; + } + STBI_FREE(data); + return output; +} +#endif + +#ifndef STBI_NO_HDR +#define stbi__float2int(x) ((int) (x)) +static stbi_uc *stbi__hdr_to_ldr(float *data, int x, int y, int comp) +{ + int i,k,n; + stbi_uc *output = (stbi_uc *) stbi__malloc(x * y * comp); + if (output == NULL) { STBI_FREE(data); return stbi__errpuc("outofmem", "Out of memory"); } + // compute number of non-alpha components + if (comp & 1) n = comp; else n = comp-1; + for (i=0; i < x*y; ++i) { + for (k=0; k < n; ++k) { + float z = (float) pow(data[i*comp+k]*stbi__h2l_scale_i, stbi__h2l_gamma_i) * 255 + 0.5f; + if (z < 0) z = 0; + if (z > 255) z = 255; + output[i*comp + k] = (stbi_uc) stbi__float2int(z); + } + if (k < comp) { + float z = data[i*comp+k] * 255 + 0.5f; + if (z < 0) z = 0; + if (z > 255) z = 255; + output[i*comp + k] = (stbi_uc) stbi__float2int(z); + } + } + STBI_FREE(data); + return output; +} +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// "baseline" JPEG/JFIF decoder +// +// simple implementation +// - doesn't support delayed output of y-dimension +// - simple interface (only one output format: 8-bit interleaved RGB) +// - doesn't try to recover corrupt jpegs +// - doesn't allow partial loading, loading multiple at once +// - still fast on x86 (copying globals into locals doesn't help x86) +// - allocates lots of intermediate memory (full size of all components) +// - non-interleaved case requires this anyway +// - allows good upsampling (see next) +// high-quality +// - upsampled channels are bilinearly interpolated, even across blocks +// - quality integer IDCT derived from IJG's 'slow' +// performance +// - fast huffman; reasonable integer IDCT +// - some SIMD kernels for common paths on targets with SSE2/NEON +// - uses a lot of intermediate memory, could cache poorly + +#ifndef STBI_NO_JPEG + +// huffman decoding acceleration +#define FAST_BITS 9 // larger handles more cases; smaller stomps less cache + +typedef struct +{ + stbi_uc fast[1 << FAST_BITS]; + // weirdly, repacking this into AoS is a 10% speed loss, instead of a win + stbi__uint16 code[256]; + stbi_uc values[256]; + stbi_uc size[257]; + unsigned int maxcode[18]; + int delta[17]; // old 'firstsymbol' - old 'firstcode' +} stbi__huffman; + +typedef struct +{ + stbi__context *s; + stbi__huffman huff_dc[4]; + stbi__huffman huff_ac[4]; + stbi_uc dequant[4][64]; + stbi__int16 fast_ac[4][1 << FAST_BITS]; + +// sizes for components, interleaved MCUs + int img_h_max, img_v_max; + int img_mcu_x, img_mcu_y; + int img_mcu_w, img_mcu_h; + +// definition of jpeg image component + struct + { + int id; + int h,v; + int tq; + int hd,ha; + int dc_pred; + + int x,y,w2,h2; + stbi_uc *data; + void *raw_data, *raw_coeff; + stbi_uc *linebuf; + short *coeff; // progressive only + int coeff_w, coeff_h; // number of 8x8 coefficient blocks + } img_comp[4]; + + stbi__uint32 code_buffer; // jpeg entropy-coded buffer + int code_bits; // number of valid bits + unsigned char marker; // marker seen while filling entropy buffer + int nomore; // flag if we saw a marker so must stop + + int progressive; + int spec_start; + int spec_end; + int succ_high; + int succ_low; + int eob_run; + int rgb; + + int scan_n, order[4]; + int restart_interval, todo; + +// kernels + void (*idct_block_kernel)(stbi_uc *out, int out_stride, short data[64]); + void (*YCbCr_to_RGB_kernel)(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step); + stbi_uc *(*resample_row_hv_2_kernel)(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs); +} stbi__jpeg; + +static int stbi__build_huffman(stbi__huffman *h, int *count) +{ + int i,j,k=0,code; + // build size list for each symbol (from JPEG spec) + for (i=0; i < 16; ++i) + for (j=0; j < count[i]; ++j) + h->size[k++] = (stbi_uc) (i+1); + h->size[k] = 0; + + // compute actual symbols (from jpeg spec) + code = 0; + k = 0; + for(j=1; j <= 16; ++j) { + // compute delta to add to code to compute symbol id + h->delta[j] = k - code; + if (h->size[k] == j) { + while (h->size[k] == j) + h->code[k++] = (stbi__uint16) (code++); + if (code-1 >= (1 << j)) return stbi__err("bad code lengths","Corrupt JPEG"); + } + // compute largest code + 1 for this size, preshifted as needed later + h->maxcode[j] = code << (16-j); + code <<= 1; + } + h->maxcode[j] = 0xffffffff; + + // build non-spec acceleration table; 255 is flag for not-accelerated + memset(h->fast, 255, 1 << FAST_BITS); + for (i=0; i < k; ++i) { + int s = h->size[i]; + if (s <= FAST_BITS) { + int c = h->code[i] << (FAST_BITS-s); + int m = 1 << (FAST_BITS-s); + for (j=0; j < m; ++j) { + h->fast[c+j] = (stbi_uc) i; + } + } + } + return 1; +} + +// build a table that decodes both magnitude and value of small ACs in +// one go. +static void stbi__build_fast_ac(stbi__int16 *fast_ac, stbi__huffman *h) +{ + int i; + for (i=0; i < (1 << FAST_BITS); ++i) { + stbi_uc fast = h->fast[i]; + fast_ac[i] = 0; + if (fast < 255) { + int rs = h->values[fast]; + int run = (rs >> 4) & 15; + int magbits = rs & 15; + int len = h->size[fast]; + + if (magbits && len + magbits <= FAST_BITS) { + // magnitude code followed by receive_extend code + int k = ((i << len) & ((1 << FAST_BITS) - 1)) >> (FAST_BITS - magbits); + int m = 1 << (magbits - 1); + if (k < m) k += (-1 << magbits) + 1; + // if the result is small enough, we can fit it in fast_ac table + if (k >= -128 && k <= 127) + fast_ac[i] = (stbi__int16) ((k << 8) + (run << 4) + (len + magbits)); + } + } + } +} + +static void stbi__grow_buffer_unsafe(stbi__jpeg *j) +{ + do { + int b = j->nomore ? 0 : stbi__get8(j->s); + if (b == 0xff) { + int c = stbi__get8(j->s); + if (c != 0) { + j->marker = (unsigned char) c; + j->nomore = 1; + return; + } + } + j->code_buffer |= b << (24 - j->code_bits); + j->code_bits += 8; + } while (j->code_bits <= 24); +} + +// (1 << n) - 1 +static stbi__uint32 stbi__bmask[17]={0,1,3,7,15,31,63,127,255,511,1023,2047,4095,8191,16383,32767,65535}; + +// decode a jpeg huffman value from the bitstream +stbi_inline static int stbi__jpeg_huff_decode(stbi__jpeg *j, stbi__huffman *h) +{ + unsigned int temp; + int c,k; + + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + + // look at the top FAST_BITS and determine what symbol ID it is, + // if the code is <= FAST_BITS + c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); + k = h->fast[c]; + if (k < 255) { + int s = h->size[k]; + if (s > j->code_bits) + return -1; + j->code_buffer <<= s; + j->code_bits -= s; + return h->values[k]; + } + + // naive test is to shift the code_buffer down so k bits are + // valid, then test against maxcode. To speed this up, we've + // preshifted maxcode left so that it has (16-k) 0s at the + // end; in other words, regardless of the number of bits, it + // wants to be compared against something shifted to have 16; + // that way we don't need to shift inside the loop. + temp = j->code_buffer >> 16; + for (k=FAST_BITS+1 ; ; ++k) + if (temp < h->maxcode[k]) + break; + if (k == 17) { + // error! code not found + j->code_bits -= 16; + return -1; + } + + if (k > j->code_bits) + return -1; + + // convert the huffman code to the symbol id + c = ((j->code_buffer >> (32 - k)) & stbi__bmask[k]) + h->delta[k]; + STBI_ASSERT((((j->code_buffer) >> (32 - h->size[c])) & stbi__bmask[h->size[c]]) == h->code[c]); + + // convert the id to a symbol + j->code_bits -= k; + j->code_buffer <<= k; + return h->values[c]; +} + +// bias[n] = (-1<code_bits < n) stbi__grow_buffer_unsafe(j); + + sgn = (stbi__int32)j->code_buffer >> 31; // sign bit is always in MSB + k = stbi_lrot(j->code_buffer, n); + STBI_ASSERT(n >= 0 && n < (int) (sizeof(stbi__bmask)/sizeof(*stbi__bmask))); + j->code_buffer = k & ~stbi__bmask[n]; + k &= stbi__bmask[n]; + j->code_bits -= n; + return k + (stbi__jbias[n] & ~sgn); +} + +// get some unsigned bits +stbi_inline static int stbi__jpeg_get_bits(stbi__jpeg *j, int n) +{ + unsigned int k; + if (j->code_bits < n) stbi__grow_buffer_unsafe(j); + k = stbi_lrot(j->code_buffer, n); + j->code_buffer = k & ~stbi__bmask[n]; + k &= stbi__bmask[n]; + j->code_bits -= n; + return k; +} + +stbi_inline static int stbi__jpeg_get_bit(stbi__jpeg *j) +{ + unsigned int k; + if (j->code_bits < 1) stbi__grow_buffer_unsafe(j); + k = j->code_buffer; + j->code_buffer <<= 1; + --j->code_bits; + return k & 0x80000000; +} + +// given a value that's at position X in the zigzag stream, +// where does it appear in the 8x8 matrix coded as row-major? +static stbi_uc stbi__jpeg_dezigzag[64+15] = +{ + 0, 1, 8, 16, 9, 2, 3, 10, + 17, 24, 32, 25, 18, 11, 4, 5, + 12, 19, 26, 33, 40, 48, 41, 34, + 27, 20, 13, 6, 7, 14, 21, 28, + 35, 42, 49, 56, 57, 50, 43, 36, + 29, 22, 15, 23, 30, 37, 44, 51, + 58, 59, 52, 45, 38, 31, 39, 46, + 53, 60, 61, 54, 47, 55, 62, 63, + // let corrupt input sample past end + 63, 63, 63, 63, 63, 63, 63, 63, + 63, 63, 63, 63, 63, 63, 63 +}; + +// decode one 64-entry block-- +static int stbi__jpeg_decode_block(stbi__jpeg *j, short data[64], stbi__huffman *hdc, stbi__huffman *hac, stbi__int16 *fac, int b, stbi_uc *dequant) +{ + int diff,dc,k; + int t; + + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + t = stbi__jpeg_huff_decode(j, hdc); + if (t < 0) return stbi__err("bad huffman code","Corrupt JPEG"); + + // 0 all the ac values now so we can do it 32-bits at a time + memset(data,0,64*sizeof(data[0])); + + diff = t ? stbi__extend_receive(j, t) : 0; + dc = j->img_comp[b].dc_pred + diff; + j->img_comp[b].dc_pred = dc; + data[0] = (short) (dc * dequant[0]); + + // decode AC components, see JPEG spec + k = 1; + do { + unsigned int zig; + int c,r,s; + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); + r = fac[c]; + if (r) { // fast-AC path + k += (r >> 4) & 15; // run + s = r & 15; // combined length + j->code_buffer <<= s; + j->code_bits -= s; + // decode into unzigzag'd location + zig = stbi__jpeg_dezigzag[k++]; + data[zig] = (short) ((r >> 8) * dequant[zig]); + } else { + int rs = stbi__jpeg_huff_decode(j, hac); + if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); + s = rs & 15; + r = rs >> 4; + if (s == 0) { + if (rs != 0xf0) break; // end block + k += 16; + } else { + k += r; + // decode into unzigzag'd location + zig = stbi__jpeg_dezigzag[k++]; + data[zig] = (short) (stbi__extend_receive(j,s) * dequant[zig]); + } + } + } while (k < 64); + return 1; +} + +static int stbi__jpeg_decode_block_prog_dc(stbi__jpeg *j, short data[64], stbi__huffman *hdc, int b) +{ + int diff,dc; + int t; + if (j->spec_end != 0) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); + + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + + if (j->succ_high == 0) { + // first scan for DC coefficient, must be first + memset(data,0,64*sizeof(data[0])); // 0 all the ac values now + t = stbi__jpeg_huff_decode(j, hdc); + diff = t ? stbi__extend_receive(j, t) : 0; + + dc = j->img_comp[b].dc_pred + diff; + j->img_comp[b].dc_pred = dc; + data[0] = (short) (dc << j->succ_low); + } else { + // refinement scan for DC coefficient + if (stbi__jpeg_get_bit(j)) + data[0] += (short) (1 << j->succ_low); + } + return 1; +} + +// @OPTIMIZE: store non-zigzagged during the decode passes, +// and only de-zigzag when dequantizing +static int stbi__jpeg_decode_block_prog_ac(stbi__jpeg *j, short data[64], stbi__huffman *hac, stbi__int16 *fac) +{ + int k; + if (j->spec_start == 0) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); + + if (j->succ_high == 0) { + int shift = j->succ_low; + + if (j->eob_run) { + --j->eob_run; + return 1; + } + + k = j->spec_start; + do { + unsigned int zig; + int c,r,s; + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); + r = fac[c]; + if (r) { // fast-AC path + k += (r >> 4) & 15; // run + s = r & 15; // combined length + j->code_buffer <<= s; + j->code_bits -= s; + zig = stbi__jpeg_dezigzag[k++]; + data[zig] = (short) ((r >> 8) << shift); + } else { + int rs = stbi__jpeg_huff_decode(j, hac); + if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); + s = rs & 15; + r = rs >> 4; + if (s == 0) { + if (r < 15) { + j->eob_run = (1 << r); + if (r) + j->eob_run += stbi__jpeg_get_bits(j, r); + --j->eob_run; + break; + } + k += 16; + } else { + k += r; + zig = stbi__jpeg_dezigzag[k++]; + data[zig] = (short) (stbi__extend_receive(j,s) << shift); + } + } + } while (k <= j->spec_end); + } else { + // refinement scan for these AC coefficients + + short bit = (short) (1 << j->succ_low); + + if (j->eob_run) { + --j->eob_run; + for (k = j->spec_start; k <= j->spec_end; ++k) { + short *p = &data[stbi__jpeg_dezigzag[k]]; + if (*p != 0) + if (stbi__jpeg_get_bit(j)) + if ((*p & bit)==0) { + if (*p > 0) + *p += bit; + else + *p -= bit; + } + } + } else { + k = j->spec_start; + do { + int r,s; + int rs = stbi__jpeg_huff_decode(j, hac); // @OPTIMIZE see if we can use the fast path here, advance-by-r is so slow, eh + if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); + s = rs & 15; + r = rs >> 4; + if (s == 0) { + if (r < 15) { + j->eob_run = (1 << r) - 1; + if (r) + j->eob_run += stbi__jpeg_get_bits(j, r); + r = 64; // force end of block + } else { + // r=15 s=0 should write 16 0s, so we just do + // a run of 15 0s and then write s (which is 0), + // so we don't have to do anything special here + } + } else { + if (s != 1) return stbi__err("bad huffman code", "Corrupt JPEG"); + // sign bit + if (stbi__jpeg_get_bit(j)) + s = bit; + else + s = -bit; + } + + // advance by r + while (k <= j->spec_end) { + short *p = &data[stbi__jpeg_dezigzag[k++]]; + if (*p != 0) { + if (stbi__jpeg_get_bit(j)) + if ((*p & bit)==0) { + if (*p > 0) + *p += bit; + else + *p -= bit; + } + } else { + if (r == 0) { + *p = (short) s; + break; + } + --r; + } + } + } while (k <= j->spec_end); + } + } + return 1; +} + +// take a -128..127 value and stbi__clamp it and convert to 0..255 +stbi_inline static stbi_uc stbi__clamp(int x) +{ + // trick to use a single test to catch both cases + if ((unsigned int) x > 255) { + if (x < 0) return 0; + if (x > 255) return 255; + } + return (stbi_uc) x; +} + +#define stbi__f2f(x) ((int) (((x) * 4096 + 0.5))) +#define stbi__fsh(x) ((x) << 12) + +// derived from jidctint -- DCT_ISLOW +#define STBI__IDCT_1D(s0,s1,s2,s3,s4,s5,s6,s7) \ + int t0,t1,t2,t3,p1,p2,p3,p4,p5,x0,x1,x2,x3; \ + p2 = s2; \ + p3 = s6; \ + p1 = (p2+p3) * stbi__f2f(0.5411961f); \ + t2 = p1 + p3*stbi__f2f(-1.847759065f); \ + t3 = p1 + p2*stbi__f2f( 0.765366865f); \ + p2 = s0; \ + p3 = s4; \ + t0 = stbi__fsh(p2+p3); \ + t1 = stbi__fsh(p2-p3); \ + x0 = t0+t3; \ + x3 = t0-t3; \ + x1 = t1+t2; \ + x2 = t1-t2; \ + t0 = s7; \ + t1 = s5; \ + t2 = s3; \ + t3 = s1; \ + p3 = t0+t2; \ + p4 = t1+t3; \ + p1 = t0+t3; \ + p2 = t1+t2; \ + p5 = (p3+p4)*stbi__f2f( 1.175875602f); \ + t0 = t0*stbi__f2f( 0.298631336f); \ + t1 = t1*stbi__f2f( 2.053119869f); \ + t2 = t2*stbi__f2f( 3.072711026f); \ + t3 = t3*stbi__f2f( 1.501321110f); \ + p1 = p5 + p1*stbi__f2f(-0.899976223f); \ + p2 = p5 + p2*stbi__f2f(-2.562915447f); \ + p3 = p3*stbi__f2f(-1.961570560f); \ + p4 = p4*stbi__f2f(-0.390180644f); \ + t3 += p1+p4; \ + t2 += p2+p3; \ + t1 += p2+p4; \ + t0 += p1+p3; + +static void stbi__idct_block(stbi_uc *out, int out_stride, short data[64]) +{ + int i,val[64],*v=val; + stbi_uc *o; + short *d = data; + + // columns + for (i=0; i < 8; ++i,++d, ++v) { + // if all zeroes, shortcut -- this avoids dequantizing 0s and IDCTing + if (d[ 8]==0 && d[16]==0 && d[24]==0 && d[32]==0 + && d[40]==0 && d[48]==0 && d[56]==0) { + // no shortcut 0 seconds + // (1|2|3|4|5|6|7)==0 0 seconds + // all separate -0.047 seconds + // 1 && 2|3 && 4|5 && 6|7: -0.047 seconds + int dcterm = d[0] << 2; + v[0] = v[8] = v[16] = v[24] = v[32] = v[40] = v[48] = v[56] = dcterm; + } else { + STBI__IDCT_1D(d[ 0],d[ 8],d[16],d[24],d[32],d[40],d[48],d[56]) + // constants scaled things up by 1<<12; let's bring them back + // down, but keep 2 extra bits of precision + x0 += 512; x1 += 512; x2 += 512; x3 += 512; + v[ 0] = (x0+t3) >> 10; + v[56] = (x0-t3) >> 10; + v[ 8] = (x1+t2) >> 10; + v[48] = (x1-t2) >> 10; + v[16] = (x2+t1) >> 10; + v[40] = (x2-t1) >> 10; + v[24] = (x3+t0) >> 10; + v[32] = (x3-t0) >> 10; + } + } + + for (i=0, v=val, o=out; i < 8; ++i,v+=8,o+=out_stride) { + // no fast case since the first 1D IDCT spread components out + STBI__IDCT_1D(v[0],v[1],v[2],v[3],v[4],v[5],v[6],v[7]) + // constants scaled things up by 1<<12, plus we had 1<<2 from first + // loop, plus horizontal and vertical each scale by sqrt(8) so together + // we've got an extra 1<<3, so 1<<17 total we need to remove. + // so we want to round that, which means adding 0.5 * 1<<17, + // aka 65536. Also, we'll end up with -128 to 127 that we want + // to encode as 0..255 by adding 128, so we'll add that before the shift + x0 += 65536 + (128<<17); + x1 += 65536 + (128<<17); + x2 += 65536 + (128<<17); + x3 += 65536 + (128<<17); + // tried computing the shifts into temps, or'ing the temps to see + // if any were out of range, but that was slower + o[0] = stbi__clamp((x0+t3) >> 17); + o[7] = stbi__clamp((x0-t3) >> 17); + o[1] = stbi__clamp((x1+t2) >> 17); + o[6] = stbi__clamp((x1-t2) >> 17); + o[2] = stbi__clamp((x2+t1) >> 17); + o[5] = stbi__clamp((x2-t1) >> 17); + o[3] = stbi__clamp((x3+t0) >> 17); + o[4] = stbi__clamp((x3-t0) >> 17); + } +} + +#ifdef STBI_SSE2 +// sse2 integer IDCT. not the fastest possible implementation but it +// produces bit-identical results to the generic C version so it's +// fully "transparent". +static void stbi__idct_simd(stbi_uc *out, int out_stride, short data[64]) +{ + // This is constructed to match our regular (generic) integer IDCT exactly. + __m128i row0, row1, row2, row3, row4, row5, row6, row7; + __m128i tmp; + + // dot product constant: even elems=x, odd elems=y + #define dct_const(x,y) _mm_setr_epi16((x),(y),(x),(y),(x),(y),(x),(y)) + + // out(0) = c0[even]*x + c0[odd]*y (c0, x, y 16-bit, out 32-bit) + // out(1) = c1[even]*x + c1[odd]*y + #define dct_rot(out0,out1, x,y,c0,c1) \ + __m128i c0##lo = _mm_unpacklo_epi16((x),(y)); \ + __m128i c0##hi = _mm_unpackhi_epi16((x),(y)); \ + __m128i out0##_l = _mm_madd_epi16(c0##lo, c0); \ + __m128i out0##_h = _mm_madd_epi16(c0##hi, c0); \ + __m128i out1##_l = _mm_madd_epi16(c0##lo, c1); \ + __m128i out1##_h = _mm_madd_epi16(c0##hi, c1) + + // out = in << 12 (in 16-bit, out 32-bit) + #define dct_widen(out, in) \ + __m128i out##_l = _mm_srai_epi32(_mm_unpacklo_epi16(_mm_setzero_si128(), (in)), 4); \ + __m128i out##_h = _mm_srai_epi32(_mm_unpackhi_epi16(_mm_setzero_si128(), (in)), 4) + + // wide add + #define dct_wadd(out, a, b) \ + __m128i out##_l = _mm_add_epi32(a##_l, b##_l); \ + __m128i out##_h = _mm_add_epi32(a##_h, b##_h) + + // wide sub + #define dct_wsub(out, a, b) \ + __m128i out##_l = _mm_sub_epi32(a##_l, b##_l); \ + __m128i out##_h = _mm_sub_epi32(a##_h, b##_h) + + // butterfly a/b, add bias, then shift by "s" and pack + #define dct_bfly32o(out0, out1, a,b,bias,s) \ + { \ + __m128i abiased_l = _mm_add_epi32(a##_l, bias); \ + __m128i abiased_h = _mm_add_epi32(a##_h, bias); \ + dct_wadd(sum, abiased, b); \ + dct_wsub(dif, abiased, b); \ + out0 = _mm_packs_epi32(_mm_srai_epi32(sum_l, s), _mm_srai_epi32(sum_h, s)); \ + out1 = _mm_packs_epi32(_mm_srai_epi32(dif_l, s), _mm_srai_epi32(dif_h, s)); \ + } + + // 8-bit interleave step (for transposes) + #define dct_interleave8(a, b) \ + tmp = a; \ + a = _mm_unpacklo_epi8(a, b); \ + b = _mm_unpackhi_epi8(tmp, b) + + // 16-bit interleave step (for transposes) + #define dct_interleave16(a, b) \ + tmp = a; \ + a = _mm_unpacklo_epi16(a, b); \ + b = _mm_unpackhi_epi16(tmp, b) + + #define dct_pass(bias,shift) \ + { \ + /* even part */ \ + dct_rot(t2e,t3e, row2,row6, rot0_0,rot0_1); \ + __m128i sum04 = _mm_add_epi16(row0, row4); \ + __m128i dif04 = _mm_sub_epi16(row0, row4); \ + dct_widen(t0e, sum04); \ + dct_widen(t1e, dif04); \ + dct_wadd(x0, t0e, t3e); \ + dct_wsub(x3, t0e, t3e); \ + dct_wadd(x1, t1e, t2e); \ + dct_wsub(x2, t1e, t2e); \ + /* odd part */ \ + dct_rot(y0o,y2o, row7,row3, rot2_0,rot2_1); \ + dct_rot(y1o,y3o, row5,row1, rot3_0,rot3_1); \ + __m128i sum17 = _mm_add_epi16(row1, row7); \ + __m128i sum35 = _mm_add_epi16(row3, row5); \ + dct_rot(y4o,y5o, sum17,sum35, rot1_0,rot1_1); \ + dct_wadd(x4, y0o, y4o); \ + dct_wadd(x5, y1o, y5o); \ + dct_wadd(x6, y2o, y5o); \ + dct_wadd(x7, y3o, y4o); \ + dct_bfly32o(row0,row7, x0,x7,bias,shift); \ + dct_bfly32o(row1,row6, x1,x6,bias,shift); \ + dct_bfly32o(row2,row5, x2,x5,bias,shift); \ + dct_bfly32o(row3,row4, x3,x4,bias,shift); \ + } + + __m128i rot0_0 = dct_const(stbi__f2f(0.5411961f), stbi__f2f(0.5411961f) + stbi__f2f(-1.847759065f)); + __m128i rot0_1 = dct_const(stbi__f2f(0.5411961f) + stbi__f2f( 0.765366865f), stbi__f2f(0.5411961f)); + __m128i rot1_0 = dct_const(stbi__f2f(1.175875602f) + stbi__f2f(-0.899976223f), stbi__f2f(1.175875602f)); + __m128i rot1_1 = dct_const(stbi__f2f(1.175875602f), stbi__f2f(1.175875602f) + stbi__f2f(-2.562915447f)); + __m128i rot2_0 = dct_const(stbi__f2f(-1.961570560f) + stbi__f2f( 0.298631336f), stbi__f2f(-1.961570560f)); + __m128i rot2_1 = dct_const(stbi__f2f(-1.961570560f), stbi__f2f(-1.961570560f) + stbi__f2f( 3.072711026f)); + __m128i rot3_0 = dct_const(stbi__f2f(-0.390180644f) + stbi__f2f( 2.053119869f), stbi__f2f(-0.390180644f)); + __m128i rot3_1 = dct_const(stbi__f2f(-0.390180644f), stbi__f2f(-0.390180644f) + stbi__f2f( 1.501321110f)); + + // rounding biases in column/row passes, see stbi__idct_block for explanation. + __m128i bias_0 = _mm_set1_epi32(512); + __m128i bias_1 = _mm_set1_epi32(65536 + (128<<17)); + + // load + row0 = _mm_load_si128((const __m128i *) (data + 0*8)); + row1 = _mm_load_si128((const __m128i *) (data + 1*8)); + row2 = _mm_load_si128((const __m128i *) (data + 2*8)); + row3 = _mm_load_si128((const __m128i *) (data + 3*8)); + row4 = _mm_load_si128((const __m128i *) (data + 4*8)); + row5 = _mm_load_si128((const __m128i *) (data + 5*8)); + row6 = _mm_load_si128((const __m128i *) (data + 6*8)); + row7 = _mm_load_si128((const __m128i *) (data + 7*8)); + + // column pass + dct_pass(bias_0, 10); + + { + // 16bit 8x8 transpose pass 1 + dct_interleave16(row0, row4); + dct_interleave16(row1, row5); + dct_interleave16(row2, row6); + dct_interleave16(row3, row7); + + // transpose pass 2 + dct_interleave16(row0, row2); + dct_interleave16(row1, row3); + dct_interleave16(row4, row6); + dct_interleave16(row5, row7); + + // transpose pass 3 + dct_interleave16(row0, row1); + dct_interleave16(row2, row3); + dct_interleave16(row4, row5); + dct_interleave16(row6, row7); + } + + // row pass + dct_pass(bias_1, 17); + + { + // pack + __m128i p0 = _mm_packus_epi16(row0, row1); // a0a1a2a3...a7b0b1b2b3...b7 + __m128i p1 = _mm_packus_epi16(row2, row3); + __m128i p2 = _mm_packus_epi16(row4, row5); + __m128i p3 = _mm_packus_epi16(row6, row7); + + // 8bit 8x8 transpose pass 1 + dct_interleave8(p0, p2); // a0e0a1e1... + dct_interleave8(p1, p3); // c0g0c1g1... + + // transpose pass 2 + dct_interleave8(p0, p1); // a0c0e0g0... + dct_interleave8(p2, p3); // b0d0f0h0... + + // transpose pass 3 + dct_interleave8(p0, p2); // a0b0c0d0... + dct_interleave8(p1, p3); // a4b4c4d4... + + // store + _mm_storel_epi64((__m128i *) out, p0); out += out_stride; + _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p0, 0x4e)); out += out_stride; + _mm_storel_epi64((__m128i *) out, p2); out += out_stride; + _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p2, 0x4e)); out += out_stride; + _mm_storel_epi64((__m128i *) out, p1); out += out_stride; + _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p1, 0x4e)); out += out_stride; + _mm_storel_epi64((__m128i *) out, p3); out += out_stride; + _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p3, 0x4e)); + } + +#undef dct_const +#undef dct_rot +#undef dct_widen +#undef dct_wadd +#undef dct_wsub +#undef dct_bfly32o +#undef dct_interleave8 +#undef dct_interleave16 +#undef dct_pass +} + +#endif // STBI_SSE2 + +#ifdef STBI_NEON + +// NEON integer IDCT. should produce bit-identical +// results to the generic C version. +static void stbi__idct_simd(stbi_uc *out, int out_stride, short data[64]) +{ + int16x8_t row0, row1, row2, row3, row4, row5, row6, row7; + + int16x4_t rot0_0 = vdup_n_s16(stbi__f2f(0.5411961f)); + int16x4_t rot0_1 = vdup_n_s16(stbi__f2f(-1.847759065f)); + int16x4_t rot0_2 = vdup_n_s16(stbi__f2f( 0.765366865f)); + int16x4_t rot1_0 = vdup_n_s16(stbi__f2f( 1.175875602f)); + int16x4_t rot1_1 = vdup_n_s16(stbi__f2f(-0.899976223f)); + int16x4_t rot1_2 = vdup_n_s16(stbi__f2f(-2.562915447f)); + int16x4_t rot2_0 = vdup_n_s16(stbi__f2f(-1.961570560f)); + int16x4_t rot2_1 = vdup_n_s16(stbi__f2f(-0.390180644f)); + int16x4_t rot3_0 = vdup_n_s16(stbi__f2f( 0.298631336f)); + int16x4_t rot3_1 = vdup_n_s16(stbi__f2f( 2.053119869f)); + int16x4_t rot3_2 = vdup_n_s16(stbi__f2f( 3.072711026f)); + int16x4_t rot3_3 = vdup_n_s16(stbi__f2f( 1.501321110f)); + +#define dct_long_mul(out, inq, coeff) \ + int32x4_t out##_l = vmull_s16(vget_low_s16(inq), coeff); \ + int32x4_t out##_h = vmull_s16(vget_high_s16(inq), coeff) + +#define dct_long_mac(out, acc, inq, coeff) \ + int32x4_t out##_l = vmlal_s16(acc##_l, vget_low_s16(inq), coeff); \ + int32x4_t out##_h = vmlal_s16(acc##_h, vget_high_s16(inq), coeff) + +#define dct_widen(out, inq) \ + int32x4_t out##_l = vshll_n_s16(vget_low_s16(inq), 12); \ + int32x4_t out##_h = vshll_n_s16(vget_high_s16(inq), 12) + +// wide add +#define dct_wadd(out, a, b) \ + int32x4_t out##_l = vaddq_s32(a##_l, b##_l); \ + int32x4_t out##_h = vaddq_s32(a##_h, b##_h) + +// wide sub +#define dct_wsub(out, a, b) \ + int32x4_t out##_l = vsubq_s32(a##_l, b##_l); \ + int32x4_t out##_h = vsubq_s32(a##_h, b##_h) + +// butterfly a/b, then shift using "shiftop" by "s" and pack +#define dct_bfly32o(out0,out1, a,b,shiftop,s) \ + { \ + dct_wadd(sum, a, b); \ + dct_wsub(dif, a, b); \ + out0 = vcombine_s16(shiftop(sum_l, s), shiftop(sum_h, s)); \ + out1 = vcombine_s16(shiftop(dif_l, s), shiftop(dif_h, s)); \ + } + +#define dct_pass(shiftop, shift) \ + { \ + /* even part */ \ + int16x8_t sum26 = vaddq_s16(row2, row6); \ + dct_long_mul(p1e, sum26, rot0_0); \ + dct_long_mac(t2e, p1e, row6, rot0_1); \ + dct_long_mac(t3e, p1e, row2, rot0_2); \ + int16x8_t sum04 = vaddq_s16(row0, row4); \ + int16x8_t dif04 = vsubq_s16(row0, row4); \ + dct_widen(t0e, sum04); \ + dct_widen(t1e, dif04); \ + dct_wadd(x0, t0e, t3e); \ + dct_wsub(x3, t0e, t3e); \ + dct_wadd(x1, t1e, t2e); \ + dct_wsub(x2, t1e, t2e); \ + /* odd part */ \ + int16x8_t sum15 = vaddq_s16(row1, row5); \ + int16x8_t sum17 = vaddq_s16(row1, row7); \ + int16x8_t sum35 = vaddq_s16(row3, row5); \ + int16x8_t sum37 = vaddq_s16(row3, row7); \ + int16x8_t sumodd = vaddq_s16(sum17, sum35); \ + dct_long_mul(p5o, sumodd, rot1_0); \ + dct_long_mac(p1o, p5o, sum17, rot1_1); \ + dct_long_mac(p2o, p5o, sum35, rot1_2); \ + dct_long_mul(p3o, sum37, rot2_0); \ + dct_long_mul(p4o, sum15, rot2_1); \ + dct_wadd(sump13o, p1o, p3o); \ + dct_wadd(sump24o, p2o, p4o); \ + dct_wadd(sump23o, p2o, p3o); \ + dct_wadd(sump14o, p1o, p4o); \ + dct_long_mac(x4, sump13o, row7, rot3_0); \ + dct_long_mac(x5, sump24o, row5, rot3_1); \ + dct_long_mac(x6, sump23o, row3, rot3_2); \ + dct_long_mac(x7, sump14o, row1, rot3_3); \ + dct_bfly32o(row0,row7, x0,x7,shiftop,shift); \ + dct_bfly32o(row1,row6, x1,x6,shiftop,shift); \ + dct_bfly32o(row2,row5, x2,x5,shiftop,shift); \ + dct_bfly32o(row3,row4, x3,x4,shiftop,shift); \ + } + + // load + row0 = vld1q_s16(data + 0*8); + row1 = vld1q_s16(data + 1*8); + row2 = vld1q_s16(data + 2*8); + row3 = vld1q_s16(data + 3*8); + row4 = vld1q_s16(data + 4*8); + row5 = vld1q_s16(data + 5*8); + row6 = vld1q_s16(data + 6*8); + row7 = vld1q_s16(data + 7*8); + + // add DC bias + row0 = vaddq_s16(row0, vsetq_lane_s16(1024, vdupq_n_s16(0), 0)); + + // column pass + dct_pass(vrshrn_n_s32, 10); + + // 16bit 8x8 transpose + { +// these three map to a single VTRN.16, VTRN.32, and VSWP, respectively. +// whether compilers actually get this is another story, sadly. +#define dct_trn16(x, y) { int16x8x2_t t = vtrnq_s16(x, y); x = t.val[0]; y = t.val[1]; } +#define dct_trn32(x, y) { int32x4x2_t t = vtrnq_s32(vreinterpretq_s32_s16(x), vreinterpretq_s32_s16(y)); x = vreinterpretq_s16_s32(t.val[0]); y = vreinterpretq_s16_s32(t.val[1]); } +#define dct_trn64(x, y) { int16x8_t x0 = x; int16x8_t y0 = y; x = vcombine_s16(vget_low_s16(x0), vget_low_s16(y0)); y = vcombine_s16(vget_high_s16(x0), vget_high_s16(y0)); } + + // pass 1 + dct_trn16(row0, row1); // a0b0a2b2a4b4a6b6 + dct_trn16(row2, row3); + dct_trn16(row4, row5); + dct_trn16(row6, row7); + + // pass 2 + dct_trn32(row0, row2); // a0b0c0d0a4b4c4d4 + dct_trn32(row1, row3); + dct_trn32(row4, row6); + dct_trn32(row5, row7); + + // pass 3 + dct_trn64(row0, row4); // a0b0c0d0e0f0g0h0 + dct_trn64(row1, row5); + dct_trn64(row2, row6); + dct_trn64(row3, row7); + +#undef dct_trn16 +#undef dct_trn32 +#undef dct_trn64 + } + + // row pass + // vrshrn_n_s32 only supports shifts up to 16, we need + // 17. so do a non-rounding shift of 16 first then follow + // up with a rounding shift by 1. + dct_pass(vshrn_n_s32, 16); + + { + // pack and round + uint8x8_t p0 = vqrshrun_n_s16(row0, 1); + uint8x8_t p1 = vqrshrun_n_s16(row1, 1); + uint8x8_t p2 = vqrshrun_n_s16(row2, 1); + uint8x8_t p3 = vqrshrun_n_s16(row3, 1); + uint8x8_t p4 = vqrshrun_n_s16(row4, 1); + uint8x8_t p5 = vqrshrun_n_s16(row5, 1); + uint8x8_t p6 = vqrshrun_n_s16(row6, 1); + uint8x8_t p7 = vqrshrun_n_s16(row7, 1); + + // again, these can translate into one instruction, but often don't. +#define dct_trn8_8(x, y) { uint8x8x2_t t = vtrn_u8(x, y); x = t.val[0]; y = t.val[1]; } +#define dct_trn8_16(x, y) { uint16x4x2_t t = vtrn_u16(vreinterpret_u16_u8(x), vreinterpret_u16_u8(y)); x = vreinterpret_u8_u16(t.val[0]); y = vreinterpret_u8_u16(t.val[1]); } +#define dct_trn8_32(x, y) { uint32x2x2_t t = vtrn_u32(vreinterpret_u32_u8(x), vreinterpret_u32_u8(y)); x = vreinterpret_u8_u32(t.val[0]); y = vreinterpret_u8_u32(t.val[1]); } + + // sadly can't use interleaved stores here since we only write + // 8 bytes to each scan line! + + // 8x8 8-bit transpose pass 1 + dct_trn8_8(p0, p1); + dct_trn8_8(p2, p3); + dct_trn8_8(p4, p5); + dct_trn8_8(p6, p7); + + // pass 2 + dct_trn8_16(p0, p2); + dct_trn8_16(p1, p3); + dct_trn8_16(p4, p6); + dct_trn8_16(p5, p7); + + // pass 3 + dct_trn8_32(p0, p4); + dct_trn8_32(p1, p5); + dct_trn8_32(p2, p6); + dct_trn8_32(p3, p7); + + // store + vst1_u8(out, p0); out += out_stride; + vst1_u8(out, p1); out += out_stride; + vst1_u8(out, p2); out += out_stride; + vst1_u8(out, p3); out += out_stride; + vst1_u8(out, p4); out += out_stride; + vst1_u8(out, p5); out += out_stride; + vst1_u8(out, p6); out += out_stride; + vst1_u8(out, p7); + +#undef dct_trn8_8 +#undef dct_trn8_16 +#undef dct_trn8_32 + } + +#undef dct_long_mul +#undef dct_long_mac +#undef dct_widen +#undef dct_wadd +#undef dct_wsub +#undef dct_bfly32o +#undef dct_pass +} + +#endif // STBI_NEON + +#define STBI__MARKER_none 0xff +// if there's a pending marker from the entropy stream, return that +// otherwise, fetch from the stream and get a marker. if there's no +// marker, return 0xff, which is never a valid marker value +static stbi_uc stbi__get_marker(stbi__jpeg *j) +{ + stbi_uc x; + if (j->marker != STBI__MARKER_none) { x = j->marker; j->marker = STBI__MARKER_none; return x; } + x = stbi__get8(j->s); + if (x != 0xff) return STBI__MARKER_none; + while (x == 0xff) + x = stbi__get8(j->s); + return x; +} + +// in each scan, we'll have scan_n components, and the order +// of the components is specified by order[] +#define STBI__RESTART(x) ((x) >= 0xd0 && (x) <= 0xd7) + +// after a restart interval, stbi__jpeg_reset the entropy decoder and +// the dc prediction +static void stbi__jpeg_reset(stbi__jpeg *j) +{ + j->code_bits = 0; + j->code_buffer = 0; + j->nomore = 0; + j->img_comp[0].dc_pred = j->img_comp[1].dc_pred = j->img_comp[2].dc_pred = 0; + j->marker = STBI__MARKER_none; + j->todo = j->restart_interval ? j->restart_interval : 0x7fffffff; + j->eob_run = 0; + // no more than 1<<31 MCUs if no restart_interal? that's plenty safe, + // since we don't even allow 1<<30 pixels +} + +static int stbi__parse_entropy_coded_data(stbi__jpeg *z) +{ + stbi__jpeg_reset(z); + if (!z->progressive) { + if (z->scan_n == 1) { + int i,j; + STBI_SIMD_ALIGN(short, data[64]); + int n = z->order[0]; + // non-interleaved data, we just need to process one block at a time, + // in trivial scanline order + // number of blocks to do just depends on how many actual "pixels" this + // component has, independent of interleaved MCU blocking and such + int w = (z->img_comp[n].x+7) >> 3; + int h = (z->img_comp[n].y+7) >> 3; + for (j=0; j < h; ++j) { + for (i=0; i < w; ++i) { + int ha = z->img_comp[n].ha; + if (!stbi__jpeg_decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+ha, z->fast_ac[ha], n, z->dequant[z->img_comp[n].tq])) return 0; + z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data); + // every data block is an MCU, so countdown the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); + // if it's NOT a restart, then just bail, so we get corrupt data + // rather than no data + if (!STBI__RESTART(z->marker)) return 1; + stbi__jpeg_reset(z); + } + } + } + return 1; + } else { // interleaved + int i,j,k,x,y; + STBI_SIMD_ALIGN(short, data[64]); + for (j=0; j < z->img_mcu_y; ++j) { + for (i=0; i < z->img_mcu_x; ++i) { + // scan an interleaved mcu... process scan_n components in order + for (k=0; k < z->scan_n; ++k) { + int n = z->order[k]; + // scan out an mcu's worth of this component; that's just determined + // by the basic H and V specified for the component + for (y=0; y < z->img_comp[n].v; ++y) { + for (x=0; x < z->img_comp[n].h; ++x) { + int x2 = (i*z->img_comp[n].h + x)*8; + int y2 = (j*z->img_comp[n].v + y)*8; + int ha = z->img_comp[n].ha; + if (!stbi__jpeg_decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+ha, z->fast_ac[ha], n, z->dequant[z->img_comp[n].tq])) return 0; + z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*y2+x2, z->img_comp[n].w2, data); + } + } + } + // after all interleaved components, that's an interleaved MCU, + // so now count down the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); + if (!STBI__RESTART(z->marker)) return 1; + stbi__jpeg_reset(z); + } + } + } + return 1; + } + } else { + if (z->scan_n == 1) { + int i,j; + int n = z->order[0]; + // non-interleaved data, we just need to process one block at a time, + // in trivial scanline order + // number of blocks to do just depends on how many actual "pixels" this + // component has, independent of interleaved MCU blocking and such + int w = (z->img_comp[n].x+7) >> 3; + int h = (z->img_comp[n].y+7) >> 3; + for (j=0; j < h; ++j) { + for (i=0; i < w; ++i) { + short *data = z->img_comp[n].coeff + 64 * (i + j * z->img_comp[n].coeff_w); + if (z->spec_start == 0) { + if (!stbi__jpeg_decode_block_prog_dc(z, data, &z->huff_dc[z->img_comp[n].hd], n)) + return 0; + } else { + int ha = z->img_comp[n].ha; + if (!stbi__jpeg_decode_block_prog_ac(z, data, &z->huff_ac[ha], z->fast_ac[ha])) + return 0; + } + // every data block is an MCU, so countdown the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); + if (!STBI__RESTART(z->marker)) return 1; + stbi__jpeg_reset(z); + } + } + } + return 1; + } else { // interleaved + int i,j,k,x,y; + for (j=0; j < z->img_mcu_y; ++j) { + for (i=0; i < z->img_mcu_x; ++i) { + // scan an interleaved mcu... process scan_n components in order + for (k=0; k < z->scan_n; ++k) { + int n = z->order[k]; + // scan out an mcu's worth of this component; that's just determined + // by the basic H and V specified for the component + for (y=0; y < z->img_comp[n].v; ++y) { + for (x=0; x < z->img_comp[n].h; ++x) { + int x2 = (i*z->img_comp[n].h + x); + int y2 = (j*z->img_comp[n].v + y); + short *data = z->img_comp[n].coeff + 64 * (x2 + y2 * z->img_comp[n].coeff_w); + if (!stbi__jpeg_decode_block_prog_dc(z, data, &z->huff_dc[z->img_comp[n].hd], n)) + return 0; + } + } + } + // after all interleaved components, that's an interleaved MCU, + // so now count down the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); + if (!STBI__RESTART(z->marker)) return 1; + stbi__jpeg_reset(z); + } + } + } + return 1; + } + } +} + +static void stbi__jpeg_dequantize(short *data, stbi_uc *dequant) +{ + int i; + for (i=0; i < 64; ++i) + data[i] *= dequant[i]; +} + +static void stbi__jpeg_finish(stbi__jpeg *z) +{ + if (z->progressive) { + // dequantize and idct the data + int i,j,n; + for (n=0; n < z->s->img_n; ++n) { + int w = (z->img_comp[n].x+7) >> 3; + int h = (z->img_comp[n].y+7) >> 3; + for (j=0; j < h; ++j) { + for (i=0; i < w; ++i) { + short *data = z->img_comp[n].coeff + 64 * (i + j * z->img_comp[n].coeff_w); + stbi__jpeg_dequantize(data, z->dequant[z->img_comp[n].tq]); + z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data); + } + } + } + } +} + +static int stbi__process_marker(stbi__jpeg *z, int m) +{ + int L; + switch (m) { + case STBI__MARKER_none: // no marker found + return stbi__err("expected marker","Corrupt JPEG"); + + case 0xDD: // DRI - specify restart interval + if (stbi__get16be(z->s) != 4) return stbi__err("bad DRI len","Corrupt JPEG"); + z->restart_interval = stbi__get16be(z->s); + return 1; + + case 0xDB: // DQT - define quantization table + L = stbi__get16be(z->s)-2; + while (L > 0) { + int q = stbi__get8(z->s); + int p = q >> 4; + int t = q & 15,i; + if (p != 0) return stbi__err("bad DQT type","Corrupt JPEG"); + if (t > 3) return stbi__err("bad DQT table","Corrupt JPEG"); + for (i=0; i < 64; ++i) + z->dequant[t][stbi__jpeg_dezigzag[i]] = stbi__get8(z->s); + L -= 65; + } + return L==0; + + case 0xC4: // DHT - define huffman table + L = stbi__get16be(z->s)-2; + while (L > 0) { + stbi_uc *v; + int sizes[16],i,n=0; + int q = stbi__get8(z->s); + int tc = q >> 4; + int th = q & 15; + if (tc > 1 || th > 3) return stbi__err("bad DHT header","Corrupt JPEG"); + for (i=0; i < 16; ++i) { + sizes[i] = stbi__get8(z->s); + n += sizes[i]; + } + L -= 17; + if (tc == 0) { + if (!stbi__build_huffman(z->huff_dc+th, sizes)) return 0; + v = z->huff_dc[th].values; + } else { + if (!stbi__build_huffman(z->huff_ac+th, sizes)) return 0; + v = z->huff_ac[th].values; + } + for (i=0; i < n; ++i) + v[i] = stbi__get8(z->s); + if (tc != 0) + stbi__build_fast_ac(z->fast_ac[th], z->huff_ac + th); + L -= n; + } + return L==0; + } + // check for comment block or APP blocks + if ((m >= 0xE0 && m <= 0xEF) || m == 0xFE) { + stbi__skip(z->s, stbi__get16be(z->s)-2); + return 1; + } + return 0; +} + +// after we see SOS +static int stbi__process_scan_header(stbi__jpeg *z) +{ + int i; + int Ls = stbi__get16be(z->s); + z->scan_n = stbi__get8(z->s); + if (z->scan_n < 1 || z->scan_n > 4 || z->scan_n > (int) z->s->img_n) return stbi__err("bad SOS component count","Corrupt JPEG"); + if (Ls != 6+2*z->scan_n) return stbi__err("bad SOS len","Corrupt JPEG"); + for (i=0; i < z->scan_n; ++i) { + int id = stbi__get8(z->s), which; + int q = stbi__get8(z->s); + for (which = 0; which < z->s->img_n; ++which) + if (z->img_comp[which].id == id) + break; + if (which == z->s->img_n) return 0; // no match + z->img_comp[which].hd = q >> 4; if (z->img_comp[which].hd > 3) return stbi__err("bad DC huff","Corrupt JPEG"); + z->img_comp[which].ha = q & 15; if (z->img_comp[which].ha > 3) return stbi__err("bad AC huff","Corrupt JPEG"); + z->order[i] = which; + } + + { + int aa; + z->spec_start = stbi__get8(z->s); + z->spec_end = stbi__get8(z->s); // should be 63, but might be 0 + aa = stbi__get8(z->s); + z->succ_high = (aa >> 4); + z->succ_low = (aa & 15); + if (z->progressive) { + if (z->spec_start > 63 || z->spec_end > 63 || z->spec_start > z->spec_end || z->succ_high > 13 || z->succ_low > 13) + return stbi__err("bad SOS", "Corrupt JPEG"); + } else { + if (z->spec_start != 0) return stbi__err("bad SOS","Corrupt JPEG"); + if (z->succ_high != 0 || z->succ_low != 0) return stbi__err("bad SOS","Corrupt JPEG"); + z->spec_end = 63; + } + } + + return 1; +} + +static int stbi__process_frame_header(stbi__jpeg *z, int scan) +{ + stbi__context *s = z->s; + int Lf,p,i,q, h_max=1,v_max=1,c; + Lf = stbi__get16be(s); if (Lf < 11) return stbi__err("bad SOF len","Corrupt JPEG"); // JPEG + p = stbi__get8(s); if (p != 8) return stbi__err("only 8-bit","JPEG format not supported: 8-bit only"); // JPEG baseline + s->img_y = stbi__get16be(s); if (s->img_y == 0) return stbi__err("no header height", "JPEG format not supported: delayed height"); // Legal, but we don't handle it--but neither does IJG + s->img_x = stbi__get16be(s); if (s->img_x == 0) return stbi__err("0 width","Corrupt JPEG"); // JPEG requires + c = stbi__get8(s); + if (c != 3 && c != 1) return stbi__err("bad component count","Corrupt JPEG"); // JFIF requires + s->img_n = c; + for (i=0; i < c; ++i) { + z->img_comp[i].data = NULL; + z->img_comp[i].linebuf = NULL; + } + + if (Lf != 8+3*s->img_n) return stbi__err("bad SOF len","Corrupt JPEG"); + + z->rgb = 0; + for (i=0; i < s->img_n; ++i) { + static unsigned char rgb[3] = { 'R', 'G', 'B' }; + z->img_comp[i].id = stbi__get8(s); + if (z->img_comp[i].id != i+1) // JFIF requires + if (z->img_comp[i].id != i) { // some version of jpegtran outputs non-JFIF-compliant files! + // somethings output this (see http://fileformats.archiveteam.org/wiki/JPEG#Color_format) + if (z->img_comp[i].id != rgb[i]) + return stbi__err("bad component ID","Corrupt JPEG"); + ++z->rgb; + } + q = stbi__get8(s); + z->img_comp[i].h = (q >> 4); if (!z->img_comp[i].h || z->img_comp[i].h > 4) return stbi__err("bad H","Corrupt JPEG"); + z->img_comp[i].v = q & 15; if (!z->img_comp[i].v || z->img_comp[i].v > 4) return stbi__err("bad V","Corrupt JPEG"); + z->img_comp[i].tq = stbi__get8(s); if (z->img_comp[i].tq > 3) return stbi__err("bad TQ","Corrupt JPEG"); + } + + if (scan != STBI__SCAN_load) return 1; + + if ((1 << 30) / s->img_x / s->img_n < s->img_y) return stbi__err("too large", "Image too large to decode"); + + for (i=0; i < s->img_n; ++i) { + if (z->img_comp[i].h > h_max) h_max = z->img_comp[i].h; + if (z->img_comp[i].v > v_max) v_max = z->img_comp[i].v; + } + + // compute interleaved mcu info + z->img_h_max = h_max; + z->img_v_max = v_max; + z->img_mcu_w = h_max * 8; + z->img_mcu_h = v_max * 8; + z->img_mcu_x = (s->img_x + z->img_mcu_w-1) / z->img_mcu_w; + z->img_mcu_y = (s->img_y + z->img_mcu_h-1) / z->img_mcu_h; + + for (i=0; i < s->img_n; ++i) { + // number of effective pixels (e.g. for non-interleaved MCU) + z->img_comp[i].x = (s->img_x * z->img_comp[i].h + h_max-1) / h_max; + z->img_comp[i].y = (s->img_y * z->img_comp[i].v + v_max-1) / v_max; + // to simplify generation, we'll allocate enough memory to decode + // the bogus oversized data from using interleaved MCUs and their + // big blocks (e.g. a 16x16 iMCU on an image of width 33); we won't + // discard the extra data until colorspace conversion + z->img_comp[i].w2 = z->img_mcu_x * z->img_comp[i].h * 8; + z->img_comp[i].h2 = z->img_mcu_y * z->img_comp[i].v * 8; + z->img_comp[i].raw_data = stbi__malloc(z->img_comp[i].w2 * z->img_comp[i].h2+15); + + if (z->img_comp[i].raw_data == NULL) { + for(--i; i >= 0; --i) { + STBI_FREE(z->img_comp[i].raw_data); + z->img_comp[i].raw_data = NULL; + } + return stbi__err("outofmem", "Out of memory"); + } + // align blocks for idct using mmx/sse + z->img_comp[i].data = (stbi_uc*) (((size_t) z->img_comp[i].raw_data + 15) & ~15); + z->img_comp[i].linebuf = NULL; + if (z->progressive) { + z->img_comp[i].coeff_w = (z->img_comp[i].w2 + 7) >> 3; + z->img_comp[i].coeff_h = (z->img_comp[i].h2 + 7) >> 3; + z->img_comp[i].raw_coeff = STBI_MALLOC(z->img_comp[i].coeff_w * z->img_comp[i].coeff_h * 64 * sizeof(short) + 15); + z->img_comp[i].coeff = (short*) (((size_t) z->img_comp[i].raw_coeff + 15) & ~15); + } else { + z->img_comp[i].coeff = 0; + z->img_comp[i].raw_coeff = 0; + } + } + + return 1; +} + +// use comparisons since in some cases we handle more than one case (e.g. SOF) +#define stbi__DNL(x) ((x) == 0xdc) +#define stbi__SOI(x) ((x) == 0xd8) +#define stbi__EOI(x) ((x) == 0xd9) +#define stbi__SOF(x) ((x) == 0xc0 || (x) == 0xc1 || (x) == 0xc2) +#define stbi__SOS(x) ((x) == 0xda) + +#define stbi__SOF_progressive(x) ((x) == 0xc2) + +static int stbi__decode_jpeg_header(stbi__jpeg *z, int scan) +{ + int m; + z->marker = STBI__MARKER_none; // initialize cached marker to empty + m = stbi__get_marker(z); + if (!stbi__SOI(m)) return stbi__err("no SOI","Corrupt JPEG"); + if (scan == STBI__SCAN_type) return 1; + m = stbi__get_marker(z); + while (!stbi__SOF(m)) { + if (!stbi__process_marker(z,m)) return 0; + m = stbi__get_marker(z); + while (m == STBI__MARKER_none) { + // some files have extra padding after their blocks, so ok, we'll scan + if (stbi__at_eof(z->s)) return stbi__err("no SOF", "Corrupt JPEG"); + m = stbi__get_marker(z); + } + } + z->progressive = stbi__SOF_progressive(m); + if (!stbi__process_frame_header(z, scan)) return 0; + return 1; +} + +// decode image to YCbCr format +static int stbi__decode_jpeg_image(stbi__jpeg *j) +{ + int m; + for (m = 0; m < 4; m++) { + j->img_comp[m].raw_data = NULL; + j->img_comp[m].raw_coeff = NULL; + } + j->restart_interval = 0; + if (!stbi__decode_jpeg_header(j, STBI__SCAN_load)) return 0; + m = stbi__get_marker(j); + while (!stbi__EOI(m)) { + if (stbi__SOS(m)) { + if (!stbi__process_scan_header(j)) return 0; + if (!stbi__parse_entropy_coded_data(j)) return 0; + if (j->marker == STBI__MARKER_none ) { + // handle 0s at the end of image data from IP Kamera 9060 + while (!stbi__at_eof(j->s)) { + int x = stbi__get8(j->s); + if (x == 255) { + j->marker = stbi__get8(j->s); + break; + } else if (x != 0) { + return stbi__err("junk before marker", "Corrupt JPEG"); + } + } + // if we reach eof without hitting a marker, stbi__get_marker() below will fail and we'll eventually return 0 + } + } else { + if (!stbi__process_marker(j, m)) return 0; + } + m = stbi__get_marker(j); + } + if (j->progressive) + stbi__jpeg_finish(j); + return 1; +} + +// static jfif-centered resampling (across block boundaries) + +typedef stbi_uc *(*resample_row_func)(stbi_uc *out, stbi_uc *in0, stbi_uc *in1, + int w, int hs); + +#define stbi__div4(x) ((stbi_uc) ((x) >> 2)) + +static stbi_uc *resample_row_1(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + STBI_NOTUSED(out); + STBI_NOTUSED(in_far); + STBI_NOTUSED(w); + STBI_NOTUSED(hs); + return in_near; +} + +static stbi_uc* stbi__resample_row_v_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // need to generate two samples vertically for every one in input + int i; + STBI_NOTUSED(hs); + for (i=0; i < w; ++i) + out[i] = stbi__div4(3*in_near[i] + in_far[i] + 2); + return out; +} + +static stbi_uc* stbi__resample_row_h_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // need to generate two samples horizontally for every one in input + int i; + stbi_uc *input = in_near; + + if (w == 1) { + // if only one sample, can't do any interpolation + out[0] = out[1] = input[0]; + return out; + } + + out[0] = input[0]; + out[1] = stbi__div4(input[0]*3 + input[1] + 2); + for (i=1; i < w-1; ++i) { + int n = 3*input[i]+2; + out[i*2+0] = stbi__div4(n+input[i-1]); + out[i*2+1] = stbi__div4(n+input[i+1]); + } + out[i*2+0] = stbi__div4(input[w-2]*3 + input[w-1] + 2); + out[i*2+1] = input[w-1]; + + STBI_NOTUSED(in_far); + STBI_NOTUSED(hs); + + return out; +} + +#define stbi__div16(x) ((stbi_uc) ((x) >> 4)) + +static stbi_uc *stbi__resample_row_hv_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // need to generate 2x2 samples for every one in input + int i,t0,t1; + if (w == 1) { + out[0] = out[1] = stbi__div4(3*in_near[0] + in_far[0] + 2); + return out; + } + + t1 = 3*in_near[0] + in_far[0]; + out[0] = stbi__div4(t1+2); + for (i=1; i < w; ++i) { + t0 = t1; + t1 = 3*in_near[i]+in_far[i]; + out[i*2-1] = stbi__div16(3*t0 + t1 + 8); + out[i*2 ] = stbi__div16(3*t1 + t0 + 8); + } + out[w*2-1] = stbi__div4(t1+2); + + STBI_NOTUSED(hs); + + return out; +} + +#if defined(STBI_SSE2) || defined(STBI_NEON) +static stbi_uc *stbi__resample_row_hv_2_simd(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // need to generate 2x2 samples for every one in input + int i=0,t0,t1; + + if (w == 1) { + out[0] = out[1] = stbi__div4(3*in_near[0] + in_far[0] + 2); + return out; + } + + t1 = 3*in_near[0] + in_far[0]; + // process groups of 8 pixels for as long as we can. + // note we can't handle the last pixel in a row in this loop + // because we need to handle the filter boundary conditions. + for (; i < ((w-1) & ~7); i += 8) { +#if defined(STBI_SSE2) + // load and perform the vertical filtering pass + // this uses 3*x + y = 4*x + (y - x) + __m128i zero = _mm_setzero_si128(); + __m128i farb = _mm_loadl_epi64((__m128i *) (in_far + i)); + __m128i nearb = _mm_loadl_epi64((__m128i *) (in_near + i)); + __m128i farw = _mm_unpacklo_epi8(farb, zero); + __m128i nearw = _mm_unpacklo_epi8(nearb, zero); + __m128i diff = _mm_sub_epi16(farw, nearw); + __m128i nears = _mm_slli_epi16(nearw, 2); + __m128i curr = _mm_add_epi16(nears, diff); // current row + + // horizontal filter works the same based on shifted vers of current + // row. "prev" is current row shifted right by 1 pixel; we need to + // insert the previous pixel value (from t1). + // "next" is current row shifted left by 1 pixel, with first pixel + // of next block of 8 pixels added in. + __m128i prv0 = _mm_slli_si128(curr, 2); + __m128i nxt0 = _mm_srli_si128(curr, 2); + __m128i prev = _mm_insert_epi16(prv0, t1, 0); + __m128i next = _mm_insert_epi16(nxt0, 3*in_near[i+8] + in_far[i+8], 7); + + // horizontal filter, polyphase implementation since it's convenient: + // even pixels = 3*cur + prev = cur*4 + (prev - cur) + // odd pixels = 3*cur + next = cur*4 + (next - cur) + // note the shared term. + __m128i bias = _mm_set1_epi16(8); + __m128i curs = _mm_slli_epi16(curr, 2); + __m128i prvd = _mm_sub_epi16(prev, curr); + __m128i nxtd = _mm_sub_epi16(next, curr); + __m128i curb = _mm_add_epi16(curs, bias); + __m128i even = _mm_add_epi16(prvd, curb); + __m128i odd = _mm_add_epi16(nxtd, curb); + + // interleave even and odd pixels, then undo scaling. + __m128i int0 = _mm_unpacklo_epi16(even, odd); + __m128i int1 = _mm_unpackhi_epi16(even, odd); + __m128i de0 = _mm_srli_epi16(int0, 4); + __m128i de1 = _mm_srli_epi16(int1, 4); + + // pack and write output + __m128i outv = _mm_packus_epi16(de0, de1); + _mm_storeu_si128((__m128i *) (out + i*2), outv); +#elif defined(STBI_NEON) + // load and perform the vertical filtering pass + // this uses 3*x + y = 4*x + (y - x) + uint8x8_t farb = vld1_u8(in_far + i); + uint8x8_t nearb = vld1_u8(in_near + i); + int16x8_t diff = vreinterpretq_s16_u16(vsubl_u8(farb, nearb)); + int16x8_t nears = vreinterpretq_s16_u16(vshll_n_u8(nearb, 2)); + int16x8_t curr = vaddq_s16(nears, diff); // current row + + // horizontal filter works the same based on shifted vers of current + // row. "prev" is current row shifted right by 1 pixel; we need to + // insert the previous pixel value (from t1). + // "next" is current row shifted left by 1 pixel, with first pixel + // of next block of 8 pixels added in. + int16x8_t prv0 = vextq_s16(curr, curr, 7); + int16x8_t nxt0 = vextq_s16(curr, curr, 1); + int16x8_t prev = vsetq_lane_s16(t1, prv0, 0); + int16x8_t next = vsetq_lane_s16(3*in_near[i+8] + in_far[i+8], nxt0, 7); + + // horizontal filter, polyphase implementation since it's convenient: + // even pixels = 3*cur + prev = cur*4 + (prev - cur) + // odd pixels = 3*cur + next = cur*4 + (next - cur) + // note the shared term. + int16x8_t curs = vshlq_n_s16(curr, 2); + int16x8_t prvd = vsubq_s16(prev, curr); + int16x8_t nxtd = vsubq_s16(next, curr); + int16x8_t even = vaddq_s16(curs, prvd); + int16x8_t odd = vaddq_s16(curs, nxtd); + + // undo scaling and round, then store with even/odd phases interleaved + uint8x8x2_t o; + o.val[0] = vqrshrun_n_s16(even, 4); + o.val[1] = vqrshrun_n_s16(odd, 4); + vst2_u8(out + i*2, o); +#endif + + // "previous" value for next iter + t1 = 3*in_near[i+7] + in_far[i+7]; + } + + t0 = t1; + t1 = 3*in_near[i] + in_far[i]; + out[i*2] = stbi__div16(3*t1 + t0 + 8); + + for (++i; i < w; ++i) { + t0 = t1; + t1 = 3*in_near[i]+in_far[i]; + out[i*2-1] = stbi__div16(3*t0 + t1 + 8); + out[i*2 ] = stbi__div16(3*t1 + t0 + 8); + } + out[w*2-1] = stbi__div4(t1+2); + + STBI_NOTUSED(hs); + + return out; +} +#endif + +static stbi_uc *stbi__resample_row_generic(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // resample with nearest-neighbor + int i,j; + STBI_NOTUSED(in_far); + for (i=0; i < w; ++i) + for (j=0; j < hs; ++j) + out[i*hs+j] = in_near[i]; + return out; +} + +#ifdef STBI_JPEG_OLD +// this is the same YCbCr-to-RGB calculation that stb_image has used +// historically before the algorithm changes in 1.49 +#define float2fixed(x) ((int) ((x) * 65536 + 0.5)) +static void stbi__YCbCr_to_RGB_row(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step) +{ + int i; + for (i=0; i < count; ++i) { + int y_fixed = (y[i] << 16) + 32768; // rounding + int r,g,b; + int cr = pcr[i] - 128; + int cb = pcb[i] - 128; + r = y_fixed + cr*float2fixed(1.40200f); + g = y_fixed - cr*float2fixed(0.71414f) - cb*float2fixed(0.34414f); + b = y_fixed + cb*float2fixed(1.77200f); + r >>= 16; + g >>= 16; + b >>= 16; + if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; } + if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; } + if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; } + out[0] = (stbi_uc)r; + out[1] = (stbi_uc)g; + out[2] = (stbi_uc)b; + out[3] = 255; + out += step; + } +} +#else +// this is a reduced-precision calculation of YCbCr-to-RGB introduced +// to make sure the code produces the same results in both SIMD and scalar +#define float2fixed(x) (((int) ((x) * 4096.0f + 0.5f)) << 8) +static void stbi__YCbCr_to_RGB_row(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step) +{ + int i; + for (i=0; i < count; ++i) { + int y_fixed = (y[i] << 20) + (1<<19); // rounding + int r,g,b; + int cr = pcr[i] - 128; + int cb = pcb[i] - 128; + r = y_fixed + cr* float2fixed(1.40200f); + g = y_fixed + (cr*-float2fixed(0.71414f)) + ((cb*-float2fixed(0.34414f)) & 0xffff0000); + b = y_fixed + cb* float2fixed(1.77200f); + r >>= 20; + g >>= 20; + b >>= 20; + if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; } + if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; } + if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; } + out[0] = (stbi_uc)r; + out[1] = (stbi_uc)g; + out[2] = (stbi_uc)b; + out[3] = 255; + out += step; + } +} +#endif + +#if defined(STBI_SSE2) || defined(STBI_NEON) +static void stbi__YCbCr_to_RGB_simd(stbi_uc *out, stbi_uc const *y, stbi_uc const *pcb, stbi_uc const *pcr, int count, int step) +{ + int i = 0; + +#ifdef STBI_SSE2 + // step == 3 is pretty ugly on the final interleave, and i'm not convinced + // it's useful in practice (you wouldn't use it for textures, for example). + // so just accelerate step == 4 case. + if (step == 4) { + // this is a fairly straightforward implementation and not super-optimized. + __m128i signflip = _mm_set1_epi8(-0x80); + __m128i cr_const0 = _mm_set1_epi16( (short) ( 1.40200f*4096.0f+0.5f)); + __m128i cr_const1 = _mm_set1_epi16( - (short) ( 0.71414f*4096.0f+0.5f)); + __m128i cb_const0 = _mm_set1_epi16( - (short) ( 0.34414f*4096.0f+0.5f)); + __m128i cb_const1 = _mm_set1_epi16( (short) ( 1.77200f*4096.0f+0.5f)); + __m128i y_bias = _mm_set1_epi8((char) (unsigned char) 128); + __m128i xw = _mm_set1_epi16(255); // alpha channel + + for (; i+7 < count; i += 8) { + // load + __m128i y_bytes = _mm_loadl_epi64((__m128i *) (y+i)); + __m128i cr_bytes = _mm_loadl_epi64((__m128i *) (pcr+i)); + __m128i cb_bytes = _mm_loadl_epi64((__m128i *) (pcb+i)); + __m128i cr_biased = _mm_xor_si128(cr_bytes, signflip); // -128 + __m128i cb_biased = _mm_xor_si128(cb_bytes, signflip); // -128 + + // unpack to short (and left-shift cr, cb by 8) + __m128i yw = _mm_unpacklo_epi8(y_bias, y_bytes); + __m128i crw = _mm_unpacklo_epi8(_mm_setzero_si128(), cr_biased); + __m128i cbw = _mm_unpacklo_epi8(_mm_setzero_si128(), cb_biased); + + // color transform + __m128i yws = _mm_srli_epi16(yw, 4); + __m128i cr0 = _mm_mulhi_epi16(cr_const0, crw); + __m128i cb0 = _mm_mulhi_epi16(cb_const0, cbw); + __m128i cb1 = _mm_mulhi_epi16(cbw, cb_const1); + __m128i cr1 = _mm_mulhi_epi16(crw, cr_const1); + __m128i rws = _mm_add_epi16(cr0, yws); + __m128i gwt = _mm_add_epi16(cb0, yws); + __m128i bws = _mm_add_epi16(yws, cb1); + __m128i gws = _mm_add_epi16(gwt, cr1); + + // descale + __m128i rw = _mm_srai_epi16(rws, 4); + __m128i bw = _mm_srai_epi16(bws, 4); + __m128i gw = _mm_srai_epi16(gws, 4); + + // back to byte, set up for transpose + __m128i brb = _mm_packus_epi16(rw, bw); + __m128i gxb = _mm_packus_epi16(gw, xw); + + // transpose to interleave channels + __m128i t0 = _mm_unpacklo_epi8(brb, gxb); + __m128i t1 = _mm_unpackhi_epi8(brb, gxb); + __m128i o0 = _mm_unpacklo_epi16(t0, t1); + __m128i o1 = _mm_unpackhi_epi16(t0, t1); + + // store + _mm_storeu_si128((__m128i *) (out + 0), o0); + _mm_storeu_si128((__m128i *) (out + 16), o1); + out += 32; + } + } +#endif + +#ifdef STBI_NEON + // in this version, step=3 support would be easy to add. but is there demand? + if (step == 4) { + // this is a fairly straightforward implementation and not super-optimized. + uint8x8_t signflip = vdup_n_u8(0x80); + int16x8_t cr_const0 = vdupq_n_s16( (short) ( 1.40200f*4096.0f+0.5f)); + int16x8_t cr_const1 = vdupq_n_s16( - (short) ( 0.71414f*4096.0f+0.5f)); + int16x8_t cb_const0 = vdupq_n_s16( - (short) ( 0.34414f*4096.0f+0.5f)); + int16x8_t cb_const1 = vdupq_n_s16( (short) ( 1.77200f*4096.0f+0.5f)); + + for (; i+7 < count; i += 8) { + // load + uint8x8_t y_bytes = vld1_u8(y + i); + uint8x8_t cr_bytes = vld1_u8(pcr + i); + uint8x8_t cb_bytes = vld1_u8(pcb + i); + int8x8_t cr_biased = vreinterpret_s8_u8(vsub_u8(cr_bytes, signflip)); + int8x8_t cb_biased = vreinterpret_s8_u8(vsub_u8(cb_bytes, signflip)); + + // expand to s16 + int16x8_t yws = vreinterpretq_s16_u16(vshll_n_u8(y_bytes, 4)); + int16x8_t crw = vshll_n_s8(cr_biased, 7); + int16x8_t cbw = vshll_n_s8(cb_biased, 7); + + // color transform + int16x8_t cr0 = vqdmulhq_s16(crw, cr_const0); + int16x8_t cb0 = vqdmulhq_s16(cbw, cb_const0); + int16x8_t cr1 = vqdmulhq_s16(crw, cr_const1); + int16x8_t cb1 = vqdmulhq_s16(cbw, cb_const1); + int16x8_t rws = vaddq_s16(yws, cr0); + int16x8_t gws = vaddq_s16(vaddq_s16(yws, cb0), cr1); + int16x8_t bws = vaddq_s16(yws, cb1); + + // undo scaling, round, convert to byte + uint8x8x4_t o; + o.val[0] = vqrshrun_n_s16(rws, 4); + o.val[1] = vqrshrun_n_s16(gws, 4); + o.val[2] = vqrshrun_n_s16(bws, 4); + o.val[3] = vdup_n_u8(255); + + // store, interleaving r/g/b/a + vst4_u8(out, o); + out += 8*4; + } + } +#endif + + for (; i < count; ++i) { + int y_fixed = (y[i] << 20) + (1<<19); // rounding + int r,g,b; + int cr = pcr[i] - 128; + int cb = pcb[i] - 128; + r = y_fixed + cr* float2fixed(1.40200f); + g = y_fixed + cr*-float2fixed(0.71414f) + ((cb*-float2fixed(0.34414f)) & 0xffff0000); + b = y_fixed + cb* float2fixed(1.77200f); + r >>= 20; + g >>= 20; + b >>= 20; + if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; } + if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; } + if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; } + out[0] = (stbi_uc)r; + out[1] = (stbi_uc)g; + out[2] = (stbi_uc)b; + out[3] = 255; + out += step; + } +} +#endif + +// set up the kernels +static void stbi__setup_jpeg(stbi__jpeg *j) +{ + j->idct_block_kernel = stbi__idct_block; + j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_row; + j->resample_row_hv_2_kernel = stbi__resample_row_hv_2; + +#ifdef STBI_SSE2 + if (stbi__sse2_available()) { + j->idct_block_kernel = stbi__idct_simd; + #ifndef STBI_JPEG_OLD + j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_simd; + #endif + j->resample_row_hv_2_kernel = stbi__resample_row_hv_2_simd; + } +#endif + +#ifdef STBI_NEON + j->idct_block_kernel = stbi__idct_simd; + #ifndef STBI_JPEG_OLD + j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_simd; + #endif + j->resample_row_hv_2_kernel = stbi__resample_row_hv_2_simd; +#endif +} + +// clean up the temporary component buffers +static void stbi__cleanup_jpeg(stbi__jpeg *j) +{ + int i; + for (i=0; i < j->s->img_n; ++i) { + if (j->img_comp[i].raw_data) { + STBI_FREE(j->img_comp[i].raw_data); + j->img_comp[i].raw_data = NULL; + j->img_comp[i].data = NULL; + } + if (j->img_comp[i].raw_coeff) { + STBI_FREE(j->img_comp[i].raw_coeff); + j->img_comp[i].raw_coeff = 0; + j->img_comp[i].coeff = 0; + } + if (j->img_comp[i].linebuf) { + STBI_FREE(j->img_comp[i].linebuf); + j->img_comp[i].linebuf = NULL; + } + } +} + +typedef struct +{ + resample_row_func resample; + stbi_uc *line0,*line1; + int hs,vs; // expansion factor in each axis + int w_lores; // horizontal pixels pre-expansion + int ystep; // how far through vertical expansion we are + int ypos; // which pre-expansion row we're on +} stbi__resample; + +static stbi_uc *load_jpeg_image(stbi__jpeg *z, int *out_x, int *out_y, int *comp, int req_comp) +{ + int n, decode_n; + z->s->img_n = 0; // make stbi__cleanup_jpeg safe + + // validate req_comp + if (req_comp < 0 || req_comp > 4) return stbi__errpuc("bad req_comp", "Internal error"); + + // load a jpeg image from whichever source, but leave in YCbCr format + if (!stbi__decode_jpeg_image(z)) { stbi__cleanup_jpeg(z); return NULL; } + + // determine actual number of components to generate + n = req_comp ? req_comp : z->s->img_n; + + if (z->s->img_n == 3 && n < 3) + decode_n = 1; + else + decode_n = z->s->img_n; + + // resample and color-convert + { + int k; + unsigned int i,j; + stbi_uc *output; + stbi_uc *coutput[4]; + + stbi__resample res_comp[4]; + + for (k=0; k < decode_n; ++k) { + stbi__resample *r = &res_comp[k]; + + // allocate line buffer big enough for upsampling off the edges + // with upsample factor of 4 + z->img_comp[k].linebuf = (stbi_uc *) stbi__malloc(z->s->img_x + 3); + if (!z->img_comp[k].linebuf) { stbi__cleanup_jpeg(z); return stbi__errpuc("outofmem", "Out of memory"); } + + r->hs = z->img_h_max / z->img_comp[k].h; + r->vs = z->img_v_max / z->img_comp[k].v; + r->ystep = r->vs >> 1; + r->w_lores = (z->s->img_x + r->hs-1) / r->hs; + r->ypos = 0; + r->line0 = r->line1 = z->img_comp[k].data; + + if (r->hs == 1 && r->vs == 1) r->resample = resample_row_1; + else if (r->hs == 1 && r->vs == 2) r->resample = stbi__resample_row_v_2; + else if (r->hs == 2 && r->vs == 1) r->resample = stbi__resample_row_h_2; + else if (r->hs == 2 && r->vs == 2) r->resample = z->resample_row_hv_2_kernel; + else r->resample = stbi__resample_row_generic; + } + + // can't error after this so, this is safe + output = (stbi_uc *) stbi__malloc(n * z->s->img_x * z->s->img_y + 1); + if (!output) { stbi__cleanup_jpeg(z); return stbi__errpuc("outofmem", "Out of memory"); } + + // now go ahead and resample + for (j=0; j < z->s->img_y; ++j) { + stbi_uc *out = output + n * z->s->img_x * j; + for (k=0; k < decode_n; ++k) { + stbi__resample *r = &res_comp[k]; + int y_bot = r->ystep >= (r->vs >> 1); + coutput[k] = r->resample(z->img_comp[k].linebuf, + y_bot ? r->line1 : r->line0, + y_bot ? r->line0 : r->line1, + r->w_lores, r->hs); + if (++r->ystep >= r->vs) { + r->ystep = 0; + r->line0 = r->line1; + if (++r->ypos < z->img_comp[k].y) + r->line1 += z->img_comp[k].w2; + } + } + if (n >= 3) { + stbi_uc *y = coutput[0]; + if (z->s->img_n == 3) { + if (z->rgb == 3) { + for (i=0; i < z->s->img_x; ++i) { + out[0] = y[i]; + out[1] = coutput[1][i]; + out[2] = coutput[2][i]; + out[3] = 255; + out += n; + } + } else { + z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); + } + } else + for (i=0; i < z->s->img_x; ++i) { + out[0] = out[1] = out[2] = y[i]; + out[3] = 255; // not used if n==3 + out += n; + } + } else { + stbi_uc *y = coutput[0]; + if (n == 1) + for (i=0; i < z->s->img_x; ++i) out[i] = y[i]; + else + for (i=0; i < z->s->img_x; ++i) *out++ = y[i], *out++ = 255; + } + } + stbi__cleanup_jpeg(z); + *out_x = z->s->img_x; + *out_y = z->s->img_y; + if (comp) *comp = z->s->img_n; // report original components, not output + return output; + } +} + +static unsigned char *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + unsigned char* result; + stbi__jpeg* j = (stbi__jpeg*) stbi__malloc(sizeof(stbi__jpeg)); + j->s = s; + stbi__setup_jpeg(j); + result = load_jpeg_image(j, x,y,comp,req_comp); + STBI_FREE(j); + return result; +} + +static int stbi__jpeg_test(stbi__context *s) +{ + int r; + stbi__jpeg j; + j.s = s; + stbi__setup_jpeg(&j); + r = stbi__decode_jpeg_header(&j, STBI__SCAN_type); + stbi__rewind(s); + return r; +} + +static int stbi__jpeg_info_raw(stbi__jpeg *j, int *x, int *y, int *comp) +{ + if (!stbi__decode_jpeg_header(j, STBI__SCAN_header)) { + stbi__rewind( j->s ); + return 0; + } + if (x) *x = j->s->img_x; + if (y) *y = j->s->img_y; + if (comp) *comp = j->s->img_n; + return 1; +} + +static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp) +{ + int result; + stbi__jpeg* j = (stbi__jpeg*) (stbi__malloc(sizeof(stbi__jpeg))); + j->s = s; + result = stbi__jpeg_info_raw(j, x, y, comp); + STBI_FREE(j); + return result; +} +#endif + +// public domain zlib decode v0.2 Sean Barrett 2006-11-18 +// simple implementation +// - all input must be provided in an upfront buffer +// - all output is written to a single output buffer (can malloc/realloc) +// performance +// - fast huffman + +#ifndef STBI_NO_ZLIB + +// fast-way is faster to check than jpeg huffman, but slow way is slower +#define STBI__ZFAST_BITS 9 // accelerate all cases in default tables +#define STBI__ZFAST_MASK ((1 << STBI__ZFAST_BITS) - 1) + +// zlib-style huffman encoding +// (jpegs packs from left, zlib from right, so can't share code) +typedef struct +{ + stbi__uint16 fast[1 << STBI__ZFAST_BITS]; + stbi__uint16 firstcode[16]; + int maxcode[17]; + stbi__uint16 firstsymbol[16]; + stbi_uc size[288]; + stbi__uint16 value[288]; +} stbi__zhuffman; + +stbi_inline static int stbi__bitreverse16(int n) +{ + n = ((n & 0xAAAA) >> 1) | ((n & 0x5555) << 1); + n = ((n & 0xCCCC) >> 2) | ((n & 0x3333) << 2); + n = ((n & 0xF0F0) >> 4) | ((n & 0x0F0F) << 4); + n = ((n & 0xFF00) >> 8) | ((n & 0x00FF) << 8); + return n; +} + +stbi_inline static int stbi__bit_reverse(int v, int bits) +{ + STBI_ASSERT(bits <= 16); + // to bit reverse n bits, reverse 16 and shift + // e.g. 11 bits, bit reverse and shift away 5 + return stbi__bitreverse16(v) >> (16-bits); +} + +static int stbi__zbuild_huffman(stbi__zhuffman *z, stbi_uc *sizelist, int num) +{ + int i,k=0; + int code, next_code[16], sizes[17]; + + // DEFLATE spec for generating codes + memset(sizes, 0, sizeof(sizes)); + memset(z->fast, 0, sizeof(z->fast)); + for (i=0; i < num; ++i) + ++sizes[sizelist[i]]; + sizes[0] = 0; + for (i=1; i < 16; ++i) + if (sizes[i] > (1 << i)) + return stbi__err("bad sizes", "Corrupt PNG"); + code = 0; + for (i=1; i < 16; ++i) { + next_code[i] = code; + z->firstcode[i] = (stbi__uint16) code; + z->firstsymbol[i] = (stbi__uint16) k; + code = (code + sizes[i]); + if (sizes[i]) + if (code-1 >= (1 << i)) return stbi__err("bad codelengths","Corrupt PNG"); + z->maxcode[i] = code << (16-i); // preshift for inner loop + code <<= 1; + k += sizes[i]; + } + z->maxcode[16] = 0x10000; // sentinel + for (i=0; i < num; ++i) { + int s = sizelist[i]; + if (s) { + int c = next_code[s] - z->firstcode[s] + z->firstsymbol[s]; + stbi__uint16 fastv = (stbi__uint16) ((s << 9) | i); + z->size [c] = (stbi_uc ) s; + z->value[c] = (stbi__uint16) i; + if (s <= STBI__ZFAST_BITS) { + int j = stbi__bit_reverse(next_code[s],s); + while (j < (1 << STBI__ZFAST_BITS)) { + z->fast[j] = fastv; + j += (1 << s); + } + } + ++next_code[s]; + } + } + return 1; +} + +// zlib-from-memory implementation for PNG reading +// because PNG allows splitting the zlib stream arbitrarily, +// and it's annoying structurally to have PNG call ZLIB call PNG, +// we require PNG read all the IDATs and combine them into a single +// memory buffer + +typedef struct +{ + stbi_uc *zbuffer, *zbuffer_end; + int num_bits; + stbi__uint32 code_buffer; + + char *zout; + char *zout_start; + char *zout_end; + int z_expandable; + + stbi__zhuffman z_length, z_distance; +} stbi__zbuf; + +stbi_inline static stbi_uc stbi__zget8(stbi__zbuf *z) +{ + if (z->zbuffer >= z->zbuffer_end) return 0; + return *z->zbuffer++; +} + +static void stbi__fill_bits(stbi__zbuf *z) +{ + do { + STBI_ASSERT(z->code_buffer < (1U << z->num_bits)); + z->code_buffer |= (unsigned int) stbi__zget8(z) << z->num_bits; + z->num_bits += 8; + } while (z->num_bits <= 24); +} + +stbi_inline static unsigned int stbi__zreceive(stbi__zbuf *z, int n) +{ + unsigned int k; + if (z->num_bits < n) stbi__fill_bits(z); + k = z->code_buffer & ((1 << n) - 1); + z->code_buffer >>= n; + z->num_bits -= n; + return k; +} + +static int stbi__zhuffman_decode_slowpath(stbi__zbuf *a, stbi__zhuffman *z) +{ + int b,s,k; + // not resolved by fast table, so compute it the slow way + // use jpeg approach, which requires MSbits at top + k = stbi__bit_reverse(a->code_buffer, 16); + for (s=STBI__ZFAST_BITS+1; ; ++s) + if (k < z->maxcode[s]) + break; + if (s == 16) return -1; // invalid code! + // code size is s, so: + b = (k >> (16-s)) - z->firstcode[s] + z->firstsymbol[s]; + STBI_ASSERT(z->size[b] == s); + a->code_buffer >>= s; + a->num_bits -= s; + return z->value[b]; +} + +stbi_inline static int stbi__zhuffman_decode(stbi__zbuf *a, stbi__zhuffman *z) +{ + int b,s; + if (a->num_bits < 16) stbi__fill_bits(a); + b = z->fast[a->code_buffer & STBI__ZFAST_MASK]; + if (b) { + s = b >> 9; + a->code_buffer >>= s; + a->num_bits -= s; + return b & 511; + } + return stbi__zhuffman_decode_slowpath(a, z); +} + +static int stbi__zexpand(stbi__zbuf *z, char *zout, int n) // need to make room for n bytes +{ + char *q; + int cur, limit, old_limit; + z->zout = zout; + if (!z->z_expandable) return stbi__err("output buffer limit","Corrupt PNG"); + cur = (int) (z->zout - z->zout_start); + limit = old_limit = (int) (z->zout_end - z->zout_start); + while (cur + n > limit) + limit *= 2; + q = (char *) STBI_REALLOC_SIZED(z->zout_start, old_limit, limit); + STBI_NOTUSED(old_limit); + if (q == NULL) return stbi__err("outofmem", "Out of memory"); + z->zout_start = q; + z->zout = q + cur; + z->zout_end = q + limit; + return 1; +} + +static int stbi__zlength_base[31] = { + 3,4,5,6,7,8,9,10,11,13, + 15,17,19,23,27,31,35,43,51,59, + 67,83,99,115,131,163,195,227,258,0,0 }; + +static int stbi__zlength_extra[31]= +{ 0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0 }; + +static int stbi__zdist_base[32] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193, +257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577,0,0}; + +static int stbi__zdist_extra[32] = +{ 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13}; + +static int stbi__parse_huffman_block(stbi__zbuf *a) +{ + char *zout = a->zout; + for(;;) { + int z = stbi__zhuffman_decode(a, &a->z_length); + if (z < 256) { + if (z < 0) return stbi__err("bad huffman code","Corrupt PNG"); // error in huffman codes + if (zout >= a->zout_end) { + if (!stbi__zexpand(a, zout, 1)) return 0; + zout = a->zout; + } + *zout++ = (char) z; + } else { + stbi_uc *p; + int len,dist; + if (z == 256) { + a->zout = zout; + return 1; + } + z -= 257; + len = stbi__zlength_base[z]; + if (stbi__zlength_extra[z]) len += stbi__zreceive(a, stbi__zlength_extra[z]); + z = stbi__zhuffman_decode(a, &a->z_distance); + if (z < 0) return stbi__err("bad huffman code","Corrupt PNG"); + dist = stbi__zdist_base[z]; + if (stbi__zdist_extra[z]) dist += stbi__zreceive(a, stbi__zdist_extra[z]); + if (zout - a->zout_start < dist) return stbi__err("bad dist","Corrupt PNG"); + if (zout + len > a->zout_end) { + if (!stbi__zexpand(a, zout, len)) return 0; + zout = a->zout; + } + p = (stbi_uc *) (zout - dist); + if (dist == 1) { // run of one byte; common in images. + stbi_uc v = *p; + if (len) { do *zout++ = v; while (--len); } + } else { + if (len) { do *zout++ = *p++; while (--len); } + } + } + } +} + +static int stbi__compute_huffman_codes(stbi__zbuf *a) +{ + static stbi_uc length_dezigzag[19] = { 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15 }; + stbi__zhuffman z_codelength; + stbi_uc lencodes[286+32+137];//padding for maximum single op + stbi_uc codelength_sizes[19]; + int i,n; + + int hlit = stbi__zreceive(a,5) + 257; + int hdist = stbi__zreceive(a,5) + 1; + int hclen = stbi__zreceive(a,4) + 4; + + memset(codelength_sizes, 0, sizeof(codelength_sizes)); + for (i=0; i < hclen; ++i) { + int s = stbi__zreceive(a,3); + codelength_sizes[length_dezigzag[i]] = (stbi_uc) s; + } + if (!stbi__zbuild_huffman(&z_codelength, codelength_sizes, 19)) return 0; + + n = 0; + while (n < hlit + hdist) { + int c = stbi__zhuffman_decode(a, &z_codelength); + if (c < 0 || c >= 19) return stbi__err("bad codelengths", "Corrupt PNG"); + if (c < 16) + lencodes[n++] = (stbi_uc) c; + else if (c == 16) { + c = stbi__zreceive(a,2)+3; + memset(lencodes+n, lencodes[n-1], c); + n += c; + } else if (c == 17) { + c = stbi__zreceive(a,3)+3; + memset(lencodes+n, 0, c); + n += c; + } else { + STBI_ASSERT(c == 18); + c = stbi__zreceive(a,7)+11; + memset(lencodes+n, 0, c); + n += c; + } + } + if (n != hlit+hdist) return stbi__err("bad codelengths","Corrupt PNG"); + if (!stbi__zbuild_huffman(&a->z_length, lencodes, hlit)) return 0; + if (!stbi__zbuild_huffman(&a->z_distance, lencodes+hlit, hdist)) return 0; + return 1; +} + +static int stbi__parse_uncompressed_block(stbi__zbuf *a) +{ + stbi_uc header[4]; + int len,nlen,k; + if (a->num_bits & 7) + stbi__zreceive(a, a->num_bits & 7); // discard + // drain the bit-packed data into header + k = 0; + while (a->num_bits > 0) { + header[k++] = (stbi_uc) (a->code_buffer & 255); // suppress MSVC run-time check + a->code_buffer >>= 8; + a->num_bits -= 8; + } + STBI_ASSERT(a->num_bits == 0); + // now fill header the normal way + while (k < 4) + header[k++] = stbi__zget8(a); + len = header[1] * 256 + header[0]; + nlen = header[3] * 256 + header[2]; + if (nlen != (len ^ 0xffff)) return stbi__err("zlib corrupt","Corrupt PNG"); + if (a->zbuffer + len > a->zbuffer_end) return stbi__err("read past buffer","Corrupt PNG"); + if (a->zout + len > a->zout_end) + if (!stbi__zexpand(a, a->zout, len)) return 0; + memcpy(a->zout, a->zbuffer, len); + a->zbuffer += len; + a->zout += len; + return 1; +} + +static int stbi__parse_zlib_header(stbi__zbuf *a) +{ + int cmf = stbi__zget8(a); + int cm = cmf & 15; + /* int cinfo = cmf >> 4; */ + int flg = stbi__zget8(a); + if ((cmf*256+flg) % 31 != 0) return stbi__err("bad zlib header","Corrupt PNG"); // zlib spec + if (flg & 32) return stbi__err("no preset dict","Corrupt PNG"); // preset dictionary not allowed in png + if (cm != 8) return stbi__err("bad compression","Corrupt PNG"); // DEFLATE required for png + // window = 1 << (8 + cinfo)... but who cares, we fully buffer output + return 1; +} + +// @TODO: should statically initialize these for optimal thread safety +static stbi_uc stbi__zdefault_length[288], stbi__zdefault_distance[32]; +static void stbi__init_zdefaults(void) +{ + int i; // use <= to match clearly with spec + for (i=0; i <= 143; ++i) stbi__zdefault_length[i] = 8; + for ( ; i <= 255; ++i) stbi__zdefault_length[i] = 9; + for ( ; i <= 279; ++i) stbi__zdefault_length[i] = 7; + for ( ; i <= 287; ++i) stbi__zdefault_length[i] = 8; + + for (i=0; i <= 31; ++i) stbi__zdefault_distance[i] = 5; +} + +static int stbi__parse_zlib(stbi__zbuf *a, int parse_header) +{ + int final, type; + if (parse_header) + if (!stbi__parse_zlib_header(a)) return 0; + a->num_bits = 0; + a->code_buffer = 0; + do { + final = stbi__zreceive(a,1); + type = stbi__zreceive(a,2); + if (type == 0) { + if (!stbi__parse_uncompressed_block(a)) return 0; + } else if (type == 3) { + return 0; + } else { + if (type == 1) { + // use fixed code lengths + if (!stbi__zdefault_distance[31]) stbi__init_zdefaults(); + if (!stbi__zbuild_huffman(&a->z_length , stbi__zdefault_length , 288)) return 0; + if (!stbi__zbuild_huffman(&a->z_distance, stbi__zdefault_distance, 32)) return 0; + } else { + if (!stbi__compute_huffman_codes(a)) return 0; + } + if (!stbi__parse_huffman_block(a)) return 0; + } + } while (!final); + return 1; +} + +static int stbi__do_zlib(stbi__zbuf *a, char *obuf, int olen, int exp, int parse_header) +{ + a->zout_start = obuf; + a->zout = obuf; + a->zout_end = obuf + olen; + a->z_expandable = exp; + + return stbi__parse_zlib(a, parse_header); +} + +STBIDEF char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen) +{ + stbi__zbuf a; + char *p = (char *) stbi__malloc(initial_size); + if (p == NULL) return NULL; + a.zbuffer = (stbi_uc *) buffer; + a.zbuffer_end = (stbi_uc *) buffer + len; + if (stbi__do_zlib(&a, p, initial_size, 1, 1)) { + if (outlen) *outlen = (int) (a.zout - a.zout_start); + return a.zout_start; + } else { + STBI_FREE(a.zout_start); + return NULL; + } +} + +STBIDEF char *stbi_zlib_decode_malloc(char const *buffer, int len, int *outlen) +{ + return stbi_zlib_decode_malloc_guesssize(buffer, len, 16384, outlen); +} + +STBIDEF char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header) +{ + stbi__zbuf a; + char *p = (char *) stbi__malloc(initial_size); + if (p == NULL) return NULL; + a.zbuffer = (stbi_uc *) buffer; + a.zbuffer_end = (stbi_uc *) buffer + len; + if (stbi__do_zlib(&a, p, initial_size, 1, parse_header)) { + if (outlen) *outlen = (int) (a.zout - a.zout_start); + return a.zout_start; + } else { + STBI_FREE(a.zout_start); + return NULL; + } +} + +STBIDEF int stbi_zlib_decode_buffer(char *obuffer, int olen, char const *ibuffer, int ilen) +{ + stbi__zbuf a; + a.zbuffer = (stbi_uc *) ibuffer; + a.zbuffer_end = (stbi_uc *) ibuffer + ilen; + if (stbi__do_zlib(&a, obuffer, olen, 0, 1)) + return (int) (a.zout - a.zout_start); + else + return -1; +} + +STBIDEF char *stbi_zlib_decode_noheader_malloc(char const *buffer, int len, int *outlen) +{ + stbi__zbuf a; + char *p = (char *) stbi__malloc(16384); + if (p == NULL) return NULL; + a.zbuffer = (stbi_uc *) buffer; + a.zbuffer_end = (stbi_uc *) buffer+len; + if (stbi__do_zlib(&a, p, 16384, 1, 0)) { + if (outlen) *outlen = (int) (a.zout - a.zout_start); + return a.zout_start; + } else { + STBI_FREE(a.zout_start); + return NULL; + } +} + +STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen) +{ + stbi__zbuf a; + a.zbuffer = (stbi_uc *) ibuffer; + a.zbuffer_end = (stbi_uc *) ibuffer + ilen; + if (stbi__do_zlib(&a, obuffer, olen, 0, 0)) + return (int) (a.zout - a.zout_start); + else + return -1; +} +#endif + +// public domain "baseline" PNG decoder v0.10 Sean Barrett 2006-11-18 +// simple implementation +// - only 8-bit samples +// - no CRC checking +// - allocates lots of intermediate memory +// - avoids problem of streaming data between subsystems +// - avoids explicit window management +// performance +// - uses stb_zlib, a PD zlib implementation with fast huffman decoding + +#ifndef STBI_NO_PNG +typedef struct +{ + stbi__uint32 length; + stbi__uint32 type; +} stbi__pngchunk; + +static stbi__pngchunk stbi__get_chunk_header(stbi__context *s) +{ + stbi__pngchunk c; + c.length = stbi__get32be(s); + c.type = stbi__get32be(s); + return c; +} + +static int stbi__check_png_header(stbi__context *s) +{ + static stbi_uc png_sig[8] = { 137,80,78,71,13,10,26,10 }; + int i; + for (i=0; i < 8; ++i) + if (stbi__get8(s) != png_sig[i]) return stbi__err("bad png sig","Not a PNG"); + return 1; +} + +typedef struct +{ + stbi__context *s; + stbi_uc *idata, *expanded, *out; + int depth; +} stbi__png; + + +enum { + STBI__F_none=0, + STBI__F_sub=1, + STBI__F_up=2, + STBI__F_avg=3, + STBI__F_paeth=4, + // synthetic filters used for first scanline to avoid needing a dummy row of 0s + STBI__F_avg_first, + STBI__F_paeth_first +}; + +static stbi_uc first_row_filter[5] = +{ + STBI__F_none, + STBI__F_sub, + STBI__F_none, + STBI__F_avg_first, + STBI__F_paeth_first +}; + +static int stbi__paeth(int a, int b, int c) +{ + int p = a + b - c; + int pa = abs(p-a); + int pb = abs(p-b); + int pc = abs(p-c); + if (pa <= pb && pa <= pc) return a; + if (pb <= pc) return b; + return c; +} + +static stbi_uc stbi__depth_scale_table[9] = { 0, 0xff, 0x55, 0, 0x11, 0,0,0, 0x01 }; + +// create the png data from post-deflated data +static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 raw_len, int out_n, stbi__uint32 x, stbi__uint32 y, int depth, int color) +{ + int bytes = (depth == 16? 2 : 1); + stbi__context *s = a->s; + stbi__uint32 i,j,stride = x*out_n*bytes; + stbi__uint32 img_len, img_width_bytes; + int k; + int img_n = s->img_n; // copy it into a local for later + + int output_bytes = out_n*bytes; + int filter_bytes = img_n*bytes; + int width = x; + + STBI_ASSERT(out_n == s->img_n || out_n == s->img_n+1); + a->out = (stbi_uc *) stbi__malloc(x * y * output_bytes); // extra bytes to write off the end into + if (!a->out) return stbi__err("outofmem", "Out of memory"); + + img_width_bytes = (((img_n * x * depth) + 7) >> 3); + img_len = (img_width_bytes + 1) * y; + if (s->img_x == x && s->img_y == y) { + if (raw_len != img_len) return stbi__err("not enough pixels","Corrupt PNG"); + } else { // interlaced: + if (raw_len < img_len) return stbi__err("not enough pixels","Corrupt PNG"); + } + + for (j=0; j < y; ++j) { + stbi_uc *cur = a->out + stride*j; + stbi_uc *prior = cur - stride; + int filter = *raw++; + + if (filter > 4) + return stbi__err("invalid filter","Corrupt PNG"); + + if (depth < 8) { + STBI_ASSERT(img_width_bytes <= x); + cur += x*out_n - img_width_bytes; // store output to the rightmost img_len bytes, so we can decode in place + filter_bytes = 1; + width = img_width_bytes; + } + + // if first row, use special filter that doesn't sample previous row + if (j == 0) filter = first_row_filter[filter]; + + // handle first byte explicitly + for (k=0; k < filter_bytes; ++k) { + switch (filter) { + case STBI__F_none : cur[k] = raw[k]; break; + case STBI__F_sub : cur[k] = raw[k]; break; + case STBI__F_up : cur[k] = STBI__BYTECAST(raw[k] + prior[k]); break; + case STBI__F_avg : cur[k] = STBI__BYTECAST(raw[k] + (prior[k]>>1)); break; + case STBI__F_paeth : cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(0,prior[k],0)); break; + case STBI__F_avg_first : cur[k] = raw[k]; break; + case STBI__F_paeth_first: cur[k] = raw[k]; break; + } + } + + if (depth == 8) { + if (img_n != out_n) + cur[img_n] = 255; // first pixel + raw += img_n; + cur += out_n; + prior += out_n; + } else if (depth == 16) { + if (img_n != out_n) { + cur[filter_bytes] = 255; // first pixel top byte + cur[filter_bytes+1] = 255; // first pixel bottom byte + } + raw += filter_bytes; + cur += output_bytes; + prior += output_bytes; + } else { + raw += 1; + cur += 1; + prior += 1; + } + + // this is a little gross, so that we don't switch per-pixel or per-component + if (depth < 8 || img_n == out_n) { + int nk = (width - 1)*filter_bytes; + #define CASE(f) \ + case f: \ + for (k=0; k < nk; ++k) + switch (filter) { + // "none" filter turns into a memcpy here; make that explicit. + case STBI__F_none: memcpy(cur, raw, nk); break; + CASE(STBI__F_sub) cur[k] = STBI__BYTECAST(raw[k] + cur[k-filter_bytes]); break; + CASE(STBI__F_up) cur[k] = STBI__BYTECAST(raw[k] + prior[k]); break; + CASE(STBI__F_avg) cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k-filter_bytes])>>1)); break; + CASE(STBI__F_paeth) cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],prior[k],prior[k-filter_bytes])); break; + CASE(STBI__F_avg_first) cur[k] = STBI__BYTECAST(raw[k] + (cur[k-filter_bytes] >> 1)); break; + CASE(STBI__F_paeth_first) cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],0,0)); break; + } + #undef CASE + raw += nk; + } else { + STBI_ASSERT(img_n+1 == out_n); + #define CASE(f) \ + case f: \ + for (i=x-1; i >= 1; --i, cur[filter_bytes]=255,raw+=filter_bytes,cur+=output_bytes,prior+=output_bytes) \ + for (k=0; k < filter_bytes; ++k) + switch (filter) { + CASE(STBI__F_none) cur[k] = raw[k]; break; + CASE(STBI__F_sub) cur[k] = STBI__BYTECAST(raw[k] + cur[k- output_bytes]); break; + CASE(STBI__F_up) cur[k] = STBI__BYTECAST(raw[k] + prior[k]); break; + CASE(STBI__F_avg) cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k- output_bytes])>>1)); break; + CASE(STBI__F_paeth) cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k- output_bytes],prior[k],prior[k- output_bytes])); break; + CASE(STBI__F_avg_first) cur[k] = STBI__BYTECAST(raw[k] + (cur[k- output_bytes] >> 1)); break; + CASE(STBI__F_paeth_first) cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k- output_bytes],0,0)); break; + } + #undef CASE + + // the loop above sets the high byte of the pixels' alpha, but for + // 16 bit png files we also need the low byte set. we'll do that here. + if (depth == 16) { + cur = a->out + stride*j; // start at the beginning of the row again + for (i=0; i < x; ++i,cur+=output_bytes) { + cur[filter_bytes+1] = 255; + } + } + } + } + + // we make a separate pass to expand bits to pixels; for performance, + // this could run two scanlines behind the above code, so it won't + // intefere with filtering but will still be in the cache. + if (depth < 8) { + for (j=0; j < y; ++j) { + stbi_uc *cur = a->out + stride*j; + stbi_uc *in = a->out + stride*j + x*out_n - img_width_bytes; + // unpack 1/2/4-bit into a 8-bit buffer. allows us to keep the common 8-bit path optimal at minimal cost for 1/2/4-bit + // png guarante byte alignment, if width is not multiple of 8/4/2 we'll decode dummy trailing data that will be skipped in the later loop + stbi_uc scale = (color == 0) ? stbi__depth_scale_table[depth] : 1; // scale grayscale values to 0..255 range + + // note that the final byte might overshoot and write more data than desired. + // we can allocate enough data that this never writes out of memory, but it + // could also overwrite the next scanline. can it overwrite non-empty data + // on the next scanline? yes, consider 1-pixel-wide scanlines with 1-bit-per-pixel. + // so we need to explicitly clamp the final ones + + if (depth == 4) { + for (k=x*img_n; k >= 2; k-=2, ++in) { + *cur++ = scale * ((*in >> 4) ); + *cur++ = scale * ((*in ) & 0x0f); + } + if (k > 0) *cur++ = scale * ((*in >> 4) ); + } else if (depth == 2) { + for (k=x*img_n; k >= 4; k-=4, ++in) { + *cur++ = scale * ((*in >> 6) ); + *cur++ = scale * ((*in >> 4) & 0x03); + *cur++ = scale * ((*in >> 2) & 0x03); + *cur++ = scale * ((*in ) & 0x03); + } + if (k > 0) *cur++ = scale * ((*in >> 6) ); + if (k > 1) *cur++ = scale * ((*in >> 4) & 0x03); + if (k > 2) *cur++ = scale * ((*in >> 2) & 0x03); + } else if (depth == 1) { + for (k=x*img_n; k >= 8; k-=8, ++in) { + *cur++ = scale * ((*in >> 7) ); + *cur++ = scale * ((*in >> 6) & 0x01); + *cur++ = scale * ((*in >> 5) & 0x01); + *cur++ = scale * ((*in >> 4) & 0x01); + *cur++ = scale * ((*in >> 3) & 0x01); + *cur++ = scale * ((*in >> 2) & 0x01); + *cur++ = scale * ((*in >> 1) & 0x01); + *cur++ = scale * ((*in ) & 0x01); + } + if (k > 0) *cur++ = scale * ((*in >> 7) ); + if (k > 1) *cur++ = scale * ((*in >> 6) & 0x01); + if (k > 2) *cur++ = scale * ((*in >> 5) & 0x01); + if (k > 3) *cur++ = scale * ((*in >> 4) & 0x01); + if (k > 4) *cur++ = scale * ((*in >> 3) & 0x01); + if (k > 5) *cur++ = scale * ((*in >> 2) & 0x01); + if (k > 6) *cur++ = scale * ((*in >> 1) & 0x01); + } + if (img_n != out_n) { + int q; + // insert alpha = 255 + cur = a->out + stride*j; + if (img_n == 1) { + for (q=x-1; q >= 0; --q) { + cur[q*2+1] = 255; + cur[q*2+0] = cur[q]; + } + } else { + STBI_ASSERT(img_n == 3); + for (q=x-1; q >= 0; --q) { + cur[q*4+3] = 255; + cur[q*4+2] = cur[q*3+2]; + cur[q*4+1] = cur[q*3+1]; + cur[q*4+0] = cur[q*3+0]; + } + } + } + } + } else if (depth == 16) { + // force the image data from big-endian to platform-native. + // this is done in a separate pass due to the decoding relying + // on the data being untouched, but could probably be done + // per-line during decode if care is taken. + stbi_uc *cur = a->out; + stbi__uint16 *cur16 = (stbi__uint16*)cur; + + for(i=0; i < x*y*out_n; ++i,cur16++,cur+=2) { + *cur16 = (cur[0] << 8) | cur[1]; + } + } + + return 1; +} + +static int stbi__create_png_image(stbi__png *a, stbi_uc *image_data, stbi__uint32 image_data_len, int out_n, int depth, int color, int interlaced) +{ + stbi_uc *final; + int p; + if (!interlaced) + return stbi__create_png_image_raw(a, image_data, image_data_len, out_n, a->s->img_x, a->s->img_y, depth, color); + + // de-interlacing + final = (stbi_uc *) stbi__malloc(a->s->img_x * a->s->img_y * out_n); + for (p=0; p < 7; ++p) { + int xorig[] = { 0,4,0,2,0,1,0 }; + int yorig[] = { 0,0,4,0,2,0,1 }; + int xspc[] = { 8,8,4,4,2,2,1 }; + int yspc[] = { 8,8,8,4,4,2,2 }; + int i,j,x,y; + // pass1_x[4] = 0, pass1_x[5] = 1, pass1_x[12] = 1 + x = (a->s->img_x - xorig[p] + xspc[p]-1) / xspc[p]; + y = (a->s->img_y - yorig[p] + yspc[p]-1) / yspc[p]; + if (x && y) { + stbi__uint32 img_len = ((((a->s->img_n * x * depth) + 7) >> 3) + 1) * y; + if (!stbi__create_png_image_raw(a, image_data, image_data_len, out_n, x, y, depth, color)) { + STBI_FREE(final); + return 0; + } + for (j=0; j < y; ++j) { + for (i=0; i < x; ++i) { + int out_y = j*yspc[p]+yorig[p]; + int out_x = i*xspc[p]+xorig[p]; + memcpy(final + out_y*a->s->img_x*out_n + out_x*out_n, + a->out + (j*x+i)*out_n, out_n); + } + } + STBI_FREE(a->out); + image_data += img_len; + image_data_len -= img_len; + } + } + a->out = final; + + return 1; +} + +static int stbi__compute_transparency(stbi__png *z, stbi_uc tc[3], int out_n) +{ + stbi__context *s = z->s; + stbi__uint32 i, pixel_count = s->img_x * s->img_y; + stbi_uc *p = z->out; + + // compute color-based transparency, assuming we've + // already got 255 as the alpha value in the output + STBI_ASSERT(out_n == 2 || out_n == 4); + + if (out_n == 2) { + for (i=0; i < pixel_count; ++i) { + p[1] = (p[0] == tc[0] ? 0 : 255); + p += 2; + } + } else { + for (i=0; i < pixel_count; ++i) { + if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2]) + p[3] = 0; + p += 4; + } + } + return 1; +} + +static int stbi__compute_transparency16(stbi__png *z, stbi__uint16 tc[3], int out_n) +{ + stbi__context *s = z->s; + stbi__uint32 i, pixel_count = s->img_x * s->img_y; + stbi__uint16 *p = (stbi__uint16*) z->out; + + // compute color-based transparency, assuming we've + // already got 65535 as the alpha value in the output + STBI_ASSERT(out_n == 2 || out_n == 4); + + if (out_n == 2) { + for (i = 0; i < pixel_count; ++i) { + p[1] = (p[0] == tc[0] ? 0 : 65535); + p += 2; + } + } else { + for (i = 0; i < pixel_count; ++i) { + if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2]) + p[3] = 0; + p += 4; + } + } + return 1; +} + +static int stbi__expand_png_palette(stbi__png *a, stbi_uc *palette, int len, int pal_img_n) +{ + stbi__uint32 i, pixel_count = a->s->img_x * a->s->img_y; + stbi_uc *p, *temp_out, *orig = a->out; + + p = (stbi_uc *) stbi__malloc(pixel_count * pal_img_n); + if (p == NULL) return stbi__err("outofmem", "Out of memory"); + + // between here and free(out) below, exitting would leak + temp_out = p; + + if (pal_img_n == 3) { + for (i=0; i < pixel_count; ++i) { + int n = orig[i]*4; + p[0] = palette[n ]; + p[1] = palette[n+1]; + p[2] = palette[n+2]; + p += 3; + } + } else { + for (i=0; i < pixel_count; ++i) { + int n = orig[i]*4; + p[0] = palette[n ]; + p[1] = palette[n+1]; + p[2] = palette[n+2]; + p[3] = palette[n+3]; + p += 4; + } + } + STBI_FREE(a->out); + a->out = temp_out; + + STBI_NOTUSED(len); + + return 1; +} + +static int stbi__reduce_png(stbi__png *p) +{ + int i; + int img_len = p->s->img_x * p->s->img_y * p->s->img_out_n; + stbi_uc *reduced; + stbi__uint16 *orig = (stbi__uint16*)p->out; + + if (p->depth != 16) return 1; // don't need to do anything if not 16-bit data + + reduced = (stbi_uc *)stbi__malloc(img_len); + if (p == NULL) return stbi__err("outofmem", "Out of memory"); + + for (i = 0; i < img_len; ++i) reduced[i] = (stbi_uc)((orig[i] >> 8) & 0xFF); // top half of each byte is a decent approx of 16->8 bit scaling + + p->out = reduced; + STBI_FREE(orig); + + return 1; +} + +static int stbi__unpremultiply_on_load = 0; +static int stbi__de_iphone_flag = 0; + +STBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply) +{ + stbi__unpremultiply_on_load = flag_true_if_should_unpremultiply; +} + +STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert) +{ + stbi__de_iphone_flag = flag_true_if_should_convert; +} + +static void stbi__de_iphone(stbi__png *z) +{ + stbi__context *s = z->s; + stbi__uint32 i, pixel_count = s->img_x * s->img_y; + stbi_uc *p = z->out; + + if (s->img_out_n == 3) { // convert bgr to rgb + for (i=0; i < pixel_count; ++i) { + stbi_uc t = p[0]; + p[0] = p[2]; + p[2] = t; + p += 3; + } + } else { + STBI_ASSERT(s->img_out_n == 4); + if (stbi__unpremultiply_on_load) { + // convert bgr to rgb and unpremultiply + for (i=0; i < pixel_count; ++i) { + stbi_uc a = p[3]; + stbi_uc t = p[0]; + if (a) { + p[0] = p[2] * 255 / a; + p[1] = p[1] * 255 / a; + p[2] = t * 255 / a; + } else { + p[0] = p[2]; + p[2] = t; + } + p += 4; + } + } else { + // convert bgr to rgb + for (i=0; i < pixel_count; ++i) { + stbi_uc t = p[0]; + p[0] = p[2]; + p[2] = t; + p += 4; + } + } + } +} + +#define STBI__PNG_TYPE(a,b,c,d) (((a) << 24) + ((b) << 16) + ((c) << 8) + (d)) + +static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp) +{ + stbi_uc palette[1024], pal_img_n=0; + stbi_uc has_trans=0, tc[3]; + stbi__uint16 tc16[3]; + stbi__uint32 ioff=0, idata_limit=0, i, pal_len=0; + int first=1,k,interlace=0, color=0, is_iphone=0; + stbi__context *s = z->s; + + z->expanded = NULL; + z->idata = NULL; + z->out = NULL; + + if (!stbi__check_png_header(s)) return 0; + + if (scan == STBI__SCAN_type) return 1; + + for (;;) { + stbi__pngchunk c = stbi__get_chunk_header(s); + switch (c.type) { + case STBI__PNG_TYPE('C','g','B','I'): + is_iphone = 1; + stbi__skip(s, c.length); + break; + case STBI__PNG_TYPE('I','H','D','R'): { + int comp,filter; + if (!first) return stbi__err("multiple IHDR","Corrupt PNG"); + first = 0; + if (c.length != 13) return stbi__err("bad IHDR len","Corrupt PNG"); + s->img_x = stbi__get32be(s); if (s->img_x > (1 << 24)) return stbi__err("too large","Very large image (corrupt?)"); + s->img_y = stbi__get32be(s); if (s->img_y > (1 << 24)) return stbi__err("too large","Very large image (corrupt?)"); + z->depth = stbi__get8(s); if (z->depth != 1 && z->depth != 2 && z->depth != 4 && z->depth != 8 && z->depth != 16) return stbi__err("1/2/4/8/16-bit only","PNG not supported: 1/2/4/8/16-bit only"); + color = stbi__get8(s); if (color > 6) return stbi__err("bad ctype","Corrupt PNG"); + if (color == 3 && z->depth == 16) return stbi__err("bad ctype","Corrupt PNG"); + if (color == 3) pal_img_n = 3; else if (color & 1) return stbi__err("bad ctype","Corrupt PNG"); + comp = stbi__get8(s); if (comp) return stbi__err("bad comp method","Corrupt PNG"); + filter= stbi__get8(s); if (filter) return stbi__err("bad filter method","Corrupt PNG"); + interlace = stbi__get8(s); if (interlace>1) return stbi__err("bad interlace method","Corrupt PNG"); + if (!s->img_x || !s->img_y) return stbi__err("0-pixel image","Corrupt PNG"); + if (!pal_img_n) { + s->img_n = (color & 2 ? 3 : 1) + (color & 4 ? 1 : 0); + if ((1 << 30) / s->img_x / s->img_n < s->img_y) return stbi__err("too large", "Image too large to decode"); + if (scan == STBI__SCAN_header) return 1; + } else { + // if paletted, then pal_n is our final components, and + // img_n is # components to decompress/filter. + s->img_n = 1; + if ((1 << 30) / s->img_x / 4 < s->img_y) return stbi__err("too large","Corrupt PNG"); + // if SCAN_header, have to scan to see if we have a tRNS + } + break; + } + + case STBI__PNG_TYPE('P','L','T','E'): { + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if (c.length > 256*3) return stbi__err("invalid PLTE","Corrupt PNG"); + pal_len = c.length / 3; + if (pal_len * 3 != c.length) return stbi__err("invalid PLTE","Corrupt PNG"); + for (i=0; i < pal_len; ++i) { + palette[i*4+0] = stbi__get8(s); + palette[i*4+1] = stbi__get8(s); + palette[i*4+2] = stbi__get8(s); + palette[i*4+3] = 255; + } + break; + } + + case STBI__PNG_TYPE('t','R','N','S'): { + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if (z->idata) return stbi__err("tRNS after IDAT","Corrupt PNG"); + if (pal_img_n) { + if (scan == STBI__SCAN_header) { s->img_n = 4; return 1; } + if (pal_len == 0) return stbi__err("tRNS before PLTE","Corrupt PNG"); + if (c.length > pal_len) return stbi__err("bad tRNS len","Corrupt PNG"); + pal_img_n = 4; + for (i=0; i < c.length; ++i) + palette[i*4+3] = stbi__get8(s); + } else { + if (!(s->img_n & 1)) return stbi__err("tRNS with alpha","Corrupt PNG"); + if (c.length != (stbi__uint32) s->img_n*2) return stbi__err("bad tRNS len","Corrupt PNG"); + has_trans = 1; + if (z->depth == 16) { + for (k = 0; k < s->img_n; ++k) tc16[k] = stbi__get16be(s); // copy the values as-is + } else { + for (k = 0; k < s->img_n; ++k) tc[k] = (stbi_uc)(stbi__get16be(s) & 255) * stbi__depth_scale_table[z->depth]; // non 8-bit images will be larger + } + } + break; + } + + case STBI__PNG_TYPE('I','D','A','T'): { + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if (pal_img_n && !pal_len) return stbi__err("no PLTE","Corrupt PNG"); + if (scan == STBI__SCAN_header) { s->img_n = pal_img_n; return 1; } + if ((int)(ioff + c.length) < (int)ioff) return 0; + if (ioff + c.length > idata_limit) { + stbi__uint32 idata_limit_old = idata_limit; + stbi_uc *p; + if (idata_limit == 0) idata_limit = c.length > 4096 ? c.length : 4096; + while (ioff + c.length > idata_limit) + idata_limit *= 2; + STBI_NOTUSED(idata_limit_old); + p = (stbi_uc *) STBI_REALLOC_SIZED(z->idata, idata_limit_old, idata_limit); if (p == NULL) return stbi__err("outofmem", "Out of memory"); + z->idata = p; + } + if (!stbi__getn(s, z->idata+ioff,c.length)) return stbi__err("outofdata","Corrupt PNG"); + ioff += c.length; + break; + } + + case STBI__PNG_TYPE('I','E','N','D'): { + stbi__uint32 raw_len, bpl; + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if (scan != STBI__SCAN_load) return 1; + if (z->idata == NULL) return stbi__err("no IDAT","Corrupt PNG"); + // initial guess for decoded data size to avoid unnecessary reallocs + bpl = (s->img_x * z->depth + 7) / 8; // bytes per line, per component + raw_len = bpl * s->img_y * s->img_n /* pixels */ + s->img_y /* filter mode per row */; + z->expanded = (stbi_uc *) stbi_zlib_decode_malloc_guesssize_headerflag((char *) z->idata, ioff, raw_len, (int *) &raw_len, !is_iphone); + if (z->expanded == NULL) return 0; // zlib should set error + STBI_FREE(z->idata); z->idata = NULL; + if ((req_comp == s->img_n+1 && req_comp != 3 && !pal_img_n) || has_trans) + s->img_out_n = s->img_n+1; + else + s->img_out_n = s->img_n; + if (!stbi__create_png_image(z, z->expanded, raw_len, s->img_out_n, z->depth, color, interlace)) return 0; + if (has_trans) { + if (z->depth == 16) { + if (!stbi__compute_transparency16(z, tc16, s->img_out_n)) return 0; + } else { + if (!stbi__compute_transparency(z, tc, s->img_out_n)) return 0; + } + } + if (is_iphone && stbi__de_iphone_flag && s->img_out_n > 2) + stbi__de_iphone(z); + if (pal_img_n) { + // pal_img_n == 3 or 4 + s->img_n = pal_img_n; // record the actual colors we had + s->img_out_n = pal_img_n; + if (req_comp >= 3) s->img_out_n = req_comp; + if (!stbi__expand_png_palette(z, palette, pal_len, s->img_out_n)) + return 0; + } + STBI_FREE(z->expanded); z->expanded = NULL; + return 1; + } + + default: + // if critical, fail + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if ((c.type & (1 << 29)) == 0) { + #ifndef STBI_NO_FAILURE_STRINGS + // not threadsafe + static char invalid_chunk[] = "XXXX PNG chunk not known"; + invalid_chunk[0] = STBI__BYTECAST(c.type >> 24); + invalid_chunk[1] = STBI__BYTECAST(c.type >> 16); + invalid_chunk[2] = STBI__BYTECAST(c.type >> 8); + invalid_chunk[3] = STBI__BYTECAST(c.type >> 0); + #endif + return stbi__err(invalid_chunk, "PNG not supported: unknown PNG chunk type"); + } + stbi__skip(s, c.length); + break; + } + // end of PNG chunk, read and skip CRC + stbi__get32be(s); + } +} + +static unsigned char *stbi__do_png(stbi__png *p, int *x, int *y, int *n, int req_comp) +{ + unsigned char *result=NULL; + if (req_comp < 0 || req_comp > 4) return stbi__errpuc("bad req_comp", "Internal error"); + if (stbi__parse_png_file(p, STBI__SCAN_load, req_comp)) { + if (p->depth == 16) { + if (!stbi__reduce_png(p)) { + return result; + } + } + result = p->out; + p->out = NULL; + if (req_comp && req_comp != p->s->img_out_n) { + result = stbi__convert_format(result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y); + p->s->img_out_n = req_comp; + if (result == NULL) return result; + } + *x = p->s->img_x; + *y = p->s->img_y; + if (n) *n = p->s->img_n; + } + STBI_FREE(p->out); p->out = NULL; + STBI_FREE(p->expanded); p->expanded = NULL; + STBI_FREE(p->idata); p->idata = NULL; + + return result; +} + +static unsigned char *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + stbi__png p; + p.s = s; + return stbi__do_png(&p, x,y,comp,req_comp); +} + +static int stbi__png_test(stbi__context *s) +{ + int r; + r = stbi__check_png_header(s); + stbi__rewind(s); + return r; +} + +static int stbi__png_info_raw(stbi__png *p, int *x, int *y, int *comp) +{ + if (!stbi__parse_png_file(p, STBI__SCAN_header, 0)) { + stbi__rewind( p->s ); + return 0; + } + if (x) *x = p->s->img_x; + if (y) *y = p->s->img_y; + if (comp) *comp = p->s->img_n; + return 1; +} + +static int stbi__png_info(stbi__context *s, int *x, int *y, int *comp) +{ + stbi__png p; + p.s = s; + return stbi__png_info_raw(&p, x, y, comp); +} +#endif + +// Microsoft/Windows BMP image + +#ifndef STBI_NO_BMP +static int stbi__bmp_test_raw(stbi__context *s) +{ + int r; + int sz; + if (stbi__get8(s) != 'B') return 0; + if (stbi__get8(s) != 'M') return 0; + stbi__get32le(s); // discard filesize + stbi__get16le(s); // discard reserved + stbi__get16le(s); // discard reserved + stbi__get32le(s); // discard data offset + sz = stbi__get32le(s); + r = (sz == 12 || sz == 40 || sz == 56 || sz == 108 || sz == 124); + return r; +} + +static int stbi__bmp_test(stbi__context *s) +{ + int r = stbi__bmp_test_raw(s); + stbi__rewind(s); + return r; +} + + +// returns 0..31 for the highest set bit +static int stbi__high_bit(unsigned int z) +{ + int n=0; + if (z == 0) return -1; + if (z >= 0x10000) n += 16, z >>= 16; + if (z >= 0x00100) n += 8, z >>= 8; + if (z >= 0x00010) n += 4, z >>= 4; + if (z >= 0x00004) n += 2, z >>= 2; + if (z >= 0x00002) n += 1, z >>= 1; + return n; +} + +static int stbi__bitcount(unsigned int a) +{ + a = (a & 0x55555555) + ((a >> 1) & 0x55555555); // max 2 + a = (a & 0x33333333) + ((a >> 2) & 0x33333333); // max 4 + a = (a + (a >> 4)) & 0x0f0f0f0f; // max 8 per 4, now 8 bits + a = (a + (a >> 8)); // max 16 per 8 bits + a = (a + (a >> 16)); // max 32 per 8 bits + return a & 0xff; +} + +static int stbi__shiftsigned(int v, int shift, int bits) +{ + int result; + int z=0; + + if (shift < 0) v <<= -shift; + else v >>= shift; + result = v; + + z = bits; + while (z < 8) { + result += v >> z; + z += bits; + } + return result; +} + +typedef struct +{ + int bpp, offset, hsz; + unsigned int mr,mg,mb,ma, all_a; +} stbi__bmp_data; + +static void *stbi__bmp_parse_header(stbi__context *s, stbi__bmp_data *info) +{ + int hsz; + if (stbi__get8(s) != 'B' || stbi__get8(s) != 'M') return stbi__errpuc("not BMP", "Corrupt BMP"); + stbi__get32le(s); // discard filesize + stbi__get16le(s); // discard reserved + stbi__get16le(s); // discard reserved + info->offset = stbi__get32le(s); + info->hsz = hsz = stbi__get32le(s); + info->mr = info->mg = info->mb = info->ma = 0; + + if (hsz != 12 && hsz != 40 && hsz != 56 && hsz != 108 && hsz != 124) return stbi__errpuc("unknown BMP", "BMP type not supported: unknown"); + if (hsz == 12) { + s->img_x = stbi__get16le(s); + s->img_y = stbi__get16le(s); + } else { + s->img_x = stbi__get32le(s); + s->img_y = stbi__get32le(s); + } + if (stbi__get16le(s) != 1) return stbi__errpuc("bad BMP", "bad BMP"); + info->bpp = stbi__get16le(s); + if (info->bpp == 1) return stbi__errpuc("monochrome", "BMP type not supported: 1-bit"); + if (hsz != 12) { + int compress = stbi__get32le(s); + if (compress == 1 || compress == 2) return stbi__errpuc("BMP RLE", "BMP type not supported: RLE"); + stbi__get32le(s); // discard sizeof + stbi__get32le(s); // discard hres + stbi__get32le(s); // discard vres + stbi__get32le(s); // discard colorsused + stbi__get32le(s); // discard max important + if (hsz == 40 || hsz == 56) { + if (hsz == 56) { + stbi__get32le(s); + stbi__get32le(s); + stbi__get32le(s); + stbi__get32le(s); + } + if (info->bpp == 16 || info->bpp == 32) { + if (compress == 0) { + if (info->bpp == 32) { + info->mr = 0xffu << 16; + info->mg = 0xffu << 8; + info->mb = 0xffu << 0; + info->ma = 0xffu << 24; + info->all_a = 0; // if all_a is 0 at end, then we loaded alpha channel but it was all 0 + } else { + info->mr = 31u << 10; + info->mg = 31u << 5; + info->mb = 31u << 0; + } + } else if (compress == 3) { + info->mr = stbi__get32le(s); + info->mg = stbi__get32le(s); + info->mb = stbi__get32le(s); + // not documented, but generated by photoshop and handled by mspaint + if (info->mr == info->mg && info->mg == info->mb) { + // ?!?!? + return stbi__errpuc("bad BMP", "bad BMP"); + } + } else + return stbi__errpuc("bad BMP", "bad BMP"); + } + } else { + int i; + if (hsz != 108 && hsz != 124) + return stbi__errpuc("bad BMP", "bad BMP"); + info->mr = stbi__get32le(s); + info->mg = stbi__get32le(s); + info->mb = stbi__get32le(s); + info->ma = stbi__get32le(s); + stbi__get32le(s); // discard color space + for (i=0; i < 12; ++i) + stbi__get32le(s); // discard color space parameters + if (hsz == 124) { + stbi__get32le(s); // discard rendering intent + stbi__get32le(s); // discard offset of profile data + stbi__get32le(s); // discard size of profile data + stbi__get32le(s); // discard reserved + } + } + } + return (void *) 1; +} + + +static stbi_uc *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + stbi_uc *out; + unsigned int mr=0,mg=0,mb=0,ma=0, all_a; + stbi_uc pal[256][4]; + int psize=0,i,j,width; + int flip_vertically, pad, target; + stbi__bmp_data info; + + info.all_a = 255; + if (stbi__bmp_parse_header(s, &info) == NULL) + return NULL; // error code already set + + flip_vertically = ((int) s->img_y) > 0; + s->img_y = abs((int) s->img_y); + + mr = info.mr; + mg = info.mg; + mb = info.mb; + ma = info.ma; + all_a = info.all_a; + + if (info.hsz == 12) { + if (info.bpp < 24) + psize = (info.offset - 14 - 24) / 3; + } else { + if (info.bpp < 16) + psize = (info.offset - 14 - info.hsz) >> 2; + } + + s->img_n = ma ? 4 : 3; + if (req_comp && req_comp >= 3) // we can directly decode 3 or 4 + target = req_comp; + else + target = s->img_n; // if they want monochrome, we'll post-convert + + out = (stbi_uc *) stbi__malloc(target * s->img_x * s->img_y); + if (!out) return stbi__errpuc("outofmem", "Out of memory"); + if (info.bpp < 16) { + int z=0; + if (psize == 0 || psize > 256) { STBI_FREE(out); return stbi__errpuc("invalid", "Corrupt BMP"); } + for (i=0; i < psize; ++i) { + pal[i][2] = stbi__get8(s); + pal[i][1] = stbi__get8(s); + pal[i][0] = stbi__get8(s); + if (info.hsz != 12) stbi__get8(s); + pal[i][3] = 255; + } + stbi__skip(s, info.offset - 14 - info.hsz - psize * (info.hsz == 12 ? 3 : 4)); + if (info.bpp == 4) width = (s->img_x + 1) >> 1; + else if (info.bpp == 8) width = s->img_x; + else { STBI_FREE(out); return stbi__errpuc("bad bpp", "Corrupt BMP"); } + pad = (-width)&3; + for (j=0; j < (int) s->img_y; ++j) { + for (i=0; i < (int) s->img_x; i += 2) { + int v=stbi__get8(s),v2=0; + if (info.bpp == 4) { + v2 = v & 15; + v >>= 4; + } + out[z++] = pal[v][0]; + out[z++] = pal[v][1]; + out[z++] = pal[v][2]; + if (target == 4) out[z++] = 255; + if (i+1 == (int) s->img_x) break; + v = (info.bpp == 8) ? stbi__get8(s) : v2; + out[z++] = pal[v][0]; + out[z++] = pal[v][1]; + out[z++] = pal[v][2]; + if (target == 4) out[z++] = 255; + } + stbi__skip(s, pad); + } + } else { + int rshift=0,gshift=0,bshift=0,ashift=0,rcount=0,gcount=0,bcount=0,acount=0; + int z = 0; + int easy=0; + stbi__skip(s, info.offset - 14 - info.hsz); + if (info.bpp == 24) width = 3 * s->img_x; + else if (info.bpp == 16) width = 2*s->img_x; + else /* bpp = 32 and pad = 0 */ width=0; + pad = (-width) & 3; + if (info.bpp == 24) { + easy = 1; + } else if (info.bpp == 32) { + if (mb == 0xff && mg == 0xff00 && mr == 0x00ff0000 && ma == 0xff000000) + easy = 2; + } + if (!easy) { + if (!mr || !mg || !mb) { STBI_FREE(out); return stbi__errpuc("bad masks", "Corrupt BMP"); } + // right shift amt to put high bit in position #7 + rshift = stbi__high_bit(mr)-7; rcount = stbi__bitcount(mr); + gshift = stbi__high_bit(mg)-7; gcount = stbi__bitcount(mg); + bshift = stbi__high_bit(mb)-7; bcount = stbi__bitcount(mb); + ashift = stbi__high_bit(ma)-7; acount = stbi__bitcount(ma); + } + for (j=0; j < (int) s->img_y; ++j) { + if (easy) { + for (i=0; i < (int) s->img_x; ++i) { + unsigned char a; + out[z+2] = stbi__get8(s); + out[z+1] = stbi__get8(s); + out[z+0] = stbi__get8(s); + z += 3; + a = (easy == 2 ? stbi__get8(s) : 255); + all_a |= a; + if (target == 4) out[z++] = a; + } + } else { + int bpp = info.bpp; + for (i=0; i < (int) s->img_x; ++i) { + stbi__uint32 v = (bpp == 16 ? (stbi__uint32) stbi__get16le(s) : stbi__get32le(s)); + int a; + out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mr, rshift, rcount)); + out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mg, gshift, gcount)); + out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mb, bshift, bcount)); + a = (ma ? stbi__shiftsigned(v & ma, ashift, acount) : 255); + all_a |= a; + if (target == 4) out[z++] = STBI__BYTECAST(a); + } + } + stbi__skip(s, pad); + } + } + + // if alpha channel is all 0s, replace with all 255s + if (target == 4 && all_a == 0) + for (i=4*s->img_x*s->img_y-1; i >= 0; i -= 4) + out[i] = 255; + + if (flip_vertically) { + stbi_uc t; + for (j=0; j < (int) s->img_y>>1; ++j) { + stbi_uc *p1 = out + j *s->img_x*target; + stbi_uc *p2 = out + (s->img_y-1-j)*s->img_x*target; + for (i=0; i < (int) s->img_x*target; ++i) { + t = p1[i], p1[i] = p2[i], p2[i] = t; + } + } + } + + if (req_comp && req_comp != target) { + out = stbi__convert_format(out, target, req_comp, s->img_x, s->img_y); + if (out == NULL) return out; // stbi__convert_format frees input on failure + } + + *x = s->img_x; + *y = s->img_y; + if (comp) *comp = s->img_n; + return out; +} +#endif + +// Targa Truevision - TGA +// by Jonathan Dummer +#ifndef STBI_NO_TGA +// returns STBI_rgb or whatever, 0 on error +static int stbi__tga_get_comp(int bits_per_pixel, int is_grey, int* is_rgb16) +{ + // only RGB or RGBA (incl. 16bit) or grey allowed + if(is_rgb16) *is_rgb16 = 0; + switch(bits_per_pixel) { + case 8: return STBI_grey; + case 16: if(is_grey) return STBI_grey_alpha; + // else: fall-through + case 15: if(is_rgb16) *is_rgb16 = 1; + return STBI_rgb; + case 24: // fall-through + case 32: return bits_per_pixel/8; + default: return 0; + } +} + +static int stbi__tga_info(stbi__context *s, int *x, int *y, int *comp) +{ + int tga_w, tga_h, tga_comp, tga_image_type, tga_bits_per_pixel, tga_colormap_bpp; + int sz, tga_colormap_type; + stbi__get8(s); // discard Offset + tga_colormap_type = stbi__get8(s); // colormap type + if( tga_colormap_type > 1 ) { + stbi__rewind(s); + return 0; // only RGB or indexed allowed + } + tga_image_type = stbi__get8(s); // image type + if ( tga_colormap_type == 1 ) { // colormapped (paletted) image + if (tga_image_type != 1 && tga_image_type != 9) { + stbi__rewind(s); + return 0; + } + stbi__skip(s,4); // skip index of first colormap entry and number of entries + sz = stbi__get8(s); // check bits per palette color entry + if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) { + stbi__rewind(s); + return 0; + } + stbi__skip(s,4); // skip image x and y origin + tga_colormap_bpp = sz; + } else { // "normal" image w/o colormap - only RGB or grey allowed, +/- RLE + if ( (tga_image_type != 2) && (tga_image_type != 3) && (tga_image_type != 10) && (tga_image_type != 11) ) { + stbi__rewind(s); + return 0; // only RGB or grey allowed, +/- RLE + } + stbi__skip(s,9); // skip colormap specification and image x/y origin + tga_colormap_bpp = 0; + } + tga_w = stbi__get16le(s); + if( tga_w < 1 ) { + stbi__rewind(s); + return 0; // test width + } + tga_h = stbi__get16le(s); + if( tga_h < 1 ) { + stbi__rewind(s); + return 0; // test height + } + tga_bits_per_pixel = stbi__get8(s); // bits per pixel + stbi__get8(s); // ignore alpha bits + if (tga_colormap_bpp != 0) { + if((tga_bits_per_pixel != 8) && (tga_bits_per_pixel != 16)) { + // when using a colormap, tga_bits_per_pixel is the size of the indexes + // I don't think anything but 8 or 16bit indexes makes sense + stbi__rewind(s); + return 0; + } + tga_comp = stbi__tga_get_comp(tga_colormap_bpp, 0, NULL); + } else { + tga_comp = stbi__tga_get_comp(tga_bits_per_pixel, (tga_image_type == 3) || (tga_image_type == 11), NULL); + } + if(!tga_comp) { + stbi__rewind(s); + return 0; + } + if (x) *x = tga_w; + if (y) *y = tga_h; + if (comp) *comp = tga_comp; + return 1; // seems to have passed everything +} + +static int stbi__tga_test(stbi__context *s) +{ + int res = 0; + int sz, tga_color_type; + stbi__get8(s); // discard Offset + tga_color_type = stbi__get8(s); // color type + if ( tga_color_type > 1 ) goto errorEnd; // only RGB or indexed allowed + sz = stbi__get8(s); // image type + if ( tga_color_type == 1 ) { // colormapped (paletted) image + if (sz != 1 && sz != 9) goto errorEnd; // colortype 1 demands image type 1 or 9 + stbi__skip(s,4); // skip index of first colormap entry and number of entries + sz = stbi__get8(s); // check bits per palette color entry + if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) goto errorEnd; + stbi__skip(s,4); // skip image x and y origin + } else { // "normal" image w/o colormap + if ( (sz != 2) && (sz != 3) && (sz != 10) && (sz != 11) ) goto errorEnd; // only RGB or grey allowed, +/- RLE + stbi__skip(s,9); // skip colormap specification and image x/y origin + } + if ( stbi__get16le(s) < 1 ) goto errorEnd; // test width + if ( stbi__get16le(s) < 1 ) goto errorEnd; // test height + sz = stbi__get8(s); // bits per pixel + if ( (tga_color_type == 1) && (sz != 8) && (sz != 16) ) goto errorEnd; // for colormapped images, bpp is size of an index + if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) goto errorEnd; + + res = 1; // if we got this far, everything's good and we can return 1 instead of 0 + +errorEnd: + stbi__rewind(s); + return res; +} + +// read 16bit value and convert to 24bit RGB +void stbi__tga_read_rgb16(stbi__context *s, stbi_uc* out) +{ + stbi__uint16 px = stbi__get16le(s); + stbi__uint16 fiveBitMask = 31; + // we have 3 channels with 5bits each + int r = (px >> 10) & fiveBitMask; + int g = (px >> 5) & fiveBitMask; + int b = px & fiveBitMask; + // Note that this saves the data in RGB(A) order, so it doesn't need to be swapped later + out[0] = (r * 255)/31; + out[1] = (g * 255)/31; + out[2] = (b * 255)/31; + + // some people claim that the most significant bit might be used for alpha + // (possibly if an alpha-bit is set in the "image descriptor byte") + // but that only made 16bit test images completely translucent.. + // so let's treat all 15 and 16bit TGAs as RGB with no alpha. +} + +static stbi_uc *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + // read in the TGA header stuff + int tga_offset = stbi__get8(s); + int tga_indexed = stbi__get8(s); + int tga_image_type = stbi__get8(s); + int tga_is_RLE = 0; + int tga_palette_start = stbi__get16le(s); + int tga_palette_len = stbi__get16le(s); + int tga_palette_bits = stbi__get8(s); + int tga_x_origin = stbi__get16le(s); + int tga_y_origin = stbi__get16le(s); + int tga_width = stbi__get16le(s); + int tga_height = stbi__get16le(s); + int tga_bits_per_pixel = stbi__get8(s); + int tga_comp, tga_rgb16=0; + int tga_inverted = stbi__get8(s); + // int tga_alpha_bits = tga_inverted & 15; // the 4 lowest bits - unused (useless?) + // image data + unsigned char *tga_data; + unsigned char *tga_palette = NULL; + int i, j; + unsigned char raw_data[4]; + int RLE_count = 0; + int RLE_repeating = 0; + int read_next_pixel = 1; + + // do a tiny bit of precessing + if ( tga_image_type >= 8 ) + { + tga_image_type -= 8; + tga_is_RLE = 1; + } + tga_inverted = 1 - ((tga_inverted >> 5) & 1); + + // If I'm paletted, then I'll use the number of bits from the palette + if ( tga_indexed ) tga_comp = stbi__tga_get_comp(tga_palette_bits, 0, &tga_rgb16); + else tga_comp = stbi__tga_get_comp(tga_bits_per_pixel, (tga_image_type == 3), &tga_rgb16); + + if(!tga_comp) // shouldn't really happen, stbi__tga_test() should have ensured basic consistency + return stbi__errpuc("bad format", "Can't find out TGA pixelformat"); + + // tga info + *x = tga_width; + *y = tga_height; + if (comp) *comp = tga_comp; + + tga_data = (unsigned char*)stbi__malloc( (size_t)tga_width * tga_height * tga_comp ); + if (!tga_data) return stbi__errpuc("outofmem", "Out of memory"); + + // skip to the data's starting position (offset usually = 0) + stbi__skip(s, tga_offset ); + + if ( !tga_indexed && !tga_is_RLE && !tga_rgb16 ) { + for (i=0; i < tga_height; ++i) { + int row = tga_inverted ? tga_height -i - 1 : i; + stbi_uc *tga_row = tga_data + row*tga_width*tga_comp; + stbi__getn(s, tga_row, tga_width * tga_comp); + } + } else { + // do I need to load a palette? + if ( tga_indexed) + { + // any data to skip? (offset usually = 0) + stbi__skip(s, tga_palette_start ); + // load the palette + tga_palette = (unsigned char*)stbi__malloc( tga_palette_len * tga_comp ); + if (!tga_palette) { + STBI_FREE(tga_data); + return stbi__errpuc("outofmem", "Out of memory"); + } + if (tga_rgb16) { + stbi_uc *pal_entry = tga_palette; + STBI_ASSERT(tga_comp == STBI_rgb); + for (i=0; i < tga_palette_len; ++i) { + stbi__tga_read_rgb16(s, pal_entry); + pal_entry += tga_comp; + } + } else if (!stbi__getn(s, tga_palette, tga_palette_len * tga_comp)) { + STBI_FREE(tga_data); + STBI_FREE(tga_palette); + return stbi__errpuc("bad palette", "Corrupt TGA"); + } + } + // load the data + for (i=0; i < tga_width * tga_height; ++i) + { + // if I'm in RLE mode, do I need to get a RLE stbi__pngchunk? + if ( tga_is_RLE ) + { + if ( RLE_count == 0 ) + { + // yep, get the next byte as a RLE command + int RLE_cmd = stbi__get8(s); + RLE_count = 1 + (RLE_cmd & 127); + RLE_repeating = RLE_cmd >> 7; + read_next_pixel = 1; + } else if ( !RLE_repeating ) + { + read_next_pixel = 1; + } + } else + { + read_next_pixel = 1; + } + // OK, if I need to read a pixel, do it now + if ( read_next_pixel ) + { + // load however much data we did have + if ( tga_indexed ) + { + // read in index, then perform the lookup + int pal_idx = (tga_bits_per_pixel == 8) ? stbi__get8(s) : stbi__get16le(s); + if ( pal_idx >= tga_palette_len ) { + // invalid index + pal_idx = 0; + } + pal_idx *= tga_comp; + for (j = 0; j < tga_comp; ++j) { + raw_data[j] = tga_palette[pal_idx+j]; + } + } else if(tga_rgb16) { + STBI_ASSERT(tga_comp == STBI_rgb); + stbi__tga_read_rgb16(s, raw_data); + } else { + // read in the data raw + for (j = 0; j < tga_comp; ++j) { + raw_data[j] = stbi__get8(s); + } + } + // clear the reading flag for the next pixel + read_next_pixel = 0; + } // end of reading a pixel + + // copy data + for (j = 0; j < tga_comp; ++j) + tga_data[i*tga_comp+j] = raw_data[j]; + + // in case we're in RLE mode, keep counting down + --RLE_count; + } + // do I need to invert the image? + if ( tga_inverted ) + { + for (j = 0; j*2 < tga_height; ++j) + { + int index1 = j * tga_width * tga_comp; + int index2 = (tga_height - 1 - j) * tga_width * tga_comp; + for (i = tga_width * tga_comp; i > 0; --i) + { + unsigned char temp = tga_data[index1]; + tga_data[index1] = tga_data[index2]; + tga_data[index2] = temp; + ++index1; + ++index2; + } + } + } + // clear my palette, if I had one + if ( tga_palette != NULL ) + { + STBI_FREE( tga_palette ); + } + } + + // swap RGB - if the source data was RGB16, it already is in the right order + if (tga_comp >= 3 && !tga_rgb16) + { + unsigned char* tga_pixel = tga_data; + for (i=0; i < tga_width * tga_height; ++i) + { + unsigned char temp = tga_pixel[0]; + tga_pixel[0] = tga_pixel[2]; + tga_pixel[2] = temp; + tga_pixel += tga_comp; + } + } + + // convert to target component count + if (req_comp && req_comp != tga_comp) + tga_data = stbi__convert_format(tga_data, tga_comp, req_comp, tga_width, tga_height); + + // the things I do to get rid of an error message, and yet keep + // Microsoft's C compilers happy... [8^( + tga_palette_start = tga_palette_len = tga_palette_bits = + tga_x_origin = tga_y_origin = 0; + // OK, done + return tga_data; +} +#endif + +// ************************************************************************************************* +// Photoshop PSD loader -- PD by Thatcher Ulrich, integration by Nicolas Schulz, tweaked by STB + +#ifndef STBI_NO_PSD +static int stbi__psd_test(stbi__context *s) +{ + int r = (stbi__get32be(s) == 0x38425053); + stbi__rewind(s); + return r; +} + +static stbi_uc *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + int pixelCount; + int channelCount, compression; + int channel, i, count, len; + int bitdepth; + int w,h; + stbi_uc *out; + + // Check identifier + if (stbi__get32be(s) != 0x38425053) // "8BPS" + return stbi__errpuc("not PSD", "Corrupt PSD image"); + + // Check file type version. + if (stbi__get16be(s) != 1) + return stbi__errpuc("wrong version", "Unsupported version of PSD image"); + + // Skip 6 reserved bytes. + stbi__skip(s, 6 ); + + // Read the number of channels (R, G, B, A, etc). + channelCount = stbi__get16be(s); + if (channelCount < 0 || channelCount > 16) + return stbi__errpuc("wrong channel count", "Unsupported number of channels in PSD image"); + + // Read the rows and columns of the image. + h = stbi__get32be(s); + w = stbi__get32be(s); + + // Make sure the depth is 8 bits. + bitdepth = stbi__get16be(s); + if (bitdepth != 8 && bitdepth != 16) + return stbi__errpuc("unsupported bit depth", "PSD bit depth is not 8 or 16 bit"); + + // Make sure the color mode is RGB. + // Valid options are: + // 0: Bitmap + // 1: Grayscale + // 2: Indexed color + // 3: RGB color + // 4: CMYK color + // 7: Multichannel + // 8: Duotone + // 9: Lab color + if (stbi__get16be(s) != 3) + return stbi__errpuc("wrong color format", "PSD is not in RGB color format"); + + // Skip the Mode Data. (It's the palette for indexed color; other info for other modes.) + stbi__skip(s,stbi__get32be(s) ); + + // Skip the image resources. (resolution, pen tool paths, etc) + stbi__skip(s, stbi__get32be(s) ); + + // Skip the reserved data. + stbi__skip(s, stbi__get32be(s) ); + + // Find out if the data is compressed. + // Known values: + // 0: no compression + // 1: RLE compressed + compression = stbi__get16be(s); + if (compression > 1) + return stbi__errpuc("bad compression", "PSD has an unknown compression format"); + + // Create the destination image. + out = (stbi_uc *) stbi__malloc(4 * w*h); + if (!out) return stbi__errpuc("outofmem", "Out of memory"); + pixelCount = w*h; + + // Initialize the data to zero. + //memset( out, 0, pixelCount * 4 ); + + // Finally, the image data. + if (compression) { + // RLE as used by .PSD and .TIFF + // Loop until you get the number of unpacked bytes you are expecting: + // Read the next source byte into n. + // If n is between 0 and 127 inclusive, copy the next n+1 bytes literally. + // Else if n is between -127 and -1 inclusive, copy the next byte -n+1 times. + // Else if n is 128, noop. + // Endloop + + // The RLE-compressed data is preceeded by a 2-byte data count for each row in the data, + // which we're going to just skip. + stbi__skip(s, h * channelCount * 2 ); + + // Read the RLE data by channel. + for (channel = 0; channel < 4; channel++) { + stbi_uc *p; + + p = out+channel; + if (channel >= channelCount) { + // Fill this channel with default data. + for (i = 0; i < pixelCount; i++, p += 4) + *p = (channel == 3 ? 255 : 0); + } else { + // Read the RLE data. + count = 0; + while (count < pixelCount) { + len = stbi__get8(s); + if (len == 128) { + // No-op. + } else if (len < 128) { + // Copy next len+1 bytes literally. + len++; + count += len; + while (len) { + *p = stbi__get8(s); + p += 4; + len--; + } + } else if (len > 128) { + stbi_uc val; + // Next -len+1 bytes in the dest are replicated from next source byte. + // (Interpret len as a negative 8-bit int.) + len ^= 0x0FF; + len += 2; + val = stbi__get8(s); + count += len; + while (len) { + *p = val; + p += 4; + len--; + } + } + } + } + } + + } else { + // We're at the raw image data. It's each channel in order (Red, Green, Blue, Alpha, ...) + // where each channel consists of an 8-bit value for each pixel in the image. + + // Read the data by channel. + for (channel = 0; channel < 4; channel++) { + stbi_uc *p; + + p = out + channel; + if (channel >= channelCount) { + // Fill this channel with default data. + stbi_uc val = channel == 3 ? 255 : 0; + for (i = 0; i < pixelCount; i++, p += 4) + *p = val; + } else { + // Read the data. + if (bitdepth == 16) { + for (i = 0; i < pixelCount; i++, p += 4) + *p = (stbi_uc) (stbi__get16be(s) >> 8); + } else { + for (i = 0; i < pixelCount; i++, p += 4) + *p = stbi__get8(s); + } + } + } + } + + if (channelCount >= 4) { + for (i=0; i < w*h; ++i) { + unsigned char *pixel = out + 4*i; + if (pixel[3] != 0 && pixel[3] != 255) { + // remove weird white matte from PSD + float a = pixel[3] / 255.0f; + float ra = 1.0f / a; + float inv_a = 255.0f * (1 - ra); + pixel[0] = (unsigned char) (pixel[0]*ra + inv_a); + pixel[1] = (unsigned char) (pixel[1]*ra + inv_a); + pixel[2] = (unsigned char) (pixel[2]*ra + inv_a); + } + } + } + + if (req_comp && req_comp != 4) { + out = stbi__convert_format(out, 4, req_comp, w, h); + if (out == NULL) return out; // stbi__convert_format frees input on failure + } + + if (comp) *comp = 4; + *y = h; + *x = w; + + return out; +} +#endif + +// ************************************************************************************************* +// Softimage PIC loader +// by Tom Seddon +// +// See http://softimage.wiki.softimage.com/index.php/INFO:_PIC_file_format +// See http://ozviz.wasp.uwa.edu.au/~pbourke/dataformats/softimagepic/ + +#ifndef STBI_NO_PIC +static int stbi__pic_is4(stbi__context *s,const char *str) +{ + int i; + for (i=0; i<4; ++i) + if (stbi__get8(s) != (stbi_uc)str[i]) + return 0; + + return 1; +} + +static int stbi__pic_test_core(stbi__context *s) +{ + int i; + + if (!stbi__pic_is4(s,"\x53\x80\xF6\x34")) + return 0; + + for(i=0;i<84;++i) + stbi__get8(s); + + if (!stbi__pic_is4(s,"PICT")) + return 0; + + return 1; +} + +typedef struct +{ + stbi_uc size,type,channel; +} stbi__pic_packet; + +static stbi_uc *stbi__readval(stbi__context *s, int channel, stbi_uc *dest) +{ + int mask=0x80, i; + + for (i=0; i<4; ++i, mask>>=1) { + if (channel & mask) { + if (stbi__at_eof(s)) return stbi__errpuc("bad file","PIC file too short"); + dest[i]=stbi__get8(s); + } + } + + return dest; +} + +static void stbi__copyval(int channel,stbi_uc *dest,const stbi_uc *src) +{ + int mask=0x80,i; + + for (i=0;i<4; ++i, mask>>=1) + if (channel&mask) + dest[i]=src[i]; +} + +static stbi_uc *stbi__pic_load_core(stbi__context *s,int width,int height,int *comp, stbi_uc *result) +{ + int act_comp=0,num_packets=0,y,chained; + stbi__pic_packet packets[10]; + + // this will (should...) cater for even some bizarre stuff like having data + // for the same channel in multiple packets. + do { + stbi__pic_packet *packet; + + if (num_packets==sizeof(packets)/sizeof(packets[0])) + return stbi__errpuc("bad format","too many packets"); + + packet = &packets[num_packets++]; + + chained = stbi__get8(s); + packet->size = stbi__get8(s); + packet->type = stbi__get8(s); + packet->channel = stbi__get8(s); + + act_comp |= packet->channel; + + if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (reading packets)"); + if (packet->size != 8) return stbi__errpuc("bad format","packet isn't 8bpp"); + } while (chained); + + *comp = (act_comp & 0x10 ? 4 : 3); // has alpha channel? + + for(y=0; ytype) { + default: + return stbi__errpuc("bad format","packet has bad compression type"); + + case 0: {//uncompressed + int x; + + for(x=0;xchannel,dest)) + return 0; + break; + } + + case 1://Pure RLE + { + int left=width, i; + + while (left>0) { + stbi_uc count; + stbi_uc value[ 4 ] = { 0 }; + + count=stbi__get8(s); + if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (pure read count)"); + + if (count > left) + count = (stbi_uc) left; + + if (!stbi__readval(s,packet->channel,value)) return 0; + + for(i=0; ichannel,dest,value); + left -= count; + } + } + break; + + case 2: {//Mixed RLE + int left=width; + while (left>0) { + int count = stbi__get8(s), i; + if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (mixed read count)"); + + if (count >= 128) { // Repeated + stbi_uc value[4] = { 0 }; + int i; + + if (count==128) + count = stbi__get16be(s); + else + count -= 127; + if (count > left) + return stbi__errpuc("bad file","scanline overrun"); + + if (!stbi__readval(s,packet->channel,value)) + return 0; + + for(i=0;ichannel,dest,value); + } else { // Raw + ++count; + if (count>left) return stbi__errpuc("bad file","scanline overrun"); + + for(i=0;ichannel,dest)) + return 0; + } + left-=count; + } + break; + } + } + } + } + + return result; +} + +static stbi_uc *stbi__pic_load(stbi__context *s,int *px,int *py,int *comp,int req_comp) +{ + stbi_uc *result; + int i, x,y; + + for (i=0; i<92; ++i) + stbi__get8(s); + + x = stbi__get16be(s); + y = stbi__get16be(s); + if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (pic header)"); + if ((1 << 28) / x < y) return stbi__errpuc("too large", "Image too large to decode"); + + stbi__get32be(s); //skip `ratio' + stbi__get16be(s); //skip `fields' + stbi__get16be(s); //skip `pad' + + // intermediate buffer is RGBA + result = (stbi_uc *) stbi__malloc(x*y*4); + memset(result, 0xff, x*y*4); + + if (!stbi__pic_load_core(s,x,y,comp, result)) { + STBI_FREE(result); + result=0; + } + *px = x; + *py = y; + if (req_comp == 0) req_comp = *comp; + result=stbi__convert_format(result,4,req_comp,x,y); + + return result; +} + +static int stbi__pic_test(stbi__context *s) +{ + int r = stbi__pic_test_core(s); + stbi__rewind(s); + return r; +} +#endif + +// ************************************************************************************************* +// GIF loader -- public domain by Jean-Marc Lienher -- simplified/shrunk by stb + +#ifndef STBI_NO_GIF +typedef struct +{ + stbi__int16 prefix; + stbi_uc first; + stbi_uc suffix; +} stbi__gif_lzw; + +typedef struct +{ + int w,h; + stbi_uc *out, *old_out; // output buffer (always 4 components) + int flags, bgindex, ratio, transparent, eflags, delay; + stbi_uc pal[256][4]; + stbi_uc lpal[256][4]; + stbi__gif_lzw codes[4096]; + stbi_uc *color_table; + int parse, step; + int lflags; + int start_x, start_y; + int max_x, max_y; + int cur_x, cur_y; + int line_size; +} stbi__gif; + +static int stbi__gif_test_raw(stbi__context *s) +{ + int sz; + if (stbi__get8(s) != 'G' || stbi__get8(s) != 'I' || stbi__get8(s) != 'F' || stbi__get8(s) != '8') return 0; + sz = stbi__get8(s); + if (sz != '9' && sz != '7') return 0; + if (stbi__get8(s) != 'a') return 0; + return 1; +} + +static int stbi__gif_test(stbi__context *s) +{ + int r = stbi__gif_test_raw(s); + stbi__rewind(s); + return r; +} + +static void stbi__gif_parse_colortable(stbi__context *s, stbi_uc pal[256][4], int num_entries, int transp) +{ + int i; + for (i=0; i < num_entries; ++i) { + pal[i][2] = stbi__get8(s); + pal[i][1] = stbi__get8(s); + pal[i][0] = stbi__get8(s); + pal[i][3] = transp == i ? 0 : 255; + } +} + +static int stbi__gif_header(stbi__context *s, stbi__gif *g, int *comp, int is_info) +{ + stbi_uc version; + if (stbi__get8(s) != 'G' || stbi__get8(s) != 'I' || stbi__get8(s) != 'F' || stbi__get8(s) != '8') + return stbi__err("not GIF", "Corrupt GIF"); + + version = stbi__get8(s); + if (version != '7' && version != '9') return stbi__err("not GIF", "Corrupt GIF"); + if (stbi__get8(s) != 'a') return stbi__err("not GIF", "Corrupt GIF"); + + stbi__g_failure_reason = ""; + g->w = stbi__get16le(s); + g->h = stbi__get16le(s); + g->flags = stbi__get8(s); + g->bgindex = stbi__get8(s); + g->ratio = stbi__get8(s); + g->transparent = -1; + + if (comp != 0) *comp = 4; // can't actually tell whether it's 3 or 4 until we parse the comments + + if (is_info) return 1; + + if (g->flags & 0x80) + stbi__gif_parse_colortable(s,g->pal, 2 << (g->flags & 7), -1); + + return 1; +} + +static int stbi__gif_info_raw(stbi__context *s, int *x, int *y, int *comp) +{ + stbi__gif* g = (stbi__gif*) stbi__malloc(sizeof(stbi__gif)); + if (!stbi__gif_header(s, g, comp, 1)) { + STBI_FREE(g); + stbi__rewind( s ); + return 0; + } + if (x) *x = g->w; + if (y) *y = g->h; + STBI_FREE(g); + return 1; +} + +static void stbi__out_gif_code(stbi__gif *g, stbi__uint16 code) +{ + stbi_uc *p, *c; + + // recurse to decode the prefixes, since the linked-list is backwards, + // and working backwards through an interleaved image would be nasty + if (g->codes[code].prefix >= 0) + stbi__out_gif_code(g, g->codes[code].prefix); + + if (g->cur_y >= g->max_y) return; + + p = &g->out[g->cur_x + g->cur_y]; + c = &g->color_table[g->codes[code].suffix * 4]; + + if (c[3] >= 128) { + p[0] = c[2]; + p[1] = c[1]; + p[2] = c[0]; + p[3] = c[3]; + } + g->cur_x += 4; + + if (g->cur_x >= g->max_x) { + g->cur_x = g->start_x; + g->cur_y += g->step; + + while (g->cur_y >= g->max_y && g->parse > 0) { + g->step = (1 << g->parse) * g->line_size; + g->cur_y = g->start_y + (g->step >> 1); + --g->parse; + } + } +} + +static stbi_uc *stbi__process_gif_raster(stbi__context *s, stbi__gif *g) +{ + stbi_uc lzw_cs; + stbi__int32 len, init_code; + stbi__uint32 first; + stbi__int32 codesize, codemask, avail, oldcode, bits, valid_bits, clear; + stbi__gif_lzw *p; + + lzw_cs = stbi__get8(s); + if (lzw_cs > 12) return NULL; + clear = 1 << lzw_cs; + first = 1; + codesize = lzw_cs + 1; + codemask = (1 << codesize) - 1; + bits = 0; + valid_bits = 0; + for (init_code = 0; init_code < clear; init_code++) { + g->codes[init_code].prefix = -1; + g->codes[init_code].first = (stbi_uc) init_code; + g->codes[init_code].suffix = (stbi_uc) init_code; + } + + // support no starting clear code + avail = clear+2; + oldcode = -1; + + len = 0; + for(;;) { + if (valid_bits < codesize) { + if (len == 0) { + len = stbi__get8(s); // start new block + if (len == 0) + return g->out; + } + --len; + bits |= (stbi__int32) stbi__get8(s) << valid_bits; + valid_bits += 8; + } else { + stbi__int32 code = bits & codemask; + bits >>= codesize; + valid_bits -= codesize; + // @OPTIMIZE: is there some way we can accelerate the non-clear path? + if (code == clear) { // clear code + codesize = lzw_cs + 1; + codemask = (1 << codesize) - 1; + avail = clear + 2; + oldcode = -1; + first = 0; + } else if (code == clear + 1) { // end of stream code + stbi__skip(s, len); + while ((len = stbi__get8(s)) > 0) + stbi__skip(s,len); + return g->out; + } else if (code <= avail) { + if (first) return stbi__errpuc("no clear code", "Corrupt GIF"); + + if (oldcode >= 0) { + p = &g->codes[avail++]; + if (avail > 4096) return stbi__errpuc("too many codes", "Corrupt GIF"); + p->prefix = (stbi__int16) oldcode; + p->first = g->codes[oldcode].first; + p->suffix = (code == avail) ? p->first : g->codes[code].first; + } else if (code == avail) + return stbi__errpuc("illegal code in raster", "Corrupt GIF"); + + stbi__out_gif_code(g, (stbi__uint16) code); + + if ((avail & codemask) == 0 && avail <= 0x0FFF) { + codesize++; + codemask = (1 << codesize) - 1; + } + + oldcode = code; + } else { + return stbi__errpuc("illegal code in raster", "Corrupt GIF"); + } + } + } +} + +static void stbi__fill_gif_background(stbi__gif *g, int x0, int y0, int x1, int y1) +{ + int x, y; + stbi_uc *c = g->pal[g->bgindex]; + for (y = y0; y < y1; y += 4 * g->w) { + for (x = x0; x < x1; x += 4) { + stbi_uc *p = &g->out[y + x]; + p[0] = c[2]; + p[1] = c[1]; + p[2] = c[0]; + p[3] = 0; + } + } +} + +// this function is designed to support animated gifs, although stb_image doesn't support it +static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, int req_comp) +{ + int i; + stbi_uc *prev_out = 0; + + if (g->out == 0 && !stbi__gif_header(s, g, comp,0)) + return 0; // stbi__g_failure_reason set by stbi__gif_header + + prev_out = g->out; + g->out = (stbi_uc *) stbi__malloc(4 * g->w * g->h); + if (g->out == 0) return stbi__errpuc("outofmem", "Out of memory"); + + switch ((g->eflags & 0x1C) >> 2) { + case 0: // unspecified (also always used on 1st frame) + stbi__fill_gif_background(g, 0, 0, 4 * g->w, 4 * g->w * g->h); + break; + case 1: // do not dispose + if (prev_out) memcpy(g->out, prev_out, 4 * g->w * g->h); + g->old_out = prev_out; + break; + case 2: // dispose to background + if (prev_out) memcpy(g->out, prev_out, 4 * g->w * g->h); + stbi__fill_gif_background(g, g->start_x, g->start_y, g->max_x, g->max_y); + break; + case 3: // dispose to previous + if (g->old_out) { + for (i = g->start_y; i < g->max_y; i += 4 * g->w) + memcpy(&g->out[i + g->start_x], &g->old_out[i + g->start_x], g->max_x - g->start_x); + } + break; + } + + for (;;) { + switch (stbi__get8(s)) { + case 0x2C: /* Image Descriptor */ + { + int prev_trans = -1; + stbi__int32 x, y, w, h; + stbi_uc *o; + + x = stbi__get16le(s); + y = stbi__get16le(s); + w = stbi__get16le(s); + h = stbi__get16le(s); + if (((x + w) > (g->w)) || ((y + h) > (g->h))) + return stbi__errpuc("bad Image Descriptor", "Corrupt GIF"); + + g->line_size = g->w * 4; + g->start_x = x * 4; + g->start_y = y * g->line_size; + g->max_x = g->start_x + w * 4; + g->max_y = g->start_y + h * g->line_size; + g->cur_x = g->start_x; + g->cur_y = g->start_y; + + g->lflags = stbi__get8(s); + + if (g->lflags & 0x40) { + g->step = 8 * g->line_size; // first interlaced spacing + g->parse = 3; + } else { + g->step = g->line_size; + g->parse = 0; + } + + if (g->lflags & 0x80) { + stbi__gif_parse_colortable(s,g->lpal, 2 << (g->lflags & 7), g->eflags & 0x01 ? g->transparent : -1); + g->color_table = (stbi_uc *) g->lpal; + } else if (g->flags & 0x80) { + if (g->transparent >= 0 && (g->eflags & 0x01)) { + prev_trans = g->pal[g->transparent][3]; + g->pal[g->transparent][3] = 0; + } + g->color_table = (stbi_uc *) g->pal; + } else + return stbi__errpuc("missing color table", "Corrupt GIF"); + + o = stbi__process_gif_raster(s, g); + if (o == NULL) return NULL; + + if (prev_trans != -1) + g->pal[g->transparent][3] = (stbi_uc) prev_trans; + + return o; + } + + case 0x21: // Comment Extension. + { + int len; + if (stbi__get8(s) == 0xF9) { // Graphic Control Extension. + len = stbi__get8(s); + if (len == 4) { + g->eflags = stbi__get8(s); + g->delay = stbi__get16le(s); + g->transparent = stbi__get8(s); + } else { + stbi__skip(s, len); + break; + } + } + while ((len = stbi__get8(s)) != 0) + stbi__skip(s, len); + break; + } + + case 0x3B: // gif stream termination code + return (stbi_uc *) s; // using '1' causes warning on some compilers + + default: + return stbi__errpuc("unknown code", "Corrupt GIF"); + } + } + + STBI_NOTUSED(req_comp); +} + +static stbi_uc *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + stbi_uc *u = 0; + stbi__gif* g = (stbi__gif*) stbi__malloc(sizeof(stbi__gif)); + memset(g, 0, sizeof(*g)); + + u = stbi__gif_load_next(s, g, comp, req_comp); + if (u == (stbi_uc *) s) u = 0; // end of animated gif marker + if (u) { + *x = g->w; + *y = g->h; + if (req_comp && req_comp != 4) + u = stbi__convert_format(u, 4, req_comp, g->w, g->h); + } + else if (g->out) + STBI_FREE(g->out); + STBI_FREE(g); + return u; +} + +static int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp) +{ + return stbi__gif_info_raw(s,x,y,comp); +} +#endif + +// ************************************************************************************************* +// Radiance RGBE HDR loader +// originally by Nicolas Schulz +#ifndef STBI_NO_HDR +static int stbi__hdr_test_core(stbi__context *s) +{ + const char *signature = "#?RADIANCE\n"; + int i; + for (i=0; signature[i]; ++i) + if (stbi__get8(s) != signature[i]) + return 0; + return 1; +} + +static int stbi__hdr_test(stbi__context* s) +{ + int r = stbi__hdr_test_core(s); + stbi__rewind(s); + return r; +} + +#define STBI__HDR_BUFLEN 1024 +static char *stbi__hdr_gettoken(stbi__context *z, char *buffer) +{ + int len=0; + char c = '\0'; + + c = (char) stbi__get8(z); + + while (!stbi__at_eof(z) && c != '\n') { + buffer[len++] = c; + if (len == STBI__HDR_BUFLEN-1) { + // flush to end of line + while (!stbi__at_eof(z) && stbi__get8(z) != '\n') + ; + break; + } + c = (char) stbi__get8(z); + } + + buffer[len] = 0; + return buffer; +} + +static void stbi__hdr_convert(float *output, stbi_uc *input, int req_comp) +{ + if ( input[3] != 0 ) { + float f1; + // Exponent + f1 = (float) ldexp(1.0f, input[3] - (int)(128 + 8)); + if (req_comp <= 2) + output[0] = (input[0] + input[1] + input[2]) * f1 / 3; + else { + output[0] = input[0] * f1; + output[1] = input[1] * f1; + output[2] = input[2] * f1; + } + if (req_comp == 2) output[1] = 1; + if (req_comp == 4) output[3] = 1; + } else { + switch (req_comp) { + case 4: output[3] = 1; /* fallthrough */ + case 3: output[0] = output[1] = output[2] = 0; + break; + case 2: output[1] = 1; /* fallthrough */ + case 1: output[0] = 0; + break; + } + } +} + +static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + char buffer[STBI__HDR_BUFLEN]; + char *token; + int valid = 0; + int width, height; + stbi_uc *scanline; + float *hdr_data; + int len; + unsigned char count, value; + int i, j, k, c1,c2, z; + + + // Check identifier + if (strcmp(stbi__hdr_gettoken(s,buffer), "#?RADIANCE") != 0) + return stbi__errpf("not HDR", "Corrupt HDR image"); + + // Parse header + for(;;) { + token = stbi__hdr_gettoken(s,buffer); + if (token[0] == 0) break; + if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1; + } + + if (!valid) return stbi__errpf("unsupported format", "Unsupported HDR format"); + + // Parse width and height + // can't use sscanf() if we're not using stdio! + token = stbi__hdr_gettoken(s,buffer); + if (strncmp(token, "-Y ", 3)) return stbi__errpf("unsupported data layout", "Unsupported HDR format"); + token += 3; + height = (int) strtol(token, &token, 10); + while (*token == ' ') ++token; + if (strncmp(token, "+X ", 3)) return stbi__errpf("unsupported data layout", "Unsupported HDR format"); + token += 3; + width = (int) strtol(token, NULL, 10); + + *x = width; + *y = height; + + if (comp) *comp = 3; + if (req_comp == 0) req_comp = 3; + + // Read data + hdr_data = (float *) stbi__malloc(height * width * req_comp * sizeof(float)); + + // Load image data + // image data is stored as some number of sca + if ( width < 8 || width >= 32768) { + // Read flat data + for (j=0; j < height; ++j) { + for (i=0; i < width; ++i) { + stbi_uc rgbe[4]; + main_decode_loop: + stbi__getn(s, rgbe, 4); + stbi__hdr_convert(hdr_data + j * width * req_comp + i * req_comp, rgbe, req_comp); + } + } + } else { + // Read RLE-encoded data + scanline = NULL; + + for (j = 0; j < height; ++j) { + c1 = stbi__get8(s); + c2 = stbi__get8(s); + len = stbi__get8(s); + if (c1 != 2 || c2 != 2 || (len & 0x80)) { + // not run-length encoded, so we have to actually use THIS data as a decoded + // pixel (note this can't be a valid pixel--one of RGB must be >= 128) + stbi_uc rgbe[4]; + rgbe[0] = (stbi_uc) c1; + rgbe[1] = (stbi_uc) c2; + rgbe[2] = (stbi_uc) len; + rgbe[3] = (stbi_uc) stbi__get8(s); + stbi__hdr_convert(hdr_data, rgbe, req_comp); + i = 1; + j = 0; + STBI_FREE(scanline); + goto main_decode_loop; // yes, this makes no sense + } + len <<= 8; + len |= stbi__get8(s); + if (len != width) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("invalid decoded scanline length", "corrupt HDR"); } + if (scanline == NULL) scanline = (stbi_uc *) stbi__malloc(width * 4); + + for (k = 0; k < 4; ++k) { + i = 0; + while (i < width) { + count = stbi__get8(s); + if (count > 128) { + // Run + value = stbi__get8(s); + count -= 128; + for (z = 0; z < count; ++z) + scanline[i++ * 4 + k] = value; + } else { + // Dump + for (z = 0; z < count; ++z) + scanline[i++ * 4 + k] = stbi__get8(s); + } + } + } + for (i=0; i < width; ++i) + stbi__hdr_convert(hdr_data+(j*width + i)*req_comp, scanline + i*4, req_comp); + } + STBI_FREE(scanline); + } + + return hdr_data; +} + +static int stbi__hdr_info(stbi__context *s, int *x, int *y, int *comp) +{ + char buffer[STBI__HDR_BUFLEN]; + char *token; + int valid = 0; + + if (stbi__hdr_test(s) == 0) { + stbi__rewind( s ); + return 0; + } + + for(;;) { + token = stbi__hdr_gettoken(s,buffer); + if (token[0] == 0) break; + if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1; + } + + if (!valid) { + stbi__rewind( s ); + return 0; + } + token = stbi__hdr_gettoken(s,buffer); + if (strncmp(token, "-Y ", 3)) { + stbi__rewind( s ); + return 0; + } + token += 3; + *y = (int) strtol(token, &token, 10); + while (*token == ' ') ++token; + if (strncmp(token, "+X ", 3)) { + stbi__rewind( s ); + return 0; + } + token += 3; + *x = (int) strtol(token, NULL, 10); + *comp = 3; + return 1; +} +#endif // STBI_NO_HDR + +#ifndef STBI_NO_BMP +static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp) +{ + void *p; + stbi__bmp_data info; + + info.all_a = 255; + p = stbi__bmp_parse_header(s, &info); + stbi__rewind( s ); + if (p == NULL) + return 0; + *x = s->img_x; + *y = s->img_y; + *comp = info.ma ? 4 : 3; + return 1; +} +#endif + +#ifndef STBI_NO_PSD +static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp) +{ + int channelCount; + if (stbi__get32be(s) != 0x38425053) { + stbi__rewind( s ); + return 0; + } + if (stbi__get16be(s) != 1) { + stbi__rewind( s ); + return 0; + } + stbi__skip(s, 6); + channelCount = stbi__get16be(s); + if (channelCount < 0 || channelCount > 16) { + stbi__rewind( s ); + return 0; + } + *y = stbi__get32be(s); + *x = stbi__get32be(s); + if (stbi__get16be(s) != 8) { + stbi__rewind( s ); + return 0; + } + if (stbi__get16be(s) != 3) { + stbi__rewind( s ); + return 0; + } + *comp = 4; + return 1; +} +#endif + +#ifndef STBI_NO_PIC +static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp) +{ + int act_comp=0,num_packets=0,chained; + stbi__pic_packet packets[10]; + + if (!stbi__pic_is4(s,"\x53\x80\xF6\x34")) { + stbi__rewind(s); + return 0; + } + + stbi__skip(s, 88); + + *x = stbi__get16be(s); + *y = stbi__get16be(s); + if (stbi__at_eof(s)) { + stbi__rewind( s); + return 0; + } + if ( (*x) != 0 && (1 << 28) / (*x) < (*y)) { + stbi__rewind( s ); + return 0; + } + + stbi__skip(s, 8); + + do { + stbi__pic_packet *packet; + + if (num_packets==sizeof(packets)/sizeof(packets[0])) + return 0; + + packet = &packets[num_packets++]; + chained = stbi__get8(s); + packet->size = stbi__get8(s); + packet->type = stbi__get8(s); + packet->channel = stbi__get8(s); + act_comp |= packet->channel; + + if (stbi__at_eof(s)) { + stbi__rewind( s ); + return 0; + } + if (packet->size != 8) { + stbi__rewind( s ); + return 0; + } + } while (chained); + + *comp = (act_comp & 0x10 ? 4 : 3); + + return 1; +} +#endif + +// ************************************************************************************************* +// Portable Gray Map and Portable Pixel Map loader +// by Ken Miller +// +// PGM: http://netpbm.sourceforge.net/doc/pgm.html +// PPM: http://netpbm.sourceforge.net/doc/ppm.html +// +// Known limitations: +// Does not support comments in the header section +// Does not support ASCII image data (formats P2 and P3) +// Does not support 16-bit-per-channel + +#ifndef STBI_NO_PNM + +static int stbi__pnm_test(stbi__context *s) +{ + char p, t; + p = (char) stbi__get8(s); + t = (char) stbi__get8(s); + if (p != 'P' || (t != '5' && t != '6')) { + stbi__rewind( s ); + return 0; + } + return 1; +} + +static stbi_uc *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + stbi_uc *out; + if (!stbi__pnm_info(s, (int *)&s->img_x, (int *)&s->img_y, (int *)&s->img_n)) + return 0; + *x = s->img_x; + *y = s->img_y; + *comp = s->img_n; + + out = (stbi_uc *) stbi__malloc(s->img_n * s->img_x * s->img_y); + if (!out) return stbi__errpuc("outofmem", "Out of memory"); + stbi__getn(s, out, s->img_n * s->img_x * s->img_y); + + if (req_comp && req_comp != s->img_n) { + out = stbi__convert_format(out, s->img_n, req_comp, s->img_x, s->img_y); + if (out == NULL) return out; // stbi__convert_format frees input on failure + } + return out; +} + +static int stbi__pnm_isspace(char c) +{ + return c == ' ' || c == '\t' || c == '\n' || c == '\v' || c == '\f' || c == '\r'; +} + +static void stbi__pnm_skip_whitespace(stbi__context *s, char *c) +{ + for (;;) { + while (!stbi__at_eof(s) && stbi__pnm_isspace(*c)) + *c = (char) stbi__get8(s); + + if (stbi__at_eof(s) || *c != '#') + break; + + while (!stbi__at_eof(s) && *c != '\n' && *c != '\r' ) + *c = (char) stbi__get8(s); + } +} + +static int stbi__pnm_isdigit(char c) +{ + return c >= '0' && c <= '9'; +} + +static int stbi__pnm_getinteger(stbi__context *s, char *c) +{ + int value = 0; + + while (!stbi__at_eof(s) && stbi__pnm_isdigit(*c)) { + value = value*10 + (*c - '0'); + *c = (char) stbi__get8(s); + } + + return value; +} + +static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp) +{ + int maxv; + char c, p, t; + + stbi__rewind( s ); + + // Get identifier + p = (char) stbi__get8(s); + t = (char) stbi__get8(s); + if (p != 'P' || (t != '5' && t != '6')) { + stbi__rewind( s ); + return 0; + } + + *comp = (t == '6') ? 3 : 1; // '5' is 1-component .pgm; '6' is 3-component .ppm + + c = (char) stbi__get8(s); + stbi__pnm_skip_whitespace(s, &c); + + *x = stbi__pnm_getinteger(s, &c); // read width + stbi__pnm_skip_whitespace(s, &c); + + *y = stbi__pnm_getinteger(s, &c); // read height + stbi__pnm_skip_whitespace(s, &c); + + maxv = stbi__pnm_getinteger(s, &c); // read max value + + if (maxv > 255) + return stbi__err("max value > 255", "PPM image not 8-bit"); + else + return 1; +} +#endif + +static int stbi__info_main(stbi__context *s, int *x, int *y, int *comp) +{ + #ifndef STBI_NO_JPEG + if (stbi__jpeg_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_PNG + if (stbi__png_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_GIF + if (stbi__gif_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_BMP + if (stbi__bmp_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_PSD + if (stbi__psd_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_PIC + if (stbi__pic_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_PNM + if (stbi__pnm_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_HDR + if (stbi__hdr_info(s, x, y, comp)) return 1; + #endif + + // test tga last because it's a crappy test! + #ifndef STBI_NO_TGA + if (stbi__tga_info(s, x, y, comp)) + return 1; + #endif + return stbi__err("unknown image type", "Image not of any known type, or corrupt"); +} + +#ifndef STBI_NO_STDIO +STBIDEF int stbi_info(char const *filename, int *x, int *y, int *comp) +{ + FILE *f = stbi__fopen(filename, "rb"); + int result; + if (!f) return stbi__err("can't fopen", "Unable to open file"); + result = stbi_info_from_file(f, x, y, comp); + fclose(f); + return result; +} + +STBIDEF int stbi_info_from_file(FILE *f, int *x, int *y, int *comp) +{ + int r; + stbi__context s; + long pos = ftell(f); + stbi__start_file(&s, f); + r = stbi__info_main(&s,x,y,comp); + fseek(f,pos,SEEK_SET); + return r; +} +#endif // !STBI_NO_STDIO + +STBIDEF int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp) +{ + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__info_main(&s,x,y,comp); +} + +STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *c, void *user, int *x, int *y, int *comp) +{ + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) c, user); + return stbi__info_main(&s,x,y,comp); +} + +#endif // STB_IMAGE_IMPLEMENTATION + +/* + revision history: + 2.12 (2016-04-02) fix typo in 2.11 PSD fix that caused crashes + 2.11 (2016-04-02) allocate large structures on the stack + remove white matting for transparent PSD + fix reported channel count for PNG & BMP + re-enable SSE2 in non-gcc 64-bit + support RGB-formatted JPEG + read 16-bit PNGs (only as 8-bit) + 2.10 (2016-01-22) avoid warning introduced in 2.09 by STBI_REALLOC_SIZED + 2.09 (2016-01-16) allow comments in PNM files + 16-bit-per-pixel TGA (not bit-per-component) + info() for TGA could break due to .hdr handling + info() for BMP to shares code instead of sloppy parse + can use STBI_REALLOC_SIZED if allocator doesn't support realloc + code cleanup + 2.08 (2015-09-13) fix to 2.07 cleanup, reading RGB PSD as RGBA + 2.07 (2015-09-13) fix compiler warnings + partial animated GIF support + limited 16-bpc PSD support + #ifdef unused functions + bug with < 92 byte PIC,PNM,HDR,TGA + 2.06 (2015-04-19) fix bug where PSD returns wrong '*comp' value + 2.05 (2015-04-19) fix bug in progressive JPEG handling, fix warning + 2.04 (2015-04-15) try to re-enable SIMD on MinGW 64-bit + 2.03 (2015-04-12) extra corruption checking (mmozeiko) + stbi_set_flip_vertically_on_load (nguillemot) + fix NEON support; fix mingw support + 2.02 (2015-01-19) fix incorrect assert, fix warning + 2.01 (2015-01-17) fix various warnings; suppress SIMD on gcc 32-bit without -msse2 + 2.00b (2014-12-25) fix STBI_MALLOC in progressive JPEG + 2.00 (2014-12-25) optimize JPG, including x86 SSE2 & NEON SIMD (ryg) + progressive JPEG (stb) + PGM/PPM support (Ken Miller) + STBI_MALLOC,STBI_REALLOC,STBI_FREE + GIF bugfix -- seemingly never worked + STBI_NO_*, STBI_ONLY_* + 1.48 (2014-12-14) fix incorrectly-named assert() + 1.47 (2014-12-14) 1/2/4-bit PNG support, both direct and paletted (Omar Cornut & stb) + optimize PNG (ryg) + fix bug in interlaced PNG with user-specified channel count (stb) + 1.46 (2014-08-26) + fix broken tRNS chunk (colorkey-style transparency) in non-paletted PNG + 1.45 (2014-08-16) + fix MSVC-ARM internal compiler error by wrapping malloc + 1.44 (2014-08-07) + various warning fixes from Ronny Chevalier + 1.43 (2014-07-15) + fix MSVC-only compiler problem in code changed in 1.42 + 1.42 (2014-07-09) + don't define _CRT_SECURE_NO_WARNINGS (affects user code) + fixes to stbi__cleanup_jpeg path + added STBI_ASSERT to avoid requiring assert.h + 1.41 (2014-06-25) + fix search&replace from 1.36 that messed up comments/error messages + 1.40 (2014-06-22) + fix gcc struct-initialization warning + 1.39 (2014-06-15) + fix to TGA optimization when req_comp != number of components in TGA; + fix to GIF loading because BMP wasn't rewinding (whoops, no GIFs in my test suite) + add support for BMP version 5 (more ignored fields) + 1.38 (2014-06-06) + suppress MSVC warnings on integer casts truncating values + fix accidental rename of 'skip' field of I/O + 1.37 (2014-06-04) + remove duplicate typedef + 1.36 (2014-06-03) + convert to header file single-file library + if de-iphone isn't set, load iphone images color-swapped instead of returning NULL + 1.35 (2014-05-27) + various warnings + fix broken STBI_SIMD path + fix bug where stbi_load_from_file no longer left file pointer in correct place + fix broken non-easy path for 32-bit BMP (possibly never used) + TGA optimization by Arseny Kapoulkine + 1.34 (unknown) + use STBI_NOTUSED in stbi__resample_row_generic(), fix one more leak in tga failure case + 1.33 (2011-07-14) + make stbi_is_hdr work in STBI_NO_HDR (as specified), minor compiler-friendly improvements + 1.32 (2011-07-13) + support for "info" function for all supported filetypes (SpartanJ) + 1.31 (2011-06-20) + a few more leak fixes, bug in PNG handling (SpartanJ) + 1.30 (2011-06-11) + added ability to load files via callbacks to accomidate custom input streams (Ben Wenger) + removed deprecated format-specific test/load functions + removed support for installable file formats (stbi_loader) -- would have been broken for IO callbacks anyway + error cases in bmp and tga give messages and don't leak (Raymond Barbiero, grisha) + fix inefficiency in decoding 32-bit BMP (David Woo) + 1.29 (2010-08-16) + various warning fixes from Aurelien Pocheville + 1.28 (2010-08-01) + fix bug in GIF palette transparency (SpartanJ) + 1.27 (2010-08-01) + cast-to-stbi_uc to fix warnings + 1.26 (2010-07-24) + fix bug in file buffering for PNG reported by SpartanJ + 1.25 (2010-07-17) + refix trans_data warning (Won Chun) + 1.24 (2010-07-12) + perf improvements reading from files on platforms with lock-heavy fgetc() + minor perf improvements for jpeg + deprecated type-specific functions so we'll get feedback if they're needed + attempt to fix trans_data warning (Won Chun) + 1.23 fixed bug in iPhone support + 1.22 (2010-07-10) + removed image *writing* support + stbi_info support from Jetro Lauha + GIF support from Jean-Marc Lienher + iPhone PNG-extensions from James Brown + warning-fixes from Nicolas Schulz and Janez Zemva (i.stbi__err. Janez (U+017D)emva) + 1.21 fix use of 'stbi_uc' in header (reported by jon blow) + 1.20 added support for Softimage PIC, by Tom Seddon + 1.19 bug in interlaced PNG corruption check (found by ryg) + 1.18 (2008-08-02) + fix a threading bug (local mutable static) + 1.17 support interlaced PNG + 1.16 major bugfix - stbi__convert_format converted one too many pixels + 1.15 initialize some fields for thread safety + 1.14 fix threadsafe conversion bug + header-file-only version (#define STBI_HEADER_FILE_ONLY before including) + 1.13 threadsafe + 1.12 const qualifiers in the API + 1.11 Support installable IDCT, colorspace conversion routines + 1.10 Fixes for 64-bit (don't use "unsigned long") + optimized upsampling by Fabian "ryg" Giesen + 1.09 Fix format-conversion for PSD code (bad global variables!) + 1.08 Thatcher Ulrich's PSD code integrated by Nicolas Schulz + 1.07 attempt to fix C++ warning/errors again + 1.06 attempt to fix C++ warning/errors again + 1.05 fix TGA loading to return correct *comp and use good luminance calc + 1.04 default float alpha is 1, not 255; use 'void *' for stbi_image_free + 1.03 bugfixes to STBI_NO_STDIO, STBI_NO_HDR + 1.02 support for (subset of) HDR files, float interface for preferred access to them + 1.01 fix bug: possible bug in handling right-side up bmps... not sure + fix bug: the stbi__bmp_load() and stbi__tga_load() functions didn't work at all + 1.00 interface to zlib that skips zlib header + 0.99 correct handling of alpha in palette + 0.98 TGA loader by lonesock; dynamically add loaders (untested) + 0.97 jpeg errors on too large a file; also catch another malloc failure + 0.96 fix detection of invalid v value - particleman@mollyrocket forum + 0.95 during header scan, seek to markers in case of padding + 0.94 STBI_NO_STDIO to disable stdio usage; rename all #defines the same + 0.93 handle jpegtran output; verbose errors + 0.92 read 4,8,16,24,32-bit BMP files of several formats + 0.91 output 24-bit Windows 3.0 BMP files + 0.90 fix a few more warnings; bump version number to approach 1.0 + 0.61 bugfixes due to Marc LeBlanc, Christopher Lloyd + 0.60 fix compiling as c++ + 0.59 fix warnings: merge Dave Moore's -Wall fixes + 0.58 fix bug: zlib uncompressed mode len/nlen was wrong endian + 0.57 fix bug: jpg last huffman symbol before marker was >9 bits but less than 16 available + 0.56 fix bug: zlib uncompressed mode len vs. nlen + 0.55 fix bug: restart_interval not initialized to 0 + 0.54 allow NULL for 'int *comp' + 0.53 fix bug in png 3->4; speedup png decoding + 0.52 png handles req_comp=3,4 directly; minor cleanup; jpeg comments + 0.51 obey req_comp requests, 1-component jpegs return as 1-component, + on 'test' only check type, not whether we support this variant + 0.50 (2006-11-19) + first released version +*/ diff --git a/Samples/3rdParty/stb/src/stb_image.h b/Samples/3rdParty/stb/src/stb_image.h new file mode 100755 index 0000000..9318eb5 --- /dev/null +++ b/Samples/3rdParty/stb/src/stb_image.h @@ -0,0 +1,523 @@ +/* stb_image - v2.12 - public domain image loader - http://nothings.org/stb_image.h + no warranty implied; use at your own risk + + Do this: + #define STB_IMAGE_IMPLEMENTATION + before you include this file in *one* C or C++ file to create the implementation. + + // i.e. it should look like this: + #include ... + #include ... + #include ... + #define STB_IMAGE_IMPLEMENTATION + #include "stb_image.h" + + You can #define STBI_ASSERT(x) before the #include to avoid using assert.h. + And #define STBI_MALLOC, STBI_REALLOC, and STBI_FREE to avoid using malloc,realloc,free + + + QUICK NOTES: + Primarily of interest to game developers and other people who can + avoid problematic images and only need the trivial interface + + JPEG baseline & progressive (12 bpc/arithmetic not supported, same as stock IJG lib) + PNG 1/2/4/8-bit-per-channel (16 bpc not supported) + + TGA (not sure what subset, if a subset) + BMP non-1bpp, non-RLE + PSD (composited view only, no extra channels, 8/16 bit-per-channel) + + GIF (*comp always reports as 4-channel) + HDR (radiance rgbE format) + PIC (Softimage PIC) + PNM (PPM and PGM binary only) + + Animated GIF still needs a proper API, but here's one way to do it: + http://gist.github.com/urraka/685d9a6340b26b830d49 + + - decode from memory or through FILE (define STBI_NO_STDIO to remove code) + - decode from arbitrary I/O callbacks + - SIMD acceleration on x86/x64 (SSE2) and ARM (NEON) + + Full documentation under "DOCUMENTATION" below. + + + Revision 2.00 release notes: + + - Progressive JPEG is now supported. + + - PPM and PGM binary formats are now supported, thanks to Ken Miller. + + - x86 platforms now make use of SSE2 SIMD instructions for + JPEG decoding, and ARM platforms can use NEON SIMD if requested. + This work was done by Fabian "ryg" Giesen. SSE2 is used by + default, but NEON must be enabled explicitly; see docs. + + With other JPEG optimizations included in this version, we see + 2x speedup on a JPEG on an x86 machine, and a 1.5x speedup + on a JPEG on an ARM machine, relative to previous versions of this + library. The same results will not obtain for all JPGs and for all + x86/ARM machines. (Note that progressive JPEGs are significantly + slower to decode than regular JPEGs.) This doesn't mean that this + is the fastest JPEG decoder in the land; rather, it brings it + closer to parity with standard libraries. If you want the fastest + decode, look elsewhere. (See "Philosophy" section of docs below.) + + See final bullet items below for more info on SIMD. + + - Added STBI_MALLOC, STBI_REALLOC, and STBI_FREE macros for replacing + the memory allocator. Unlike other STBI libraries, these macros don't + support a context parameter, so if you need to pass a context in to + the allocator, you'll have to store it in a global or a thread-local + variable. + + - Split existing STBI_NO_HDR flag into two flags, STBI_NO_HDR and + STBI_NO_LINEAR. + STBI_NO_HDR: suppress implementation of .hdr reader format + STBI_NO_LINEAR: suppress high-dynamic-range light-linear float API + + - You can suppress implementation of any of the decoders to reduce + your code footprint by #defining one or more of the following + symbols before creating the implementation. + + STBI_NO_JPEG + STBI_NO_PNG + STBI_NO_BMP + STBI_NO_PSD + STBI_NO_TGA + STBI_NO_GIF + STBI_NO_HDR + STBI_NO_PIC + STBI_NO_PNM (.ppm and .pgm) + + - You can request *only* certain decoders and suppress all other ones + (this will be more forward-compatible, as addition of new decoders + doesn't require you to disable them explicitly): + + STBI_ONLY_JPEG + STBI_ONLY_PNG + STBI_ONLY_BMP + STBI_ONLY_PSD + STBI_ONLY_TGA + STBI_ONLY_GIF + STBI_ONLY_HDR + STBI_ONLY_PIC + STBI_ONLY_PNM (.ppm and .pgm) + + Note that you can define multiples of these, and you will get all + of them ("only x" and "only y" is interpreted to mean "only x&y"). + + - If you use STBI_NO_PNG (or _ONLY_ without PNG), and you still + want the zlib decoder to be available, #define STBI_SUPPORT_ZLIB + + - Compilation of all SIMD code can be suppressed with + #define STBI_NO_SIMD + It should not be necessary to disable SIMD unless you have issues + compiling (e.g. using an x86 compiler which doesn't support SSE + intrinsics or that doesn't support the method used to detect + SSE2 support at run-time), and even those can be reported as + bugs so I can refine the built-in compile-time checking to be + smarter. + + - The old STBI_SIMD system which allowed installing a user-defined + IDCT etc. has been removed. If you need this, don't upgrade. My + assumption is that almost nobody was doing this, and those who + were will find the built-in SIMD more satisfactory anyway. + + - RGB values computed for JPEG images are slightly different from + previous versions of stb_image. (This is due to using less + integer precision in SIMD.) The C code has been adjusted so + that the same RGB values will be computed regardless of whether + SIMD support is available, so your app should always produce + consistent results. But these results are slightly different from + previous versions. (Specifically, about 3% of available YCbCr values + will compute different RGB results from pre-1.49 versions by +-1; + most of the deviating values are one smaller in the G channel.) + + - If you must produce consistent results with previous versions of + stb_image, #define STBI_JPEG_OLD and you will get the same results + you used to; however, you will not get the SIMD speedups for + the YCbCr-to-RGB conversion step (although you should still see + significant JPEG speedup from the other changes). + + Please note that STBI_JPEG_OLD is a temporary feature; it will be + removed in future versions of the library. It is only intended for + near-term back-compatibility use. + + + Latest revision history: + 2.12 (2016-04-02) fix typo in 2.11 PSD fix that caused crashes + 2.11 (2016-04-02) 16-bit PNGS; enable SSE2 in non-gcc x64 + RGB-format JPEG; remove white matting in PSD; + allocate large structures on the stack; + correct channel count for PNG & BMP + 2.10 (2016-01-22) avoid warning introduced in 2.09 + 2.09 (2016-01-16) 16-bit TGA; comments in PNM files; STBI_REALLOC_SIZED + 2.08 (2015-09-13) fix to 2.07 cleanup, reading RGB PSD as RGBA + 2.07 (2015-09-13) partial animated GIF support + limited 16-bit PSD support + minor bugs, code cleanup, and compiler warnings + 2.06 (2015-04-19) fix bug where PSD returns wrong '*comp' value + 2.05 (2015-04-19) fix bug in progressive JPEG handling, fix warning + 2.04 (2015-04-15) try to re-enable SIMD on MinGW 64-bit + 2.03 (2015-04-12) additional corruption checking + stbi_set_flip_vertically_on_load + fix NEON support; fix mingw support + 2.02 (2015-01-19) fix incorrect assert, fix warning + 2.01 (2015-01-17) fix various warnings + 2.00b (2014-12-25) fix STBI_MALLOC in progressive JPEG + 2.00 (2014-12-25) optimize JPEG, including x86 SSE2 & ARM NEON SIMD + progressive JPEG + PGM/PPM support + STBI_MALLOC,STBI_REALLOC,STBI_FREE + STBI_NO_*, STBI_ONLY_* + GIF bugfix + + See end of file for full revision history. + + + ============================ Contributors ========================= + + Image formats Extensions, features + Sean Barrett (jpeg, png, bmp) Jetro Lauha (stbi_info) + Nicolas Schulz (hdr, psd) Martin "SpartanJ" Golini (stbi_info) + Jonathan Dummer (tga) James "moose2000" Brown (iPhone PNG) + Jean-Marc Lienher (gif) Ben "Disch" Wenger (io callbacks) + Tom Seddon (pic) Omar Cornut (1/2/4-bit PNG) + Thatcher Ulrich (psd) Nicolas Guillemot (vertical flip) + Ken Miller (pgm, ppm) Richard Mitton (16-bit PSD) + urraka@github (animated gif) Junggon Kim (PNM comments) + Daniel Gibson (16-bit TGA) + + Optimizations & bugfixes + Fabian "ryg" Giesen + Arseny Kapoulkine + + Bug & warning fixes + Marc LeBlanc David Woo Guillaume George Martins Mozeiko + Christpher Lloyd Martin Golini Jerry Jansson Joseph Thomson + Dave Moore Roy Eltham Hayaki Saito Phil Jordan + Won Chun Luke Graham Johan Duparc Nathan Reed + the Horde3D community Thomas Ruf Ronny Chevalier Nick Verigakis + Janez Zemva John Bartholomew Michal Cichon svdijk@github + Jonathan Blow Ken Hamada Tero Hanninen Baldur Karlsson + Laurent Gomila Cort Stratton Sergio Gonzalez romigrou@github + Aruelien Pocheville Thibault Reuille Cass Everitt Matthew Gregan + Ryamond Barbiero Paul Du Bois Engin Manap snagar@github + Michaelangel007@github Oriol Ferrer Mesia socks-the-fox + Blazej Dariusz Roszkowski + + +LICENSE + +This software is dual-licensed to the public domain and under the following +license: you are granted a perpetual, irrevocable license to copy, modify, +publish, and distribute this file as you see fit. + +*/ + +#ifndef STBI_INCLUDE_STB_IMAGE_H +#define STBI_INCLUDE_STB_IMAGE_H + +// DOCUMENTATION +// +// Limitations: +// - no 16-bit-per-channel PNG +// - no 12-bit-per-channel JPEG +// - no JPEGs with arithmetic coding +// - no 1-bit BMP +// - GIF always returns *comp=4 +// +// Basic usage (see HDR discussion below for HDR usage): +// int x,y,n; +// unsigned char *data = stbi_load(filename, &x, &y, &n, 0); +// // ... process data if not NULL ... +// // ... x = width, y = height, n = # 8-bit components per pixel ... +// // ... replace '0' with '1'..'4' to force that many components per pixel +// // ... but 'n' will always be the number that it would have been if you said 0 +// stbi_image_free(data) +// +// Standard parameters: +// int *x -- outputs image width in pixels +// int *y -- outputs image height in pixels +// int *comp -- outputs # of image components in image file +// int req_comp -- if non-zero, # of image components requested in result +// +// The return value from an image loader is an 'unsigned char *' which points +// to the pixel data, or NULL on an allocation failure or if the image is +// corrupt or invalid. The pixel data consists of *y scanlines of *x pixels, +// with each pixel consisting of N interleaved 8-bit components; the first +// pixel pointed to is top-left-most in the image. There is no padding between +// image scanlines or between pixels, regardless of format. The number of +// components N is 'req_comp' if req_comp is non-zero, or *comp otherwise. +// If req_comp is non-zero, *comp has the number of components that _would_ +// have been output otherwise. E.g. if you set req_comp to 4, you will always +// get RGBA output, but you can check *comp to see if it's trivially opaque +// because e.g. there were only 3 channels in the source image. +// +// An output image with N components has the following components interleaved +// in this order in each pixel: +// +// N=#comp components +// 1 grey +// 2 grey, alpha +// 3 red, green, blue +// 4 red, green, blue, alpha +// +// If image loading fails for any reason, the return value will be NULL, +// and *x, *y, *comp will be unchanged. The function stbi_failure_reason() +// can be queried for an extremely brief, end-user unfriendly explanation +// of why the load failed. Define STBI_NO_FAILURE_STRINGS to avoid +// compiling these strings at all, and STBI_FAILURE_USERMSG to get slightly +// more user-friendly ones. +// +// Paletted PNG, BMP, GIF, and PIC images are automatically depalettized. +// +// =========================================================================== +// +// Philosophy +// +// stb libraries are designed with the following priorities: +// +// 1. easy to use +// 2. easy to maintain +// 3. good performance +// +// Sometimes I let "good performance" creep up in priority over "easy to maintain", +// and for best performance I may provide less-easy-to-use APIs that give higher +// performance, in addition to the easy to use ones. Nevertheless, it's important +// to keep in mind that from the standpoint of you, a client of this library, +// all you care about is #1 and #3, and stb libraries do not emphasize #3 above all. +// +// Some secondary priorities arise directly from the first two, some of which +// make more explicit reasons why performance can't be emphasized. +// +// - Portable ("ease of use") +// - Small footprint ("easy to maintain") +// - No dependencies ("ease of use") +// +// =========================================================================== +// +// I/O callbacks +// +// I/O callbacks allow you to read from arbitrary sources, like packaged +// files or some other source. Data read from callbacks are processed +// through a small internal buffer (currently 128 bytes) to try to reduce +// overhead. +// +// The three functions you must define are "read" (reads some bytes of data), +// "skip" (skips some bytes of data), "eof" (reports if the stream is at the end). +// +// =========================================================================== +// +// SIMD support +// +// The JPEG decoder will try to automatically use SIMD kernels on x86 when +// supported by the compiler. For ARM Neon support, you must explicitly +// request it. +// +// (The old do-it-yourself SIMD API is no longer supported in the current +// code.) +// +// On x86, SSE2 will automatically be used when available based on a run-time +// test; if not, the generic C versions are used as a fall-back. On ARM targets, +// the typical path is to have separate builds for NEON and non-NEON devices +// (at least this is true for iOS and Android). Therefore, the NEON support is +// toggled by a build flag: define STBI_NEON to get NEON loops. +// +// The output of the JPEG decoder is slightly different from versions where +// SIMD support was introduced (that is, for versions before 1.49). The +// difference is only +-1 in the 8-bit RGB channels, and only on a small +// fraction of pixels. You can force the pre-1.49 behavior by defining +// STBI_JPEG_OLD, but this will disable some of the SIMD decoding path +// and hence cost some performance. +// +// If for some reason you do not want to use any of SIMD code, or if +// you have issues compiling it, you can disable it entirely by +// defining STBI_NO_SIMD. +// +// =========================================================================== +// +// HDR image support (disable by defining STBI_NO_HDR) +// +// stb_image now supports loading HDR images in general, and currently +// the Radiance .HDR file format, although the support is provided +// generically. You can still load any file through the existing interface; +// if you attempt to load an HDR file, it will be automatically remapped to +// LDR, assuming gamma 2.2 and an arbitrary scale factor defaulting to 1; +// both of these constants can be reconfigured through this interface: +// +// stbi_hdr_to_ldr_gamma(2.2f); +// stbi_hdr_to_ldr_scale(1.0f); +// +// (note, do not use _inverse_ constants; stbi_image will invert them +// appropriately). +// +// Additionally, there is a new, parallel interface for loading files as +// (linear) floats to preserve the full dynamic range: +// +// float *data = stbi_loadf(filename, &x, &y, &n, 0); +// +// If you load LDR images through this interface, those images will +// be promoted to floating point values, run through the inverse of +// constants corresponding to the above: +// +// stbi_ldr_to_hdr_scale(1.0f); +// stbi_ldr_to_hdr_gamma(2.2f); +// +// Finally, given a filename (or an open file or memory block--see header +// file for details) containing image data, you can query for the "most +// appropriate" interface to use (that is, whether the image is HDR or +// not), using: +// +// stbi_is_hdr(char *filename); +// +// =========================================================================== +// +// iPhone PNG support: +// +// By default we convert iphone-formatted PNGs back to RGB, even though +// they are internally encoded differently. You can disable this conversion +// by by calling stbi_convert_iphone_png_to_rgb(0), in which case +// you will always just get the native iphone "format" through (which +// is BGR stored in RGB). +// +// Call stbi_set_unpremultiply_on_load(1) as well to force a divide per +// pixel to remove any premultiplied alpha *only* if the image file explicitly +// says there's premultiplied data (currently only happens in iPhone images, +// and only if iPhone convert-to-rgb processing is on). +// + + +#ifndef STBI_NO_STDIO +#include +#endif // STBI_NO_STDIO + +#define STBI_VERSION 1 + +enum +{ + STBI_default = 0, // only used for req_comp + + STBI_grey = 1, + STBI_grey_alpha = 2, + STBI_rgb = 3, + STBI_rgb_alpha = 4 +}; + +typedef unsigned char stbi_uc; + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef STB_IMAGE_STATIC +#define STBIDEF static +#else +#define STBIDEF extern +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// PRIMARY API - works on images of any type +// + +// +// load image by filename, open file, or memory buffer +// + +typedef struct +{ + int (*read) (void *user,char *data,int size); // fill 'data' with 'size' bytes. return number of bytes actually read + void (*skip) (void *user,int n); // skip the next 'n' bytes, or 'unget' the last -n bytes if negative + int (*eof) (void *user); // returns nonzero if we are at end of file/data +} stbi_io_callbacks; + +STBIDEF stbi_uc *stbi_load (char const *filename, int *x, int *y, int *comp, int req_comp); +STBIDEF stbi_uc *stbi_load_from_memory (stbi_uc const *buffer, int len , int *x, int *y, int *comp, int req_comp); +STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk , void *user, int *x, int *y, int *comp, int req_comp); + +#ifndef STBI_NO_STDIO +STBIDEF stbi_uc *stbi_load_from_file (FILE *f, int *x, int *y, int *comp, int req_comp); +// for stbi_load_from_file, file pointer is left pointing immediately after image +#endif + +#ifndef STBI_NO_LINEAR + STBIDEF float *stbi_loadf (char const *filename, int *x, int *y, int *comp, int req_comp); + STBIDEF float *stbi_loadf_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp); + STBIDEF float *stbi_loadf_from_callbacks (stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp); + + #ifndef STBI_NO_STDIO + STBIDEF float *stbi_loadf_from_file (FILE *f, int *x, int *y, int *comp, int req_comp); + #endif +#endif + +#ifndef STBI_NO_HDR + STBIDEF void stbi_hdr_to_ldr_gamma(float gamma); + STBIDEF void stbi_hdr_to_ldr_scale(float scale); +#endif // STBI_NO_HDR + +#ifndef STBI_NO_LINEAR + STBIDEF void stbi_ldr_to_hdr_gamma(float gamma); + STBIDEF void stbi_ldr_to_hdr_scale(float scale); +#endif // STBI_NO_LINEAR + +// stbi_is_hdr is always defined, but always returns false if STBI_NO_HDR +STBIDEF int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user); +STBIDEF int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len); +#ifndef STBI_NO_STDIO +STBIDEF int stbi_is_hdr (char const *filename); +STBIDEF int stbi_is_hdr_from_file(FILE *f); +#endif // STBI_NO_STDIO + + +// get a VERY brief reason for failure +// NOT THREADSAFE +STBIDEF const char *stbi_failure_reason (void); + +// free the loaded image -- this is just free() +STBIDEF void stbi_image_free (void *retval_from_stbi_load); + +// get image dimensions & components without fully decoding +STBIDEF int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp); +STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp); + +#ifndef STBI_NO_STDIO +STBIDEF int stbi_info (char const *filename, int *x, int *y, int *comp); +STBIDEF int stbi_info_from_file (FILE *f, int *x, int *y, int *comp); + +#endif + + + +// for image formats that explicitly notate that they have premultiplied alpha, +// we just return the colors as stored in the file. set this flag to force +// unpremultiplication. results are undefined if the unpremultiply overflow. +STBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply); + +// indicate whether we should process iphone images back to canonical format, +// or just pass them through "as-is" +STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert); + +// flip the image vertically, so the first pixel in the output array is the bottom left +STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip); + +// ZLIB client - used by PNG, available for other purposes + +STBIDEF char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen); +STBIDEF char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header); +STBIDEF char *stbi_zlib_decode_malloc(const char *buffer, int len, int *outlen); +STBIDEF int stbi_zlib_decode_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); + +STBIDEF char *stbi_zlib_decode_noheader_malloc(const char *buffer, int len, int *outlen); +STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); + + +#ifdef __cplusplus +} +#endif + +// +// +//// end header file ///////////////////////////////////////////////////// +#endif // STBI_INCLUDE_STB_IMAGE_H diff --git a/Samples/3rdParty/stb/src/stb_image_write.c b/Samples/3rdParty/stb/src/stb_image_write.c new file mode 100755 index 0000000..dc2feb8 --- /dev/null +++ b/Samples/3rdParty/stb/src/stb_image_write.c @@ -0,0 +1,1018 @@ +/* stb_image_write - v1.02 - public domain - http://nothings.org/stb/stb_image_write.h + writes out PNG/BMP/TGA images to C stdio - Sean Barrett 2010-2015 + no warranty implied; use at your own risk + + Before #including, + + #define STB_IMAGE_WRITE_IMPLEMENTATION + + in the file that you want to have the implementation. + + Will probably not work correctly with strict-aliasing optimizations. + +ABOUT: + + This header file is a library for writing images to C stdio. It could be + adapted to write to memory or a general streaming interface; let me know. + + The PNG output is not optimal; it is 20-50% larger than the file + written by a decent optimizing implementation. This library is designed + for source code compactness and simplicity, not optimal image file size + or run-time performance. + +BUILDING: + + You can #define STBIW_ASSERT(x) before the #include to avoid using assert.h. + You can #define STBIW_MALLOC(), STBIW_REALLOC(), and STBIW_FREE() to replace + malloc,realloc,free. + You can define STBIW_MEMMOVE() to replace memmove() + +USAGE: + + There are four functions, one for each image file format: + + int stbi_write_png(char const *filename, int w, int h, int comp, const void *data, int stride_in_bytes); + int stbi_write_bmp(char const *filename, int w, int h, int comp, const void *data); + int stbi_write_tga(char const *filename, int w, int h, int comp, const void *data); + int stbi_write_hdr(char const *filename, int w, int h, int comp, const float *data); + + There are also four equivalent functions that use an arbitrary write function. You are + expected to open/close your file-equivalent before and after calling these: + + int stbi_write_png_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data, int stride_in_bytes); + int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); + int stbi_write_tga_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); + int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const float *data); + + where the callback is: + void stbi_write_func(void *context, void *data, int size); + + You can define STBI_WRITE_NO_STDIO to disable the file variant of these + functions, so the library will not use stdio.h at all. However, this will + also disable HDR writing, because it requires stdio for formatted output. + + Each function returns 0 on failure and non-0 on success. + + The functions create an image file defined by the parameters. The image + is a rectangle of pixels stored from left-to-right, top-to-bottom. + Each pixel contains 'comp' channels of data stored interleaved with 8-bits + per channel, in the following order: 1=Y, 2=YA, 3=RGB, 4=RGBA. (Y is + monochrome color.) The rectangle is 'w' pixels wide and 'h' pixels tall. + The *data pointer points to the first byte of the top-left-most pixel. + For PNG, "stride_in_bytes" is the distance in bytes from the first byte of + a row of pixels to the first byte of the next row of pixels. + + PNG creates output files with the same number of components as the input. + The BMP format expands Y to RGB in the file format and does not + output alpha. + + PNG supports writing rectangles of data even when the bytes storing rows of + data are not consecutive in memory (e.g. sub-rectangles of a larger image), + by supplying the stride between the beginning of adjacent rows. The other + formats do not. (Thus you cannot write a native-format BMP through the BMP + writer, both because it is in BGR order and because it may have padding + at the end of the line.) + + HDR expects linear float data. Since the format is always 32-bit rgb(e) + data, alpha (if provided) is discarded, and for monochrome data it is + replicated across all three channels. + + TGA supports RLE or non-RLE compressed data. To use non-RLE-compressed + data, set the global variable 'stbi_write_tga_with_rle' to 0. + +CREDITS: + + PNG/BMP/TGA + Sean Barrett + HDR + Baldur Karlsson + TGA monochrome: + Jean-Sebastien Guay + misc enhancements: + Tim Kelsey + TGA RLE + Alan Hickman + initial file IO callback implementation + Emmanuel Julien + bugfixes: + github:Chribba + Guillaume Chereau + github:jry2 + github:romigrou + Sergio Gonzalez + Jonas Karlsson + Filip Wasil + Thatcher Ulrich + +LICENSE + +This software is dual-licensed to the public domain and under the following +license: you are granted a perpetual, irrevocable license to copy, modify, +publish, and distribute this file as you see fit. + +*/ + +#include "stb_image_write.h" + +#define STB_IMAGE_WRITE_IMPLEMENTATION + +#ifdef STB_IMAGE_WRITE_IMPLEMENTATION + +#ifdef _WIN32 + #ifndef _CRT_SECURE_NO_WARNINGS + #define _CRT_SECURE_NO_WARNINGS + #endif + #ifndef _CRT_NONSTDC_NO_DEPRECATE + #define _CRT_NONSTDC_NO_DEPRECATE + #endif +#endif + +#ifndef STBI_WRITE_NO_STDIO +#include +#endif // STBI_WRITE_NO_STDIO + +#include +#include +#include +#include + +#if defined(STBIW_MALLOC) && defined(STBIW_FREE) && (defined(STBIW_REALLOC) || defined(STBIW_REALLOC_SIZED)) +// ok +#elif !defined(STBIW_MALLOC) && !defined(STBIW_FREE) && !defined(STBIW_REALLOC) && !defined(STBIW_REALLOC_SIZED) +// ok +#else +#error "Must define all or none of STBIW_MALLOC, STBIW_FREE, and STBIW_REALLOC (or STBIW_REALLOC_SIZED)." +#endif + +#ifndef STBIW_MALLOC +#define STBIW_MALLOC(sz) malloc(sz) +#define STBIW_REALLOC(p,newsz) realloc(p,newsz) +#define STBIW_FREE(p) free(p) +#endif + +#ifndef STBIW_REALLOC_SIZED +#define STBIW_REALLOC_SIZED(p,oldsz,newsz) STBIW_REALLOC(p,newsz) +#endif + + +#ifndef STBIW_MEMMOVE +#define STBIW_MEMMOVE(a,b,sz) memmove(a,b,sz) +#endif + + +#ifndef STBIW_ASSERT +#include +#define STBIW_ASSERT(x) assert(x) +#endif + +#define STBIW_UCHAR(x) (unsigned char) ((x) & 0xff) + +typedef struct +{ + stbi_write_func *func; + void *context; +} stbi__write_context; + +// initialize a callback-based context +static void stbi__start_write_callbacks(stbi__write_context *s, stbi_write_func *c, void *context) +{ + s->func = c; + s->context = context; +} + +#ifndef STBI_WRITE_NO_STDIO + +static void stbi__stdio_write(void *context, void *data, int size) +{ + fwrite(data,1,size,(FILE*) context); +} + +static int stbi__start_write_file(stbi__write_context *s, const char *filename) +{ + FILE *f = fopen(filename, "wb"); + stbi__start_write_callbacks(s, stbi__stdio_write, (void *) f); + return f != NULL; +} + +static void stbi__end_write_file(stbi__write_context *s) +{ + fclose((FILE *)s->context); +} + +#endif // !STBI_WRITE_NO_STDIO + +typedef unsigned int stbiw_uint32; +typedef int stb_image_write_test[sizeof(stbiw_uint32)==4 ? 1 : -1]; + +#ifdef STB_IMAGE_WRITE_STATIC +static int stbi_write_tga_with_rle = 1; +#else +int stbi_write_tga_with_rle = 1; +#endif + +static void stbiw__writefv(stbi__write_context *s, const char *fmt, va_list v) +{ + while (*fmt) { + switch (*fmt++) { + case ' ': break; + case '1': { unsigned char x = STBIW_UCHAR(va_arg(v, int)); + s->func(s->context,&x,1); + break; } + case '2': { int x = va_arg(v,int); + unsigned char b[2]; + b[0] = STBIW_UCHAR(x); + b[1] = STBIW_UCHAR(x>>8); + s->func(s->context,b,2); + break; } + case '4': { stbiw_uint32 x = va_arg(v,int); + unsigned char b[4]; + b[0]=STBIW_UCHAR(x); + b[1]=STBIW_UCHAR(x>>8); + b[2]=STBIW_UCHAR(x>>16); + b[3]=STBIW_UCHAR(x>>24); + s->func(s->context,b,4); + break; } + default: + STBIW_ASSERT(0); + return; + } + } +} + +static void stbiw__writef(stbi__write_context *s, const char *fmt, ...) +{ + va_list v; + va_start(v, fmt); + stbiw__writefv(s, fmt, v); + va_end(v); +} + +static void stbiw__write3(stbi__write_context *s, unsigned char a, unsigned char b, unsigned char c) +{ + unsigned char arr[3]; + arr[0] = a, arr[1] = b, arr[2] = c; + s->func(s->context, arr, 3); +} + +static void stbiw__write_pixel(stbi__write_context *s, int rgb_dir, int comp, int write_alpha, int expand_mono, unsigned char *d) +{ + unsigned char bg[3] = { 255, 0, 255}, px[3]; + int k; + + if (write_alpha < 0) + s->func(s->context, &d[comp - 1], 1); + + switch (comp) { + case 1: + s->func(s->context,d,1); + break; + case 2: + if (expand_mono) + stbiw__write3(s, d[0], d[0], d[0]); // monochrome bmp + else + s->func(s->context, d, 1); // monochrome TGA + break; + case 4: + if (!write_alpha) { + // composite against pink background + for (k = 0; k < 3; ++k) + px[k] = bg[k] + ((d[k] - bg[k]) * d[3]) / 255; + stbiw__write3(s, px[1 - rgb_dir], px[1], px[1 + rgb_dir]); + break; + } + /* FALLTHROUGH */ + case 3: + stbiw__write3(s, d[1 - rgb_dir], d[1], d[1 + rgb_dir]); + break; + } + if (write_alpha > 0) + s->func(s->context, &d[comp - 1], 1); +} + +static void stbiw__write_pixels(stbi__write_context *s, int rgb_dir, int vdir, int x, int y, int comp, void *data, int write_alpha, int scanline_pad, int expand_mono) +{ + stbiw_uint32 zero = 0; + int i,j, j_end; + + if (y <= 0) + return; + + if (vdir < 0) + j_end = -1, j = y-1; + else + j_end = y, j = 0; + + for (; j != j_end; j += vdir) { + for (i=0; i < x; ++i) { + unsigned char *d = (unsigned char *) data + (j*x+i)*comp; + stbiw__write_pixel(s, rgb_dir, comp, write_alpha, expand_mono, d); + } + s->func(s->context, &zero, scanline_pad); + } +} + +static int stbiw__outfile(stbi__write_context *s, int rgb_dir, int vdir, int x, int y, int comp, int expand_mono, void *data, int alpha, int pad, const char *fmt, ...) +{ + if (y < 0 || x < 0) { + return 0; + } else { + va_list v; + va_start(v, fmt); + stbiw__writefv(s, fmt, v); + va_end(v); + stbiw__write_pixels(s,rgb_dir,vdir,x,y,comp,data,alpha,pad, expand_mono); + return 1; + } +} + +static int stbi_write_bmp_core(stbi__write_context *s, int x, int y, int comp, const void *data) +{ + int pad = (-x*3) & 3; + return stbiw__outfile(s,-1,-1,x,y,comp,1,(void *) data,0,pad, + "11 4 22 4" "4 44 22 444444", + 'B', 'M', 14+40+(x*3+pad)*y, 0,0, 14+40, // file header + 40, x,y, 1,24, 0,0,0,0,0,0); // bitmap header +} + +STBIWDEF int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data) +{ + stbi__write_context s; + stbi__start_write_callbacks(&s, func, context); + return stbi_write_bmp_core(&s, x, y, comp, data); +} + +#ifndef STBI_WRITE_NO_STDIO +STBIWDEF int stbi_write_bmp(char const *filename, int x, int y, int comp, const void *data) +{ + stbi__write_context s; + if (stbi__start_write_file(&s,filename)) { + int r = stbi_write_bmp_core(&s, x, y, comp, data); + stbi__end_write_file(&s); + return r; + } else + return 0; +} +#endif //!STBI_WRITE_NO_STDIO + +static int stbi_write_tga_core(stbi__write_context *s, int x, int y, int comp, void *data) +{ + int has_alpha = (comp == 2 || comp == 4); + int colorbytes = has_alpha ? comp-1 : comp; + int format = colorbytes < 2 ? 3 : 2; // 3 color channels (RGB/RGBA) = 2, 1 color channel (Y/YA) = 3 + + if (y < 0 || x < 0) + return 0; + + if (!stbi_write_tga_with_rle) { + return stbiw__outfile(s, -1, -1, x, y, comp, 0, (void *) data, has_alpha, 0, + "111 221 2222 11", 0, 0, format, 0, 0, 0, 0, 0, x, y, (colorbytes + has_alpha) * 8, has_alpha * 8); + } else { + int i,j,k; + + stbiw__writef(s, "111 221 2222 11", 0,0,format+8, 0,0,0, 0,0,x,y, (colorbytes + has_alpha) * 8, has_alpha * 8); + + for (j = y - 1; j >= 0; --j) { + unsigned char *row = (unsigned char *) data + j * x * comp; + int len; + + for (i = 0; i < x; i += len) { + unsigned char *begin = row + i * comp; + int diff = 1; + len = 1; + + if (i < x - 1) { + ++len; + diff = memcmp(begin, row + (i + 1) * comp, comp); + if (diff) { + const unsigned char *prev = begin; + for (k = i + 2; k < x && len < 128; ++k) { + if (memcmp(prev, row + k * comp, comp)) { + prev += comp; + ++len; + } else { + --len; + break; + } + } + } else { + for (k = i + 2; k < x && len < 128; ++k) { + if (!memcmp(begin, row + k * comp, comp)) { + ++len; + } else { + break; + } + } + } + } + + if (diff) { + unsigned char header = STBIW_UCHAR(len - 1); + s->func(s->context, &header, 1); + for (k = 0; k < len; ++k) { + stbiw__write_pixel(s, -1, comp, has_alpha, 0, begin + k * comp); + } + } else { + unsigned char header = STBIW_UCHAR(len - 129); + s->func(s->context, &header, 1); + stbiw__write_pixel(s, -1, comp, has_alpha, 0, begin); + } + } + } + } + return 1; +} + +int stbi_write_tga_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data) +{ + stbi__write_context s; + stbi__start_write_callbacks(&s, func, context); + return stbi_write_tga_core(&s, x, y, comp, (void *) data); +} + +#ifndef STBI_WRITE_NO_STDIO +int stbi_write_tga(char const *filename, int x, int y, int comp, const void *data) +{ + stbi__write_context s; + if (stbi__start_write_file(&s,filename)) { + int r = stbi_write_tga_core(&s, x, y, comp, (void *) data); + stbi__end_write_file(&s); + return r; + } else + return 0; +} +#endif + +// ************************************************************************************************* +// Radiance RGBE HDR writer +// by Baldur Karlsson +#ifndef STBI_WRITE_NO_STDIO + +#define stbiw__max(a, b) ((a) > (b) ? (a) : (b)) + +void stbiw__linear_to_rgbe(unsigned char *rgbe, float *linear) +{ + int exponent; + float maxcomp = stbiw__max(linear[0], stbiw__max(linear[1], linear[2])); + + if (maxcomp < 1e-32f) { + rgbe[0] = rgbe[1] = rgbe[2] = rgbe[3] = 0; + } else { + float normalize = (float) frexp(maxcomp, &exponent) * 256.0f/maxcomp; + + rgbe[0] = (unsigned char)(linear[0] * normalize); + rgbe[1] = (unsigned char)(linear[1] * normalize); + rgbe[2] = (unsigned char)(linear[2] * normalize); + rgbe[3] = (unsigned char)(exponent + 128); + } +} + +void stbiw__write_run_data(stbi__write_context *s, int length, unsigned char databyte) +{ + unsigned char lengthbyte = STBIW_UCHAR(length+128); + STBIW_ASSERT(length+128 <= 255); + s->func(s->context, &lengthbyte, 1); + s->func(s->context, &databyte, 1); +} + +void stbiw__write_dump_data(stbi__write_context *s, int length, unsigned char *data) +{ + unsigned char lengthbyte = STBIW_UCHAR(length); + STBIW_ASSERT(length <= 128); // inconsistent with spec but consistent with official code + s->func(s->context, &lengthbyte, 1); + s->func(s->context, data, length); +} + +void stbiw__write_hdr_scanline(stbi__write_context *s, int width, int ncomp, unsigned char *scratch, float *scanline) +{ + unsigned char scanlineheader[4] = { 2, 2, 0, 0 }; + unsigned char rgbe[4]; + float linear[3]; + int x; + + scanlineheader[2] = (width&0xff00)>>8; + scanlineheader[3] = (width&0x00ff); + + /* skip RLE for images too small or large */ + if (width < 8 || width >= 32768) { + for (x=0; x < width; x++) { + switch (ncomp) { + case 4: /* fallthrough */ + case 3: linear[2] = scanline[x*ncomp + 2]; + linear[1] = scanline[x*ncomp + 1]; + linear[0] = scanline[x*ncomp + 0]; + break; + default: + linear[0] = linear[1] = linear[2] = scanline[x*ncomp + 0]; + break; + } + stbiw__linear_to_rgbe(rgbe, linear); + s->func(s->context, rgbe, 4); + } + } else { + int c,r; + /* encode into scratch buffer */ + for (x=0; x < width; x++) { + switch(ncomp) { + case 4: /* fallthrough */ + case 3: linear[2] = scanline[x*ncomp + 2]; + linear[1] = scanline[x*ncomp + 1]; + linear[0] = scanline[x*ncomp + 0]; + break; + default: + linear[0] = linear[1] = linear[2] = scanline[x*ncomp + 0]; + break; + } + stbiw__linear_to_rgbe(rgbe, linear); + scratch[x + width*0] = rgbe[0]; + scratch[x + width*1] = rgbe[1]; + scratch[x + width*2] = rgbe[2]; + scratch[x + width*3] = rgbe[3]; + } + + s->func(s->context, scanlineheader, 4); + + /* RLE each component separately */ + for (c=0; c < 4; c++) { + unsigned char *comp = &scratch[width*c]; + + x = 0; + while (x < width) { + // find first run + r = x; + while (r+2 < width) { + if (comp[r] == comp[r+1] && comp[r] == comp[r+2]) + break; + ++r; + } + if (r+2 >= width) + r = width; + // dump up to first run + while (x < r) { + int len = r-x; + if (len > 128) len = 128; + stbiw__write_dump_data(s, len, &comp[x]); + x += len; + } + // if there's a run, output it + if (r+2 < width) { // same test as what we break out of in search loop, so only true if we break'd + // find next byte after run + while (r < width && comp[r] == comp[x]) + ++r; + // output run up to r + while (x < r) { + int len = r-x; + if (len > 127) len = 127; + stbiw__write_run_data(s, len, comp[x]); + x += len; + } + } + } + } + } +} + +static int stbi_write_hdr_core(stbi__write_context *s, int x, int y, int comp, float *data) +{ + if (y <= 0 || x <= 0 || data == NULL) + return 0; + else { + // Each component is stored separately. Allocate scratch space for full output scanline. + unsigned char *scratch = (unsigned char *) STBIW_MALLOC(x*4); + int i, len; + char buffer[128]; + char header[] = "#?RADIANCE\n# Written by stb_image_write.h\nFORMAT=32-bit_rle_rgbe\n"; + s->func(s->context, header, sizeof(header)-1); + + len = sprintf(buffer, "EXPOSURE= 1.0000000000000\n\n-Y %d +X %d\n", y, x); + s->func(s->context, buffer, len); + + for(i=0; i < y; i++) + stbiw__write_hdr_scanline(s, x, comp, scratch, data + comp*i*x); + STBIW_FREE(scratch); + return 1; + } +} + +int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const float *data) +{ + stbi__write_context s; + stbi__start_write_callbacks(&s, func, context); + return stbi_write_hdr_core(&s, x, y, comp, (float *) data); +} + +int stbi_write_hdr(char const *filename, int x, int y, int comp, const float *data) +{ + stbi__write_context s; + if (stbi__start_write_file(&s,filename)) { + int r = stbi_write_hdr_core(&s, x, y, comp, (float *) data); + stbi__end_write_file(&s); + return r; + } else + return 0; +} +#endif // STBI_WRITE_NO_STDIO + + +////////////////////////////////////////////////////////////////////////////// +// +// PNG writer +// + +// stretchy buffer; stbiw__sbpush() == vector<>::push_back() -- stbiw__sbcount() == vector<>::size() +#define stbiw__sbraw(a) ((int *) (a) - 2) +#define stbiw__sbm(a) stbiw__sbraw(a)[0] +#define stbiw__sbn(a) stbiw__sbraw(a)[1] + +#define stbiw__sbneedgrow(a,n) ((a)==0 || stbiw__sbn(a)+n >= stbiw__sbm(a)) +#define stbiw__sbmaybegrow(a,n) (stbiw__sbneedgrow(a,(n)) ? stbiw__sbgrow(a,n) : 0) +#define stbiw__sbgrow(a,n) stbiw__sbgrowf((void **) &(a), (n), sizeof(*(a))) + +#define stbiw__sbpush(a, v) (stbiw__sbmaybegrow(a,1), (a)[stbiw__sbn(a)++] = (v)) +#define stbiw__sbcount(a) ((a) ? stbiw__sbn(a) : 0) +#define stbiw__sbfree(a) ((a) ? STBIW_FREE(stbiw__sbraw(a)),0 : 0) + +static void *stbiw__sbgrowf(void **arr, int increment, int itemsize) +{ + int m = *arr ? 2*stbiw__sbm(*arr)+increment : increment+1; + void *p = STBIW_REALLOC_SIZED(*arr ? stbiw__sbraw(*arr) : 0, *arr ? (stbiw__sbm(*arr)*itemsize + sizeof(int)*2) : 0, itemsize * m + sizeof(int)*2); + STBIW_ASSERT(p); + if (p) { + if (!*arr) ((int *) p)[1] = 0; + *arr = (void *) ((int *) p + 2); + stbiw__sbm(*arr) = m; + } + return *arr; +} + +static unsigned char *stbiw__zlib_flushf(unsigned char *data, unsigned int *bitbuffer, int *bitcount) +{ + while (*bitcount >= 8) { + stbiw__sbpush(data, STBIW_UCHAR(*bitbuffer)); + *bitbuffer >>= 8; + *bitcount -= 8; + } + return data; +} + +static int stbiw__zlib_bitrev(int code, int codebits) +{ + int res=0; + while (codebits--) { + res = (res << 1) | (code & 1); + code >>= 1; + } + return res; +} + +static unsigned int stbiw__zlib_countm(unsigned char *a, unsigned char *b, int limit) +{ + int i; + for (i=0; i < limit && i < 258; ++i) + if (a[i] != b[i]) break; + return i; +} + +static unsigned int stbiw__zhash(unsigned char *data) +{ + stbiw_uint32 hash = data[0] + (data[1] << 8) + (data[2] << 16); + hash ^= hash << 3; + hash += hash >> 5; + hash ^= hash << 4; + hash += hash >> 17; + hash ^= hash << 25; + hash += hash >> 6; + return hash; +} + +#define stbiw__zlib_flush() (out = stbiw__zlib_flushf(out, &bitbuf, &bitcount)) +#define stbiw__zlib_add(code,codebits) \ + (bitbuf |= (code) << bitcount, bitcount += (codebits), stbiw__zlib_flush()) +#define stbiw__zlib_huffa(b,c) stbiw__zlib_add(stbiw__zlib_bitrev(b,c),c) +// default huffman tables +#define stbiw__zlib_huff1(n) stbiw__zlib_huffa(0x30 + (n), 8) +#define stbiw__zlib_huff2(n) stbiw__zlib_huffa(0x190 + (n)-144, 9) +#define stbiw__zlib_huff3(n) stbiw__zlib_huffa(0 + (n)-256,7) +#define stbiw__zlib_huff4(n) stbiw__zlib_huffa(0xc0 + (n)-280,8) +#define stbiw__zlib_huff(n) ((n) <= 143 ? stbiw__zlib_huff1(n) : (n) <= 255 ? stbiw__zlib_huff2(n) : (n) <= 279 ? stbiw__zlib_huff3(n) : stbiw__zlib_huff4(n)) +#define stbiw__zlib_huffb(n) ((n) <= 143 ? stbiw__zlib_huff1(n) : stbiw__zlib_huff2(n)) + +#define stbiw__ZHASH 16384 + +unsigned char * stbi_zlib_compress(unsigned char *data, int data_len, int *out_len, int quality) +{ + static unsigned short lengthc[] = { 3,4,5,6,7,8,9,10,11,13,15,17,19,23,27,31,35,43,51,59,67,83,99,115,131,163,195,227,258, 259 }; + static unsigned char lengtheb[]= { 0,0,0,0,0,0,0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0 }; + static unsigned short distc[] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577, 32768 }; + static unsigned char disteb[] = { 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13 }; + unsigned int bitbuf=0; + int i,j, bitcount=0; + unsigned char *out = NULL; + unsigned char ***hash_table = (unsigned char***) STBIW_MALLOC(stbiw__ZHASH * sizeof(char**)); + if (quality < 5) quality = 5; + + stbiw__sbpush(out, 0x78); // DEFLATE 32K window + stbiw__sbpush(out, 0x5e); // FLEVEL = 1 + stbiw__zlib_add(1,1); // BFINAL = 1 + stbiw__zlib_add(1,2); // BTYPE = 1 -- fixed huffman + + for (i=0; i < stbiw__ZHASH; ++i) + hash_table[i] = NULL; + + i=0; + while (i < data_len-3) { + // hash next 3 bytes of data to be compressed + int h = stbiw__zhash(data+i)&(stbiw__ZHASH-1), best=3; + unsigned char *bestloc = 0; + unsigned char **hlist = hash_table[h]; + int n = stbiw__sbcount(hlist); + for (j=0; j < n; ++j) { + if (hlist[j]-data > i-32768) { // if entry lies within window + int d = stbiw__zlib_countm(hlist[j], data+i, data_len-i); + if (d >= best) best=d,bestloc=hlist[j]; + } + } + // when hash table entry is too long, delete half the entries + if (hash_table[h] && stbiw__sbn(hash_table[h]) == 2*quality) { + STBIW_MEMMOVE(hash_table[h], hash_table[h]+quality, sizeof(hash_table[h][0])*quality); + stbiw__sbn(hash_table[h]) = quality; + } + stbiw__sbpush(hash_table[h],data+i); + + if (bestloc) { + // "lazy matching" - check match at *next* byte, and if it's better, do cur byte as literal + h = stbiw__zhash(data+i+1)&(stbiw__ZHASH-1); + hlist = hash_table[h]; + n = stbiw__sbcount(hlist); + for (j=0; j < n; ++j) { + if (hlist[j]-data > i-32767) { + int e = stbiw__zlib_countm(hlist[j], data+i+1, data_len-i-1); + if (e > best) { // if next match is better, bail on current match + bestloc = NULL; + break; + } + } + } + } + + if (bestloc) { + int d = (int) (data+i - bestloc); // distance back + STBIW_ASSERT(d <= 32767 && best <= 258); + for (j=0; best > lengthc[j+1]-1; ++j); + stbiw__zlib_huff(j+257); + if (lengtheb[j]) stbiw__zlib_add(best - lengthc[j], lengtheb[j]); + for (j=0; d > distc[j+1]-1; ++j); + stbiw__zlib_add(stbiw__zlib_bitrev(j,5),5); + if (disteb[j]) stbiw__zlib_add(d - distc[j], disteb[j]); + i += best; + } else { + stbiw__zlib_huffb(data[i]); + ++i; + } + } + // write out final bytes + for (;i < data_len; ++i) + stbiw__zlib_huffb(data[i]); + stbiw__zlib_huff(256); // end of block + // pad with 0 bits to byte boundary + while (bitcount) + stbiw__zlib_add(0,1); + + for (i=0; i < stbiw__ZHASH; ++i) + (void) stbiw__sbfree(hash_table[i]); + STBIW_FREE(hash_table); + + { + // compute adler32 on input + unsigned int s1=1, s2=0; + int blocklen = (int) (data_len % 5552); + j=0; + while (j < data_len) { + for (i=0; i < blocklen; ++i) s1 += data[j+i], s2 += s1; + s1 %= 65521, s2 %= 65521; + j += blocklen; + blocklen = 5552; + } + stbiw__sbpush(out, STBIW_UCHAR(s2 >> 8)); + stbiw__sbpush(out, STBIW_UCHAR(s2)); + stbiw__sbpush(out, STBIW_UCHAR(s1 >> 8)); + stbiw__sbpush(out, STBIW_UCHAR(s1)); + } + *out_len = stbiw__sbn(out); + // make returned pointer freeable + STBIW_MEMMOVE(stbiw__sbraw(out), out, *out_len); + return (unsigned char *) stbiw__sbraw(out); +} + +static unsigned int stbiw__crc32(unsigned char *buffer, int len) +{ + static unsigned int crc_table[256] = + { + 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3, + 0x0eDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, + 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, + 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, + 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, + 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, + 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F, + 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, + 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433, + 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01, + 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, + 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, + 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, + 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, + 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F, + 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD, + 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, + 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, + 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7, + 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, + 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, + 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79, + 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, + 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, + 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, + 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, + 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, + 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, + 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, + 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, + 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF, + 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D + }; + + unsigned int crc = ~0u; + int i; + for (i=0; i < len; ++i) + crc = (crc >> 8) ^ crc_table[buffer[i] ^ (crc & 0xff)]; + return ~crc; +} + +#define stbiw__wpng4(o,a,b,c,d) ((o)[0]=STBIW_UCHAR(a),(o)[1]=STBIW_UCHAR(b),(o)[2]=STBIW_UCHAR(c),(o)[3]=STBIW_UCHAR(d),(o)+=4) +#define stbiw__wp32(data,v) stbiw__wpng4(data, (v)>>24,(v)>>16,(v)>>8,(v)); +#define stbiw__wptag(data,s) stbiw__wpng4(data, s[0],s[1],s[2],s[3]) + +static void stbiw__wpcrc(unsigned char **data, int len) +{ + unsigned int crc = stbiw__crc32(*data - len - 4, len+4); + stbiw__wp32(*data, crc); +} + +static unsigned char stbiw__paeth(int a, int b, int c) +{ + int p = a + b - c, pa = abs(p-a), pb = abs(p-b), pc = abs(p-c); + if (pa <= pb && pa <= pc) return STBIW_UCHAR(a); + if (pb <= pc) return STBIW_UCHAR(b); + return STBIW_UCHAR(c); +} + +unsigned char *stbi_write_png_to_mem(unsigned char *pixels, int stride_bytes, int x, int y, int n, int *out_len) +{ + int ctype[5] = { -1, 0, 4, 2, 6 }; + unsigned char sig[8] = { 137,80,78,71,13,10,26,10 }; + unsigned char *out,*o, *filt, *zlib; + signed char *line_buffer; + int i,j,k,p,zlen; + + if (stride_bytes == 0) + stride_bytes = x * n; + + filt = (unsigned char *) STBIW_MALLOC((x*n+1) * y); if (!filt) return 0; + line_buffer = (signed char *) STBIW_MALLOC(x * n); if (!line_buffer) { STBIW_FREE(filt); return 0; } + for (j=0; j < y; ++j) { + static int mapping[] = { 0,1,2,3,4 }; + static int firstmap[] = { 0,1,0,5,6 }; + int *mymap = j ? mapping : firstmap; + int best = 0, bestval = 0x7fffffff; + for (p=0; p < 2; ++p) { + for (k= p?best:0; k < 5; ++k) { + int type = mymap[k],est=0; + unsigned char *z = pixels + stride_bytes*j; + for (i=0; i < n; ++i) + switch (type) { + case 0: line_buffer[i] = z[i]; break; + case 1: line_buffer[i] = z[i]; break; + case 2: line_buffer[i] = z[i] - z[i-stride_bytes]; break; + case 3: line_buffer[i] = z[i] - (z[i-stride_bytes]>>1); break; + case 4: line_buffer[i] = (signed char) (z[i] - stbiw__paeth(0,z[i-stride_bytes],0)); break; + case 5: line_buffer[i] = z[i]; break; + case 6: line_buffer[i] = z[i]; break; + } + for (i=n; i < x*n; ++i) { + switch (type) { + case 0: line_buffer[i] = z[i]; break; + case 1: line_buffer[i] = z[i] - z[i-n]; break; + case 2: line_buffer[i] = z[i] - z[i-stride_bytes]; break; + case 3: line_buffer[i] = z[i] - ((z[i-n] + z[i-stride_bytes])>>1); break; + case 4: line_buffer[i] = z[i] - stbiw__paeth(z[i-n], z[i-stride_bytes], z[i-stride_bytes-n]); break; + case 5: line_buffer[i] = z[i] - (z[i-n]>>1); break; + case 6: line_buffer[i] = z[i] - stbiw__paeth(z[i-n], 0,0); break; + } + } + if (p) break; + for (i=0; i < x*n; ++i) + est += abs((signed char) line_buffer[i]); + if (est < bestval) { bestval = est; best = k; } + } + } + // when we get here, best contains the filter type, and line_buffer contains the data + filt[j*(x*n+1)] = (unsigned char) best; + STBIW_MEMMOVE(filt+j*(x*n+1)+1, line_buffer, x*n); + } + STBIW_FREE(line_buffer); + zlib = stbi_zlib_compress(filt, y*( x*n+1), &zlen, 8); // increase 8 to get smaller but use more memory + STBIW_FREE(filt); + if (!zlib) return 0; + + // each tag requires 12 bytes of overhead + out = (unsigned char *) STBIW_MALLOC(8 + 12+13 + 12+zlen + 12); + if (!out) return 0; + *out_len = 8 + 12+13 + 12+zlen + 12; + + o=out; + STBIW_MEMMOVE(o,sig,8); o+= 8; + stbiw__wp32(o, 13); // header length + stbiw__wptag(o, "IHDR"); + stbiw__wp32(o, x); + stbiw__wp32(o, y); + *o++ = 8; + *o++ = STBIW_UCHAR(ctype[n]); + *o++ = 0; + *o++ = 0; + *o++ = 0; + stbiw__wpcrc(&o,13); + + stbiw__wp32(o, zlen); + stbiw__wptag(o, "IDAT"); + STBIW_MEMMOVE(o, zlib, zlen); + o += zlen; + STBIW_FREE(zlib); + stbiw__wpcrc(&o, zlen); + + stbiw__wp32(o,0); + stbiw__wptag(o, "IEND"); + stbiw__wpcrc(&o,0); + + STBIW_ASSERT(o == out + *out_len); + + return out; +} + +#ifndef STBI_WRITE_NO_STDIO +STBIWDEF int stbi_write_png(char const *filename, int x, int y, int comp, const void *data, int stride_bytes) +{ + FILE *f; + int len; + unsigned char *png = stbi_write_png_to_mem((unsigned char *) data, stride_bytes, x, y, comp, &len); + if (png == NULL) return 0; + f = fopen(filename, "wb"); + if (!f) { STBIW_FREE(png); return 0; } + fwrite(png, 1, len, f); + fclose(f); + STBIW_FREE(png); + return 1; +} +#endif + +STBIWDEF int stbi_write_png_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int stride_bytes) +{ + int len; + unsigned char *png = stbi_write_png_to_mem((unsigned char *) data, stride_bytes, x, y, comp, &len); + if (png == NULL) return 0; + func(context, png, len); + STBIW_FREE(png); + return 1; +} + +#endif // STB_IMAGE_WRITE_IMPLEMENTATION + +/* Revision history + 1.02 (2016-04-02) + avoid allocating large structures on the stack + 1.01 (2016-01-16) + STBIW_REALLOC_SIZED: support allocators with no realloc support + avoid race-condition in crc initialization + minor compile issues + 1.00 (2015-09-14) + installable file IO function + 0.99 (2015-09-13) + warning fixes; TGA rle support + 0.98 (2015-04-08) + added STBIW_MALLOC, STBIW_ASSERT etc + 0.97 (2015-01-18) + fixed HDR asserts, rewrote HDR rle logic + 0.96 (2015-01-17) + add HDR output + fix monochrome BMP + 0.95 (2014-08-17) + add monochrome TGA output + 0.94 (2014-05-31) + rename private functions to avoid conflicts with stb_image.h + 0.93 (2014-05-27) + warning fixes + 0.92 (2010-08-01) + casts to unsigned char to fix warnings + 0.91 (2010-07-17) + first public release + 0.90 first internal release +*/ diff --git a/Samples/3rdParty/stb/src/stb_image_write.h b/Samples/3rdParty/stb/src/stb_image_write.h new file mode 100755 index 0000000..8576e02 --- /dev/null +++ b/Samples/3rdParty/stb/src/stb_image_write.h @@ -0,0 +1,147 @@ +/* stb_image_write - v1.02 - public domain - http://nothings.org/stb/stb_image_write.h + writes out PNG/BMP/TGA images to C stdio - Sean Barrett 2010-2015 + no warranty implied; use at your own risk + + Before #including, + + #define STB_IMAGE_WRITE_IMPLEMENTATION + + in the file that you want to have the implementation. + + Will probably not work correctly with strict-aliasing optimizations. + +ABOUT: + + This header file is a library for writing images to C stdio. It could be + adapted to write to memory or a general streaming interface; let me know. + + The PNG output is not optimal; it is 20-50% larger than the file + written by a decent optimizing implementation. This library is designed + for source code compactness and simplicity, not optimal image file size + or run-time performance. + +BUILDING: + + You can #define STBIW_ASSERT(x) before the #include to avoid using assert.h. + You can #define STBIW_MALLOC(), STBIW_REALLOC(), and STBIW_FREE() to replace + malloc,realloc,free. + You can define STBIW_MEMMOVE() to replace memmove() + +USAGE: + + There are four functions, one for each image file format: + + int stbi_write_png(char const *filename, int w, int h, int comp, const void *data, int stride_in_bytes); + int stbi_write_bmp(char const *filename, int w, int h, int comp, const void *data); + int stbi_write_tga(char const *filename, int w, int h, int comp, const void *data); + int stbi_write_hdr(char const *filename, int w, int h, int comp, const float *data); + + There are also four equivalent functions that use an arbitrary write function. You are + expected to open/close your file-equivalent before and after calling these: + + int stbi_write_png_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data, int stride_in_bytes); + int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); + int stbi_write_tga_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); + int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const float *data); + + where the callback is: + void stbi_write_func(void *context, void *data, int size); + + You can define STBI_WRITE_NO_STDIO to disable the file variant of these + functions, so the library will not use stdio.h at all. However, this will + also disable HDR writing, because it requires stdio for formatted output. + + Each function returns 0 on failure and non-0 on success. + + The functions create an image file defined by the parameters. The image + is a rectangle of pixels stored from left-to-right, top-to-bottom. + Each pixel contains 'comp' channels of data stored interleaved with 8-bits + per channel, in the following order: 1=Y, 2=YA, 3=RGB, 4=RGBA. (Y is + monochrome color.) The rectangle is 'w' pixels wide and 'h' pixels tall. + The *data pointer points to the first byte of the top-left-most pixel. + For PNG, "stride_in_bytes" is the distance in bytes from the first byte of + a row of pixels to the first byte of the next row of pixels. + + PNG creates output files with the same number of components as the input. + The BMP format expands Y to RGB in the file format and does not + output alpha. + + PNG supports writing rectangles of data even when the bytes storing rows of + data are not consecutive in memory (e.g. sub-rectangles of a larger image), + by supplying the stride between the beginning of adjacent rows. The other + formats do not. (Thus you cannot write a native-format BMP through the BMP + writer, both because it is in BGR order and because it may have padding + at the end of the line.) + + HDR expects linear float data. Since the format is always 32-bit rgb(e) + data, alpha (if provided) is discarded, and for monochrome data it is + replicated across all three channels. + + TGA supports RLE or non-RLE compressed data. To use non-RLE-compressed + data, set the global variable 'stbi_write_tga_with_rle' to 0. + +CREDITS: + + PNG/BMP/TGA + Sean Barrett + HDR + Baldur Karlsson + TGA monochrome: + Jean-Sebastien Guay + misc enhancements: + Tim Kelsey + TGA RLE + Alan Hickman + initial file IO callback implementation + Emmanuel Julien + bugfixes: + github:Chribba + Guillaume Chereau + github:jry2 + github:romigrou + Sergio Gonzalez + Jonas Karlsson + Filip Wasil + Thatcher Ulrich + +LICENSE + +This software is dual-licensed to the public domain and under the following +license: you are granted a perpetual, irrevocable license to copy, modify, +publish, and distribute this file as you see fit. + +*/ + +#ifndef INCLUDE_STB_IMAGE_WRITE_H +#define INCLUDE_STB_IMAGE_WRITE_H + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef STB_IMAGE_WRITE_STATIC +#define STBIWDEF static +#else +#define STBIWDEF extern +extern int stbi_write_tga_with_rle; +#endif + +#ifndef STBI_WRITE_NO_STDIO +STBIWDEF int stbi_write_png(char const *filename, int w, int h, int comp, const void *data, int stride_in_bytes); +STBIWDEF int stbi_write_bmp(char const *filename, int w, int h, int comp, const void *data); +STBIWDEF int stbi_write_tga(char const *filename, int w, int h, int comp, const void *data); +STBIWDEF int stbi_write_hdr(char const *filename, int w, int h, int comp, const float *data); +#endif + +typedef void stbi_write_func(void *context, void *data, int size); + +STBIWDEF int stbi_write_png_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data, int stride_in_bytes); +STBIWDEF int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); +STBIWDEF int stbi_write_tga_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); +STBIWDEF int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const float *data); + +#ifdef __cplusplus +} +#endif + +#endif//INCLUDE_STB_IMAGE_WRITE_H diff --git a/Samples/3rdParty/stb/src/stb_vorbis.c b/Samples/3rdParty/stb/src/stb_vorbis.c new file mode 100755 index 0000000..6669606 --- /dev/null +++ b/Samples/3rdParty/stb/src/stb_vorbis.c @@ -0,0 +1,5067 @@ +// Ogg Vorbis audio decoder - v1.09 - public domain +// http://nothings.org/stb_vorbis/ +// +// Original version written by Sean Barrett in 2007. +// +// Originally sponsored by RAD Game Tools. Seeking sponsored +// by Phillip Bennefall, Marc Andersen, Aaron Baker, Elias Software, +// Aras Pranckevicius, and Sean Barrett. +// +// LICENSE +// +// This software is dual-licensed to the public domain and under the following +// license: you are granted a perpetual, irrevocable license to copy, modify, +// publish, and distribute this file as you see fit. +// +// No warranty for any purpose is expressed or implied by the author (nor +// by RAD Game Tools). Report bugs and send enhancements to the author. +// +// Limitations: +// +// - floor 0 not supported (used in old ogg vorbis files pre-2004) +// - lossless sample-truncation at beginning ignored +// - cannot concatenate multiple vorbis streams +// - sample positions are 32-bit, limiting seekable 192Khz +// files to around 6 hours (Ogg supports 64-bit) +// +// Feature contributors: +// Dougall Johnson (sample-exact seeking) +// +// Bugfix/warning contributors: +// Terje Mathisen Niklas Frykholm Andy Hill +// Casey Muratori John Bolton Gargaj +// Laurent Gomila Marc LeBlanc Ronny Chevalier +// Bernhard Wodo Evan Balster alxprd@github +// Tom Beaumont Ingo Leitgeb Nicolas Guillemot +// Phillip Bennefall Rohit Thiago Goulart +// manxorist@github saga musix +// +// Partial history: +// 1.09 - 2016/04/04 - back out 'truncation of last frame' fix from previous version +// 1.08 - 2016/04/02 - warnings; setup memory leaks; truncation of last frame +// 1.07 - 2015/01/16 - fixes for crashes on invalid files; warning fixes; const +// 1.06 - 2015/08/31 - full, correct support for seeking API (Dougall Johnson) +// some crash fixes when out of memory or with corrupt files +// fix some inappropriately signed shifts +// 1.05 - 2015/04/19 - don't define __forceinline if it's redundant +// 1.04 - 2014/08/27 - fix missing const-correct case in API +// 1.03 - 2014/08/07 - warning fixes +// 1.02 - 2014/07/09 - declare qsort comparison as explicitly _cdecl in Windows +// 1.01 - 2014/06/18 - fix stb_vorbis_get_samples_float (interleaved was correct) +// 1.0 - 2014/05/26 - fix memory leaks; fix warnings; fix bugs in >2-channel; +// (API change) report sample rate for decode-full-file funcs +// +// See end of file for full version history. + +#include "stb_vorbis.h" + +#ifndef STB_VORBIS_HEADER_ONLY + +// global configuration settings (e.g. set these in the project/makefile), +// or just set them in this file at the top (although ideally the first few +// should be visible when the header file is compiled too, although it's not +// crucial) + +// STB_VORBIS_NO_PUSHDATA_API +// does not compile the code for the various stb_vorbis_*_pushdata() +// functions +// #define STB_VORBIS_NO_PUSHDATA_API + +// STB_VORBIS_NO_PULLDATA_API +// does not compile the code for the non-pushdata APIs +// #define STB_VORBIS_NO_PULLDATA_API + +// STB_VORBIS_NO_STDIO +// does not compile the code for the APIs that use FILE *s internally +// or externally (implied by STB_VORBIS_NO_PULLDATA_API) +// #define STB_VORBIS_NO_STDIO + +// STB_VORBIS_NO_INTEGER_CONVERSION +// does not compile the code for converting audio sample data from +// float to integer (implied by STB_VORBIS_NO_PULLDATA_API) +// #define STB_VORBIS_NO_INTEGER_CONVERSION + +// STB_VORBIS_NO_FAST_SCALED_FLOAT +// does not use a fast float-to-int trick to accelerate float-to-int on +// most platforms which requires endianness be defined correctly. +//#define STB_VORBIS_NO_FAST_SCALED_FLOAT + + +// STB_VORBIS_MAX_CHANNELS [number] +// globally define this to the maximum number of channels you need. +// The spec does not put a restriction on channels except that +// the count is stored in a byte, so 255 is the hard limit. +// Reducing this saves about 16 bytes per value, so using 16 saves +// (255-16)*16 or around 4KB. Plus anything other memory usage +// I forgot to account for. Can probably go as low as 8 (7.1 audio), +// 6 (5.1 audio), or 2 (stereo only). +#ifndef STB_VORBIS_MAX_CHANNELS +#define STB_VORBIS_MAX_CHANNELS 16 // enough for anyone? +#endif + +// STB_VORBIS_PUSHDATA_CRC_COUNT [number] +// after a flush_pushdata(), stb_vorbis begins scanning for the +// next valid page, without backtracking. when it finds something +// that looks like a page, it streams through it and verifies its +// CRC32. Should that validation fail, it keeps scanning. But it's +// possible that _while_ streaming through to check the CRC32 of +// one candidate page, it sees another candidate page. This #define +// determines how many "overlapping" candidate pages it can search +// at once. Note that "real" pages are typically ~4KB to ~8KB, whereas +// garbage pages could be as big as 64KB, but probably average ~16KB. +// So don't hose ourselves by scanning an apparent 64KB page and +// missing a ton of real ones in the interim; so minimum of 2 +#ifndef STB_VORBIS_PUSHDATA_CRC_COUNT +#define STB_VORBIS_PUSHDATA_CRC_COUNT 4 +#endif + +// STB_VORBIS_FAST_HUFFMAN_LENGTH [number] +// sets the log size of the huffman-acceleration table. Maximum +// supported value is 24. with larger numbers, more decodings are O(1), +// but the table size is larger so worse cache missing, so you'll have +// to probe (and try multiple ogg vorbis files) to find the sweet spot. +#ifndef STB_VORBIS_FAST_HUFFMAN_LENGTH +#define STB_VORBIS_FAST_HUFFMAN_LENGTH 10 +#endif + +// STB_VORBIS_FAST_BINARY_LENGTH [number] +// sets the log size of the binary-search acceleration table. this +// is used in similar fashion to the fast-huffman size to set initial +// parameters for the binary search + +// STB_VORBIS_FAST_HUFFMAN_INT +// The fast huffman tables are much more efficient if they can be +// stored as 16-bit results instead of 32-bit results. This restricts +// the codebooks to having only 65535 possible outcomes, though. +// (At least, accelerated by the huffman table.) +#ifndef STB_VORBIS_FAST_HUFFMAN_INT +#define STB_VORBIS_FAST_HUFFMAN_SHORT +#endif + +// STB_VORBIS_NO_HUFFMAN_BINARY_SEARCH +// If the 'fast huffman' search doesn't succeed, then stb_vorbis falls +// back on binary searching for the correct one. This requires storing +// extra tables with the huffman codes in sorted order. Defining this +// symbol trades off space for speed by forcing a linear search in the +// non-fast case, except for "sparse" codebooks. +// #define STB_VORBIS_NO_HUFFMAN_BINARY_SEARCH + +// STB_VORBIS_DIVIDES_IN_RESIDUE +// stb_vorbis precomputes the result of the scalar residue decoding +// that would otherwise require a divide per chunk. you can trade off +// space for time by defining this symbol. +// #define STB_VORBIS_DIVIDES_IN_RESIDUE + +// STB_VORBIS_DIVIDES_IN_CODEBOOK +// vorbis VQ codebooks can be encoded two ways: with every case explicitly +// stored, or with all elements being chosen from a small range of values, +// and all values possible in all elements. By default, stb_vorbis expands +// this latter kind out to look like the former kind for ease of decoding, +// because otherwise an integer divide-per-vector-element is required to +// unpack the index. If you define STB_VORBIS_DIVIDES_IN_CODEBOOK, you can +// trade off storage for speed. +//#define STB_VORBIS_DIVIDES_IN_CODEBOOK + +#ifdef STB_VORBIS_CODEBOOK_SHORTS +#error "STB_VORBIS_CODEBOOK_SHORTS is no longer supported as it produced incorrect results for some input formats" +#endif + +// STB_VORBIS_DIVIDE_TABLE +// this replaces small integer divides in the floor decode loop with +// table lookups. made less than 1% difference, so disabled by default. + +// STB_VORBIS_NO_INLINE_DECODE +// disables the inlining of the scalar codebook fast-huffman decode. +// might save a little codespace; useful for debugging +// #define STB_VORBIS_NO_INLINE_DECODE + +// STB_VORBIS_NO_DEFER_FLOOR +// Normally we only decode the floor without synthesizing the actual +// full curve. We can instead synthesize the curve immediately. This +// requires more memory and is very likely slower, so I don't think +// you'd ever want to do it except for debugging. +// #define STB_VORBIS_NO_DEFER_FLOOR + + + + +////////////////////////////////////////////////////////////////////////////// + +#ifdef STB_VORBIS_NO_PULLDATA_API + #define STB_VORBIS_NO_INTEGER_CONVERSION + #define STB_VORBIS_NO_STDIO +#endif + +#if defined(STB_VORBIS_NO_CRT) && !defined(STB_VORBIS_NO_STDIO) + #define STB_VORBIS_NO_STDIO 1 +#endif + +#ifndef STB_VORBIS_NO_INTEGER_CONVERSION +#ifndef STB_VORBIS_NO_FAST_SCALED_FLOAT + + // only need endianness for fast-float-to-int, which we don't + // use for pushdata + + #ifndef STB_VORBIS_BIG_ENDIAN + #define STB_VORBIS_ENDIAN 0 + #else + #define STB_VORBIS_ENDIAN 1 + #endif + +#endif +#endif + + +#ifndef STB_VORBIS_NO_STDIO +#include +#endif + +#ifndef STB_VORBIS_NO_CRT +#include +#include +#include +#include +#if !(defined(__APPLE__) || defined(MACOSX) || defined(macintosh) || defined(Macintosh)) +#include +#if defined(__linux__) || defined(__linux) || defined(__EMSCRIPTEN__) +#include +#endif +#endif +#else // STB_VORBIS_NO_CRT +#define NULL 0 +#define malloc(s) 0 +#define free(s) ((void) 0) +#define realloc(s) 0 +#endif // STB_VORBIS_NO_CRT + +#include + +#ifdef __MINGW32__ + // eff you mingw: + // "fixed": + // http://sourceforge.net/p/mingw-w64/mailman/message/32882927/ + // "no that broke the build, reverted, who cares about C": + // http://sourceforge.net/p/mingw-w64/mailman/message/32890381/ + #ifdef __forceinline + #undef __forceinline + #endif + #define __forceinline +#elif !defined(_MSC_VER) + #if __GNUC__ + #define __forceinline inline + #else + #define __forceinline + #endif +#endif + +#if STB_VORBIS_MAX_CHANNELS > 256 +#error "Value of STB_VORBIS_MAX_CHANNELS outside of allowed range" +#endif + +#if STB_VORBIS_FAST_HUFFMAN_LENGTH > 24 +#error "Value of STB_VORBIS_FAST_HUFFMAN_LENGTH outside of allowed range" +#endif + + +#if 0 +#include +#define CHECK(f) _CrtIsValidHeapPointer(f->channel_buffers[1]) +#else +#define CHECK(f) ((void) 0) +#endif + +#define MAX_BLOCKSIZE_LOG 13 // from specification +#define MAX_BLOCKSIZE (1 << MAX_BLOCKSIZE_LOG) + + +typedef unsigned char uint8; +typedef signed char int8; +typedef unsigned short uint16; +typedef signed short int16; +typedef unsigned int uint32; +typedef signed int int32; + +#ifndef TRUE +#define TRUE 1 +#define FALSE 0 +#endif + +typedef float codetype; + +// @NOTE +// +// Some arrays below are tagged "//varies", which means it's actually +// a variable-sized piece of data, but rather than malloc I assume it's +// small enough it's better to just allocate it all together with the +// main thing +// +// Most of the variables are specified with the smallest size I could pack +// them into. It might give better performance to make them all full-sized +// integers. It should be safe to freely rearrange the structures or change +// the sizes larger--nothing relies on silently truncating etc., nor the +// order of variables. + +#define FAST_HUFFMAN_TABLE_SIZE (1 << STB_VORBIS_FAST_HUFFMAN_LENGTH) +#define FAST_HUFFMAN_TABLE_MASK (FAST_HUFFMAN_TABLE_SIZE - 1) + +typedef struct +{ + int dimensions, entries; + uint8 *codeword_lengths; + float minimum_value; + float delta_value; + uint8 value_bits; + uint8 lookup_type; + uint8 sequence_p; + uint8 sparse; + uint32 lookup_values; + codetype *multiplicands; + uint32 *codewords; + #ifdef STB_VORBIS_FAST_HUFFMAN_SHORT + int16 fast_huffman[FAST_HUFFMAN_TABLE_SIZE]; + #else + int32 fast_huffman[FAST_HUFFMAN_TABLE_SIZE]; + #endif + uint32 *sorted_codewords; + int *sorted_values; + int sorted_entries; +} Codebook; + +typedef struct +{ + uint8 order; + uint16 rate; + uint16 bark_map_size; + uint8 amplitude_bits; + uint8 amplitude_offset; + uint8 number_of_books; + uint8 book_list[16]; // varies +} Floor0; + +typedef struct +{ + uint8 partitions; + uint8 partition_class_list[32]; // varies + uint8 class_dimensions[16]; // varies + uint8 class_subclasses[16]; // varies + uint8 class_masterbooks[16]; // varies + int16 subclass_books[16][8]; // varies + uint16 Xlist[31*8+2]; // varies + uint8 sorted_order[31*8+2]; + uint8 neighbors[31*8+2][2]; + uint8 floor1_multiplier; + uint8 rangebits; + int values; +} Floor1; + +typedef union +{ + Floor0 floor0; + Floor1 floor1; +} Floor; + +typedef struct +{ + uint32 begin, end; + uint32 part_size; + uint8 classifications; + uint8 classbook; + uint8 **classdata; + int16 (*residue_books)[8]; +} Residue; + +typedef struct +{ + uint8 magnitude; + uint8 angle; + uint8 mux; +} MappingChannel; + +typedef struct +{ + uint16 coupling_steps; + MappingChannel *chan; + uint8 submaps; + uint8 submap_floor[15]; // varies + uint8 submap_residue[15]; // varies +} Mapping; + +typedef struct +{ + uint8 blockflag; + uint8 mapping; + uint16 windowtype; + uint16 transformtype; +} Mode; + +typedef struct +{ + uint32 goal_crc; // expected crc if match + int bytes_left; // bytes left in packet + uint32 crc_so_far; // running crc + int bytes_done; // bytes processed in _current_ chunk + uint32 sample_loc; // granule pos encoded in page +} CRCscan; + +typedef struct +{ + uint32 page_start, page_end; + uint32 last_decoded_sample; +} ProbedPage; + +struct stb_vorbis +{ + // user-accessible info + unsigned int sample_rate; + int channels; + + unsigned int setup_memory_required; + unsigned int temp_memory_required; + unsigned int setup_temp_memory_required; + + // input config +#ifndef STB_VORBIS_NO_STDIO + FILE *f; + uint32 f_start; + int close_on_free; +#endif + + uint8 *stream; + uint8 *stream_start; + uint8 *stream_end; + + uint32 stream_len; + + uint8 push_mode; + + uint32 first_audio_page_offset; + + ProbedPage p_first, p_last; + + // memory management + stb_vorbis_alloc alloc; + int setup_offset; + int temp_offset; + + // run-time results + int eof; + enum STBVorbisError error; + + // user-useful data + + // header info + int blocksize[2]; + int blocksize_0, blocksize_1; + int codebook_count; + Codebook *codebooks; + int floor_count; + uint16 floor_types[64]; // varies + Floor *floor_config; + int residue_count; + uint16 residue_types[64]; // varies + Residue *residue_config; + int mapping_count; + Mapping *mapping; + int mode_count; + Mode mode_config[64]; // varies + + uint32 total_samples; + + // decode buffer + float *channel_buffers[STB_VORBIS_MAX_CHANNELS]; + float *outputs [STB_VORBIS_MAX_CHANNELS]; + + float *previous_window[STB_VORBIS_MAX_CHANNELS]; + int previous_length; + + #ifndef STB_VORBIS_NO_DEFER_FLOOR + int16 *finalY[STB_VORBIS_MAX_CHANNELS]; + #else + float *floor_buffers[STB_VORBIS_MAX_CHANNELS]; + #endif + + uint32 current_loc; // sample location of next frame to decode + int current_loc_valid; + + // per-blocksize precomputed data + + // twiddle factors + float *A[2],*B[2],*C[2]; + float *window[2]; + uint16 *bit_reverse[2]; + + // current page/packet/segment streaming info + uint32 serial; // stream serial number for verification + int last_page; + int segment_count; + uint8 segments[255]; + uint8 page_flag; + uint8 bytes_in_seg; + uint8 first_decode; + int next_seg; + int last_seg; // flag that we're on the last segment + int last_seg_which; // what was the segment number of the last seg? + uint32 acc; + int valid_bits; + int packet_bytes; + int end_seg_with_known_loc; + uint32 known_loc_for_packet; + int discard_samples_deferred; + uint32 samples_output; + + // push mode scanning + int page_crc_tests; // only in push_mode: number of tests active; -1 if not searching +#ifndef STB_VORBIS_NO_PUSHDATA_API + CRCscan scan[STB_VORBIS_PUSHDATA_CRC_COUNT]; +#endif + + // sample-access + int channel_buffer_start; + int channel_buffer_end; +}; + +#if defined(STB_VORBIS_NO_PUSHDATA_API) + #define IS_PUSH_MODE(f) FALSE +#elif defined(STB_VORBIS_NO_PULLDATA_API) + #define IS_PUSH_MODE(f) TRUE +#else + #define IS_PUSH_MODE(f) ((f)->push_mode) +#endif + +typedef struct stb_vorbis vorb; + +static int error(vorb *f, enum STBVorbisError e) +{ + f->error = e; + if (!f->eof && e != VORBIS_need_more_data) { + f->error=e; // breakpoint for debugging + } + return 0; +} + + +// these functions are used for allocating temporary memory +// while decoding. if you can afford the stack space, use +// alloca(); otherwise, provide a temp buffer and it will +// allocate out of those. + +#define array_size_required(count,size) (count*(sizeof(void *)+(size))) + +#define temp_alloc(f,size) (f->alloc.alloc_buffer ? setup_temp_malloc(f,size) : alloca(size)) +#ifdef dealloca +#define temp_free(f,p) (f->alloc.alloc_buffer ? 0 : dealloca(size)) +#else +#define temp_free(f,p) (void)0 +#endif +#define temp_alloc_save(f) ((f)->temp_offset) +#define temp_alloc_restore(f,p) ((f)->temp_offset = (p)) + +#define temp_block_array(f,count,size) make_block_array(temp_alloc(f,array_size_required(count,size)), count, size) + +// given a sufficiently large block of memory, make an array of pointers to subblocks of it +static void *make_block_array(void *mem, int count, int size) +{ + int i; + void ** p = (void **) mem; + char *q = (char *) (p + count); + for (i=0; i < count; ++i) { + p[i] = q; + q += size; + } + return p; +} + +static void *setup_malloc(vorb *f, int sz) +{ + sz = (sz+3) & ~3; + f->setup_memory_required += sz; + if (f->alloc.alloc_buffer) { + void *p = (char *) f->alloc.alloc_buffer + f->setup_offset; + if (f->setup_offset + sz > f->temp_offset) return NULL; + f->setup_offset += sz; + return p; + } + return sz ? malloc(sz) : NULL; +} + +static void setup_free(vorb *f, void *p) +{ + if (f->alloc.alloc_buffer) return; // do nothing; setup mem is a stack + free(p); +} + +static void *setup_temp_malloc(vorb *f, int sz) +{ + sz = (sz+3) & ~3; + if (f->alloc.alloc_buffer) { + if (f->temp_offset - sz < f->setup_offset) return NULL; + f->temp_offset -= sz; + return (char *) f->alloc.alloc_buffer + f->temp_offset; + } + return malloc(sz); +} + +static void setup_temp_free(vorb *f, void *p, int sz) +{ + if (f->alloc.alloc_buffer) { + f->temp_offset += (sz+3)&~3; + return; + } + free(p); +} + +#define CRC32_POLY 0x04c11db7 // from spec + +static uint32 crc_table[256]; +static void crc32_init(void) +{ + int i,j; + uint32 s; + for(i=0; i < 256; i++) { + for (s=(uint32) i << 24, j=0; j < 8; ++j) + s = (s << 1) ^ (s >= (1U<<31) ? CRC32_POLY : 0); + crc_table[i] = s; + } +} + +static __forceinline uint32 crc32_update(uint32 crc, uint8 byte) +{ + return (crc << 8) ^ crc_table[byte ^ (crc >> 24)]; +} + + +// used in setup, and for huffman that doesn't go fast path +static unsigned int bit_reverse(unsigned int n) +{ + n = ((n & 0xAAAAAAAA) >> 1) | ((n & 0x55555555) << 1); + n = ((n & 0xCCCCCCCC) >> 2) | ((n & 0x33333333) << 2); + n = ((n & 0xF0F0F0F0) >> 4) | ((n & 0x0F0F0F0F) << 4); + n = ((n & 0xFF00FF00) >> 8) | ((n & 0x00FF00FF) << 8); + return (n >> 16) | (n << 16); +} + +static float square(float x) +{ + return x*x; +} + +// this is a weird definition of log2() for which log2(1) = 1, log2(2) = 2, log2(4) = 3 +// as required by the specification. fast(?) implementation from stb.h +// @OPTIMIZE: called multiple times per-packet with "constants"; move to setup +static int ilog(int32 n) +{ + static signed char log2_4[16] = { 0,1,2,2,3,3,3,3,4,4,4,4,4,4,4,4 }; + + // 2 compares if n < 16, 3 compares otherwise (4 if signed or n > 1<<29) + if (n < (1 << 14)) + if (n < (1 << 4)) return 0 + log2_4[n ]; + else if (n < (1 << 9)) return 5 + log2_4[n >> 5]; + else return 10 + log2_4[n >> 10]; + else if (n < (1 << 24)) + if (n < (1 << 19)) return 15 + log2_4[n >> 15]; + else return 20 + log2_4[n >> 20]; + else if (n < (1 << 29)) return 25 + log2_4[n >> 25]; + else if (n < (1 << 31)) return 30 + log2_4[n >> 30]; + else return 0; // signed n returns 0 +} + +#ifndef M_PI + #define M_PI 3.14159265358979323846264f // from CRC +#endif + +// code length assigned to a value with no huffman encoding +#define NO_CODE 255 + +/////////////////////// LEAF SETUP FUNCTIONS ////////////////////////// +// +// these functions are only called at setup, and only a few times +// per file + +static float float32_unpack(uint32 x) +{ + // from the specification + uint32 mantissa = x & 0x1fffff; + uint32 sign = x & 0x80000000; + uint32 exp = (x & 0x7fe00000) >> 21; + double res = sign ? -(double)mantissa : (double)mantissa; + return (float) ldexp((float)res, exp-788); +} + + +// zlib & jpeg huffman tables assume that the output symbols +// can either be arbitrarily arranged, or have monotonically +// increasing frequencies--they rely on the lengths being sorted; +// this makes for a very simple generation algorithm. +// vorbis allows a huffman table with non-sorted lengths. This +// requires a more sophisticated construction, since symbols in +// order do not map to huffman codes "in order". +static void add_entry(Codebook *c, uint32 huff_code, int symbol, int count, int len, uint32 *values) +{ + if (!c->sparse) { + c->codewords [symbol] = huff_code; + } else { + c->codewords [count] = huff_code; + c->codeword_lengths[count] = len; + values [count] = symbol; + } +} + +static int compute_codewords(Codebook *c, uint8 *len, int n, uint32 *values) +{ + int i,k,m=0; + uint32 available[32]; + + memset(available, 0, sizeof(available)); + // find the first entry + for (k=0; k < n; ++k) if (len[k] < NO_CODE) break; + if (k == n) { assert(c->sorted_entries == 0); return TRUE; } + // add to the list + add_entry(c, 0, k, m++, len[k], values); + // add all available leaves + for (i=1; i <= len[k]; ++i) + available[i] = 1U << (32-i); + // note that the above code treats the first case specially, + // but it's really the same as the following code, so they + // could probably be combined (except the initial code is 0, + // and I use 0 in available[] to mean 'empty') + for (i=k+1; i < n; ++i) { + uint32 res; + int z = len[i], y; + if (z == NO_CODE) continue; + // find lowest available leaf (should always be earliest, + // which is what the specification calls for) + // note that this property, and the fact we can never have + // more than one free leaf at a given level, isn't totally + // trivial to prove, but it seems true and the assert never + // fires, so! + while (z > 0 && !available[z]) --z; + if (z == 0) { return FALSE; } + res = available[z]; + assert(z >= 0 && z < 32); + available[z] = 0; + add_entry(c, bit_reverse(res), i, m++, len[i], values); + // propogate availability up the tree + if (z != len[i]) { + assert(len[i] >= 0 && len[i] < 32); + for (y=len[i]; y > z; --y) { + assert(available[y] == 0); + available[y] = res + (1 << (32-y)); + } + } + } + return TRUE; +} + +// accelerated huffman table allows fast O(1) match of all symbols +// of length <= STB_VORBIS_FAST_HUFFMAN_LENGTH +static void compute_accelerated_huffman(Codebook *c) +{ + int i, len; + for (i=0; i < FAST_HUFFMAN_TABLE_SIZE; ++i) + c->fast_huffman[i] = -1; + + len = c->sparse ? c->sorted_entries : c->entries; + #ifdef STB_VORBIS_FAST_HUFFMAN_SHORT + if (len > 32767) len = 32767; // largest possible value we can encode! + #endif + for (i=0; i < len; ++i) { + if (c->codeword_lengths[i] <= STB_VORBIS_FAST_HUFFMAN_LENGTH) { + uint32 z = c->sparse ? bit_reverse(c->sorted_codewords[i]) : c->codewords[i]; + // set table entries for all bit combinations in the higher bits + while (z < FAST_HUFFMAN_TABLE_SIZE) { + c->fast_huffman[z] = i; + z += 1 << c->codeword_lengths[i]; + } + } + } +} + +#ifdef _MSC_VER +#define STBV_CDECL __cdecl +#else +#define STBV_CDECL +#endif + +static int STBV_CDECL uint32_compare(const void *p, const void *q) +{ + uint32 x = * (uint32 *) p; + uint32 y = * (uint32 *) q; + return x < y ? -1 : x > y; +} + +static int include_in_sort(Codebook *c, uint8 len) +{ + if (c->sparse) { assert(len != NO_CODE); return TRUE; } + if (len == NO_CODE) return FALSE; + if (len > STB_VORBIS_FAST_HUFFMAN_LENGTH) return TRUE; + return FALSE; +} + +// if the fast table above doesn't work, we want to binary +// search them... need to reverse the bits +static void compute_sorted_huffman(Codebook *c, uint8 *lengths, uint32 *values) +{ + int i, len; + // build a list of all the entries + // OPTIMIZATION: don't include the short ones, since they'll be caught by FAST_HUFFMAN. + // this is kind of a frivolous optimization--I don't see any performance improvement, + // but it's like 4 extra lines of code, so. + if (!c->sparse) { + int k = 0; + for (i=0; i < c->entries; ++i) + if (include_in_sort(c, lengths[i])) + c->sorted_codewords[k++] = bit_reverse(c->codewords[i]); + assert(k == c->sorted_entries); + } else { + for (i=0; i < c->sorted_entries; ++i) + c->sorted_codewords[i] = bit_reverse(c->codewords[i]); + } + + qsort(c->sorted_codewords, c->sorted_entries, sizeof(c->sorted_codewords[0]), uint32_compare); + c->sorted_codewords[c->sorted_entries] = 0xffffffff; + + len = c->sparse ? c->sorted_entries : c->entries; + // now we need to indicate how they correspond; we could either + // #1: sort a different data structure that says who they correspond to + // #2: for each sorted entry, search the original list to find who corresponds + // #3: for each original entry, find the sorted entry + // #1 requires extra storage, #2 is slow, #3 can use binary search! + for (i=0; i < len; ++i) { + int huff_len = c->sparse ? lengths[values[i]] : lengths[i]; + if (include_in_sort(c,huff_len)) { + uint32 code = bit_reverse(c->codewords[i]); + int x=0, n=c->sorted_entries; + while (n > 1) { + // invariant: sc[x] <= code < sc[x+n] + int m = x + (n >> 1); + if (c->sorted_codewords[m] <= code) { + x = m; + n -= (n>>1); + } else { + n >>= 1; + } + } + assert(c->sorted_codewords[x] == code); + if (c->sparse) { + c->sorted_values[x] = values[i]; + c->codeword_lengths[x] = huff_len; + } else { + c->sorted_values[x] = i; + } + } + } +} + +// only run while parsing the header (3 times) +static int vorbis_validate(uint8 *data) +{ + static uint8 vorbis[6] = { 'v', 'o', 'r', 'b', 'i', 's' }; + return memcmp(data, vorbis, 6) == 0; +} + +// called from setup only, once per code book +// (formula implied by specification) +static int lookup1_values(int entries, int dim) +{ + int r = (int) floor(exp((float) log((float) entries) / dim)); + if ((int) floor(pow((float) r+1, dim)) <= entries) // (int) cast for MinGW warning; + ++r; // floor() to avoid _ftol() when non-CRT + assert(pow((float) r+1, dim) > entries); + assert((int) floor(pow((float) r, dim)) <= entries); // (int),floor() as above + return r; +} + +// called twice per file +static void compute_twiddle_factors(int n, float *A, float *B, float *C) +{ + int n4 = n >> 2, n8 = n >> 3; + int k,k2; + + for (k=k2=0; k < n4; ++k,k2+=2) { + A[k2 ] = (float) cos(4*k*M_PI/n); + A[k2+1] = (float) -sin(4*k*M_PI/n); + B[k2 ] = (float) cos((k2+1)*M_PI/n/2) * 0.5f; + B[k2+1] = (float) sin((k2+1)*M_PI/n/2) * 0.5f; + } + for (k=k2=0; k < n8; ++k,k2+=2) { + C[k2 ] = (float) cos(2*(k2+1)*M_PI/n); + C[k2+1] = (float) -sin(2*(k2+1)*M_PI/n); + } +} + +static void compute_window(int n, float *window) +{ + int n2 = n >> 1, i; + for (i=0; i < n2; ++i) + window[i] = (float) sin(0.5 * M_PI * square((float) sin((i - 0 + 0.5) / n2 * 0.5 * M_PI))); +} + +static void compute_bitreverse(int n, uint16 *rev) +{ + int ld = ilog(n) - 1; // ilog is off-by-one from normal definitions + int i, n8 = n >> 3; + for (i=0; i < n8; ++i) + rev[i] = (bit_reverse(i) >> (32-ld+3)) << 2; +} + +static int init_blocksize(vorb *f, int b, int n) +{ + int n2 = n >> 1, n4 = n >> 2, n8 = n >> 3; + f->A[b] = (float *) setup_malloc(f, sizeof(float) * n2); + f->B[b] = (float *) setup_malloc(f, sizeof(float) * n2); + f->C[b] = (float *) setup_malloc(f, sizeof(float) * n4); + if (!f->A[b] || !f->B[b] || !f->C[b]) return error(f, VORBIS_outofmem); + compute_twiddle_factors(n, f->A[b], f->B[b], f->C[b]); + f->window[b] = (float *) setup_malloc(f, sizeof(float) * n2); + if (!f->window[b]) return error(f, VORBIS_outofmem); + compute_window(n, f->window[b]); + f->bit_reverse[b] = (uint16 *) setup_malloc(f, sizeof(uint16) * n8); + if (!f->bit_reverse[b]) return error(f, VORBIS_outofmem); + compute_bitreverse(n, f->bit_reverse[b]); + return TRUE; +} + +static void neighbors(uint16 *x, int n, int *plow, int *phigh) +{ + int low = -1; + int high = 65536; + int i; + for (i=0; i < n; ++i) { + if (x[i] > low && x[i] < x[n]) { *plow = i; low = x[i]; } + if (x[i] < high && x[i] > x[n]) { *phigh = i; high = x[i]; } + } +} + +// this has been repurposed so y is now the original index instead of y +typedef struct +{ + uint16 x,y; +} Point; + +static int STBV_CDECL point_compare(const void *p, const void *q) +{ + Point *a = (Point *) p; + Point *b = (Point *) q; + return a->x < b->x ? -1 : a->x > b->x; +} + +// +/////////////////////// END LEAF SETUP FUNCTIONS ////////////////////////// + + +#if defined(STB_VORBIS_NO_STDIO) + #define USE_MEMORY(z) TRUE +#else + #define USE_MEMORY(z) ((z)->stream) +#endif + +static uint8 get8(vorb *z) +{ + if (USE_MEMORY(z)) { + if (z->stream >= z->stream_end) { z->eof = TRUE; return 0; } + return *z->stream++; + } + + #ifndef STB_VORBIS_NO_STDIO + { + int c = fgetc(z->f); + if (c == EOF) { z->eof = TRUE; return 0; } + return c; + } + #endif +} + +static uint32 get32(vorb *f) +{ + uint32 x; + x = get8(f); + x += get8(f) << 8; + x += get8(f) << 16; + x += (uint32) get8(f) << 24; + return x; +} + +static int getn(vorb *z, uint8 *data, int n) +{ + if (USE_MEMORY(z)) { + if (z->stream+n > z->stream_end) { z->eof = 1; return 0; } + memcpy(data, z->stream, n); + z->stream += n; + return 1; + } + + #ifndef STB_VORBIS_NO_STDIO + if (fread(data, n, 1, z->f) == 1) + return 1; + else { + z->eof = 1; + return 0; + } + #endif +} + +static void skip(vorb *z, int n) +{ + if (USE_MEMORY(z)) { + z->stream += n; + if (z->stream >= z->stream_end) z->eof = 1; + return; + } + #ifndef STB_VORBIS_NO_STDIO + { + long x = ftell(z->f); + fseek(z->f, x+n, SEEK_SET); + } + #endif +} + +static int set_file_offset(stb_vorbis *f, unsigned int loc) +{ + #ifndef STB_VORBIS_NO_PUSHDATA_API + if (f->push_mode) return 0; + #endif + f->eof = 0; + if (USE_MEMORY(f)) { + if (f->stream_start + loc >= f->stream_end || f->stream_start + loc < f->stream_start) { + f->stream = f->stream_end; + f->eof = 1; + return 0; + } else { + f->stream = f->stream_start + loc; + return 1; + } + } + #ifndef STB_VORBIS_NO_STDIO + if (loc + f->f_start < loc || loc >= 0x80000000) { + loc = 0x7fffffff; + f->eof = 1; + } else { + loc += f->f_start; + } + if (!fseek(f->f, loc, SEEK_SET)) + return 1; + f->eof = 1; + fseek(f->f, f->f_start, SEEK_END); + return 0; + #endif +} + + +static uint8 ogg_page_header[4] = { 0x4f, 0x67, 0x67, 0x53 }; + +static int capture_pattern(vorb *f) +{ + if (0x4f != get8(f)) return FALSE; + if (0x67 != get8(f)) return FALSE; + if (0x67 != get8(f)) return FALSE; + if (0x53 != get8(f)) return FALSE; + return TRUE; +} + +#define PAGEFLAG_continued_packet 1 +#define PAGEFLAG_first_page 2 +#define PAGEFLAG_last_page 4 + +static int start_page_no_capturepattern(vorb *f) +{ + uint32 loc0,loc1,n; + // stream structure version + if (0 != get8(f)) return error(f, VORBIS_invalid_stream_structure_version); + // header flag + f->page_flag = get8(f); + // absolute granule position + loc0 = get32(f); + loc1 = get32(f); + // @TODO: validate loc0,loc1 as valid positions? + // stream serial number -- vorbis doesn't interleave, so discard + get32(f); + //if (f->serial != get32(f)) return error(f, VORBIS_incorrect_stream_serial_number); + // page sequence number + n = get32(f); + f->last_page = n; + // CRC32 + get32(f); + // page_segments + f->segment_count = get8(f); + if (!getn(f, f->segments, f->segment_count)) + return error(f, VORBIS_unexpected_eof); + // assume we _don't_ know any the sample position of any segments + f->end_seg_with_known_loc = -2; + if (loc0 != ~0U || loc1 != ~0U) { + int i; + // determine which packet is the last one that will complete + for (i=f->segment_count-1; i >= 0; --i) + if (f->segments[i] < 255) + break; + // 'i' is now the index of the _last_ segment of a packet that ends + if (i >= 0) { + f->end_seg_with_known_loc = i; + f->known_loc_for_packet = loc0; + } + } + if (f->first_decode) { + int i,len; + ProbedPage p; + len = 0; + for (i=0; i < f->segment_count; ++i) + len += f->segments[i]; + len += 27 + f->segment_count; + p.page_start = f->first_audio_page_offset; + p.page_end = p.page_start + len; + p.last_decoded_sample = loc0; + f->p_first = p; + } + f->next_seg = 0; + return TRUE; +} + +static int start_page(vorb *f) +{ + if (!capture_pattern(f)) return error(f, VORBIS_missing_capture_pattern); + return start_page_no_capturepattern(f); +} + +static int start_packet(vorb *f) +{ + while (f->next_seg == -1) { + if (!start_page(f)) return FALSE; + if (f->page_flag & PAGEFLAG_continued_packet) + return error(f, VORBIS_continued_packet_flag_invalid); + } + f->last_seg = FALSE; + f->valid_bits = 0; + f->packet_bytes = 0; + f->bytes_in_seg = 0; + // f->next_seg is now valid + return TRUE; +} + +static int maybe_start_packet(vorb *f) +{ + if (f->next_seg == -1) { + int x = get8(f); + if (f->eof) return FALSE; // EOF at page boundary is not an error! + if (0x4f != x ) return error(f, VORBIS_missing_capture_pattern); + if (0x67 != get8(f)) return error(f, VORBIS_missing_capture_pattern); + if (0x67 != get8(f)) return error(f, VORBIS_missing_capture_pattern); + if (0x53 != get8(f)) return error(f, VORBIS_missing_capture_pattern); + if (!start_page_no_capturepattern(f)) return FALSE; + if (f->page_flag & PAGEFLAG_continued_packet) { + // set up enough state that we can read this packet if we want, + // e.g. during recovery + f->last_seg = FALSE; + f->bytes_in_seg = 0; + return error(f, VORBIS_continued_packet_flag_invalid); + } + } + return start_packet(f); +} + +static int next_segment(vorb *f) +{ + int len; + if (f->last_seg) return 0; + if (f->next_seg == -1) { + f->last_seg_which = f->segment_count-1; // in case start_page fails + if (!start_page(f)) { f->last_seg = 1; return 0; } + if (!(f->page_flag & PAGEFLAG_continued_packet)) return error(f, VORBIS_continued_packet_flag_invalid); + } + len = f->segments[f->next_seg++]; + if (len < 255) { + f->last_seg = TRUE; + f->last_seg_which = f->next_seg-1; + } + if (f->next_seg >= f->segment_count) + f->next_seg = -1; + assert(f->bytes_in_seg == 0); + f->bytes_in_seg = len; + return len; +} + +#define EOP (-1) +#define INVALID_BITS (-1) + +static int get8_packet_raw(vorb *f) +{ + if (!f->bytes_in_seg) { // CLANG! + if (f->last_seg) return EOP; + else if (!next_segment(f)) return EOP; + } + assert(f->bytes_in_seg > 0); + --f->bytes_in_seg; + ++f->packet_bytes; + return get8(f); +} + +static int get8_packet(vorb *f) +{ + int x = get8_packet_raw(f); + f->valid_bits = 0; + return x; +} + +static void flush_packet(vorb *f) +{ + while (get8_packet_raw(f) != EOP); +} + +// @OPTIMIZE: this is the secondary bit decoder, so it's probably not as important +// as the huffman decoder? +static uint32 get_bits(vorb *f, int n) +{ + uint32 z; + + if (f->valid_bits < 0) return 0; + if (f->valid_bits < n) { + if (n > 24) { + // the accumulator technique below would not work correctly in this case + z = get_bits(f, 24); + z += get_bits(f, n-24) << 24; + return z; + } + if (f->valid_bits == 0) f->acc = 0; + while (f->valid_bits < n) { + int z = get8_packet_raw(f); + if (z == EOP) { + f->valid_bits = INVALID_BITS; + return 0; + } + f->acc += z << f->valid_bits; + f->valid_bits += 8; + } + } + if (f->valid_bits < 0) return 0; + z = f->acc & ((1 << n)-1); + f->acc >>= n; + f->valid_bits -= n; + return z; +} + +// @OPTIMIZE: primary accumulator for huffman +// expand the buffer to as many bits as possible without reading off end of packet +// it might be nice to allow f->valid_bits and f->acc to be stored in registers, +// e.g. cache them locally and decode locally +static __forceinline void prep_huffman(vorb *f) +{ + if (f->valid_bits <= 24) { + if (f->valid_bits == 0) f->acc = 0; + do { + int z; + if (f->last_seg && !f->bytes_in_seg) return; + z = get8_packet_raw(f); + if (z == EOP) return; + f->acc += (unsigned) z << f->valid_bits; + f->valid_bits += 8; + } while (f->valid_bits <= 24); + } +} + +enum +{ + VORBIS_packet_id = 1, + VORBIS_packet_comment = 3, + VORBIS_packet_setup = 5 +}; + +static int codebook_decode_scalar_raw(vorb *f, Codebook *c) +{ + int i; + prep_huffman(f); + + if (c->codewords == NULL && c->sorted_codewords == NULL) + return -1; + + // cases to use binary search: sorted_codewords && !c->codewords + // sorted_codewords && c->entries > 8 + if (c->entries > 8 ? c->sorted_codewords!=NULL : !c->codewords) { + // binary search + uint32 code = bit_reverse(f->acc); + int x=0, n=c->sorted_entries, len; + + while (n > 1) { + // invariant: sc[x] <= code < sc[x+n] + int m = x + (n >> 1); + if (c->sorted_codewords[m] <= code) { + x = m; + n -= (n>>1); + } else { + n >>= 1; + } + } + // x is now the sorted index + if (!c->sparse) x = c->sorted_values[x]; + // x is now sorted index if sparse, or symbol otherwise + len = c->codeword_lengths[x]; + if (f->valid_bits >= len) { + f->acc >>= len; + f->valid_bits -= len; + return x; + } + + f->valid_bits = 0; + return -1; + } + + // if small, linear search + assert(!c->sparse); + for (i=0; i < c->entries; ++i) { + if (c->codeword_lengths[i] == NO_CODE) continue; + if (c->codewords[i] == (f->acc & ((1 << c->codeword_lengths[i])-1))) { + if (f->valid_bits >= c->codeword_lengths[i]) { + f->acc >>= c->codeword_lengths[i]; + f->valid_bits -= c->codeword_lengths[i]; + return i; + } + f->valid_bits = 0; + return -1; + } + } + + error(f, VORBIS_invalid_stream); + f->valid_bits = 0; + return -1; +} + +#ifndef STB_VORBIS_NO_INLINE_DECODE + +#define DECODE_RAW(var, f,c) \ + if (f->valid_bits < STB_VORBIS_FAST_HUFFMAN_LENGTH) \ + prep_huffman(f); \ + var = f->acc & FAST_HUFFMAN_TABLE_MASK; \ + var = c->fast_huffman[var]; \ + if (var >= 0) { \ + int n = c->codeword_lengths[var]; \ + f->acc >>= n; \ + f->valid_bits -= n; \ + if (f->valid_bits < 0) { f->valid_bits = 0; var = -1; } \ + } else { \ + var = codebook_decode_scalar_raw(f,c); \ + } + +#else + +static int codebook_decode_scalar(vorb *f, Codebook *c) +{ + int i; + if (f->valid_bits < STB_VORBIS_FAST_HUFFMAN_LENGTH) + prep_huffman(f); + // fast huffman table lookup + i = f->acc & FAST_HUFFMAN_TABLE_MASK; + i = c->fast_huffman[i]; + if (i >= 0) { + f->acc >>= c->codeword_lengths[i]; + f->valid_bits -= c->codeword_lengths[i]; + if (f->valid_bits < 0) { f->valid_bits = 0; return -1; } + return i; + } + return codebook_decode_scalar_raw(f,c); +} + +#define DECODE_RAW(var,f,c) var = codebook_decode_scalar(f,c); + +#endif + +#define DECODE(var,f,c) \ + DECODE_RAW(var,f,c) \ + if (c->sparse) var = c->sorted_values[var]; + +#ifndef STB_VORBIS_DIVIDES_IN_CODEBOOK + #define DECODE_VQ(var,f,c) DECODE_RAW(var,f,c) +#else + #define DECODE_VQ(var,f,c) DECODE(var,f,c) +#endif + + + + + + +// CODEBOOK_ELEMENT_FAST is an optimization for the CODEBOOK_FLOATS case +// where we avoid one addition +#define CODEBOOK_ELEMENT(c,off) (c->multiplicands[off]) +#define CODEBOOK_ELEMENT_FAST(c,off) (c->multiplicands[off]) +#define CODEBOOK_ELEMENT_BASE(c) (0) + +static int codebook_decode_start(vorb *f, Codebook *c) +{ + int z = -1; + + // type 0 is only legal in a scalar context + if (c->lookup_type == 0) + error(f, VORBIS_invalid_stream); + else { + DECODE_VQ(z,f,c); + if (c->sparse) assert(z < c->sorted_entries); + if (z < 0) { // check for EOP + if (!f->bytes_in_seg) + if (f->last_seg) + return z; + error(f, VORBIS_invalid_stream); + } + } + return z; +} + +static int codebook_decode(vorb *f, Codebook *c, float *output, int len) +{ + int i,z = codebook_decode_start(f,c); + if (z < 0) return FALSE; + if (len > c->dimensions) len = c->dimensions; + +#ifdef STB_VORBIS_DIVIDES_IN_CODEBOOK + if (c->lookup_type == 1) { + float last = CODEBOOK_ELEMENT_BASE(c); + int div = 1; + for (i=0; i < len; ++i) { + int off = (z / div) % c->lookup_values; + float val = CODEBOOK_ELEMENT_FAST(c,off) + last; + output[i] += val; + if (c->sequence_p) last = val + c->minimum_value; + div *= c->lookup_values; + } + return TRUE; + } +#endif + + z *= c->dimensions; + if (c->sequence_p) { + float last = CODEBOOK_ELEMENT_BASE(c); + for (i=0; i < len; ++i) { + float val = CODEBOOK_ELEMENT_FAST(c,z+i) + last; + output[i] += val; + last = val + c->minimum_value; + } + } else { + float last = CODEBOOK_ELEMENT_BASE(c); + for (i=0; i < len; ++i) { + output[i] += CODEBOOK_ELEMENT_FAST(c,z+i) + last; + } + } + + return TRUE; +} + +static int codebook_decode_step(vorb *f, Codebook *c, float *output, int len, int step) +{ + int i,z = codebook_decode_start(f,c); + float last = CODEBOOK_ELEMENT_BASE(c); + if (z < 0) return FALSE; + if (len > c->dimensions) len = c->dimensions; + +#ifdef STB_VORBIS_DIVIDES_IN_CODEBOOK + if (c->lookup_type == 1) { + int div = 1; + for (i=0; i < len; ++i) { + int off = (z / div) % c->lookup_values; + float val = CODEBOOK_ELEMENT_FAST(c,off) + last; + output[i*step] += val; + if (c->sequence_p) last = val; + div *= c->lookup_values; + } + return TRUE; + } +#endif + + z *= c->dimensions; + for (i=0; i < len; ++i) { + float val = CODEBOOK_ELEMENT_FAST(c,z+i) + last; + output[i*step] += val; + if (c->sequence_p) last = val; + } + + return TRUE; +} + +static int codebook_decode_deinterleave_repeat(vorb *f, Codebook *c, float **outputs, int ch, int *c_inter_p, int *p_inter_p, int len, int total_decode) +{ + int c_inter = *c_inter_p; + int p_inter = *p_inter_p; + int i,z, effective = c->dimensions; + + // type 0 is only legal in a scalar context + if (c->lookup_type == 0) return error(f, VORBIS_invalid_stream); + + while (total_decode > 0) { + float last = CODEBOOK_ELEMENT_BASE(c); + DECODE_VQ(z,f,c); + #ifndef STB_VORBIS_DIVIDES_IN_CODEBOOK + assert(!c->sparse || z < c->sorted_entries); + #endif + if (z < 0) { + if (!f->bytes_in_seg) + if (f->last_seg) return FALSE; + return error(f, VORBIS_invalid_stream); + } + + // if this will take us off the end of the buffers, stop short! + // we check by computing the length of the virtual interleaved + // buffer (len*ch), our current offset within it (p_inter*ch)+(c_inter), + // and the length we'll be using (effective) + if (c_inter + p_inter*ch + effective > len * ch) { + effective = len*ch - (p_inter*ch - c_inter); + } + + #ifdef STB_VORBIS_DIVIDES_IN_CODEBOOK + if (c->lookup_type == 1) { + int div = 1; + for (i=0; i < effective; ++i) { + int off = (z / div) % c->lookup_values; + float val = CODEBOOK_ELEMENT_FAST(c,off) + last; + if (outputs[c_inter]) + outputs[c_inter][p_inter] += val; + if (++c_inter == ch) { c_inter = 0; ++p_inter; } + if (c->sequence_p) last = val; + div *= c->lookup_values; + } + } else + #endif + { + z *= c->dimensions; + if (c->sequence_p) { + for (i=0; i < effective; ++i) { + float val = CODEBOOK_ELEMENT_FAST(c,z+i) + last; + if (outputs[c_inter]) + outputs[c_inter][p_inter] += val; + if (++c_inter == ch) { c_inter = 0; ++p_inter; } + last = val; + } + } else { + for (i=0; i < effective; ++i) { + float val = CODEBOOK_ELEMENT_FAST(c,z+i) + last; + if (outputs[c_inter]) + outputs[c_inter][p_inter] += val; + if (++c_inter == ch) { c_inter = 0; ++p_inter; } + } + } + } + + total_decode -= effective; + } + *c_inter_p = c_inter; + *p_inter_p = p_inter; + return TRUE; +} + +static int predict_point(int x, int x0, int x1, int y0, int y1) +{ + int dy = y1 - y0; + int adx = x1 - x0; + // @OPTIMIZE: force int division to round in the right direction... is this necessary on x86? + int err = abs(dy) * (x - x0); + int off = err / adx; + return dy < 0 ? y0 - off : y0 + off; +} + +// the following table is block-copied from the specification +static float inverse_db_table[256] = +{ + 1.0649863e-07f, 1.1341951e-07f, 1.2079015e-07f, 1.2863978e-07f, + 1.3699951e-07f, 1.4590251e-07f, 1.5538408e-07f, 1.6548181e-07f, + 1.7623575e-07f, 1.8768855e-07f, 1.9988561e-07f, 2.1287530e-07f, + 2.2670913e-07f, 2.4144197e-07f, 2.5713223e-07f, 2.7384213e-07f, + 2.9163793e-07f, 3.1059021e-07f, 3.3077411e-07f, 3.5226968e-07f, + 3.7516214e-07f, 3.9954229e-07f, 4.2550680e-07f, 4.5315863e-07f, + 4.8260743e-07f, 5.1396998e-07f, 5.4737065e-07f, 5.8294187e-07f, + 6.2082472e-07f, 6.6116941e-07f, 7.0413592e-07f, 7.4989464e-07f, + 7.9862701e-07f, 8.5052630e-07f, 9.0579828e-07f, 9.6466216e-07f, + 1.0273513e-06f, 1.0941144e-06f, 1.1652161e-06f, 1.2409384e-06f, + 1.3215816e-06f, 1.4074654e-06f, 1.4989305e-06f, 1.5963394e-06f, + 1.7000785e-06f, 1.8105592e-06f, 1.9282195e-06f, 2.0535261e-06f, + 2.1869758e-06f, 2.3290978e-06f, 2.4804557e-06f, 2.6416497e-06f, + 2.8133190e-06f, 2.9961443e-06f, 3.1908506e-06f, 3.3982101e-06f, + 3.6190449e-06f, 3.8542308e-06f, 4.1047004e-06f, 4.3714470e-06f, + 4.6555282e-06f, 4.9580707e-06f, 5.2802740e-06f, 5.6234160e-06f, + 5.9888572e-06f, 6.3780469e-06f, 6.7925283e-06f, 7.2339451e-06f, + 7.7040476e-06f, 8.2047000e-06f, 8.7378876e-06f, 9.3057248e-06f, + 9.9104632e-06f, 1.0554501e-05f, 1.1240392e-05f, 1.1970856e-05f, + 1.2748789e-05f, 1.3577278e-05f, 1.4459606e-05f, 1.5399272e-05f, + 1.6400004e-05f, 1.7465768e-05f, 1.8600792e-05f, 1.9809576e-05f, + 2.1096914e-05f, 2.2467911e-05f, 2.3928002e-05f, 2.5482978e-05f, + 2.7139006e-05f, 2.8902651e-05f, 3.0780908e-05f, 3.2781225e-05f, + 3.4911534e-05f, 3.7180282e-05f, 3.9596466e-05f, 4.2169667e-05f, + 4.4910090e-05f, 4.7828601e-05f, 5.0936773e-05f, 5.4246931e-05f, + 5.7772202e-05f, 6.1526565e-05f, 6.5524908e-05f, 6.9783085e-05f, + 7.4317983e-05f, 7.9147585e-05f, 8.4291040e-05f, 8.9768747e-05f, + 9.5602426e-05f, 0.00010181521f, 0.00010843174f, 0.00011547824f, + 0.00012298267f, 0.00013097477f, 0.00013948625f, 0.00014855085f, + 0.00015820453f, 0.00016848555f, 0.00017943469f, 0.00019109536f, + 0.00020351382f, 0.00021673929f, 0.00023082423f, 0.00024582449f, + 0.00026179955f, 0.00027881276f, 0.00029693158f, 0.00031622787f, + 0.00033677814f, 0.00035866388f, 0.00038197188f, 0.00040679456f, + 0.00043323036f, 0.00046138411f, 0.00049136745f, 0.00052329927f, + 0.00055730621f, 0.00059352311f, 0.00063209358f, 0.00067317058f, + 0.00071691700f, 0.00076350630f, 0.00081312324f, 0.00086596457f, + 0.00092223983f, 0.00098217216f, 0.0010459992f, 0.0011139742f, + 0.0011863665f, 0.0012634633f, 0.0013455702f, 0.0014330129f, + 0.0015261382f, 0.0016253153f, 0.0017309374f, 0.0018434235f, + 0.0019632195f, 0.0020908006f, 0.0022266726f, 0.0023713743f, + 0.0025254795f, 0.0026895994f, 0.0028643847f, 0.0030505286f, + 0.0032487691f, 0.0034598925f, 0.0036847358f, 0.0039241906f, + 0.0041792066f, 0.0044507950f, 0.0047400328f, 0.0050480668f, + 0.0053761186f, 0.0057254891f, 0.0060975636f, 0.0064938176f, + 0.0069158225f, 0.0073652516f, 0.0078438871f, 0.0083536271f, + 0.0088964928f, 0.009474637f, 0.010090352f, 0.010746080f, + 0.011444421f, 0.012188144f, 0.012980198f, 0.013823725f, + 0.014722068f, 0.015678791f, 0.016697687f, 0.017782797f, + 0.018938423f, 0.020169149f, 0.021479854f, 0.022875735f, + 0.024362330f, 0.025945531f, 0.027631618f, 0.029427276f, + 0.031339626f, 0.033376252f, 0.035545228f, 0.037855157f, + 0.040315199f, 0.042935108f, 0.045725273f, 0.048696758f, + 0.051861348f, 0.055231591f, 0.058820850f, 0.062643361f, + 0.066714279f, 0.071049749f, 0.075666962f, 0.080584227f, + 0.085821044f, 0.091398179f, 0.097337747f, 0.10366330f, + 0.11039993f, 0.11757434f, 0.12521498f, 0.13335215f, + 0.14201813f, 0.15124727f, 0.16107617f, 0.17154380f, + 0.18269168f, 0.19456402f, 0.20720788f, 0.22067342f, + 0.23501402f, 0.25028656f, 0.26655159f, 0.28387361f, + 0.30232132f, 0.32196786f, 0.34289114f, 0.36517414f, + 0.38890521f, 0.41417847f, 0.44109412f, 0.46975890f, + 0.50028648f, 0.53279791f, 0.56742212f, 0.60429640f, + 0.64356699f, 0.68538959f, 0.72993007f, 0.77736504f, + 0.82788260f, 0.88168307f, 0.9389798f, 1.0f +}; + + +// @OPTIMIZE: if you want to replace this bresenham line-drawing routine, +// note that you must produce bit-identical output to decode correctly; +// this specific sequence of operations is specified in the spec (it's +// drawing integer-quantized frequency-space lines that the encoder +// expects to be exactly the same) +// ... also, isn't the whole point of Bresenham's algorithm to NOT +// have to divide in the setup? sigh. +#ifndef STB_VORBIS_NO_DEFER_FLOOR +#define LINE_OP(a,b) a *= b +#else +#define LINE_OP(a,b) a = b +#endif + +#ifdef STB_VORBIS_DIVIDE_TABLE +#define DIVTAB_NUMER 32 +#define DIVTAB_DENOM 64 +int8 integer_divide_table[DIVTAB_NUMER][DIVTAB_DENOM]; // 2KB +#endif + +static __forceinline void draw_line(float *output, int x0, int y0, int x1, int y1, int n) +{ + int dy = y1 - y0; + int adx = x1 - x0; + int ady = abs(dy); + int base; + int x=x0,y=y0; + int err = 0; + int sy; + +#ifdef STB_VORBIS_DIVIDE_TABLE + if (adx < DIVTAB_DENOM && ady < DIVTAB_NUMER) { + if (dy < 0) { + base = -integer_divide_table[ady][adx]; + sy = base-1; + } else { + base = integer_divide_table[ady][adx]; + sy = base+1; + } + } else { + base = dy / adx; + if (dy < 0) + sy = base - 1; + else + sy = base+1; + } +#else + base = dy / adx; + if (dy < 0) + sy = base - 1; + else + sy = base+1; +#endif + ady -= abs(base) * adx; + if (x1 > n) x1 = n; + if (x < x1) { + LINE_OP(output[x], inverse_db_table[y]); + for (++x; x < x1; ++x) { + err += ady; + if (err >= adx) { + err -= adx; + y += sy; + } else + y += base; + LINE_OP(output[x], inverse_db_table[y]); + } + } +} + +static int residue_decode(vorb *f, Codebook *book, float *target, int offset, int n, int rtype) +{ + int k; + if (rtype == 0) { + int step = n / book->dimensions; + for (k=0; k < step; ++k) + if (!codebook_decode_step(f, book, target+offset+k, n-offset-k, step)) + return FALSE; + } else { + for (k=0; k < n; ) { + if (!codebook_decode(f, book, target+offset, n-k)) + return FALSE; + k += book->dimensions; + offset += book->dimensions; + } + } + return TRUE; +} + +static void decode_residue(vorb *f, float *residue_buffers[], int ch, int n, int rn, uint8 *do_not_decode) +{ + int i,j,pass; + Residue *r = f->residue_config + rn; + int rtype = f->residue_types[rn]; + int c = r->classbook; + int classwords = f->codebooks[c].dimensions; + int n_read = r->end - r->begin; + int part_read = n_read / r->part_size; + int temp_alloc_point = temp_alloc_save(f); + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + uint8 ***part_classdata = (uint8 ***) temp_block_array(f,f->channels, part_read * sizeof(**part_classdata)); + #else + int **classifications = (int **) temp_block_array(f,f->channels, part_read * sizeof(**classifications)); + #endif + + CHECK(f); + + for (i=0; i < ch; ++i) + if (!do_not_decode[i]) + memset(residue_buffers[i], 0, sizeof(float) * n); + + if (rtype == 2 && ch != 1) { + for (j=0; j < ch; ++j) + if (!do_not_decode[j]) + break; + if (j == ch) + goto done; + + for (pass=0; pass < 8; ++pass) { + int pcount = 0, class_set = 0; + if (ch == 2) { + while (pcount < part_read) { + int z = r->begin + pcount*r->part_size; + int c_inter = (z & 1), p_inter = z>>1; + if (pass == 0) { + Codebook *c = f->codebooks+r->classbook; + int q; + DECODE(q,f,c); + if (q == EOP) goto done; + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + part_classdata[0][class_set] = r->classdata[q]; + #else + for (i=classwords-1; i >= 0; --i) { + classifications[0][i+pcount] = q % r->classifications; + q /= r->classifications; + } + #endif + } + for (i=0; i < classwords && pcount < part_read; ++i, ++pcount) { + int z = r->begin + pcount*r->part_size; + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + int c = part_classdata[0][class_set][i]; + #else + int c = classifications[0][pcount]; + #endif + int b = r->residue_books[c][pass]; + if (b >= 0) { + Codebook *book = f->codebooks + b; + #ifdef STB_VORBIS_DIVIDES_IN_CODEBOOK + if (!codebook_decode_deinterleave_repeat(f, book, residue_buffers, ch, &c_inter, &p_inter, n, r->part_size)) + goto done; + #else + // saves 1% + if (!codebook_decode_deinterleave_repeat(f, book, residue_buffers, ch, &c_inter, &p_inter, n, r->part_size)) + goto done; + #endif + } else { + z += r->part_size; + c_inter = z & 1; + p_inter = z >> 1; + } + } + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + ++class_set; + #endif + } + } else if (ch == 1) { + while (pcount < part_read) { + int z = r->begin + pcount*r->part_size; + int c_inter = 0, p_inter = z; + if (pass == 0) { + Codebook *c = f->codebooks+r->classbook; + int q; + DECODE(q,f,c); + if (q == EOP) goto done; + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + part_classdata[0][class_set] = r->classdata[q]; + #else + for (i=classwords-1; i >= 0; --i) { + classifications[0][i+pcount] = q % r->classifications; + q /= r->classifications; + } + #endif + } + for (i=0; i < classwords && pcount < part_read; ++i, ++pcount) { + int z = r->begin + pcount*r->part_size; + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + int c = part_classdata[0][class_set][i]; + #else + int c = classifications[0][pcount]; + #endif + int b = r->residue_books[c][pass]; + if (b >= 0) { + Codebook *book = f->codebooks + b; + if (!codebook_decode_deinterleave_repeat(f, book, residue_buffers, ch, &c_inter, &p_inter, n, r->part_size)) + goto done; + } else { + z += r->part_size; + c_inter = 0; + p_inter = z; + } + } + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + ++class_set; + #endif + } + } else { + while (pcount < part_read) { + int z = r->begin + pcount*r->part_size; + int c_inter = z % ch, p_inter = z/ch; + if (pass == 0) { + Codebook *c = f->codebooks+r->classbook; + int q; + DECODE(q,f,c); + if (q == EOP) goto done; + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + part_classdata[0][class_set] = r->classdata[q]; + #else + for (i=classwords-1; i >= 0; --i) { + classifications[0][i+pcount] = q % r->classifications; + q /= r->classifications; + } + #endif + } + for (i=0; i < classwords && pcount < part_read; ++i, ++pcount) { + int z = r->begin + pcount*r->part_size; + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + int c = part_classdata[0][class_set][i]; + #else + int c = classifications[0][pcount]; + #endif + int b = r->residue_books[c][pass]; + if (b >= 0) { + Codebook *book = f->codebooks + b; + if (!codebook_decode_deinterleave_repeat(f, book, residue_buffers, ch, &c_inter, &p_inter, n, r->part_size)) + goto done; + } else { + z += r->part_size; + c_inter = z % ch; + p_inter = z / ch; + } + } + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + ++class_set; + #endif + } + } + } + goto done; + } + CHECK(f); + + for (pass=0; pass < 8; ++pass) { + int pcount = 0, class_set=0; + while (pcount < part_read) { + if (pass == 0) { + for (j=0; j < ch; ++j) { + if (!do_not_decode[j]) { + Codebook *c = f->codebooks+r->classbook; + int temp; + DECODE(temp,f,c); + if (temp == EOP) goto done; + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + part_classdata[j][class_set] = r->classdata[temp]; + #else + for (i=classwords-1; i >= 0; --i) { + classifications[j][i+pcount] = temp % r->classifications; + temp /= r->classifications; + } + #endif + } + } + } + for (i=0; i < classwords && pcount < part_read; ++i, ++pcount) { + for (j=0; j < ch; ++j) { + if (!do_not_decode[j]) { + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + int c = part_classdata[j][class_set][i]; + #else + int c = classifications[j][pcount]; + #endif + int b = r->residue_books[c][pass]; + if (b >= 0) { + float *target = residue_buffers[j]; + int offset = r->begin + pcount * r->part_size; + int n = r->part_size; + Codebook *book = f->codebooks + b; + if (!residue_decode(f, book, target, offset, n, rtype)) + goto done; + } + } + } + } + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + ++class_set; + #endif + } + } + done: + CHECK(f); + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + temp_free(f,part_classdata); + #else + temp_free(f,classifications); + #endif + temp_alloc_restore(f,temp_alloc_point); +} + + +#if 0 +// slow way for debugging +void inverse_mdct_slow(float *buffer, int n) +{ + int i,j; + int n2 = n >> 1; + float *x = (float *) malloc(sizeof(*x) * n2); + memcpy(x, buffer, sizeof(*x) * n2); + for (i=0; i < n; ++i) { + float acc = 0; + for (j=0; j < n2; ++j) + // formula from paper: + //acc += n/4.0f * x[j] * (float) cos(M_PI / 2 / n * (2 * i + 1 + n/2.0)*(2*j+1)); + // formula from wikipedia + //acc += 2.0f / n2 * x[j] * (float) cos(M_PI/n2 * (i + 0.5 + n2/2)*(j + 0.5)); + // these are equivalent, except the formula from the paper inverts the multiplier! + // however, what actually works is NO MULTIPLIER!?! + //acc += 64 * 2.0f / n2 * x[j] * (float) cos(M_PI/n2 * (i + 0.5 + n2/2)*(j + 0.5)); + acc += x[j] * (float) cos(M_PI / 2 / n * (2 * i + 1 + n/2.0)*(2*j+1)); + buffer[i] = acc; + } + free(x); +} +#elif 0 +// same as above, but just barely able to run in real time on modern machines +void inverse_mdct_slow(float *buffer, int n, vorb *f, int blocktype) +{ + float mcos[16384]; + int i,j; + int n2 = n >> 1, nmask = (n << 2) -1; + float *x = (float *) malloc(sizeof(*x) * n2); + memcpy(x, buffer, sizeof(*x) * n2); + for (i=0; i < 4*n; ++i) + mcos[i] = (float) cos(M_PI / 2 * i / n); + + for (i=0; i < n; ++i) { + float acc = 0; + for (j=0; j < n2; ++j) + acc += x[j] * mcos[(2 * i + 1 + n2)*(2*j+1) & nmask]; + buffer[i] = acc; + } + free(x); +} +#elif 0 +// transform to use a slow dct-iv; this is STILL basically trivial, +// but only requires half as many ops +void dct_iv_slow(float *buffer, int n) +{ + float mcos[16384]; + float x[2048]; + int i,j; + int n2 = n >> 1, nmask = (n << 3) - 1; + memcpy(x, buffer, sizeof(*x) * n); + for (i=0; i < 8*n; ++i) + mcos[i] = (float) cos(M_PI / 4 * i / n); + for (i=0; i < n; ++i) { + float acc = 0; + for (j=0; j < n; ++j) + acc += x[j] * mcos[((2 * i + 1)*(2*j+1)) & nmask]; + buffer[i] = acc; + } +} + +void inverse_mdct_slow(float *buffer, int n, vorb *f, int blocktype) +{ + int i, n4 = n >> 2, n2 = n >> 1, n3_4 = n - n4; + float temp[4096]; + + memcpy(temp, buffer, n2 * sizeof(float)); + dct_iv_slow(temp, n2); // returns -c'-d, a-b' + + for (i=0; i < n4 ; ++i) buffer[i] = temp[i+n4]; // a-b' + for ( ; i < n3_4; ++i) buffer[i] = -temp[n3_4 - i - 1]; // b-a', c+d' + for ( ; i < n ; ++i) buffer[i] = -temp[i - n3_4]; // c'+d +} +#endif + +#ifndef LIBVORBIS_MDCT +#define LIBVORBIS_MDCT 0 +#endif + +#if LIBVORBIS_MDCT +// directly call the vorbis MDCT using an interface documented +// by Jeff Roberts... useful for performance comparison +typedef struct +{ + int n; + int log2n; + + float *trig; + int *bitrev; + + float scale; +} mdct_lookup; + +extern void mdct_init(mdct_lookup *lookup, int n); +extern void mdct_clear(mdct_lookup *l); +extern void mdct_backward(mdct_lookup *init, float *in, float *out); + +mdct_lookup M1,M2; + +void inverse_mdct(float *buffer, int n, vorb *f, int blocktype) +{ + mdct_lookup *M; + if (M1.n == n) M = &M1; + else if (M2.n == n) M = &M2; + else if (M1.n == 0) { mdct_init(&M1, n); M = &M1; } + else { + if (M2.n) __asm int 3; + mdct_init(&M2, n); + M = &M2; + } + + mdct_backward(M, buffer, buffer); +} +#endif + + +// the following were split out into separate functions while optimizing; +// they could be pushed back up but eh. __forceinline showed no change; +// they're probably already being inlined. +static void imdct_step3_iter0_loop(int n, float *e, int i_off, int k_off, float *A) +{ + float *ee0 = e + i_off; + float *ee2 = ee0 + k_off; + int i; + + assert((n & 3) == 0); + for (i=(n>>2); i > 0; --i) { + float k00_20, k01_21; + k00_20 = ee0[ 0] - ee2[ 0]; + k01_21 = ee0[-1] - ee2[-1]; + ee0[ 0] += ee2[ 0];//ee0[ 0] = ee0[ 0] + ee2[ 0]; + ee0[-1] += ee2[-1];//ee0[-1] = ee0[-1] + ee2[-1]; + ee2[ 0] = k00_20 * A[0] - k01_21 * A[1]; + ee2[-1] = k01_21 * A[0] + k00_20 * A[1]; + A += 8; + + k00_20 = ee0[-2] - ee2[-2]; + k01_21 = ee0[-3] - ee2[-3]; + ee0[-2] += ee2[-2];//ee0[-2] = ee0[-2] + ee2[-2]; + ee0[-3] += ee2[-3];//ee0[-3] = ee0[-3] + ee2[-3]; + ee2[-2] = k00_20 * A[0] - k01_21 * A[1]; + ee2[-3] = k01_21 * A[0] + k00_20 * A[1]; + A += 8; + + k00_20 = ee0[-4] - ee2[-4]; + k01_21 = ee0[-5] - ee2[-5]; + ee0[-4] += ee2[-4];//ee0[-4] = ee0[-4] + ee2[-4]; + ee0[-5] += ee2[-5];//ee0[-5] = ee0[-5] + ee2[-5]; + ee2[-4] = k00_20 * A[0] - k01_21 * A[1]; + ee2[-5] = k01_21 * A[0] + k00_20 * A[1]; + A += 8; + + k00_20 = ee0[-6] - ee2[-6]; + k01_21 = ee0[-7] - ee2[-7]; + ee0[-6] += ee2[-6];//ee0[-6] = ee0[-6] + ee2[-6]; + ee0[-7] += ee2[-7];//ee0[-7] = ee0[-7] + ee2[-7]; + ee2[-6] = k00_20 * A[0] - k01_21 * A[1]; + ee2[-7] = k01_21 * A[0] + k00_20 * A[1]; + A += 8; + ee0 -= 8; + ee2 -= 8; + } +} + +static void imdct_step3_inner_r_loop(int lim, float *e, int d0, int k_off, float *A, int k1) +{ + int i; + float k00_20, k01_21; + + float *e0 = e + d0; + float *e2 = e0 + k_off; + + for (i=lim >> 2; i > 0; --i) { + k00_20 = e0[-0] - e2[-0]; + k01_21 = e0[-1] - e2[-1]; + e0[-0] += e2[-0];//e0[-0] = e0[-0] + e2[-0]; + e0[-1] += e2[-1];//e0[-1] = e0[-1] + e2[-1]; + e2[-0] = (k00_20)*A[0] - (k01_21) * A[1]; + e2[-1] = (k01_21)*A[0] + (k00_20) * A[1]; + + A += k1; + + k00_20 = e0[-2] - e2[-2]; + k01_21 = e0[-3] - e2[-3]; + e0[-2] += e2[-2];//e0[-2] = e0[-2] + e2[-2]; + e0[-3] += e2[-3];//e0[-3] = e0[-3] + e2[-3]; + e2[-2] = (k00_20)*A[0] - (k01_21) * A[1]; + e2[-3] = (k01_21)*A[0] + (k00_20) * A[1]; + + A += k1; + + k00_20 = e0[-4] - e2[-4]; + k01_21 = e0[-5] - e2[-5]; + e0[-4] += e2[-4];//e0[-4] = e0[-4] + e2[-4]; + e0[-5] += e2[-5];//e0[-5] = e0[-5] + e2[-5]; + e2[-4] = (k00_20)*A[0] - (k01_21) * A[1]; + e2[-5] = (k01_21)*A[0] + (k00_20) * A[1]; + + A += k1; + + k00_20 = e0[-6] - e2[-6]; + k01_21 = e0[-7] - e2[-7]; + e0[-6] += e2[-6];//e0[-6] = e0[-6] + e2[-6]; + e0[-7] += e2[-7];//e0[-7] = e0[-7] + e2[-7]; + e2[-6] = (k00_20)*A[0] - (k01_21) * A[1]; + e2[-7] = (k01_21)*A[0] + (k00_20) * A[1]; + + e0 -= 8; + e2 -= 8; + + A += k1; + } +} + +static void imdct_step3_inner_s_loop(int n, float *e, int i_off, int k_off, float *A, int a_off, int k0) +{ + int i; + float A0 = A[0]; + float A1 = A[0+1]; + float A2 = A[0+a_off]; + float A3 = A[0+a_off+1]; + float A4 = A[0+a_off*2+0]; + float A5 = A[0+a_off*2+1]; + float A6 = A[0+a_off*3+0]; + float A7 = A[0+a_off*3+1]; + + float k00,k11; + + float *ee0 = e +i_off; + float *ee2 = ee0+k_off; + + for (i=n; i > 0; --i) { + k00 = ee0[ 0] - ee2[ 0]; + k11 = ee0[-1] - ee2[-1]; + ee0[ 0] = ee0[ 0] + ee2[ 0]; + ee0[-1] = ee0[-1] + ee2[-1]; + ee2[ 0] = (k00) * A0 - (k11) * A1; + ee2[-1] = (k11) * A0 + (k00) * A1; + + k00 = ee0[-2] - ee2[-2]; + k11 = ee0[-3] - ee2[-3]; + ee0[-2] = ee0[-2] + ee2[-2]; + ee0[-3] = ee0[-3] + ee2[-3]; + ee2[-2] = (k00) * A2 - (k11) * A3; + ee2[-3] = (k11) * A2 + (k00) * A3; + + k00 = ee0[-4] - ee2[-4]; + k11 = ee0[-5] - ee2[-5]; + ee0[-4] = ee0[-4] + ee2[-4]; + ee0[-5] = ee0[-5] + ee2[-5]; + ee2[-4] = (k00) * A4 - (k11) * A5; + ee2[-5] = (k11) * A4 + (k00) * A5; + + k00 = ee0[-6] - ee2[-6]; + k11 = ee0[-7] - ee2[-7]; + ee0[-6] = ee0[-6] + ee2[-6]; + ee0[-7] = ee0[-7] + ee2[-7]; + ee2[-6] = (k00) * A6 - (k11) * A7; + ee2[-7] = (k11) * A6 + (k00) * A7; + + ee0 -= k0; + ee2 -= k0; + } +} + +static __forceinline void iter_54(float *z) +{ + float k00,k11,k22,k33; + float y0,y1,y2,y3; + + k00 = z[ 0] - z[-4]; + y0 = z[ 0] + z[-4]; + y2 = z[-2] + z[-6]; + k22 = z[-2] - z[-6]; + + z[-0] = y0 + y2; // z0 + z4 + z2 + z6 + z[-2] = y0 - y2; // z0 + z4 - z2 - z6 + + // done with y0,y2 + + k33 = z[-3] - z[-7]; + + z[-4] = k00 + k33; // z0 - z4 + z3 - z7 + z[-6] = k00 - k33; // z0 - z4 - z3 + z7 + + // done with k33 + + k11 = z[-1] - z[-5]; + y1 = z[-1] + z[-5]; + y3 = z[-3] + z[-7]; + + z[-1] = y1 + y3; // z1 + z5 + z3 + z7 + z[-3] = y1 - y3; // z1 + z5 - z3 - z7 + z[-5] = k11 - k22; // z1 - z5 + z2 - z6 + z[-7] = k11 + k22; // z1 - z5 - z2 + z6 +} + +static void imdct_step3_inner_s_loop_ld654(int n, float *e, int i_off, float *A, int base_n) +{ + int a_off = base_n >> 3; + float A2 = A[0+a_off]; + float *z = e + i_off; + float *base = z - 16 * n; + + while (z > base) { + float k00,k11; + + k00 = z[-0] - z[-8]; + k11 = z[-1] - z[-9]; + z[-0] = z[-0] + z[-8]; + z[-1] = z[-1] + z[-9]; + z[-8] = k00; + z[-9] = k11 ; + + k00 = z[ -2] - z[-10]; + k11 = z[ -3] - z[-11]; + z[ -2] = z[ -2] + z[-10]; + z[ -3] = z[ -3] + z[-11]; + z[-10] = (k00+k11) * A2; + z[-11] = (k11-k00) * A2; + + k00 = z[-12] - z[ -4]; // reverse to avoid a unary negation + k11 = z[ -5] - z[-13]; + z[ -4] = z[ -4] + z[-12]; + z[ -5] = z[ -5] + z[-13]; + z[-12] = k11; + z[-13] = k00; + + k00 = z[-14] - z[ -6]; // reverse to avoid a unary negation + k11 = z[ -7] - z[-15]; + z[ -6] = z[ -6] + z[-14]; + z[ -7] = z[ -7] + z[-15]; + z[-14] = (k00+k11) * A2; + z[-15] = (k00-k11) * A2; + + iter_54(z); + iter_54(z-8); + z -= 16; + } +} + +static void inverse_mdct(float *buffer, int n, vorb *f, int blocktype) +{ + int n2 = n >> 1, n4 = n >> 2, n8 = n >> 3, l; + int ld; + // @OPTIMIZE: reduce register pressure by using fewer variables? + int save_point = temp_alloc_save(f); + float *buf2 = (float *) temp_alloc(f, n2 * sizeof(*buf2)); + float *u=NULL,*v=NULL; + // twiddle factors + float *A = f->A[blocktype]; + + // IMDCT algorithm from "The use of multirate filter banks for coding of high quality digital audio" + // See notes about bugs in that paper in less-optimal implementation 'inverse_mdct_old' after this function. + + // kernel from paper + + + // merged: + // copy and reflect spectral data + // step 0 + + // note that it turns out that the items added together during + // this step are, in fact, being added to themselves (as reflected + // by step 0). inexplicable inefficiency! this became obvious + // once I combined the passes. + + // so there's a missing 'times 2' here (for adding X to itself). + // this propogates through linearly to the end, where the numbers + // are 1/2 too small, and need to be compensated for. + + { + float *d,*e, *AA, *e_stop; + d = &buf2[n2-2]; + AA = A; + e = &buffer[0]; + e_stop = &buffer[n2]; + while (e != e_stop) { + d[1] = (e[0] * AA[0] - e[2]*AA[1]); + d[0] = (e[0] * AA[1] + e[2]*AA[0]); + d -= 2; + AA += 2; + e += 4; + } + + e = &buffer[n2-3]; + while (d >= buf2) { + d[1] = (-e[2] * AA[0] - -e[0]*AA[1]); + d[0] = (-e[2] * AA[1] + -e[0]*AA[0]); + d -= 2; + AA += 2; + e -= 4; + } + } + + // now we use symbolic names for these, so that we can + // possibly swap their meaning as we change which operations + // are in place + + u = buffer; + v = buf2; + + // step 2 (paper output is w, now u) + // this could be in place, but the data ends up in the wrong + // place... _somebody_'s got to swap it, so this is nominated + { + float *AA = &A[n2-8]; + float *d0,*d1, *e0, *e1; + + e0 = &v[n4]; + e1 = &v[0]; + + d0 = &u[n4]; + d1 = &u[0]; + + while (AA >= A) { + float v40_20, v41_21; + + v41_21 = e0[1] - e1[1]; + v40_20 = e0[0] - e1[0]; + d0[1] = e0[1] + e1[1]; + d0[0] = e0[0] + e1[0]; + d1[1] = v41_21*AA[4] - v40_20*AA[5]; + d1[0] = v40_20*AA[4] + v41_21*AA[5]; + + v41_21 = e0[3] - e1[3]; + v40_20 = e0[2] - e1[2]; + d0[3] = e0[3] + e1[3]; + d0[2] = e0[2] + e1[2]; + d1[3] = v41_21*AA[0] - v40_20*AA[1]; + d1[2] = v40_20*AA[0] + v41_21*AA[1]; + + AA -= 8; + + d0 += 4; + d1 += 4; + e0 += 4; + e1 += 4; + } + } + + // step 3 + ld = ilog(n) - 1; // ilog is off-by-one from normal definitions + + // optimized step 3: + + // the original step3 loop can be nested r inside s or s inside r; + // it's written originally as s inside r, but this is dumb when r + // iterates many times, and s few. So I have two copies of it and + // switch between them halfway. + + // this is iteration 0 of step 3 + imdct_step3_iter0_loop(n >> 4, u, n2-1-n4*0, -(n >> 3), A); + imdct_step3_iter0_loop(n >> 4, u, n2-1-n4*1, -(n >> 3), A); + + // this is iteration 1 of step 3 + imdct_step3_inner_r_loop(n >> 5, u, n2-1 - n8*0, -(n >> 4), A, 16); + imdct_step3_inner_r_loop(n >> 5, u, n2-1 - n8*1, -(n >> 4), A, 16); + imdct_step3_inner_r_loop(n >> 5, u, n2-1 - n8*2, -(n >> 4), A, 16); + imdct_step3_inner_r_loop(n >> 5, u, n2-1 - n8*3, -(n >> 4), A, 16); + + l=2; + for (; l < (ld-3)>>1; ++l) { + int k0 = n >> (l+2), k0_2 = k0>>1; + int lim = 1 << (l+1); + int i; + for (i=0; i < lim; ++i) + imdct_step3_inner_r_loop(n >> (l+4), u, n2-1 - k0*i, -k0_2, A, 1 << (l+3)); + } + + for (; l < ld-6; ++l) { + int k0 = n >> (l+2), k1 = 1 << (l+3), k0_2 = k0>>1; + int rlim = n >> (l+6), r; + int lim = 1 << (l+1); + int i_off; + float *A0 = A; + i_off = n2-1; + for (r=rlim; r > 0; --r) { + imdct_step3_inner_s_loop(lim, u, i_off, -k0_2, A0, k1, k0); + A0 += k1*4; + i_off -= 8; + } + } + + // iterations with count: + // ld-6,-5,-4 all interleaved together + // the big win comes from getting rid of needless flops + // due to the constants on pass 5 & 4 being all 1 and 0; + // combining them to be simultaneous to improve cache made little difference + imdct_step3_inner_s_loop_ld654(n >> 5, u, n2-1, A, n); + + // output is u + + // step 4, 5, and 6 + // cannot be in-place because of step 5 + { + uint16 *bitrev = f->bit_reverse[blocktype]; + // weirdly, I'd have thought reading sequentially and writing + // erratically would have been better than vice-versa, but in + // fact that's not what my testing showed. (That is, with + // j = bitreverse(i), do you read i and write j, or read j and write i.) + + float *d0 = &v[n4-4]; + float *d1 = &v[n2-4]; + while (d0 >= v) { + int k4; + + k4 = bitrev[0]; + d1[3] = u[k4+0]; + d1[2] = u[k4+1]; + d0[3] = u[k4+2]; + d0[2] = u[k4+3]; + + k4 = bitrev[1]; + d1[1] = u[k4+0]; + d1[0] = u[k4+1]; + d0[1] = u[k4+2]; + d0[0] = u[k4+3]; + + d0 -= 4; + d1 -= 4; + bitrev += 2; + } + } + // (paper output is u, now v) + + + // data must be in buf2 + assert(v == buf2); + + // step 7 (paper output is v, now v) + // this is now in place + { + float *C = f->C[blocktype]; + float *d, *e; + + d = v; + e = v + n2 - 4; + + while (d < e) { + float a02,a11,b0,b1,b2,b3; + + a02 = d[0] - e[2]; + a11 = d[1] + e[3]; + + b0 = C[1]*a02 + C[0]*a11; + b1 = C[1]*a11 - C[0]*a02; + + b2 = d[0] + e[ 2]; + b3 = d[1] - e[ 3]; + + d[0] = b2 + b0; + d[1] = b3 + b1; + e[2] = b2 - b0; + e[3] = b1 - b3; + + a02 = d[2] - e[0]; + a11 = d[3] + e[1]; + + b0 = C[3]*a02 + C[2]*a11; + b1 = C[3]*a11 - C[2]*a02; + + b2 = d[2] + e[ 0]; + b3 = d[3] - e[ 1]; + + d[2] = b2 + b0; + d[3] = b3 + b1; + e[0] = b2 - b0; + e[1] = b1 - b3; + + C += 4; + d += 4; + e -= 4; + } + } + + // data must be in buf2 + + + // step 8+decode (paper output is X, now buffer) + // this generates pairs of data a la 8 and pushes them directly through + // the decode kernel (pushing rather than pulling) to avoid having + // to make another pass later + + // this cannot POSSIBLY be in place, so we refer to the buffers directly + + { + float *d0,*d1,*d2,*d3; + + float *B = f->B[blocktype] + n2 - 8; + float *e = buf2 + n2 - 8; + d0 = &buffer[0]; + d1 = &buffer[n2-4]; + d2 = &buffer[n2]; + d3 = &buffer[n-4]; + while (e >= v) { + float p0,p1,p2,p3; + + p3 = e[6]*B[7] - e[7]*B[6]; + p2 = -e[6]*B[6] - e[7]*B[7]; + + d0[0] = p3; + d1[3] = - p3; + d2[0] = p2; + d3[3] = p2; + + p1 = e[4]*B[5] - e[5]*B[4]; + p0 = -e[4]*B[4] - e[5]*B[5]; + + d0[1] = p1; + d1[2] = - p1; + d2[1] = p0; + d3[2] = p0; + + p3 = e[2]*B[3] - e[3]*B[2]; + p2 = -e[2]*B[2] - e[3]*B[3]; + + d0[2] = p3; + d1[1] = - p3; + d2[2] = p2; + d3[1] = p2; + + p1 = e[0]*B[1] - e[1]*B[0]; + p0 = -e[0]*B[0] - e[1]*B[1]; + + d0[3] = p1; + d1[0] = - p1; + d2[3] = p0; + d3[0] = p0; + + B -= 8; + e -= 8; + d0 += 4; + d2 += 4; + d1 -= 4; + d3 -= 4; + } + } + + temp_free(f,buf2); + temp_alloc_restore(f,save_point); +} + +#if 0 +// this is the original version of the above code, if you want to optimize it from scratch +void inverse_mdct_naive(float *buffer, int n) +{ + float s; + float A[1 << 12], B[1 << 12], C[1 << 11]; + int i,k,k2,k4, n2 = n >> 1, n4 = n >> 2, n8 = n >> 3, l; + int n3_4 = n - n4, ld; + // how can they claim this only uses N words?! + // oh, because they're only used sparsely, whoops + float u[1 << 13], X[1 << 13], v[1 << 13], w[1 << 13]; + // set up twiddle factors + + for (k=k2=0; k < n4; ++k,k2+=2) { + A[k2 ] = (float) cos(4*k*M_PI/n); + A[k2+1] = (float) -sin(4*k*M_PI/n); + B[k2 ] = (float) cos((k2+1)*M_PI/n/2); + B[k2+1] = (float) sin((k2+1)*M_PI/n/2); + } + for (k=k2=0; k < n8; ++k,k2+=2) { + C[k2 ] = (float) cos(2*(k2+1)*M_PI/n); + C[k2+1] = (float) -sin(2*(k2+1)*M_PI/n); + } + + // IMDCT algorithm from "The use of multirate filter banks for coding of high quality digital audio" + // Note there are bugs in that pseudocode, presumably due to them attempting + // to rename the arrays nicely rather than representing the way their actual + // implementation bounces buffers back and forth. As a result, even in the + // "some formulars corrected" version, a direct implementation fails. These + // are noted below as "paper bug". + + // copy and reflect spectral data + for (k=0; k < n2; ++k) u[k] = buffer[k]; + for ( ; k < n ; ++k) u[k] = -buffer[n - k - 1]; + // kernel from paper + // step 1 + for (k=k2=k4=0; k < n4; k+=1, k2+=2, k4+=4) { + v[n-k4-1] = (u[k4] - u[n-k4-1]) * A[k2] - (u[k4+2] - u[n-k4-3])*A[k2+1]; + v[n-k4-3] = (u[k4] - u[n-k4-1]) * A[k2+1] + (u[k4+2] - u[n-k4-3])*A[k2]; + } + // step 2 + for (k=k4=0; k < n8; k+=1, k4+=4) { + w[n2+3+k4] = v[n2+3+k4] + v[k4+3]; + w[n2+1+k4] = v[n2+1+k4] + v[k4+1]; + w[k4+3] = (v[n2+3+k4] - v[k4+3])*A[n2-4-k4] - (v[n2+1+k4]-v[k4+1])*A[n2-3-k4]; + w[k4+1] = (v[n2+1+k4] - v[k4+1])*A[n2-4-k4] + (v[n2+3+k4]-v[k4+3])*A[n2-3-k4]; + } + // step 3 + ld = ilog(n) - 1; // ilog is off-by-one from normal definitions + for (l=0; l < ld-3; ++l) { + int k0 = n >> (l+2), k1 = 1 << (l+3); + int rlim = n >> (l+4), r4, r; + int s2lim = 1 << (l+2), s2; + for (r=r4=0; r < rlim; r4+=4,++r) { + for (s2=0; s2 < s2lim; s2+=2) { + u[n-1-k0*s2-r4] = w[n-1-k0*s2-r4] + w[n-1-k0*(s2+1)-r4]; + u[n-3-k0*s2-r4] = w[n-3-k0*s2-r4] + w[n-3-k0*(s2+1)-r4]; + u[n-1-k0*(s2+1)-r4] = (w[n-1-k0*s2-r4] - w[n-1-k0*(s2+1)-r4]) * A[r*k1] + - (w[n-3-k0*s2-r4] - w[n-3-k0*(s2+1)-r4]) * A[r*k1+1]; + u[n-3-k0*(s2+1)-r4] = (w[n-3-k0*s2-r4] - w[n-3-k0*(s2+1)-r4]) * A[r*k1] + + (w[n-1-k0*s2-r4] - w[n-1-k0*(s2+1)-r4]) * A[r*k1+1]; + } + } + if (l+1 < ld-3) { + // paper bug: ping-ponging of u&w here is omitted + memcpy(w, u, sizeof(u)); + } + } + + // step 4 + for (i=0; i < n8; ++i) { + int j = bit_reverse(i) >> (32-ld+3); + assert(j < n8); + if (i == j) { + // paper bug: original code probably swapped in place; if copying, + // need to directly copy in this case + int i8 = i << 3; + v[i8+1] = u[i8+1]; + v[i8+3] = u[i8+3]; + v[i8+5] = u[i8+5]; + v[i8+7] = u[i8+7]; + } else if (i < j) { + int i8 = i << 3, j8 = j << 3; + v[j8+1] = u[i8+1], v[i8+1] = u[j8 + 1]; + v[j8+3] = u[i8+3], v[i8+3] = u[j8 + 3]; + v[j8+5] = u[i8+5], v[i8+5] = u[j8 + 5]; + v[j8+7] = u[i8+7], v[i8+7] = u[j8 + 7]; + } + } + // step 5 + for (k=0; k < n2; ++k) { + w[k] = v[k*2+1]; + } + // step 6 + for (k=k2=k4=0; k < n8; ++k, k2 += 2, k4 += 4) { + u[n-1-k2] = w[k4]; + u[n-2-k2] = w[k4+1]; + u[n3_4 - 1 - k2] = w[k4+2]; + u[n3_4 - 2 - k2] = w[k4+3]; + } + // step 7 + for (k=k2=0; k < n8; ++k, k2 += 2) { + v[n2 + k2 ] = ( u[n2 + k2] + u[n-2-k2] + C[k2+1]*(u[n2+k2]-u[n-2-k2]) + C[k2]*(u[n2+k2+1]+u[n-2-k2+1]))/2; + v[n-2 - k2] = ( u[n2 + k2] + u[n-2-k2] - C[k2+1]*(u[n2+k2]-u[n-2-k2]) - C[k2]*(u[n2+k2+1]+u[n-2-k2+1]))/2; + v[n2+1+ k2] = ( u[n2+1+k2] - u[n-1-k2] + C[k2+1]*(u[n2+1+k2]+u[n-1-k2]) - C[k2]*(u[n2+k2]-u[n-2-k2]))/2; + v[n-1 - k2] = (-u[n2+1+k2] + u[n-1-k2] + C[k2+1]*(u[n2+1+k2]+u[n-1-k2]) - C[k2]*(u[n2+k2]-u[n-2-k2]))/2; + } + // step 8 + for (k=k2=0; k < n4; ++k,k2 += 2) { + X[k] = v[k2+n2]*B[k2 ] + v[k2+1+n2]*B[k2+1]; + X[n2-1-k] = v[k2+n2]*B[k2+1] - v[k2+1+n2]*B[k2 ]; + } + + // decode kernel to output + // determined the following value experimentally + // (by first figuring out what made inverse_mdct_slow work); then matching that here + // (probably vorbis encoder premultiplies by n or n/2, to save it on the decoder?) + s = 0.5; // theoretically would be n4 + + // [[[ note! the s value of 0.5 is compensated for by the B[] in the current code, + // so it needs to use the "old" B values to behave correctly, or else + // set s to 1.0 ]]] + for (i=0; i < n4 ; ++i) buffer[i] = s * X[i+n4]; + for ( ; i < n3_4; ++i) buffer[i] = -s * X[n3_4 - i - 1]; + for ( ; i < n ; ++i) buffer[i] = -s * X[i - n3_4]; +} +#endif + +static float *get_window(vorb *f, int len) +{ + len <<= 1; + if (len == f->blocksize_0) return f->window[0]; + if (len == f->blocksize_1) return f->window[1]; + assert(0); + return NULL; +} + +#ifndef STB_VORBIS_NO_DEFER_FLOOR +typedef int16 YTYPE; +#else +typedef int YTYPE; +#endif +static int do_floor(vorb *f, Mapping *map, int i, int n, float *target, YTYPE *finalY, uint8 *step2_flag) +{ + int n2 = n >> 1; + int s = map->chan[i].mux, floor; + floor = map->submap_floor[s]; + if (f->floor_types[floor] == 0) { + return error(f, VORBIS_invalid_stream); + } else { + Floor1 *g = &f->floor_config[floor].floor1; + int j,q; + int lx = 0, ly = finalY[0] * g->floor1_multiplier; + for (q=1; q < g->values; ++q) { + j = g->sorted_order[q]; + #ifndef STB_VORBIS_NO_DEFER_FLOOR + if (finalY[j] >= 0) + #else + if (step2_flag[j]) + #endif + { + int hy = finalY[j] * g->floor1_multiplier; + int hx = g->Xlist[j]; + if (lx != hx) + draw_line(target, lx,ly, hx,hy, n2); + CHECK(f); + lx = hx, ly = hy; + } + } + if (lx < n2) { + // optimization of: draw_line(target, lx,ly, n,ly, n2); + for (j=lx; j < n2; ++j) + LINE_OP(target[j], inverse_db_table[ly]); + CHECK(f); + } + } + return TRUE; +} + +// The meaning of "left" and "right" +// +// For a given frame: +// we compute samples from 0..n +// window_center is n/2 +// we'll window and mix the samples from left_start to left_end with data from the previous frame +// all of the samples from left_end to right_start can be output without mixing; however, +// this interval is 0-length except when transitioning between short and long frames +// all of the samples from right_start to right_end need to be mixed with the next frame, +// which we don't have, so those get saved in a buffer +// frame N's right_end-right_start, the number of samples to mix with the next frame, +// has to be the same as frame N+1's left_end-left_start (which they are by +// construction) + +static int vorbis_decode_initial(vorb *f, int *p_left_start, int *p_left_end, int *p_right_start, int *p_right_end, int *mode) +{ + Mode *m; + int i, n, prev, next, window_center; + f->channel_buffer_start = f->channel_buffer_end = 0; + + retry: + if (f->eof) return FALSE; + if (!maybe_start_packet(f)) + return FALSE; + // check packet type + if (get_bits(f,1) != 0) { + if (IS_PUSH_MODE(f)) + return error(f,VORBIS_bad_packet_type); + while (EOP != get8_packet(f)); + goto retry; + } + + if (f->alloc.alloc_buffer) + assert(f->alloc.alloc_buffer_length_in_bytes == f->temp_offset); + + i = get_bits(f, ilog(f->mode_count-1)); + if (i == EOP) return FALSE; + if (i >= f->mode_count) return FALSE; + *mode = i; + m = f->mode_config + i; + if (m->blockflag) { + n = f->blocksize_1; + prev = get_bits(f,1); + next = get_bits(f,1); + } else { + prev = next = 0; + n = f->blocksize_0; + } + +// WINDOWING + + window_center = n >> 1; + if (m->blockflag && !prev) { + *p_left_start = (n - f->blocksize_0) >> 2; + *p_left_end = (n + f->blocksize_0) >> 2; + } else { + *p_left_start = 0; + *p_left_end = window_center; + } + if (m->blockflag && !next) { + *p_right_start = (n*3 - f->blocksize_0) >> 2; + *p_right_end = (n*3 + f->blocksize_0) >> 2; + } else { + *p_right_start = window_center; + *p_right_end = n; + } + + return TRUE; +} + +static int vorbis_decode_packet_rest(vorb *f, int *len, Mode *m, int left_start, int left_end, int right_start, int right_end, int *p_left) +{ + Mapping *map; + int i,j,k,n,n2; + int zero_channel[256]; + int really_zero_channel[256]; + +// WINDOWING + + n = f->blocksize[m->blockflag]; + map = &f->mapping[m->mapping]; + +// FLOORS + n2 = n >> 1; + + CHECK(f); + + for (i=0; i < f->channels; ++i) { + int s = map->chan[i].mux, floor; + zero_channel[i] = FALSE; + floor = map->submap_floor[s]; + if (f->floor_types[floor] == 0) { + return error(f, VORBIS_invalid_stream); + } else { + Floor1 *g = &f->floor_config[floor].floor1; + if (get_bits(f, 1)) { + short *finalY; + uint8 step2_flag[256]; + static int range_list[4] = { 256, 128, 86, 64 }; + int range = range_list[g->floor1_multiplier-1]; + int offset = 2; + finalY = f->finalY[i]; + finalY[0] = get_bits(f, ilog(range)-1); + finalY[1] = get_bits(f, ilog(range)-1); + for (j=0; j < g->partitions; ++j) { + int pclass = g->partition_class_list[j]; + int cdim = g->class_dimensions[pclass]; + int cbits = g->class_subclasses[pclass]; + int csub = (1 << cbits)-1; + int cval = 0; + if (cbits) { + Codebook *c = f->codebooks + g->class_masterbooks[pclass]; + DECODE(cval,f,c); + } + for (k=0; k < cdim; ++k) { + int book = g->subclass_books[pclass][cval & csub]; + cval = cval >> cbits; + if (book >= 0) { + int temp; + Codebook *c = f->codebooks + book; + DECODE(temp,f,c); + finalY[offset++] = temp; + } else + finalY[offset++] = 0; + } + } + if (f->valid_bits == INVALID_BITS) goto error; // behavior according to spec + step2_flag[0] = step2_flag[1] = 1; + for (j=2; j < g->values; ++j) { + int low, high, pred, highroom, lowroom, room, val; + low = g->neighbors[j][0]; + high = g->neighbors[j][1]; + //neighbors(g->Xlist, j, &low, &high); + pred = predict_point(g->Xlist[j], g->Xlist[low], g->Xlist[high], finalY[low], finalY[high]); + val = finalY[j]; + highroom = range - pred; + lowroom = pred; + if (highroom < lowroom) + room = highroom * 2; + else + room = lowroom * 2; + if (val) { + step2_flag[low] = step2_flag[high] = 1; + step2_flag[j] = 1; + if (val >= room) + if (highroom > lowroom) + finalY[j] = val - lowroom + pred; + else + finalY[j] = pred - val + highroom - 1; + else + if (val & 1) + finalY[j] = pred - ((val+1)>>1); + else + finalY[j] = pred + (val>>1); + } else { + step2_flag[j] = 0; + finalY[j] = pred; + } + } + +#ifdef STB_VORBIS_NO_DEFER_FLOOR + do_floor(f, map, i, n, f->floor_buffers[i], finalY, step2_flag); +#else + // defer final floor computation until _after_ residue + for (j=0; j < g->values; ++j) { + if (!step2_flag[j]) + finalY[j] = -1; + } +#endif + } else { + error: + zero_channel[i] = TRUE; + } + // So we just defer everything else to later + + // at this point we've decoded the floor into buffer + } + } + CHECK(f); + // at this point we've decoded all floors + + if (f->alloc.alloc_buffer) + assert(f->alloc.alloc_buffer_length_in_bytes == f->temp_offset); + + // re-enable coupled channels if necessary + memcpy(really_zero_channel, zero_channel, sizeof(really_zero_channel[0]) * f->channels); + for (i=0; i < map->coupling_steps; ++i) + if (!zero_channel[map->chan[i].magnitude] || !zero_channel[map->chan[i].angle]) { + zero_channel[map->chan[i].magnitude] = zero_channel[map->chan[i].angle] = FALSE; + } + + CHECK(f); +// RESIDUE DECODE + for (i=0; i < map->submaps; ++i) { + float *residue_buffers[STB_VORBIS_MAX_CHANNELS]; + int r; + uint8 do_not_decode[256]; + int ch = 0; + for (j=0; j < f->channels; ++j) { + if (map->chan[j].mux == i) { + if (zero_channel[j]) { + do_not_decode[ch] = TRUE; + residue_buffers[ch] = NULL; + } else { + do_not_decode[ch] = FALSE; + residue_buffers[ch] = f->channel_buffers[j]; + } + ++ch; + } + } + r = map->submap_residue[i]; + decode_residue(f, residue_buffers, ch, n2, r, do_not_decode); + } + + if (f->alloc.alloc_buffer) + assert(f->alloc.alloc_buffer_length_in_bytes == f->temp_offset); + CHECK(f); + +// INVERSE COUPLING + for (i = map->coupling_steps-1; i >= 0; --i) { + int n2 = n >> 1; + float *m = f->channel_buffers[map->chan[i].magnitude]; + float *a = f->channel_buffers[map->chan[i].angle ]; + for (j=0; j < n2; ++j) { + float a2,m2; + if (m[j] > 0) + if (a[j] > 0) + m2 = m[j], a2 = m[j] - a[j]; + else + a2 = m[j], m2 = m[j] + a[j]; + else + if (a[j] > 0) + m2 = m[j], a2 = m[j] + a[j]; + else + a2 = m[j], m2 = m[j] - a[j]; + m[j] = m2; + a[j] = a2; + } + } + CHECK(f); + + // finish decoding the floors +#ifndef STB_VORBIS_NO_DEFER_FLOOR + for (i=0; i < f->channels; ++i) { + if (really_zero_channel[i]) { + memset(f->channel_buffers[i], 0, sizeof(*f->channel_buffers[i]) * n2); + } else { + do_floor(f, map, i, n, f->channel_buffers[i], f->finalY[i], NULL); + } + } +#else + for (i=0; i < f->channels; ++i) { + if (really_zero_channel[i]) { + memset(f->channel_buffers[i], 0, sizeof(*f->channel_buffers[i]) * n2); + } else { + for (j=0; j < n2; ++j) + f->channel_buffers[i][j] *= f->floor_buffers[i][j]; + } + } +#endif + +// INVERSE MDCT + CHECK(f); + for (i=0; i < f->channels; ++i) + inverse_mdct(f->channel_buffers[i], n, f, m->blockflag); + CHECK(f); + + // this shouldn't be necessary, unless we exited on an error + // and want to flush to get to the next packet + flush_packet(f); + + if (f->first_decode) { + // assume we start so first non-discarded sample is sample 0 + // this isn't to spec, but spec would require us to read ahead + // and decode the size of all current frames--could be done, + // but presumably it's not a commonly used feature + f->current_loc = -n2; // start of first frame is positioned for discard + // we might have to discard samples "from" the next frame too, + // if we're lapping a large block then a small at the start? + f->discard_samples_deferred = n - right_end; + f->current_loc_valid = TRUE; + f->first_decode = FALSE; + } else if (f->discard_samples_deferred) { + if (f->discard_samples_deferred >= right_start - left_start) { + f->discard_samples_deferred -= (right_start - left_start); + left_start = right_start; + *p_left = left_start; + } else { + left_start += f->discard_samples_deferred; + *p_left = left_start; + f->discard_samples_deferred = 0; + } + } else if (f->previous_length == 0 && f->current_loc_valid) { + // we're recovering from a seek... that means we're going to discard + // the samples from this packet even though we know our position from + // the last page header, so we need to update the position based on + // the discarded samples here + // but wait, the code below is going to add this in itself even + // on a discard, so we don't need to do it here... + } + + // check if we have ogg information about the sample # for this packet + if (f->last_seg_which == f->end_seg_with_known_loc) { + // if we have a valid current loc, and this is final: + if (f->current_loc_valid && (f->page_flag & PAGEFLAG_last_page)) { + uint32 current_end = f->known_loc_for_packet - (n-right_end); + // then let's infer the size of the (probably) short final frame + if (current_end < f->current_loc + (right_end-left_start)) { + if (current_end < f->current_loc) { + // negative truncation, that's impossible! + *len = 0; + } else { + *len = current_end - f->current_loc; + } + *len += left_start; + if (*len > right_end) *len = right_end; // this should never happen + f->current_loc += *len; + return TRUE; + } + } + // otherwise, just set our sample loc + // guess that the ogg granule pos refers to the _middle_ of the + // last frame? + // set f->current_loc to the position of left_start + f->current_loc = f->known_loc_for_packet - (n2-left_start); + f->current_loc_valid = TRUE; + } + if (f->current_loc_valid) + f->current_loc += (right_start - left_start); + + if (f->alloc.alloc_buffer) + assert(f->alloc.alloc_buffer_length_in_bytes == f->temp_offset); + *len = right_end; // ignore samples after the window goes to 0 + CHECK(f); + + return TRUE; +} + +static int vorbis_decode_packet(vorb *f, int *len, int *p_left, int *p_right) +{ + int mode, left_end, right_end; + if (!vorbis_decode_initial(f, p_left, &left_end, p_right, &right_end, &mode)) return 0; + return vorbis_decode_packet_rest(f, len, f->mode_config + mode, *p_left, left_end, *p_right, right_end, p_left); +} + +static int vorbis_finish_frame(stb_vorbis *f, int len, int left, int right) +{ + int prev,i,j; + // we use right&left (the start of the right- and left-window sin()-regions) + // to determine how much to return, rather than inferring from the rules + // (same result, clearer code); 'left' indicates where our sin() window + // starts, therefore where the previous window's right edge starts, and + // therefore where to start mixing from the previous buffer. 'right' + // indicates where our sin() ending-window starts, therefore that's where + // we start saving, and where our returned-data ends. + + // mixin from previous window + if (f->previous_length) { + int i,j, n = f->previous_length; + float *w = get_window(f, n); + for (i=0; i < f->channels; ++i) { + for (j=0; j < n; ++j) + f->channel_buffers[i][left+j] = + f->channel_buffers[i][left+j]*w[ j] + + f->previous_window[i][ j]*w[n-1-j]; + } + } + + prev = f->previous_length; + + // last half of this data becomes previous window + f->previous_length = len - right; + + // @OPTIMIZE: could avoid this copy by double-buffering the + // output (flipping previous_window with channel_buffers), but + // then previous_window would have to be 2x as large, and + // channel_buffers couldn't be temp mem (although they're NOT + // currently temp mem, they could be (unless we want to level + // performance by spreading out the computation)) + for (i=0; i < f->channels; ++i) + for (j=0; right+j < len; ++j) + f->previous_window[i][j] = f->channel_buffers[i][right+j]; + + if (!prev) + // there was no previous packet, so this data isn't valid... + // this isn't entirely true, only the would-have-overlapped data + // isn't valid, but this seems to be what the spec requires + return 0; + + // truncate a short frame + if (len < right) right = len; + + f->samples_output += right-left; + + return right - left; +} + +static void vorbis_pump_first_frame(stb_vorbis *f) +{ + int len, right, left; + if (vorbis_decode_packet(f, &len, &left, &right)) + vorbis_finish_frame(f, len, left, right); +} + +#ifndef STB_VORBIS_NO_PUSHDATA_API +static int is_whole_packet_present(stb_vorbis *f, int end_page) +{ + // make sure that we have the packet available before continuing... + // this requires a full ogg parse, but we know we can fetch from f->stream + + // instead of coding this out explicitly, we could save the current read state, + // read the next packet with get8() until end-of-packet, check f->eof, then + // reset the state? but that would be slower, esp. since we'd have over 256 bytes + // of state to restore (primarily the page segment table) + + int s = f->next_seg, first = TRUE; + uint8 *p = f->stream; + + if (s != -1) { // if we're not starting the packet with a 'continue on next page' flag + for (; s < f->segment_count; ++s) { + p += f->segments[s]; + if (f->segments[s] < 255) // stop at first short segment + break; + } + // either this continues, or it ends it... + if (end_page) + if (s < f->segment_count-1) return error(f, VORBIS_invalid_stream); + if (s == f->segment_count) + s = -1; // set 'crosses page' flag + if (p > f->stream_end) return error(f, VORBIS_need_more_data); + first = FALSE; + } + for (; s == -1;) { + uint8 *q; + int n; + + // check that we have the page header ready + if (p + 26 >= f->stream_end) return error(f, VORBIS_need_more_data); + // validate the page + if (memcmp(p, ogg_page_header, 4)) return error(f, VORBIS_invalid_stream); + if (p[4] != 0) return error(f, VORBIS_invalid_stream); + if (first) { // the first segment must NOT have 'continued_packet', later ones MUST + if (f->previous_length) + if ((p[5] & PAGEFLAG_continued_packet)) return error(f, VORBIS_invalid_stream); + // if no previous length, we're resynching, so we can come in on a continued-packet, + // which we'll just drop + } else { + if (!(p[5] & PAGEFLAG_continued_packet)) return error(f, VORBIS_invalid_stream); + } + n = p[26]; // segment counts + q = p+27; // q points to segment table + p = q + n; // advance past header + // make sure we've read the segment table + if (p > f->stream_end) return error(f, VORBIS_need_more_data); + for (s=0; s < n; ++s) { + p += q[s]; + if (q[s] < 255) + break; + } + if (end_page) + if (s < n-1) return error(f, VORBIS_invalid_stream); + if (s == n) + s = -1; // set 'crosses page' flag + if (p > f->stream_end) return error(f, VORBIS_need_more_data); + first = FALSE; + } + return TRUE; +} +#endif // !STB_VORBIS_NO_PUSHDATA_API + +static int start_decoder(vorb *f) +{ + uint8 header[6], x,y; + int len,i,j,k, max_submaps = 0; + int longest_floorlist=0; + + // first page, first packet + + if (!start_page(f)) return FALSE; + // validate page flag + if (!(f->page_flag & PAGEFLAG_first_page)) return error(f, VORBIS_invalid_first_page); + if (f->page_flag & PAGEFLAG_last_page) return error(f, VORBIS_invalid_first_page); + if (f->page_flag & PAGEFLAG_continued_packet) return error(f, VORBIS_invalid_first_page); + // check for expected packet length + if (f->segment_count != 1) return error(f, VORBIS_invalid_first_page); + if (f->segments[0] != 30) return error(f, VORBIS_invalid_first_page); + // read packet + // check packet header + if (get8(f) != VORBIS_packet_id) return error(f, VORBIS_invalid_first_page); + if (!getn(f, header, 6)) return error(f, VORBIS_unexpected_eof); + if (!vorbis_validate(header)) return error(f, VORBIS_invalid_first_page); + // vorbis_version + if (get32(f) != 0) return error(f, VORBIS_invalid_first_page); + f->channels = get8(f); if (!f->channels) return error(f, VORBIS_invalid_first_page); + if (f->channels > STB_VORBIS_MAX_CHANNELS) return error(f, VORBIS_too_many_channels); + f->sample_rate = get32(f); if (!f->sample_rate) return error(f, VORBIS_invalid_first_page); + get32(f); // bitrate_maximum + get32(f); // bitrate_nominal + get32(f); // bitrate_minimum + x = get8(f); + { + int log0,log1; + log0 = x & 15; + log1 = x >> 4; + f->blocksize_0 = 1 << log0; + f->blocksize_1 = 1 << log1; + if (log0 < 6 || log0 > 13) return error(f, VORBIS_invalid_setup); + if (log1 < 6 || log1 > 13) return error(f, VORBIS_invalid_setup); + if (log0 > log1) return error(f, VORBIS_invalid_setup); + } + + // framing_flag + x = get8(f); + if (!(x & 1)) return error(f, VORBIS_invalid_first_page); + + // second packet! + if (!start_page(f)) return FALSE; + + if (!start_packet(f)) return FALSE; + do { + len = next_segment(f); + skip(f, len); + f->bytes_in_seg = 0; + } while (len); + + // third packet! + if (!start_packet(f)) return FALSE; + + #ifndef STB_VORBIS_NO_PUSHDATA_API + if (IS_PUSH_MODE(f)) { + if (!is_whole_packet_present(f, TRUE)) { + // convert error in ogg header to write type + if (f->error == VORBIS_invalid_stream) + f->error = VORBIS_invalid_setup; + return FALSE; + } + } + #endif + + crc32_init(); // always init it, to avoid multithread race conditions + + if (get8_packet(f) != VORBIS_packet_setup) return error(f, VORBIS_invalid_setup); + for (i=0; i < 6; ++i) header[i] = get8_packet(f); + if (!vorbis_validate(header)) return error(f, VORBIS_invalid_setup); + + // codebooks + + f->codebook_count = get_bits(f,8) + 1; + f->codebooks = (Codebook *) setup_malloc(f, sizeof(*f->codebooks) * f->codebook_count); + if (f->codebooks == NULL) return error(f, VORBIS_outofmem); + memset(f->codebooks, 0, sizeof(*f->codebooks) * f->codebook_count); + for (i=0; i < f->codebook_count; ++i) { + uint32 *values; + int ordered, sorted_count; + int total=0; + uint8 *lengths; + Codebook *c = f->codebooks+i; + CHECK(f); + x = get_bits(f, 8); if (x != 0x42) return error(f, VORBIS_invalid_setup); + x = get_bits(f, 8); if (x != 0x43) return error(f, VORBIS_invalid_setup); + x = get_bits(f, 8); if (x != 0x56) return error(f, VORBIS_invalid_setup); + x = get_bits(f, 8); + c->dimensions = (get_bits(f, 8)<<8) + x; + x = get_bits(f, 8); + y = get_bits(f, 8); + c->entries = (get_bits(f, 8)<<16) + (y<<8) + x; + ordered = get_bits(f,1); + c->sparse = ordered ? 0 : get_bits(f,1); + + if (c->dimensions == 0 && c->entries != 0) return error(f, VORBIS_invalid_setup); + + if (c->sparse) + lengths = (uint8 *) setup_temp_malloc(f, c->entries); + else + lengths = c->codeword_lengths = (uint8 *) setup_malloc(f, c->entries); + + if (!lengths) return error(f, VORBIS_outofmem); + + if (ordered) { + int current_entry = 0; + int current_length = get_bits(f,5) + 1; + while (current_entry < c->entries) { + int limit = c->entries - current_entry; + int n = get_bits(f, ilog(limit)); + if (current_entry + n > (int) c->entries) { return error(f, VORBIS_invalid_setup); } + memset(lengths + current_entry, current_length, n); + current_entry += n; + ++current_length; + } + } else { + for (j=0; j < c->entries; ++j) { + int present = c->sparse ? get_bits(f,1) : 1; + if (present) { + lengths[j] = get_bits(f, 5) + 1; + ++total; + if (lengths[j] == 32) + return error(f, VORBIS_invalid_setup); + } else { + lengths[j] = NO_CODE; + } + } + } + + if (c->sparse && total >= c->entries >> 2) { + // convert sparse items to non-sparse! + if (c->entries > (int) f->setup_temp_memory_required) + f->setup_temp_memory_required = c->entries; + + c->codeword_lengths = (uint8 *) setup_malloc(f, c->entries); + if (c->codeword_lengths == NULL) return error(f, VORBIS_outofmem); + memcpy(c->codeword_lengths, lengths, c->entries); + setup_temp_free(f, lengths, c->entries); // note this is only safe if there have been no intervening temp mallocs! + lengths = c->codeword_lengths; + c->sparse = 0; + } + + // compute the size of the sorted tables + if (c->sparse) { + sorted_count = total; + } else { + sorted_count = 0; + #ifndef STB_VORBIS_NO_HUFFMAN_BINARY_SEARCH + for (j=0; j < c->entries; ++j) + if (lengths[j] > STB_VORBIS_FAST_HUFFMAN_LENGTH && lengths[j] != NO_CODE) + ++sorted_count; + #endif + } + + c->sorted_entries = sorted_count; + values = NULL; + + CHECK(f); + if (!c->sparse) { + c->codewords = (uint32 *) setup_malloc(f, sizeof(c->codewords[0]) * c->entries); + if (!c->codewords) return error(f, VORBIS_outofmem); + } else { + unsigned int size; + if (c->sorted_entries) { + c->codeword_lengths = (uint8 *) setup_malloc(f, c->sorted_entries); + if (!c->codeword_lengths) return error(f, VORBIS_outofmem); + c->codewords = (uint32 *) setup_temp_malloc(f, sizeof(*c->codewords) * c->sorted_entries); + if (!c->codewords) return error(f, VORBIS_outofmem); + values = (uint32 *) setup_temp_malloc(f, sizeof(*values) * c->sorted_entries); + if (!values) return error(f, VORBIS_outofmem); + } + size = c->entries + (sizeof(*c->codewords) + sizeof(*values)) * c->sorted_entries; + if (size > f->setup_temp_memory_required) + f->setup_temp_memory_required = size; + } + + if (!compute_codewords(c, lengths, c->entries, values)) { + if (c->sparse) setup_temp_free(f, values, 0); + return error(f, VORBIS_invalid_setup); + } + + if (c->sorted_entries) { + // allocate an extra slot for sentinels + c->sorted_codewords = (uint32 *) setup_malloc(f, sizeof(*c->sorted_codewords) * (c->sorted_entries+1)); + if (c->sorted_codewords == NULL) return error(f, VORBIS_outofmem); + // allocate an extra slot at the front so that c->sorted_values[-1] is defined + // so that we can catch that case without an extra if + c->sorted_values = ( int *) setup_malloc(f, sizeof(*c->sorted_values ) * (c->sorted_entries+1)); + if (c->sorted_values == NULL) return error(f, VORBIS_outofmem); + ++c->sorted_values; + c->sorted_values[-1] = -1; + compute_sorted_huffman(c, lengths, values); + } + + if (c->sparse) { + setup_temp_free(f, values, sizeof(*values)*c->sorted_entries); + setup_temp_free(f, c->codewords, sizeof(*c->codewords)*c->sorted_entries); + setup_temp_free(f, lengths, c->entries); + c->codewords = NULL; + } + + compute_accelerated_huffman(c); + + CHECK(f); + c->lookup_type = get_bits(f, 4); + if (c->lookup_type > 2) return error(f, VORBIS_invalid_setup); + if (c->lookup_type > 0) { + uint16 *mults; + c->minimum_value = float32_unpack(get_bits(f, 32)); + c->delta_value = float32_unpack(get_bits(f, 32)); + c->value_bits = get_bits(f, 4)+1; + c->sequence_p = get_bits(f,1); + if (c->lookup_type == 1) { + c->lookup_values = lookup1_values(c->entries, c->dimensions); + } else { + c->lookup_values = c->entries * c->dimensions; + } + if (c->lookup_values == 0) return error(f, VORBIS_invalid_setup); + mults = (uint16 *) setup_temp_malloc(f, sizeof(mults[0]) * c->lookup_values); + if (mults == NULL) return error(f, VORBIS_outofmem); + for (j=0; j < (int) c->lookup_values; ++j) { + int q = get_bits(f, c->value_bits); + if (q == EOP) { setup_temp_free(f,mults,sizeof(mults[0])*c->lookup_values); return error(f, VORBIS_invalid_setup); } + mults[j] = q; + } + +#ifndef STB_VORBIS_DIVIDES_IN_CODEBOOK + if (c->lookup_type == 1) { + int len, sparse = c->sparse; + float last=0; + // pre-expand the lookup1-style multiplicands, to avoid a divide in the inner loop + if (sparse) { + if (c->sorted_entries == 0) goto skip; + c->multiplicands = (codetype *) setup_malloc(f, sizeof(c->multiplicands[0]) * c->sorted_entries * c->dimensions); + } else + c->multiplicands = (codetype *) setup_malloc(f, sizeof(c->multiplicands[0]) * c->entries * c->dimensions); + if (c->multiplicands == NULL) { setup_temp_free(f,mults,sizeof(mults[0])*c->lookup_values); return error(f, VORBIS_outofmem); } + len = sparse ? c->sorted_entries : c->entries; + for (j=0; j < len; ++j) { + unsigned int z = sparse ? c->sorted_values[j] : j; + unsigned int div=1; + for (k=0; k < c->dimensions; ++k) { + int off = (z / div) % c->lookup_values; + float val = mults[off]; + val = mults[off]*c->delta_value + c->minimum_value + last; + c->multiplicands[j*c->dimensions + k] = val; + if (c->sequence_p) + last = val; + if (k+1 < c->dimensions) { + if (div > UINT_MAX / (unsigned int) c->lookup_values) { + setup_temp_free(f, mults,sizeof(mults[0])*c->lookup_values); + return error(f, VORBIS_invalid_setup); + } + div *= c->lookup_values; + } + } + } + c->lookup_type = 2; + } + else +#endif + { + float last=0; + CHECK(f); + c->multiplicands = (codetype *) setup_malloc(f, sizeof(c->multiplicands[0]) * c->lookup_values); + if (c->multiplicands == NULL) { setup_temp_free(f, mults,sizeof(mults[0])*c->lookup_values); return error(f, VORBIS_outofmem); } + for (j=0; j < (int) c->lookup_values; ++j) { + float val = mults[j] * c->delta_value + c->minimum_value + last; + c->multiplicands[j] = val; + if (c->sequence_p) + last = val; + } + } +#ifndef STB_VORBIS_DIVIDES_IN_CODEBOOK + skip:; +#endif + setup_temp_free(f, mults, sizeof(mults[0])*c->lookup_values); + + CHECK(f); + } + CHECK(f); + } + + // time domain transfers (notused) + + x = get_bits(f, 6) + 1; + for (i=0; i < x; ++i) { + uint32 z = get_bits(f, 16); + if (z != 0) return error(f, VORBIS_invalid_setup); + } + + // Floors + f->floor_count = get_bits(f, 6)+1; + f->floor_config = (Floor *) setup_malloc(f, f->floor_count * sizeof(*f->floor_config)); + if (f->floor_config == NULL) return error(f, VORBIS_outofmem); + for (i=0; i < f->floor_count; ++i) { + f->floor_types[i] = get_bits(f, 16); + if (f->floor_types[i] > 1) return error(f, VORBIS_invalid_setup); + if (f->floor_types[i] == 0) { + Floor0 *g = &f->floor_config[i].floor0; + g->order = get_bits(f,8); + g->rate = get_bits(f,16); + g->bark_map_size = get_bits(f,16); + g->amplitude_bits = get_bits(f,6); + g->amplitude_offset = get_bits(f,8); + g->number_of_books = get_bits(f,4) + 1; + for (j=0; j < g->number_of_books; ++j) + g->book_list[j] = get_bits(f,8); + return error(f, VORBIS_feature_not_supported); + } else { + Point p[31*8+2]; + Floor1 *g = &f->floor_config[i].floor1; + int max_class = -1; + g->partitions = get_bits(f, 5); + for (j=0; j < g->partitions; ++j) { + g->partition_class_list[j] = get_bits(f, 4); + if (g->partition_class_list[j] > max_class) + max_class = g->partition_class_list[j]; + } + for (j=0; j <= max_class; ++j) { + g->class_dimensions[j] = get_bits(f, 3)+1; + g->class_subclasses[j] = get_bits(f, 2); + if (g->class_subclasses[j]) { + g->class_masterbooks[j] = get_bits(f, 8); + if (g->class_masterbooks[j] >= f->codebook_count) return error(f, VORBIS_invalid_setup); + } + for (k=0; k < 1 << g->class_subclasses[j]; ++k) { + g->subclass_books[j][k] = get_bits(f,8)-1; + if (g->subclass_books[j][k] >= f->codebook_count) return error(f, VORBIS_invalid_setup); + } + } + g->floor1_multiplier = get_bits(f,2)+1; + g->rangebits = get_bits(f,4); + g->Xlist[0] = 0; + g->Xlist[1] = 1 << g->rangebits; + g->values = 2; + for (j=0; j < g->partitions; ++j) { + int c = g->partition_class_list[j]; + for (k=0; k < g->class_dimensions[c]; ++k) { + g->Xlist[g->values] = get_bits(f, g->rangebits); + ++g->values; + } + } + // precompute the sorting + for (j=0; j < g->values; ++j) { + p[j].x = g->Xlist[j]; + p[j].y = j; + } + qsort(p, g->values, sizeof(p[0]), point_compare); + for (j=0; j < g->values; ++j) + g->sorted_order[j] = (uint8) p[j].y; + // precompute the neighbors + for (j=2; j < g->values; ++j) { + int low = 0; + int hi = 0; + neighbors(g->Xlist, j, &low,&hi); + g->neighbors[j][0] = low; + g->neighbors[j][1] = hi; + } + + if (g->values > longest_floorlist) + longest_floorlist = g->values; + } + } + + // Residue + f->residue_count = get_bits(f, 6)+1; + f->residue_config = (Residue *) setup_malloc(f, f->residue_count * sizeof(f->residue_config[0])); + if (f->residue_config == NULL) return error(f, VORBIS_outofmem); + memset(f->residue_config, 0, f->residue_count * sizeof(f->residue_config[0])); + for (i=0; i < f->residue_count; ++i) { + uint8 residue_cascade[64]; + Residue *r = f->residue_config+i; + f->residue_types[i] = get_bits(f, 16); + if (f->residue_types[i] > 2) return error(f, VORBIS_invalid_setup); + r->begin = get_bits(f, 24); + r->end = get_bits(f, 24); + if (r->end < r->begin) return error(f, VORBIS_invalid_setup); + r->part_size = get_bits(f,24)+1; + r->classifications = get_bits(f,6)+1; + r->classbook = get_bits(f,8); + if (r->classbook >= f->codebook_count) return error(f, VORBIS_invalid_setup); + for (j=0; j < r->classifications; ++j) { + uint8 high_bits=0; + uint8 low_bits=get_bits(f,3); + if (get_bits(f,1)) + high_bits = get_bits(f,5); + residue_cascade[j] = high_bits*8 + low_bits; + } + r->residue_books = (short (*)[8]) setup_malloc(f, sizeof(r->residue_books[0]) * r->classifications); + if (r->residue_books == NULL) return error(f, VORBIS_outofmem); + for (j=0; j < r->classifications; ++j) { + for (k=0; k < 8; ++k) { + if (residue_cascade[j] & (1 << k)) { + r->residue_books[j][k] = get_bits(f, 8); + if (r->residue_books[j][k] >= f->codebook_count) return error(f, VORBIS_invalid_setup); + } else { + r->residue_books[j][k] = -1; + } + } + } + // precompute the classifications[] array to avoid inner-loop mod/divide + // call it 'classdata' since we already have r->classifications + r->classdata = (uint8 **) setup_malloc(f, sizeof(*r->classdata) * f->codebooks[r->classbook].entries); + if (!r->classdata) return error(f, VORBIS_outofmem); + memset(r->classdata, 0, sizeof(*r->classdata) * f->codebooks[r->classbook].entries); + for (j=0; j < f->codebooks[r->classbook].entries; ++j) { + int classwords = f->codebooks[r->classbook].dimensions; + int temp = j; + r->classdata[j] = (uint8 *) setup_malloc(f, sizeof(r->classdata[j][0]) * classwords); + if (r->classdata[j] == NULL) return error(f, VORBIS_outofmem); + for (k=classwords-1; k >= 0; --k) { + r->classdata[j][k] = temp % r->classifications; + temp /= r->classifications; + } + } + } + + f->mapping_count = get_bits(f,6)+1; + f->mapping = (Mapping *) setup_malloc(f, f->mapping_count * sizeof(*f->mapping)); + if (f->mapping == NULL) return error(f, VORBIS_outofmem); + memset(f->mapping, 0, f->mapping_count * sizeof(*f->mapping)); + for (i=0; i < f->mapping_count; ++i) { + Mapping *m = f->mapping + i; + int mapping_type = get_bits(f,16); + if (mapping_type != 0) return error(f, VORBIS_invalid_setup); + m->chan = (MappingChannel *) setup_malloc(f, f->channels * sizeof(*m->chan)); + if (m->chan == NULL) return error(f, VORBIS_outofmem); + if (get_bits(f,1)) + m->submaps = get_bits(f,4)+1; + else + m->submaps = 1; + if (m->submaps > max_submaps) + max_submaps = m->submaps; + if (get_bits(f,1)) { + m->coupling_steps = get_bits(f,8)+1; + for (k=0; k < m->coupling_steps; ++k) { + m->chan[k].magnitude = get_bits(f, ilog(f->channels-1)); + m->chan[k].angle = get_bits(f, ilog(f->channels-1)); + if (m->chan[k].magnitude >= f->channels) return error(f, VORBIS_invalid_setup); + if (m->chan[k].angle >= f->channels) return error(f, VORBIS_invalid_setup); + if (m->chan[k].magnitude == m->chan[k].angle) return error(f, VORBIS_invalid_setup); + } + } else + m->coupling_steps = 0; + + // reserved field + if (get_bits(f,2)) return error(f, VORBIS_invalid_setup); + if (m->submaps > 1) { + for (j=0; j < f->channels; ++j) { + m->chan[j].mux = get_bits(f, 4); + if (m->chan[j].mux >= m->submaps) return error(f, VORBIS_invalid_setup); + } + } else + // @SPECIFICATION: this case is missing from the spec + for (j=0; j < f->channels; ++j) + m->chan[j].mux = 0; + + for (j=0; j < m->submaps; ++j) { + get_bits(f,8); // discard + m->submap_floor[j] = get_bits(f,8); + m->submap_residue[j] = get_bits(f,8); + if (m->submap_floor[j] >= f->floor_count) return error(f, VORBIS_invalid_setup); + if (m->submap_residue[j] >= f->residue_count) return error(f, VORBIS_invalid_setup); + } + } + + // Modes + f->mode_count = get_bits(f, 6)+1; + for (i=0; i < f->mode_count; ++i) { + Mode *m = f->mode_config+i; + m->blockflag = get_bits(f,1); + m->windowtype = get_bits(f,16); + m->transformtype = get_bits(f,16); + m->mapping = get_bits(f,8); + if (m->windowtype != 0) return error(f, VORBIS_invalid_setup); + if (m->transformtype != 0) return error(f, VORBIS_invalid_setup); + if (m->mapping >= f->mapping_count) return error(f, VORBIS_invalid_setup); + } + + flush_packet(f); + + f->previous_length = 0; + + for (i=0; i < f->channels; ++i) { + f->channel_buffers[i] = (float *) setup_malloc(f, sizeof(float) * f->blocksize_1); + f->previous_window[i] = (float *) setup_malloc(f, sizeof(float) * f->blocksize_1/2); + f->finalY[i] = (int16 *) setup_malloc(f, sizeof(int16) * longest_floorlist); + if (f->channel_buffers[i] == NULL || f->previous_window[i] == NULL || f->finalY[i] == NULL) return error(f, VORBIS_outofmem); + #ifdef STB_VORBIS_NO_DEFER_FLOOR + f->floor_buffers[i] = (float *) setup_malloc(f, sizeof(float) * f->blocksize_1/2); + if (f->floor_buffers[i] == NULL) return error(f, VORBIS_outofmem); + #endif + } + + if (!init_blocksize(f, 0, f->blocksize_0)) return FALSE; + if (!init_blocksize(f, 1, f->blocksize_1)) return FALSE; + f->blocksize[0] = f->blocksize_0; + f->blocksize[1] = f->blocksize_1; + +#ifdef STB_VORBIS_DIVIDE_TABLE + if (integer_divide_table[1][1]==0) + for (i=0; i < DIVTAB_NUMER; ++i) + for (j=1; j < DIVTAB_DENOM; ++j) + integer_divide_table[i][j] = i / j; +#endif + + // compute how much temporary memory is needed + + // 1. + { + uint32 imdct_mem = (f->blocksize_1 * sizeof(float) >> 1); + uint32 classify_mem; + int i,max_part_read=0; + for (i=0; i < f->residue_count; ++i) { + Residue *r = f->residue_config + i; + int n_read = r->end - r->begin; + int part_read = n_read / r->part_size; + if (part_read > max_part_read) + max_part_read = part_read; + } + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + classify_mem = f->channels * (sizeof(void*) + max_part_read * sizeof(uint8 *)); + #else + classify_mem = f->channels * (sizeof(void*) + max_part_read * sizeof(int *)); + #endif + + f->temp_memory_required = classify_mem; + if (imdct_mem > f->temp_memory_required) + f->temp_memory_required = imdct_mem; + } + + f->first_decode = TRUE; + + if (f->alloc.alloc_buffer) { + assert(f->temp_offset == f->alloc.alloc_buffer_length_in_bytes); + // check if there's enough temp memory so we don't error later + if (f->setup_offset + sizeof(*f) + f->temp_memory_required > (unsigned) f->temp_offset) + return error(f, VORBIS_outofmem); + } + + f->first_audio_page_offset = stb_vorbis_get_file_offset(f); + + return TRUE; +} + +static void vorbis_deinit(stb_vorbis *p) +{ + int i,j; + if (p->residue_config) { + for (i=0; i < p->residue_count; ++i) { + Residue *r = p->residue_config+i; + if (r->classdata) { + for (j=0; j < p->codebooks[r->classbook].entries; ++j) + setup_free(p, r->classdata[j]); + setup_free(p, r->classdata); + } + setup_free(p, r->residue_books); + } + } + + if (p->codebooks) { + CHECK(p); + for (i=0; i < p->codebook_count; ++i) { + Codebook *c = p->codebooks + i; + setup_free(p, c->codeword_lengths); + setup_free(p, c->multiplicands); + setup_free(p, c->codewords); + setup_free(p, c->sorted_codewords); + // c->sorted_values[-1] is the first entry in the array + setup_free(p, c->sorted_values ? c->sorted_values-1 : NULL); + } + setup_free(p, p->codebooks); + } + setup_free(p, p->floor_config); + setup_free(p, p->residue_config); + if (p->mapping) { + for (i=0; i < p->mapping_count; ++i) + setup_free(p, p->mapping[i].chan); + setup_free(p, p->mapping); + } + CHECK(p); + for (i=0; i < p->channels && i < STB_VORBIS_MAX_CHANNELS; ++i) { + setup_free(p, p->channel_buffers[i]); + setup_free(p, p->previous_window[i]); + #ifdef STB_VORBIS_NO_DEFER_FLOOR + setup_free(p, p->floor_buffers[i]); + #endif + setup_free(p, p->finalY[i]); + } + for (i=0; i < 2; ++i) { + setup_free(p, p->A[i]); + setup_free(p, p->B[i]); + setup_free(p, p->C[i]); + setup_free(p, p->window[i]); + setup_free(p, p->bit_reverse[i]); + } + #ifndef STB_VORBIS_NO_STDIO + if (p->close_on_free) fclose(p->f); + #endif +} + +void stb_vorbis_close(stb_vorbis *p) +{ + if (p == NULL) return; + vorbis_deinit(p); + setup_free(p,p); +} + +static void vorbis_init(stb_vorbis *p, const stb_vorbis_alloc *z) +{ + memset(p, 0, sizeof(*p)); // NULL out all malloc'd pointers to start + if (z) { + p->alloc = *z; + p->alloc.alloc_buffer_length_in_bytes = (p->alloc.alloc_buffer_length_in_bytes+3) & ~3; + p->temp_offset = p->alloc.alloc_buffer_length_in_bytes; + } + p->eof = 0; + p->error = VORBIS__no_error; + p->stream = NULL; + p->codebooks = NULL; + p->page_crc_tests = -1; + #ifndef STB_VORBIS_NO_STDIO + p->close_on_free = FALSE; + p->f = NULL; + #endif +} + +int stb_vorbis_get_sample_offset(stb_vorbis *f) +{ + if (f->current_loc_valid) + return f->current_loc; + else + return -1; +} + +stb_vorbis_info stb_vorbis_get_info(stb_vorbis *f) +{ + stb_vorbis_info d; + d.channels = f->channels; + d.sample_rate = f->sample_rate; + d.setup_memory_required = f->setup_memory_required; + d.setup_temp_memory_required = f->setup_temp_memory_required; + d.temp_memory_required = f->temp_memory_required; + d.max_frame_size = f->blocksize_1 >> 1; + return d; +} + +int stb_vorbis_get_error(stb_vorbis *f) +{ + int e = f->error; + f->error = VORBIS__no_error; + return e; +} + +static stb_vorbis * vorbis_alloc(stb_vorbis *f) +{ + stb_vorbis *p = (stb_vorbis *) setup_malloc(f, sizeof(*p)); + return p; +} + +#ifndef STB_VORBIS_NO_PUSHDATA_API + +void stb_vorbis_flush_pushdata(stb_vorbis *f) +{ + f->previous_length = 0; + f->page_crc_tests = 0; + f->discard_samples_deferred = 0; + f->current_loc_valid = FALSE; + f->first_decode = FALSE; + f->samples_output = 0; + f->channel_buffer_start = 0; + f->channel_buffer_end = 0; +} + +static int vorbis_search_for_page_pushdata(vorb *f, uint8 *data, int data_len) +{ + int i,n; + for (i=0; i < f->page_crc_tests; ++i) + f->scan[i].bytes_done = 0; + + // if we have room for more scans, search for them first, because + // they may cause us to stop early if their header is incomplete + if (f->page_crc_tests < STB_VORBIS_PUSHDATA_CRC_COUNT) { + if (data_len < 4) return 0; + data_len -= 3; // need to look for 4-byte sequence, so don't miss + // one that straddles a boundary + for (i=0; i < data_len; ++i) { + if (data[i] == 0x4f) { + if (0==memcmp(data+i, ogg_page_header, 4)) { + int j,len; + uint32 crc; + // make sure we have the whole page header + if (i+26 >= data_len || i+27+data[i+26] >= data_len) { + // only read up to this page start, so hopefully we'll + // have the whole page header start next time + data_len = i; + break; + } + // ok, we have it all; compute the length of the page + len = 27 + data[i+26]; + for (j=0; j < data[i+26]; ++j) + len += data[i+27+j]; + // scan everything up to the embedded crc (which we must 0) + crc = 0; + for (j=0; j < 22; ++j) + crc = crc32_update(crc, data[i+j]); + // now process 4 0-bytes + for ( ; j < 26; ++j) + crc = crc32_update(crc, 0); + // len is the total number of bytes we need to scan + n = f->page_crc_tests++; + f->scan[n].bytes_left = len-j; + f->scan[n].crc_so_far = crc; + f->scan[n].goal_crc = data[i+22] + (data[i+23] << 8) + (data[i+24]<<16) + (data[i+25]<<24); + // if the last frame on a page is continued to the next, then + // we can't recover the sample_loc immediately + if (data[i+27+data[i+26]-1] == 255) + f->scan[n].sample_loc = ~0; + else + f->scan[n].sample_loc = data[i+6] + (data[i+7] << 8) + (data[i+ 8]<<16) + (data[i+ 9]<<24); + f->scan[n].bytes_done = i+j; + if (f->page_crc_tests == STB_VORBIS_PUSHDATA_CRC_COUNT) + break; + // keep going if we still have room for more + } + } + } + } + + for (i=0; i < f->page_crc_tests;) { + uint32 crc; + int j; + int n = f->scan[i].bytes_done; + int m = f->scan[i].bytes_left; + if (m > data_len - n) m = data_len - n; + // m is the bytes to scan in the current chunk + crc = f->scan[i].crc_so_far; + for (j=0; j < m; ++j) + crc = crc32_update(crc, data[n+j]); + f->scan[i].bytes_left -= m; + f->scan[i].crc_so_far = crc; + if (f->scan[i].bytes_left == 0) { + // does it match? + if (f->scan[i].crc_so_far == f->scan[i].goal_crc) { + // Houston, we have page + data_len = n+m; // consumption amount is wherever that scan ended + f->page_crc_tests = -1; // drop out of page scan mode + f->previous_length = 0; // decode-but-don't-output one frame + f->next_seg = -1; // start a new page + f->current_loc = f->scan[i].sample_loc; // set the current sample location + // to the amount we'd have decoded had we decoded this page + f->current_loc_valid = f->current_loc != ~0U; + return data_len; + } + // delete entry + f->scan[i] = f->scan[--f->page_crc_tests]; + } else { + ++i; + } + } + + return data_len; +} + +// return value: number of bytes we used +int stb_vorbis_decode_frame_pushdata( + stb_vorbis *f, // the file we're decoding + const uint8 *data, int data_len, // the memory available for decoding + int *channels, // place to write number of float * buffers + float ***output, // place to write float ** array of float * buffers + int *samples // place to write number of output samples + ) +{ + int i; + int len,right,left; + + if (!IS_PUSH_MODE(f)) return error(f, VORBIS_invalid_api_mixing); + + if (f->page_crc_tests >= 0) { + *samples = 0; + return vorbis_search_for_page_pushdata(f, (uint8 *) data, data_len); + } + + f->stream = (uint8 *) data; + f->stream_end = (uint8 *) data + data_len; + f->error = VORBIS__no_error; + + // check that we have the entire packet in memory + if (!is_whole_packet_present(f, FALSE)) { + *samples = 0; + return 0; + } + + if (!vorbis_decode_packet(f, &len, &left, &right)) { + // save the actual error we encountered + enum STBVorbisError error = f->error; + if (error == VORBIS_bad_packet_type) { + // flush and resynch + f->error = VORBIS__no_error; + while (get8_packet(f) != EOP) + if (f->eof) break; + *samples = 0; + return (int) (f->stream - data); + } + if (error == VORBIS_continued_packet_flag_invalid) { + if (f->previous_length == 0) { + // we may be resynching, in which case it's ok to hit one + // of these; just discard the packet + f->error = VORBIS__no_error; + while (get8_packet(f) != EOP) + if (f->eof) break; + *samples = 0; + return (int) (f->stream - data); + } + } + // if we get an error while parsing, what to do? + // well, it DEFINITELY won't work to continue from where we are! + stb_vorbis_flush_pushdata(f); + // restore the error that actually made us bail + f->error = error; + *samples = 0; + return 1; + } + + // success! + len = vorbis_finish_frame(f, len, left, right); + for (i=0; i < f->channels; ++i) + f->outputs[i] = f->channel_buffers[i] + left; + + if (channels) *channels = f->channels; + *samples = len; + *output = f->outputs; + return (int) (f->stream - data); +} + +stb_vorbis *stb_vorbis_open_pushdata( + const unsigned char *data, int data_len, // the memory available for decoding + int *data_used, // only defined if result is not NULL + int *error, const stb_vorbis_alloc *alloc) +{ + stb_vorbis *f, p; + vorbis_init(&p, alloc); + p.stream = (uint8 *) data; + p.stream_end = (uint8 *) data + data_len; + p.push_mode = TRUE; + if (!start_decoder(&p)) { + if (p.eof) + *error = VORBIS_need_more_data; + else + *error = p.error; + return NULL; + } + f = vorbis_alloc(&p); + if (f) { + *f = p; + *data_used = (int) (f->stream - data); + *error = 0; + return f; + } else { + vorbis_deinit(&p); + return NULL; + } +} +#endif // STB_VORBIS_NO_PUSHDATA_API + +unsigned int stb_vorbis_get_file_offset(stb_vorbis *f) +{ + #ifndef STB_VORBIS_NO_PUSHDATA_API + if (f->push_mode) return 0; + #endif + if (USE_MEMORY(f)) return (unsigned int) (f->stream - f->stream_start); + #ifndef STB_VORBIS_NO_STDIO + return (unsigned int) (ftell(f->f) - f->f_start); + #endif +} + +#ifndef STB_VORBIS_NO_PULLDATA_API +// +// DATA-PULLING API +// + +static uint32 vorbis_find_page(stb_vorbis *f, uint32 *end, uint32 *last) +{ + for(;;) { + int n; + if (f->eof) return 0; + n = get8(f); + if (n == 0x4f) { // page header candidate + unsigned int retry_loc = stb_vorbis_get_file_offset(f); + int i; + // check if we're off the end of a file_section stream + if (retry_loc - 25 > f->stream_len) + return 0; + // check the rest of the header + for (i=1; i < 4; ++i) + if (get8(f) != ogg_page_header[i]) + break; + if (f->eof) return 0; + if (i == 4) { + uint8 header[27]; + uint32 i, crc, goal, len; + for (i=0; i < 4; ++i) + header[i] = ogg_page_header[i]; + for (; i < 27; ++i) + header[i] = get8(f); + if (f->eof) return 0; + if (header[4] != 0) goto invalid; + goal = header[22] + (header[23] << 8) + (header[24]<<16) + (header[25]<<24); + for (i=22; i < 26; ++i) + header[i] = 0; + crc = 0; + for (i=0; i < 27; ++i) + crc = crc32_update(crc, header[i]); + len = 0; + for (i=0; i < header[26]; ++i) { + int s = get8(f); + crc = crc32_update(crc, s); + len += s; + } + if (len && f->eof) return 0; + for (i=0; i < len; ++i) + crc = crc32_update(crc, get8(f)); + // finished parsing probable page + if (crc == goal) { + // we could now check that it's either got the last + // page flag set, OR it's followed by the capture + // pattern, but I guess TECHNICALLY you could have + // a file with garbage between each ogg page and recover + // from it automatically? So even though that paranoia + // might decrease the chance of an invalid decode by + // another 2^32, not worth it since it would hose those + // invalid-but-useful files? + if (end) + *end = stb_vorbis_get_file_offset(f); + if (last) { + if (header[5] & 0x04) + *last = 1; + else + *last = 0; + } + set_file_offset(f, retry_loc-1); + return 1; + } + } + invalid: + // not a valid page, so rewind and look for next one + set_file_offset(f, retry_loc); + } + } +} + + +#define SAMPLE_unknown 0xffffffff + +// seeking is implemented with a binary search, which narrows down the range to +// 64K, before using a linear search (because finding the synchronization +// pattern can be expensive, and the chance we'd find the end page again is +// relatively high for small ranges) +// +// two initial interpolation-style probes are used at the start of the search +// to try to bound either side of the binary search sensibly, while still +// working in O(log n) time if they fail. + +static int get_seek_page_info(stb_vorbis *f, ProbedPage *z) +{ + uint8 header[27], lacing[255]; + int i,len; + + // record where the page starts + z->page_start = stb_vorbis_get_file_offset(f); + + // parse the header + getn(f, header, 27); + if (header[0] != 'O' || header[1] != 'g' || header[2] != 'g' || header[3] != 'S') + return 0; + getn(f, lacing, header[26]); + + // determine the length of the payload + len = 0; + for (i=0; i < header[26]; ++i) + len += lacing[i]; + + // this implies where the page ends + z->page_end = z->page_start + 27 + header[26] + len; + + // read the last-decoded sample out of the data + z->last_decoded_sample = header[6] + (header[7] << 8) + (header[8] << 16) + (header[9] << 24); + + // restore file state to where we were + set_file_offset(f, z->page_start); + return 1; +} + +// rarely used function to seek back to the preceeding page while finding the +// start of a packet +static int go_to_page_before(stb_vorbis *f, unsigned int limit_offset) +{ + unsigned int previous_safe, end; + + // now we want to seek back 64K from the limit + if (limit_offset >= 65536 && limit_offset-65536 >= f->first_audio_page_offset) + previous_safe = limit_offset - 65536; + else + previous_safe = f->first_audio_page_offset; + + set_file_offset(f, previous_safe); + + while (vorbis_find_page(f, &end, NULL)) { + if (end >= limit_offset && stb_vorbis_get_file_offset(f) < limit_offset) + return 1; + set_file_offset(f, end); + } + + return 0; +} + +// implements the search logic for finding a page and starting decoding. if +// the function succeeds, current_loc_valid will be true and current_loc will +// be less than or equal to the provided sample number (the closer the +// better). +static int seek_to_sample_coarse(stb_vorbis *f, uint32 sample_number) +{ + ProbedPage left, right, mid; + int i, start_seg_with_known_loc, end_pos, page_start; + uint32 delta, stream_length, padding; + double offset = 0; + double bytes_per_sample = 0; + int probe = 0; + + // find the last page and validate the target sample + stream_length = stb_vorbis_stream_length_in_samples(f); + if (stream_length == 0) return error(f, VORBIS_seek_without_length); + if (sample_number > stream_length) return error(f, VORBIS_seek_invalid); + + // this is the maximum difference between the window-center (which is the + // actual granule position value), and the right-start (which the spec + // indicates should be the granule position (give or take one)). + padding = ((f->blocksize_1 - f->blocksize_0) >> 2); + if (sample_number < padding) + sample_number = 0; + else + sample_number -= padding; + + left = f->p_first; + while (left.last_decoded_sample == ~0U) { + // (untested) the first page does not have a 'last_decoded_sample' + set_file_offset(f, left.page_end); + if (!get_seek_page_info(f, &left)) goto error; + } + + right = f->p_last; + assert(right.last_decoded_sample != ~0U); + + // starting from the start is handled differently + if (sample_number <= left.last_decoded_sample) { + stb_vorbis_seek_start(f); + return 1; + } + + while (left.page_end != right.page_start) { + assert(left.page_end < right.page_start); + // search range in bytes + delta = right.page_start - left.page_end; + if (delta <= 65536) { + // there's only 64K left to search - handle it linearly + set_file_offset(f, left.page_end); + } else { + if (probe < 2) { + if (probe == 0) { + // first probe (interpolate) + double data_bytes = right.page_end - left.page_start; + bytes_per_sample = data_bytes / right.last_decoded_sample; + offset = left.page_start + bytes_per_sample * (sample_number - left.last_decoded_sample); + } else { + // second probe (try to bound the other side) + double error = ((double) sample_number - mid.last_decoded_sample) * bytes_per_sample; + if (error >= 0 && error < 8000) error = 8000; + if (error < 0 && error > -8000) error = -8000; + offset += error * 2; + } + + // ensure the offset is valid + if (offset < left.page_end) + offset = left.page_end; + if (offset > right.page_start - 65536) + offset = right.page_start - 65536; + + set_file_offset(f, (unsigned int) offset); + } else { + // binary search for large ranges (offset by 32K to ensure + // we don't hit the right page) + set_file_offset(f, left.page_end + (delta / 2) - 32768); + } + + if (!vorbis_find_page(f, NULL, NULL)) goto error; + } + + for (;;) { + if (!get_seek_page_info(f, &mid)) goto error; + if (mid.last_decoded_sample != ~0U) break; + // (untested) no frames end on this page + set_file_offset(f, mid.page_end); + assert(mid.page_start < right.page_start); + } + + // if we've just found the last page again then we're in a tricky file, + // and we're close enough. + if (mid.page_start == right.page_start) + break; + + if (sample_number < mid.last_decoded_sample) + right = mid; + else + left = mid; + + ++probe; + } + + // seek back to start of the last packet + page_start = left.page_start; + set_file_offset(f, page_start); + if (!start_page(f)) return error(f, VORBIS_seek_failed); + end_pos = f->end_seg_with_known_loc; + assert(end_pos >= 0); + + for (;;) { + for (i = end_pos; i > 0; --i) + if (f->segments[i-1] != 255) + break; + + start_seg_with_known_loc = i; + + if (start_seg_with_known_loc > 0 || !(f->page_flag & PAGEFLAG_continued_packet)) + break; + + // (untested) the final packet begins on an earlier page + if (!go_to_page_before(f, page_start)) + goto error; + + page_start = stb_vorbis_get_file_offset(f); + if (!start_page(f)) goto error; + end_pos = f->segment_count - 1; + } + + // prepare to start decoding + f->current_loc_valid = FALSE; + f->last_seg = FALSE; + f->valid_bits = 0; + f->packet_bytes = 0; + f->bytes_in_seg = 0; + f->previous_length = 0; + f->next_seg = start_seg_with_known_loc; + + for (i = 0; i < start_seg_with_known_loc; i++) + skip(f, f->segments[i]); + + // start decoding (optimizable - this frame is generally discarded) + vorbis_pump_first_frame(f); + return 1; + +error: + // try to restore the file to a valid state + stb_vorbis_seek_start(f); + return error(f, VORBIS_seek_failed); +} + +// the same as vorbis_decode_initial, but without advancing +static int peek_decode_initial(vorb *f, int *p_left_start, int *p_left_end, int *p_right_start, int *p_right_end, int *mode) +{ + int bits_read, bytes_read; + + if (!vorbis_decode_initial(f, p_left_start, p_left_end, p_right_start, p_right_end, mode)) + return 0; + + // either 1 or 2 bytes were read, figure out which so we can rewind + bits_read = 1 + ilog(f->mode_count-1); + if (f->mode_config[*mode].blockflag) + bits_read += 2; + bytes_read = (bits_read + 7) / 8; + + f->bytes_in_seg += bytes_read; + f->packet_bytes -= bytes_read; + skip(f, -bytes_read); + if (f->next_seg == -1) + f->next_seg = f->segment_count - 1; + else + f->next_seg--; + f->valid_bits = 0; + + return 1; +} + +int stb_vorbis_seek_frame(stb_vorbis *f, unsigned int sample_number) +{ + uint32 max_frame_samples; + + if (IS_PUSH_MODE(f)) return error(f, VORBIS_invalid_api_mixing); + + // fast page-level search + if (!seek_to_sample_coarse(f, sample_number)) + return 0; + + assert(f->current_loc_valid); + assert(f->current_loc <= sample_number); + + // linear search for the relevant packet + max_frame_samples = (f->blocksize_1*3 - f->blocksize_0) >> 2; + while (f->current_loc < sample_number) { + int left_start, left_end, right_start, right_end, mode, frame_samples; + if (!peek_decode_initial(f, &left_start, &left_end, &right_start, &right_end, &mode)) + return error(f, VORBIS_seek_failed); + // calculate the number of samples returned by the next frame + frame_samples = right_start - left_start; + if (f->current_loc + frame_samples > sample_number) { + return 1; // the next frame will contain the sample + } else if (f->current_loc + frame_samples + max_frame_samples > sample_number) { + // there's a chance the frame after this could contain the sample + vorbis_pump_first_frame(f); + } else { + // this frame is too early to be relevant + f->current_loc += frame_samples; + f->previous_length = 0; + maybe_start_packet(f); + flush_packet(f); + } + } + // the next frame will start with the sample + assert(f->current_loc == sample_number); + return 1; +} + +int stb_vorbis_seek(stb_vorbis *f, unsigned int sample_number) +{ + if (!stb_vorbis_seek_frame(f, sample_number)) + return 0; + + if (sample_number != f->current_loc) { + int n; + uint32 frame_start = f->current_loc; + stb_vorbis_get_frame_float(f, &n, NULL); + assert(sample_number > frame_start); + assert(f->channel_buffer_start + (int) (sample_number-frame_start) <= f->channel_buffer_end); + f->channel_buffer_start += (sample_number - frame_start); + } + + return 1; +} + +void stb_vorbis_seek_start(stb_vorbis *f) +{ + if (IS_PUSH_MODE(f)) { error(f, VORBIS_invalid_api_mixing); return; } + set_file_offset(f, f->first_audio_page_offset); + f->previous_length = 0; + f->first_decode = TRUE; + f->next_seg = -1; + vorbis_pump_first_frame(f); +} + +unsigned int stb_vorbis_stream_length_in_samples(stb_vorbis *f) +{ + unsigned int restore_offset, previous_safe; + unsigned int end, last_page_loc; + + if (IS_PUSH_MODE(f)) return error(f, VORBIS_invalid_api_mixing); + if (!f->total_samples) { + unsigned int last; + uint32 lo,hi; + char header[6]; + + // first, store the current decode position so we can restore it + restore_offset = stb_vorbis_get_file_offset(f); + + // now we want to seek back 64K from the end (the last page must + // be at most a little less than 64K, but let's allow a little slop) + if (f->stream_len >= 65536 && f->stream_len-65536 >= f->first_audio_page_offset) + previous_safe = f->stream_len - 65536; + else + previous_safe = f->first_audio_page_offset; + + set_file_offset(f, previous_safe); + // previous_safe is now our candidate 'earliest known place that seeking + // to will lead to the final page' + + if (!vorbis_find_page(f, &end, &last)) { + // if we can't find a page, we're hosed! + f->error = VORBIS_cant_find_last_page; + f->total_samples = 0xffffffff; + goto done; + } + + // check if there are more pages + last_page_loc = stb_vorbis_get_file_offset(f); + + // stop when the last_page flag is set, not when we reach eof; + // this allows us to stop short of a 'file_section' end without + // explicitly checking the length of the section + while (!last) { + set_file_offset(f, end); + if (!vorbis_find_page(f, &end, &last)) { + // the last page we found didn't have the 'last page' flag + // set. whoops! + break; + } + previous_safe = last_page_loc+1; + last_page_loc = stb_vorbis_get_file_offset(f); + } + + set_file_offset(f, last_page_loc); + + // parse the header + getn(f, (unsigned char *)header, 6); + // extract the absolute granule position + lo = get32(f); + hi = get32(f); + if (lo == 0xffffffff && hi == 0xffffffff) { + f->error = VORBIS_cant_find_last_page; + f->total_samples = SAMPLE_unknown; + goto done; + } + if (hi) + lo = 0xfffffffe; // saturate + f->total_samples = lo; + + f->p_last.page_start = last_page_loc; + f->p_last.page_end = end; + f->p_last.last_decoded_sample = lo; + + done: + set_file_offset(f, restore_offset); + } + return f->total_samples == SAMPLE_unknown ? 0 : f->total_samples; +} + +float stb_vorbis_stream_length_in_seconds(stb_vorbis *f) +{ + return stb_vorbis_stream_length_in_samples(f) / (float) f->sample_rate; +} + + + +int stb_vorbis_get_frame_float(stb_vorbis *f, int *channels, float ***output) +{ + int len, right,left,i; + if (IS_PUSH_MODE(f)) return error(f, VORBIS_invalid_api_mixing); + + if (!vorbis_decode_packet(f, &len, &left, &right)) { + f->channel_buffer_start = f->channel_buffer_end = 0; + return 0; + } + + len = vorbis_finish_frame(f, len, left, right); + for (i=0; i < f->channels; ++i) + f->outputs[i] = f->channel_buffers[i] + left; + + f->channel_buffer_start = left; + f->channel_buffer_end = left+len; + + if (channels) *channels = f->channels; + if (output) *output = f->outputs; + return len; +} + +#ifndef STB_VORBIS_NO_STDIO + +stb_vorbis * stb_vorbis_open_file_section(FILE *file, int close_on_free, int *error, const stb_vorbis_alloc *alloc, unsigned int length) +{ + stb_vorbis *f, p; + vorbis_init(&p, alloc); + p.f = file; + p.f_start = (uint32) ftell(file); + p.stream_len = length; + p.close_on_free = close_on_free; + if (start_decoder(&p)) { + f = vorbis_alloc(&p); + if (f) { + *f = p; + vorbis_pump_first_frame(f); + return f; + } + } + if (error) *error = p.error; + vorbis_deinit(&p); + return NULL; +} + +stb_vorbis * stb_vorbis_open_file(FILE *file, int close_on_free, int *error, const stb_vorbis_alloc *alloc) +{ + unsigned int len, start; + start = (unsigned int) ftell(file); + fseek(file, 0, SEEK_END); + len = (unsigned int) (ftell(file) - start); + fseek(file, start, SEEK_SET); + return stb_vorbis_open_file_section(file, close_on_free, error, alloc, len); +} + +stb_vorbis * stb_vorbis_open_filename(const char *filename, int *error, const stb_vorbis_alloc *alloc) +{ + FILE *f = fopen(filename, "rb"); + if (f) + return stb_vorbis_open_file(f, TRUE, error, alloc); + if (error) *error = VORBIS_file_open_failure; + return NULL; +} +#endif // STB_VORBIS_NO_STDIO + +stb_vorbis * stb_vorbis_open_memory(const unsigned char *data, int len, int *error, const stb_vorbis_alloc *alloc) +{ + stb_vorbis *f, p; + if (data == NULL) return NULL; + vorbis_init(&p, alloc); + p.stream = (uint8 *) data; + p.stream_end = (uint8 *) data + len; + p.stream_start = (uint8 *) p.stream; + p.stream_len = len; + p.push_mode = FALSE; + if (start_decoder(&p)) { + f = vorbis_alloc(&p); + if (f) { + *f = p; + vorbis_pump_first_frame(f); + return f; + } + } + if (error) *error = p.error; + vorbis_deinit(&p); + return NULL; +} + +#ifndef STB_VORBIS_NO_INTEGER_CONVERSION +#define PLAYBACK_MONO 1 +#define PLAYBACK_LEFT 2 +#define PLAYBACK_RIGHT 4 + +#define L (PLAYBACK_LEFT | PLAYBACK_MONO) +#define C (PLAYBACK_LEFT | PLAYBACK_RIGHT | PLAYBACK_MONO) +#define R (PLAYBACK_RIGHT | PLAYBACK_MONO) + +static int8 channel_position[7][6] = +{ + { 0 }, + { C }, + { L, R }, + { L, C, R }, + { L, R, L, R }, + { L, C, R, L, R }, + { L, C, R, L, R, C }, +}; + + +#ifndef STB_VORBIS_NO_FAST_SCALED_FLOAT + typedef union { + float f; + int i; + } float_conv; + typedef char stb_vorbis_float_size_test[sizeof(float)==4 && sizeof(int) == 4]; + #define FASTDEF(x) float_conv x + // add (1<<23) to convert to int, then divide by 2^SHIFT, then add 0.5/2^SHIFT to round + #define MAGIC(SHIFT) (1.5f * (1 << (23-SHIFT)) + 0.5f/(1 << SHIFT)) + #define ADDEND(SHIFT) (((150-SHIFT) << 23) + (1 << 22)) + #define FAST_SCALED_FLOAT_TO_INT(temp,x,s) (temp.f = (x) + MAGIC(s), temp.i - ADDEND(s)) + #define check_endianness() +#else + #define FAST_SCALED_FLOAT_TO_INT(temp,x,s) ((int) ((x) * (1 << (s)))) + #define check_endianness() + #define FASTDEF(x) +#endif + +static void copy_samples(short *dest, float *src, int len) +{ + int i; + check_endianness(); + for (i=0; i < len; ++i) { + FASTDEF(temp); + int v = FAST_SCALED_FLOAT_TO_INT(temp, src[i],15); + if ((unsigned int) (v + 32768) > 65535) + v = v < 0 ? -32768 : 32767; + dest[i] = v; + } +} + +static void compute_samples(int mask, short *output, int num_c, float **data, int d_offset, int len) +{ + #define BUFFER_SIZE 32 + float buffer[BUFFER_SIZE]; + int i,j,o,n = BUFFER_SIZE; + check_endianness(); + for (o = 0; o < len; o += BUFFER_SIZE) { + memset(buffer, 0, sizeof(buffer)); + if (o + n > len) n = len - o; + for (j=0; j < num_c; ++j) { + if (channel_position[num_c][j] & mask) { + for (i=0; i < n; ++i) + buffer[i] += data[j][d_offset+o+i]; + } + } + for (i=0; i < n; ++i) { + FASTDEF(temp); + int v = FAST_SCALED_FLOAT_TO_INT(temp,buffer[i],15); + if ((unsigned int) (v + 32768) > 65535) + v = v < 0 ? -32768 : 32767; + output[o+i] = v; + } + } +} + +static void compute_stereo_samples(short *output, int num_c, float **data, int d_offset, int len) +{ + #define BUFFER_SIZE 32 + float buffer[BUFFER_SIZE]; + int i,j,o,n = BUFFER_SIZE >> 1; + // o is the offset in the source data + check_endianness(); + for (o = 0; o < len; o += BUFFER_SIZE >> 1) { + // o2 is the offset in the output data + int o2 = o << 1; + memset(buffer, 0, sizeof(buffer)); + if (o + n > len) n = len - o; + for (j=0; j < num_c; ++j) { + int m = channel_position[num_c][j] & (PLAYBACK_LEFT | PLAYBACK_RIGHT); + if (m == (PLAYBACK_LEFT | PLAYBACK_RIGHT)) { + for (i=0; i < n; ++i) { + buffer[i*2+0] += data[j][d_offset+o+i]; + buffer[i*2+1] += data[j][d_offset+o+i]; + } + } else if (m == PLAYBACK_LEFT) { + for (i=0; i < n; ++i) { + buffer[i*2+0] += data[j][d_offset+o+i]; + } + } else if (m == PLAYBACK_RIGHT) { + for (i=0; i < n; ++i) { + buffer[i*2+1] += data[j][d_offset+o+i]; + } + } + } + for (i=0; i < (n<<1); ++i) { + FASTDEF(temp); + int v = FAST_SCALED_FLOAT_TO_INT(temp,buffer[i],15); + if ((unsigned int) (v + 32768) > 65535) + v = v < 0 ? -32768 : 32767; + output[o2+i] = v; + } + } +} + +static void convert_samples_short(int buf_c, short **buffer, int b_offset, int data_c, float **data, int d_offset, int samples) +{ + int i; + if (buf_c != data_c && buf_c <= 2 && data_c <= 6) { + static int channel_selector[3][2] = { {0}, {PLAYBACK_MONO}, {PLAYBACK_LEFT, PLAYBACK_RIGHT} }; + for (i=0; i < buf_c; ++i) + compute_samples(channel_selector[buf_c][i], buffer[i]+b_offset, data_c, data, d_offset, samples); + } else { + int limit = buf_c < data_c ? buf_c : data_c; + for (i=0; i < limit; ++i) + copy_samples(buffer[i]+b_offset, data[i]+d_offset, samples); + for ( ; i < buf_c; ++i) + memset(buffer[i]+b_offset, 0, sizeof(short) * samples); + } +} + +int stb_vorbis_get_frame_short(stb_vorbis *f, int num_c, short **buffer, int num_samples) +{ + float **output; + int len = stb_vorbis_get_frame_float(f, NULL, &output); + if (len > num_samples) len = num_samples; + if (len) + convert_samples_short(num_c, buffer, 0, f->channels, output, 0, len); + return len; +} + +static void convert_channels_short_interleaved(int buf_c, short *buffer, int data_c, float **data, int d_offset, int len) +{ + int i; + check_endianness(); + if (buf_c != data_c && buf_c <= 2 && data_c <= 6) { + assert(buf_c == 2); + for (i=0; i < buf_c; ++i) + compute_stereo_samples(buffer, data_c, data, d_offset, len); + } else { + int limit = buf_c < data_c ? buf_c : data_c; + int j; + for (j=0; j < len; ++j) { + for (i=0; i < limit; ++i) { + FASTDEF(temp); + float f = data[i][d_offset+j]; + int v = FAST_SCALED_FLOAT_TO_INT(temp, f,15);//data[i][d_offset+j],15); + if ((unsigned int) (v + 32768) > 65535) + v = v < 0 ? -32768 : 32767; + *buffer++ = v; + } + for ( ; i < buf_c; ++i) + *buffer++ = 0; + } + } +} + +int stb_vorbis_get_frame_short_interleaved(stb_vorbis *f, int num_c, short *buffer, int num_shorts) +{ + float **output; + int len; + if (num_c == 1) return stb_vorbis_get_frame_short(f,num_c,&buffer, num_shorts); + len = stb_vorbis_get_frame_float(f, NULL, &output); + if (len) { + if (len*num_c > num_shorts) len = num_shorts / num_c; + convert_channels_short_interleaved(num_c, buffer, f->channels, output, 0, len); + } + return len; +} + +int stb_vorbis_get_samples_short_interleaved(stb_vorbis *f, int channels, short *buffer, int num_shorts) +{ + float **outputs; + int len = num_shorts / channels; + int n=0; + int z = f->channels; + if (z > channels) z = channels; + while (n < len) { + int k = f->channel_buffer_end - f->channel_buffer_start; + if (n+k >= len) k = len - n; + if (k) + convert_channels_short_interleaved(channels, buffer, f->channels, f->channel_buffers, f->channel_buffer_start, k); + buffer += k*channels; + n += k; + f->channel_buffer_start += k; + if (n == len) break; + if (!stb_vorbis_get_frame_float(f, NULL, &outputs)) break; + } + return n; +} + +int stb_vorbis_get_samples_short(stb_vorbis *f, int channels, short **buffer, int len) +{ + float **outputs; + int n=0; + int z = f->channels; + if (z > channels) z = channels; + while (n < len) { + int k = f->channel_buffer_end - f->channel_buffer_start; + if (n+k >= len) k = len - n; + if (k) + convert_samples_short(channels, buffer, n, f->channels, f->channel_buffers, f->channel_buffer_start, k); + n += k; + f->channel_buffer_start += k; + if (n == len) break; + if (!stb_vorbis_get_frame_float(f, NULL, &outputs)) break; + } + return n; +} + +#ifndef STB_VORBIS_NO_STDIO +int stb_vorbis_decode_filename(const char *filename, int *channels, int *sample_rate, short **output) +{ + int data_len, offset, total, limit, error; + short *data; + stb_vorbis *v = stb_vorbis_open_filename(filename, &error, NULL); + if (v == NULL) return -1; + limit = v->channels * 4096; + *channels = v->channels; + if (sample_rate) + *sample_rate = v->sample_rate; + offset = data_len = 0; + total = limit; + data = (short *) malloc(total * sizeof(*data)); + if (data == NULL) { + stb_vorbis_close(v); + return -2; + } + for (;;) { + int n = stb_vorbis_get_frame_short_interleaved(v, v->channels, data+offset, total-offset); + if (n == 0) break; + data_len += n; + offset += n * v->channels; + if (offset + limit > total) { + short *data2; + total *= 2; + data2 = (short *) realloc(data, total * sizeof(*data)); + if (data2 == NULL) { + free(data); + stb_vorbis_close(v); + return -2; + } + data = data2; + } + } + *output = data; + stb_vorbis_close(v); + return data_len; +} +#endif // NO_STDIO + +int stb_vorbis_decode_memory(const uint8 *mem, int len, int *channels, int *sample_rate, short **output) +{ + int data_len, offset, total, limit, error; + short *data; + stb_vorbis *v = stb_vorbis_open_memory(mem, len, &error, NULL); + if (v == NULL) return -1; + limit = v->channels * 4096; + *channels = v->channels; + if (sample_rate) + *sample_rate = v->sample_rate; + offset = data_len = 0; + total = limit; + data = (short *) malloc(total * sizeof(*data)); + if (data == NULL) { + stb_vorbis_close(v); + return -2; + } + for (;;) { + int n = stb_vorbis_get_frame_short_interleaved(v, v->channels, data+offset, total-offset); + if (n == 0) break; + data_len += n; + offset += n * v->channels; + if (offset + limit > total) { + short *data2; + total *= 2; + data2 = (short *) realloc(data, total * sizeof(*data)); + if (data2 == NULL) { + free(data); + stb_vorbis_close(v); + return -2; + } + data = data2; + } + } + *output = data; + stb_vorbis_close(v); + return data_len; +} +#endif // STB_VORBIS_NO_INTEGER_CONVERSION + +int stb_vorbis_get_samples_float_interleaved(stb_vorbis *f, int channels, float *buffer, int num_floats) +{ + float **outputs; + int len = num_floats / channels; + int n=0; + int z = f->channels; + if (z > channels) z = channels; + while (n < len) { + int i,j; + int k = f->channel_buffer_end - f->channel_buffer_start; + if (n+k >= len) k = len - n; + for (j=0; j < k; ++j) { + for (i=0; i < z; ++i) + *buffer++ = f->channel_buffers[i][f->channel_buffer_start+j]; + for ( ; i < channels; ++i) + *buffer++ = 0; + } + n += k; + f->channel_buffer_start += k; + if (n == len) + break; + if (!stb_vorbis_get_frame_float(f, NULL, &outputs)) + break; + } + return n; +} + +int stb_vorbis_get_samples_float(stb_vorbis *f, int channels, float **buffer, int num_samples) +{ + float **outputs; + int n=0; + int z = f->channels; + if (z > channels) z = channels; + while (n < num_samples) { + int i; + int k = f->channel_buffer_end - f->channel_buffer_start; + if (n+k >= num_samples) k = num_samples - n; + if (k) { + for (i=0; i < z; ++i) + memcpy(buffer[i]+n, f->channel_buffers[i]+f->channel_buffer_start, sizeof(float)*k); + for ( ; i < channels; ++i) + memset(buffer[i]+n, 0, sizeof(float) * k); + } + n += k; + f->channel_buffer_start += k; + if (n == num_samples) + break; + if (!stb_vorbis_get_frame_float(f, NULL, &outputs)) + break; + } + return n; +} +#endif // STB_VORBIS_NO_PULLDATA_API + +/* Version history + 1.09 - 2016/04/04 - back out 'avoid discarding last frame' fix from previous version + 1.08 - 2016/04/02 - fixed multiple warnings; fix setup memory leaks; + avoid discarding last frame of audio data + 1.07 - 2015/01/16 - fixed some warnings, fix mingw, const-correct API + some more crash fixes when out of memory or with corrupt files + 1.06 - 2015/08/31 - full, correct support for seeking API (Dougall Johnson) + some crash fixes when out of memory or with corrupt files + 1.05 - 2015/04/19 - don't define __forceinline if it's redundant + 1.04 - 2014/08/27 - fix missing const-correct case in API + 1.03 - 2014/08/07 - Warning fixes + 1.02 - 2014/07/09 - Declare qsort compare function _cdecl on windows + 1.01 - 2014/06/18 - fix stb_vorbis_get_samples_float + 1.0 - 2014/05/26 - fix memory leaks; fix warnings; fix bugs in multichannel + (API change) report sample rate for decode-full-file funcs + 0.99996 - bracket #include for macintosh compilation by Laurent Gomila + 0.99995 - use union instead of pointer-cast for fast-float-to-int to avoid alias-optimization problem + 0.99994 - change fast-float-to-int to work in single-precision FPU mode, remove endian-dependence + 0.99993 - remove assert that fired on legal files with empty tables + 0.99992 - rewind-to-start + 0.99991 - bugfix to stb_vorbis_get_samples_short by Bernhard Wodo + 0.9999 - (should have been 0.99990) fix no-CRT support, compiling as C++ + 0.9998 - add a full-decode function with a memory source + 0.9997 - fix a bug in the read-from-FILE case in 0.9996 addition + 0.9996 - query length of vorbis stream in samples/seconds + 0.9995 - bugfix to another optimization that only happened in certain files + 0.9994 - bugfix to one of the optimizations that caused significant (but inaudible?) errors + 0.9993 - performance improvements; runs in 99% to 104% of time of reference implementation + 0.9992 - performance improvement of IMDCT; now performs close to reference implementation + 0.9991 - performance improvement of IMDCT + 0.999 - (should have been 0.9990) performance improvement of IMDCT + 0.998 - no-CRT support from Casey Muratori + 0.997 - bugfixes for bugs found by Terje Mathisen + 0.996 - bugfix: fast-huffman decode initialized incorrectly for sparse codebooks; fixing gives 10% speedup - found by Terje Mathisen + 0.995 - bugfix: fix to 'effective' overrun detection - found by Terje Mathisen + 0.994 - bugfix: garbage decode on final VQ symbol of a non-multiple - found by Terje Mathisen + 0.993 - bugfix: pushdata API required 1 extra byte for empty page (failed to consume final page if empty) - found by Terje Mathisen + 0.992 - fixes for MinGW warning + 0.991 - turn fast-float-conversion on by default + 0.990 - fix push-mode seek recovery if you seek into the headers + 0.98b - fix to bad release of 0.98 + 0.98 - fix push-mode seek recovery; robustify float-to-int and support non-fast mode + 0.97 - builds under c++ (typecasting, don't use 'class' keyword) + 0.96 - somehow MY 0.95 was right, but the web one was wrong, so here's my 0.95 rereleased as 0.96, fixes a typo in the clamping code + 0.95 - clamping code for 16-bit functions + 0.94 - not publically released + 0.93 - fixed all-zero-floor case (was decoding garbage) + 0.92 - fixed a memory leak + 0.91 - conditional compiles to omit parts of the API and the infrastructure to support them: STB_VORBIS_NO_PULLDATA_API, STB_VORBIS_NO_PUSHDATA_API, STB_VORBIS_NO_STDIO, STB_VORBIS_NO_INTEGER_CONVERSION + 0.90 - first public release +*/ + +#endif // STB_VORBIS_HEADER_ONLY diff --git a/Samples/3rdParty/stb/src/stb_vorbis.h b/Samples/3rdParty/stb/src/stb_vorbis.h new file mode 100755 index 0000000..5c57649 --- /dev/null +++ b/Samples/3rdParty/stb/src/stb_vorbis.h @@ -0,0 +1,388 @@ +// Ogg Vorbis audio decoder - v1.09 - public domain +// http://nothings.org/stb_vorbis/ +// +// Original version written by Sean Barrett in 2007. +// +// Originally sponsored by RAD Game Tools. Seeking sponsored +// by Phillip Bennefall, Marc Andersen, Aaron Baker, Elias Software, +// Aras Pranckevicius, and Sean Barrett. +// +// LICENSE +// +// This software is dual-licensed to the public domain and under the following +// license: you are granted a perpetual, irrevocable license to copy, modify, +// publish, and distribute this file as you see fit. +// +// No warranty for any purpose is expressed or implied by the author (nor +// by RAD Game Tools). Report bugs and send enhancements to the author. +// +// Limitations: +// +// - floor 0 not supported (used in old ogg vorbis files pre-2004) +// - lossless sample-truncation at beginning ignored +// - cannot concatenate multiple vorbis streams +// - sample positions are 32-bit, limiting seekable 192Khz +// files to around 6 hours (Ogg supports 64-bit) +// +// Feature contributors: +// Dougall Johnson (sample-exact seeking) +// +// Bugfix/warning contributors: +// Terje Mathisen Niklas Frykholm Andy Hill +// Casey Muratori John Bolton Gargaj +// Laurent Gomila Marc LeBlanc Ronny Chevalier +// Bernhard Wodo Evan Balster alxprd@github +// Tom Beaumont Ingo Leitgeb Nicolas Guillemot +// Phillip Bennefall Rohit Thiago Goulart +// manxorist@github saga musix +// +// Partial history: +// 1.09 - 2016/04/04 - back out 'truncation of last frame' fix from previous version +// 1.08 - 2016/04/02 - warnings; setup memory leaks; truncation of last frame +// 1.07 - 2015/01/16 - fixes for crashes on invalid files; warning fixes; const +// 1.06 - 2015/08/31 - full, correct support for seeking API (Dougall Johnson) +// some crash fixes when out of memory or with corrupt files +// fix some inappropriately signed shifts +// 1.05 - 2015/04/19 - don't define __forceinline if it's redundant +// 1.04 - 2014/08/27 - fix missing const-correct case in API +// 1.03 - 2014/08/07 - warning fixes +// 1.02 - 2014/07/09 - declare qsort comparison as explicitly _cdecl in Windows +// 1.01 - 2014/06/18 - fix stb_vorbis_get_samples_float (interleaved was correct) +// 1.0 - 2014/05/26 - fix memory leaks; fix warnings; fix bugs in >2-channel; +// (API change) report sample rate for decode-full-file funcs +// +// See end of file for full version history. + + +////////////////////////////////////////////////////////////////////////////// +// +// HEADER BEGINS HERE +// + +#ifndef STB_VORBIS_INCLUDE_STB_VORBIS_H +#define STB_VORBIS_INCLUDE_STB_VORBIS_H + +#if defined(STB_VORBIS_NO_CRT) && !defined(STB_VORBIS_NO_STDIO) +#define STB_VORBIS_NO_STDIO 1 +#endif + +#ifndef STB_VORBIS_NO_STDIO +#include +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/////////// THREAD SAFETY + +// Individual stb_vorbis* handles are not thread-safe; you cannot decode from +// them from multiple threads at the same time. However, you can have multiple +// stb_vorbis* handles and decode from them independently in multiple thrads. + + +/////////// MEMORY ALLOCATION + +// normally stb_vorbis uses malloc() to allocate memory at startup, +// and alloca() to allocate temporary memory during a frame on the +// stack. (Memory consumption will depend on the amount of setup +// data in the file and how you set the compile flags for speed +// vs. size. In my test files the maximal-size usage is ~150KB.) +// +// You can modify the wrapper functions in the source (setup_malloc, +// setup_temp_malloc, temp_malloc) to change this behavior, or you +// can use a simpler allocation model: you pass in a buffer from +// which stb_vorbis will allocate _all_ its memory (including the +// temp memory). "open" may fail with a VORBIS_outofmem if you +// do not pass in enough data; there is no way to determine how +// much you do need except to succeed (at which point you can +// query get_info to find the exact amount required. yes I know +// this is lame). +// +// If you pass in a non-NULL buffer of the type below, allocation +// will occur from it as described above. Otherwise just pass NULL +// to use malloc()/alloca() + +typedef struct +{ + char *alloc_buffer; + int alloc_buffer_length_in_bytes; +} stb_vorbis_alloc; + + +/////////// FUNCTIONS USEABLE WITH ALL INPUT MODES + +typedef struct stb_vorbis stb_vorbis; + +typedef struct +{ + unsigned int sample_rate; + int channels; + + unsigned int setup_memory_required; + unsigned int setup_temp_memory_required; + unsigned int temp_memory_required; + + int max_frame_size; +} stb_vorbis_info; + +// get general information about the file +extern stb_vorbis_info stb_vorbis_get_info(stb_vorbis *f); + +// get the last error detected (clears it, too) +extern int stb_vorbis_get_error(stb_vorbis *f); + +// close an ogg vorbis file and free all memory in use +extern void stb_vorbis_close(stb_vorbis *f); + +// this function returns the offset (in samples) from the beginning of the +// file that will be returned by the next decode, if it is known, or -1 +// otherwise. after a flush_pushdata() call, this may take a while before +// it becomes valid again. +// NOT WORKING YET after a seek with PULLDATA API +extern int stb_vorbis_get_sample_offset(stb_vorbis *f); + +// returns the current seek point within the file, or offset from the beginning +// of the memory buffer. In pushdata mode it returns 0. +extern unsigned int stb_vorbis_get_file_offset(stb_vorbis *f); + +/////////// PUSHDATA API + +#ifndef STB_VORBIS_NO_PUSHDATA_API + +// this API allows you to get blocks of data from any source and hand +// them to stb_vorbis. you have to buffer them; stb_vorbis will tell +// you how much it used, and you have to give it the rest next time; +// and stb_vorbis may not have enough data to work with and you will +// need to give it the same data again PLUS more. Note that the Vorbis +// specification does not bound the size of an individual frame. + +extern stb_vorbis *stb_vorbis_open_pushdata( + const unsigned char * datablock, int datablock_length_in_bytes, + int *datablock_memory_consumed_in_bytes, + int *error, + const stb_vorbis_alloc *alloc_buffer); +// create a vorbis decoder by passing in the initial data block containing +// the ogg&vorbis headers (you don't need to do parse them, just provide +// the first N bytes of the file--you're told if it's not enough, see below) +// on success, returns an stb_vorbis *, does not set error, returns the amount of +// data parsed/consumed on this call in *datablock_memory_consumed_in_bytes; +// on failure, returns NULL on error and sets *error, does not change *datablock_memory_consumed +// if returns NULL and *error is VORBIS_need_more_data, then the input block was +// incomplete and you need to pass in a larger block from the start of the file + +extern int stb_vorbis_decode_frame_pushdata( + stb_vorbis *f, + const unsigned char *datablock, int datablock_length_in_bytes, + int *channels, // place to write number of float * buffers + float ***output, // place to write float ** array of float * buffers + int *samples // place to write number of output samples + ); +// decode a frame of audio sample data if possible from the passed-in data block +// +// return value: number of bytes we used from datablock +// +// possible cases: +// 0 bytes used, 0 samples output (need more data) +// N bytes used, 0 samples output (resynching the stream, keep going) +// N bytes used, M samples output (one frame of data) +// note that after opening a file, you will ALWAYS get one N-bytes,0-sample +// frame, because Vorbis always "discards" the first frame. +// +// Note that on resynch, stb_vorbis will rarely consume all of the buffer, +// instead only datablock_length_in_bytes-3 or less. This is because it wants +// to avoid missing parts of a page header if they cross a datablock boundary, +// without writing state-machiney code to record a partial detection. +// +// The number of channels returned are stored in *channels (which can be +// NULL--it is always the same as the number of channels reported by +// get_info). *output will contain an array of float* buffers, one per +// channel. In other words, (*output)[0][0] contains the first sample from +// the first channel, and (*output)[1][0] contains the first sample from +// the second channel. + +extern void stb_vorbis_flush_pushdata(stb_vorbis *f); +// inform stb_vorbis that your next datablock will not be contiguous with +// previous ones (e.g. you've seeked in the data); future attempts to decode +// frames will cause stb_vorbis to resynchronize (as noted above), and +// once it sees a valid Ogg page (typically 4-8KB, as large as 64KB), it +// will begin decoding the _next_ frame. +// +// if you want to seek using pushdata, you need to seek in your file, then +// call stb_vorbis_flush_pushdata(), then start calling decoding, then once +// decoding is returning you data, call stb_vorbis_get_sample_offset, and +// if you don't like the result, seek your file again and repeat. +#endif + + +////////// PULLING INPUT API + +#ifndef STB_VORBIS_NO_PULLDATA_API +// This API assumes stb_vorbis is allowed to pull data from a source-- +// either a block of memory containing the _entire_ vorbis stream, or a +// FILE * that you or it create, or possibly some other reading mechanism +// if you go modify the source to replace the FILE * case with some kind +// of callback to your code. (But if you don't support seeking, you may +// just want to go ahead and use pushdata.) + +#if !defined(STB_VORBIS_NO_STDIO) && !defined(STB_VORBIS_NO_INTEGER_CONVERSION) +extern int stb_vorbis_decode_filename(const char *filename, int *channels, int *sample_rate, short **output); +#endif +#if !defined(STB_VORBIS_NO_INTEGER_CONVERSION) +extern int stb_vorbis_decode_memory(const unsigned char *mem, int len, int *channels, int *sample_rate, short **output); +#endif +// decode an entire file and output the data interleaved into a malloc()ed +// buffer stored in *output. The return value is the number of samples +// decoded, or -1 if the file could not be opened or was not an ogg vorbis file. +// When you're done with it, just free() the pointer returned in *output. + +extern stb_vorbis * stb_vorbis_open_memory(const unsigned char *data, int len, + int *error, const stb_vorbis_alloc *alloc_buffer); +// create an ogg vorbis decoder from an ogg vorbis stream in memory (note +// this must be the entire stream!). on failure, returns NULL and sets *error + +#ifndef STB_VORBIS_NO_STDIO +extern stb_vorbis * stb_vorbis_open_filename(const char *filename, + int *error, const stb_vorbis_alloc *alloc_buffer); +// create an ogg vorbis decoder from a filename via fopen(). on failure, +// returns NULL and sets *error (possibly to VORBIS_file_open_failure). + +extern stb_vorbis * stb_vorbis_open_file(FILE *f, int close_handle_on_close, + int *error, const stb_vorbis_alloc *alloc_buffer); +// create an ogg vorbis decoder from an open FILE *, looking for a stream at +// the _current_ seek point (ftell). on failure, returns NULL and sets *error. +// note that stb_vorbis must "own" this stream; if you seek it in between +// calls to stb_vorbis, it will become confused. Morever, if you attempt to +// perform stb_vorbis_seek_*() operations on this file, it will assume it +// owns the _entire_ rest of the file after the start point. Use the next +// function, stb_vorbis_open_file_section(), to limit it. + +extern stb_vorbis * stb_vorbis_open_file_section(FILE *f, int close_handle_on_close, + int *error, const stb_vorbis_alloc *alloc_buffer, unsigned int len); +// create an ogg vorbis decoder from an open FILE *, looking for a stream at +// the _current_ seek point (ftell); the stream will be of length 'len' bytes. +// on failure, returns NULL and sets *error. note that stb_vorbis must "own" +// this stream; if you seek it in between calls to stb_vorbis, it will become +// confused. +#endif + +extern int stb_vorbis_seek_frame(stb_vorbis *f, unsigned int sample_number); +extern int stb_vorbis_seek(stb_vorbis *f, unsigned int sample_number); +// these functions seek in the Vorbis file to (approximately) 'sample_number'. +// after calling seek_frame(), the next call to get_frame_*() will include +// the specified sample. after calling stb_vorbis_seek(), the next call to +// stb_vorbis_get_samples_* will start with the specified sample. If you +// do not need to seek to EXACTLY the target sample when using get_samples_*, +// you can also use seek_frame(). + +extern void stb_vorbis_seek_start(stb_vorbis *f); +// this function is equivalent to stb_vorbis_seek(f,0) + +extern unsigned int stb_vorbis_stream_length_in_samples(stb_vorbis *f); +extern float stb_vorbis_stream_length_in_seconds(stb_vorbis *f); +// these functions return the total length of the vorbis stream + +extern int stb_vorbis_get_frame_float(stb_vorbis *f, int *channels, float ***output); +// decode the next frame and return the number of samples. the number of +// channels returned are stored in *channels (which can be NULL--it is always +// the same as the number of channels reported by get_info). *output will +// contain an array of float* buffers, one per channel. These outputs will +// be overwritten on the next call to stb_vorbis_get_frame_*. +// +// You generally should not intermix calls to stb_vorbis_get_frame_*() +// and stb_vorbis_get_samples_*(), since the latter calls the former. + +#ifndef STB_VORBIS_NO_INTEGER_CONVERSION +extern int stb_vorbis_get_frame_short_interleaved(stb_vorbis *f, int num_c, short *buffer, int num_shorts); +extern int stb_vorbis_get_frame_short (stb_vorbis *f, int num_c, short **buffer, int num_samples); +#endif +// decode the next frame and return the number of *samples* per channel. +// Note that for interleaved data, you pass in the number of shorts (the +// size of your array), but the return value is the number of samples per +// channel, not the total number of samples. +// +// The data is coerced to the number of channels you request according to the +// channel coercion rules (see below). You must pass in the size of your +// buffer(s) so that stb_vorbis will not overwrite the end of the buffer. +// The maximum buffer size needed can be gotten from get_info(); however, +// the Vorbis I specification implies an absolute maximum of 4096 samples +// per channel. + +// Channel coercion rules: +// Let M be the number of channels requested, and N the number of channels present, +// and Cn be the nth channel; let stereo L be the sum of all L and center channels, +// and stereo R be the sum of all R and center channels (channel assignment from the +// vorbis spec). +// M N output +// 1 k sum(Ck) for all k +// 2 * stereo L, stereo R +// k l k > l, the first l channels, then 0s +// k l k <= l, the first k channels +// Note that this is not _good_ surround etc. mixing at all! It's just so +// you get something useful. + +extern int stb_vorbis_get_samples_float_interleaved(stb_vorbis *f, int channels, float *buffer, int num_floats); +extern int stb_vorbis_get_samples_float(stb_vorbis *f, int channels, float **buffer, int num_samples); +// gets num_samples samples, not necessarily on a frame boundary--this requires +// buffering so you have to supply the buffers. DOES NOT APPLY THE COERCION RULES. +// Returns the number of samples stored per channel; it may be less than requested +// at the end of the file. If there are no more samples in the file, returns 0. + +#ifndef STB_VORBIS_NO_INTEGER_CONVERSION +extern int stb_vorbis_get_samples_short_interleaved(stb_vorbis *f, int channels, short *buffer, int num_shorts); +extern int stb_vorbis_get_samples_short(stb_vorbis *f, int channels, short **buffer, int num_samples); +#endif +// gets num_samples samples, not necessarily on a frame boundary--this requires +// buffering so you have to supply the buffers. Applies the coercion rules above +// to produce 'channels' channels. Returns the number of samples stored per channel; +// it may be less than requested at the end of the file. If there are no more +// samples in the file, returns 0. + +#endif + +//////// ERROR CODES + +enum STBVorbisError +{ + VORBIS__no_error, + + VORBIS_need_more_data=1, // not a real error + + VORBIS_invalid_api_mixing, // can't mix API modes + VORBIS_outofmem, // not enough memory + VORBIS_feature_not_supported, // uses floor 0 + VORBIS_too_many_channels, // STB_VORBIS_MAX_CHANNELS is too small + VORBIS_file_open_failure, // fopen() failed + VORBIS_seek_without_length, // can't seek in unknown-length file + + VORBIS_unexpected_eof=10, // file is truncated? + VORBIS_seek_invalid, // seek past EOF + + // decoding errors (corrupt/invalid stream) -- you probably + // don't care about the exact details of these + + // vorbis errors: + VORBIS_invalid_setup=20, + VORBIS_invalid_stream, + + // ogg errors: + VORBIS_missing_capture_pattern=30, + VORBIS_invalid_stream_structure_version, + VORBIS_continued_packet_flag_invalid, + VORBIS_incorrect_stream_serial_number, + VORBIS_invalid_first_page, + VORBIS_bad_packet_type, + VORBIS_cant_find_last_page, + VORBIS_seek_failed +}; + + +#ifdef __cplusplus +} +#endif + +#endif // STB_VORBIS_INCLUDE_STB_VORBIS_H +// +// HEADER ENDS HERE +// +////////////////////////////////////////////////////////////////////////////// diff --git a/Samples/CMakeLists.txt b/Samples/CMakeLists.txt new file mode 100755 index 0000000..e36ca31 --- /dev/null +++ b/Samples/CMakeLists.txt @@ -0,0 +1,14 @@ +# Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved. + +cmake_minimum_required(VERSION 3.10.2) + +project(MetaOpenXRSDK C CXX) +set_property(GLOBAL PROPERTY USE_FOLDERS ON) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_EXTENSIONS OFF) + +add_subdirectory(3rdParty) +add_subdirectory(SampleXrFramework) +add_subdirectory(XrSamples) diff --git a/Samples/SampleXrFramework/CMakeLists.txt b/Samples/SampleXrFramework/CMakeLists.txt new file mode 100755 index 0000000..2e7fb36 --- /dev/null +++ b/Samples/SampleXrFramework/CMakeLists.txt @@ -0,0 +1,174 @@ +# Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved. + +if(NOT TARGET OpenXR::openxr_loader) + find_package(OpenXR REQUIRED) +endif() + +if(ANDROID) + add_library(app_glue STATIC ${ANDROID_NDK}/sources/android/native_app_glue/android_native_app_glue.c) +endif() + +set(OPENXR_PREVIEW_HEADER ../../OpenXR) + +set(1STPARTY_PATH ../1stParty) +set(3RDPARTY_PATH ../3rdParty) + +file(GLOB_RECURSE CPP_SOURCES "Src/*.cpp") +file(GLOB_RECURSE C_SOURCES "Src/*.c") +add_library(samplexrframework STATIC ${CPP_SOURCES} ${C_SOURCES}) + + +# Add include directories +target_include_directories( + samplexrframework + PUBLIC + Src + ${1STPARTY_PATH}/OVR/Include + ${1STPARTY_PATH}/utilities/include + #### 3rd Party Headers from the OpenXR repo #### + ${3RDPARTY_PATH}/khronos/openxr/OpenXR-SDK/src/common + # Extra vendor-provided headers + ${OPENXR_PREVIEW_HEADER} +) + +target_link_libraries( + samplexrframework + PUBLIC + minizip + stb + ktx + OpenXR::openxr_loader +) + +target_compile_definitions( + samplexrframework PUBLIC $,OVR_BUILD_DEBUG=1,> +) + +if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang") + target_compile_options( + samplexrframework + PRIVATE $<$:-Wno-invalid-offsetof> + ) +endif() + +if(ANDROID) + target_include_directories(samplexrframework PUBLIC ${ANDROID_NDK}/sources/android/native_app_glue) + target_compile_definitions( + samplexrframework + PUBLIC + XR_OS_ANDROID=1 + XR_USE_PLATFORM_ANDROID=1 + XR_USE_GRAPHICS_API_OPENGL_ES=1 + ANDROID_NDK + $,OVR_BUILD_DEBUG=1,> + ) + target_compile_options( + samplexrframework + PUBLIC + -Werror + -Wall + -Wextra + -Wno-unused-parameter + -Wno-missing-field-initializers + ) + target_link_libraries( + samplexrframework + PRIVATE + android + EGL + GLESv3 + log + app_glue + ) + if(USE_ASAN) + target_compile_options( + samplexrframework + PUBLIC $,-fsanitize=address,> + $,-fno-omit-frame-pointer,> + ) + target_link_options( + samplexrframework PUBLIC + $,-fsanitize=address,> + ) + endif() +elseif(WIN32) + add_definitions(-DNOMINMAX) + if(MSVC) + target_compile_definitions(samplexrframework PUBLIC /wd4267) + endif() + + add_library( + samplecommon_win32gl STATIC + Src/Render/GlWrapperWin32.c + ) + + target_include_directories( + samplecommon_win32gl + PUBLIC + Src + ${3RDPARTY_PATH}/glext + ${1STPARTY_PATH}/utilities/include + ${1STPARTY_PATH}/utilities/include/vulkan + ${1STPARTY_PATH}/utilities/include/utils + ) + + target_link_libraries(samplecommon_win32gl PUBLIC opengl32 gdi32 user32) + target_link_libraries(samplexrframework PUBLIC samplecommon_win32gl) + target_compile_definitions( + samplexrframework PUBLIC XR_OS_WINDOWS=1 XR_USE_PLATFORM_WIN32=1 + XR_USE_GRAPHICS_API_OPENGL=1 + ) +endif() + +# ================= OpenGL ES Based Framework ==================== +# Create an interface target +add_library(samplecommon_gl INTERFACE) +# Add include directories +target_include_directories( + samplecommon_gl + INTERFACE + ${1STPARTY_PATH}/OVR/Include + ${3RDPARTY_PATH}/khronos/openxr/OpenXR-SDK/src/common + # Extra vendor-provided headers + ${OPENXR_PREVIEW_HEADER} +) +target_link_libraries(samplecommon_gl INTERFACE OpenXR::openxr_loader) +if(ANDROID) + target_include_directories(samplecommon_gl INTERFACE ${ANDROID_NDK}/sources/android/native_app_glue) + # Link the interface target to the dependency targets + target_link_libraries(samplecommon_gl INTERFACE + android + EGL + GLESv3 + log + app_glue + ) +elseif(WIN32) + target_compile_definitions(samplecommon_gl INTERFACE _USE_MATH_DEFINES) + target_link_libraries(samplecommon_gl INTERFACE samplecommon_win32gl) +endif() + +# ================= Android NativeActivity Based Framework ==================== +if(ANDROID) + # Create an interface target + add_library(native_activity_framework INTERFACE) + # Add include directories + target_include_directories( + native_activity_framework + INTERFACE + ${1STPARTY_PATH}/OVR/Include + ${3RDPARTY_PATH}/khronos/openxr/OpenXR-SDK/src/common + # Extra vendor-provided headers + ${OPENXR_PREVIEW_HEADER} + ${ANDROID_NDK}/sources/android/native_app_glue + ) + # Link the interface target to the dependency targets + target_link_libraries(native_activity_framework INTERFACE + android + EGL + GLESv3 + log + app_glue + OpenXR::openxr_loader + ) +endif() diff --git a/Samples/SampleXrFramework/Src/CompilerUtils.h b/Samples/SampleXrFramework/Src/CompilerUtils.h new file mode 100755 index 0000000..377d6bc --- /dev/null +++ b/Samples/SampleXrFramework/Src/CompilerUtils.h @@ -0,0 +1,20 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/******************************************************************************* + +Filename : CompilerUtils.h +Content : Macros and utilities for compile-time validation. +Created : March 20, 2018 +Authors : Jonathan Wright +Language : C++ + +*******************************************************************************/ + +#pragma once + +// included here with the intention of implementing a custom assert macro later. +#include + +#ifndef OVR_UNUSED +#define OVR_UNUSED(a) (void)(a) +#endif diff --git a/Samples/SampleXrFramework/Src/FrameParams.h b/Samples/SampleXrFramework/Src/FrameParams.h new file mode 100755 index 0000000..7a14054 --- /dev/null +++ b/Samples/SampleXrFramework/Src/FrameParams.h @@ -0,0 +1,138 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/******************************************************************************* + +Filename : FrameParams.h +Content : Common frame parameters +Created : July 2020 +Authors : Federico Schliemann +Language : C++ + +*******************************************************************************/ + +#pragma once + +#include "OVR_Math.h" + +#include "Render/SurfaceRender.h" +#include + +namespace OVRFW { + +struct ovrKeyEvent { + ovrKeyEvent(const int32_t keyCode, const int32_t action, const double t) + : KeyCode(keyCode), Action(action), Time(t) {} + int32_t KeyCode = 0; + int32_t Action = 0; + double Time = 0.0; +}; + +struct ovrTouchEvent { + ovrTouchEvent(const int32_t action, const int32_t x_, const int32_t y_, const double t) + : Action(action), x(x_), y(y_), Time(t) {} + int32_t Action = 0; + int32_t x = 0; + int32_t y = 0; + double Time = 0.0; +}; + +struct FrameMatrices { + OVR::Matrix4f CenterView; // the view transform for the point between the eyes + OVR::Matrix4f EyeView[2]; // the view transforms for each of the eyes + OVR::Matrix4f EyeProjection[2]; // the projection transforms for each of the eyes +}; + +struct ovrApplFrameIn { + /// accounting + int64_t FrameIndex = 0; + /// timing + double PredictedDisplayTime = 0.0; + double RealTimeInSeconds = 0.0; + float DeltaSeconds = 0.0f; + /// device config + float IPD = 0.065f; + float EyeHeight = 1.6750f; + int32_t RecenterCount = 0; + /// tracking + OVR::Posef HeadPose; + struct { + OVR::Matrix4f ViewMatrix; + OVR::Matrix4f ProjectionMatrix; + } Eye[2]; + /// controllers + OVR::Posef LeftRemotePose; + OVR::Posef LeftRemotePointPose; + OVR::Posef RightRemotePose; + OVR::Posef RightRemotePointPose; + /// joysticks + OVR::Vector2f LeftRemoteJoystick = {0.0f, 0.0f}; + OVR::Vector2f RightRemoteJoystick = {0.0f, 0.0f}; + bool LeftRemoteTracked = false; + bool RightRemoteTracked = false; + /// controller buttons + uint32_t AllButtons = 0u; + uint32_t AllTouches = 0u; + uint32_t LastFrameAllButtons = 0u; + uint32_t LastFrameAllTouches = 0u; + bool LeftRemoteIndexClick = false; + bool RightRemoteIndexClick = false; + float LeftRemoteIndexTrigger = 0.0f; + float RightRemoteIndexTrigger = 0.0; + float LeftRemoteGripTrigger = 0.0f; + float RightRemoteGripTrigger = 0.0; + + /// Headset + bool HeadsetIsMounted = true; + bool LastFrameHeadsetIsMounted = true; + /// Key/Touch android events + std::vector KeyEvents; + std::vector TouchEvents; + + /// Convenience APIs + static const int kButtonA = 1 << 0; + static const int kButtonB = 1 << 1; + static const int kButtonX = 1 << 2; + static const int kButtonY = 1 << 3; + static const int kButtonMenu = 1 << 4; + static const int kGripTrigger = 1 << 5; + static const int kTrigger = 1 << 6; + static const int kJoystick = 1 << 7; + /// touch + static const int kTouchJoystick = 1 << 8; + static const int kTouchTrigger = 1 << 9; + static const int kTouchThumbrest = 1 << 10; + + static const int kButtonLeftThumbStick = 1 << 11; + static const int kButtonRightThumbStick = 1 << 12; + + inline bool Clicked(const uint32_t& b) const { + const bool isDown = (b & AllButtons) != 0; + const bool wasDown = (b & LastFrameAllButtons) != 0; + return (wasDown && !isDown); + } + inline bool Touched(const uint32_t& t) const { + const bool isDown = (t & AllTouches) != 0; + const bool wasDown = (t & LastFrameAllTouches) != 0; + return (wasDown && !isDown); + } + inline bool HeadsetMounted() const { + return (!LastFrameHeadsetIsMounted && HeadsetIsMounted); + } + inline bool HeadsetUnMounted() const { + return (LastFrameHeadsetIsMounted && !HeadsetIsMounted); + } +}; + +class ovrApplFrameOut { + public: + ovrApplFrameOut() {} + explicit ovrApplFrameOut(const bool exitApp) : ExitApp(exitApp) {} + bool ExitApp = false; +}; + +struct ovrRendererOutput { + FrameMatrices FrameMatrices; // view and projection transforms + std::vector Surfaces; // list of surfaces to render +}; + +} // namespace OVRFW diff --git a/Samples/SampleXrFramework/Src/GUI/ActionComponents.cpp b/Samples/SampleXrFramework/Src/GUI/ActionComponents.cpp new file mode 100755 index 0000000..5d68522 --- /dev/null +++ b/Samples/SampleXrFramework/Src/GUI/ActionComponents.cpp @@ -0,0 +1,30 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/************************************************************************************ + +Filename : ActionComponents.h +Content : Misc. VRMenu Components to handle actions +Created : September 12, 2014 +Authors : Jonathan E. Wright + +*************************************************************************************/ + +#include "ActionComponents.h" +#include "Misc/Log.h" + +namespace OVRFW { + +//============================== +// OvrButton_OnUp::OnEvent_Impl +eMsgStatus OvrButton_OnUp::OnEvent_Impl( + OvrGuiSys& guiSys, + ovrApplFrameIn const& vrFrame, + VRMenuObject* self, + VRMenuEvent const& event) { + assert(event.EventType == VRMENU_EVENT_TOUCH_UP); + ALOG("Button id %lli clicked", ButtonId.Get()); + Menu->OnItemEvent(guiSys, vrFrame, ButtonId, event); + return MSG_STATUS_CONSUMED; +} + +} // namespace OVRFW diff --git a/Samples/SampleXrFramework/Src/GUI/ActionComponents.h b/Samples/SampleXrFramework/Src/GUI/ActionComponents.h new file mode 100755 index 0000000..d0ad87e --- /dev/null +++ b/Samples/SampleXrFramework/Src/GUI/ActionComponents.h @@ -0,0 +1,51 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/************************************************************************************ + +Filename : ActionComponents.h +Content : Misc. VRMenu Components to handle actions +Created : September 12, 2014 +Authors : Jonathan E. Wright + +*************************************************************************************/ + +#pragma once + +#include "VRMenuComponent.h" +#include "VRMenu.h" + +namespace OVRFW { + +class VRMenu; + +//============================================================== +// OvrButton_OnUp +// This is a generic component that forwards a touch up to a menu (normally its owner) +class OvrButton_OnUp : public VRMenuComponent_OnTouchUp { + public: + static const int TYPE_ID = 1010; + + OvrButton_OnUp(VRMenu* menu, VRMenuId_t const buttonId) + : VRMenuComponent_OnTouchUp(), Menu(menu), ButtonId(buttonId) {} + + void SetID(VRMenuId_t newButtonId) { + ButtonId = newButtonId; + } + + virtual int GetTypeId() const { + return TYPE_ID; + } + + private: + virtual eMsgStatus OnEvent_Impl( + OvrGuiSys& guiSys, + ovrApplFrameIn const& vrFrame, + VRMenuObject* self, + VRMenuEvent const& event); + + private: + VRMenu* Menu; // menu that holds the button + VRMenuId_t ButtonId; // id of the button this control handles +}; + +} // namespace OVRFW diff --git a/Samples/SampleXrFramework/Src/GUI/AnimComponents.cpp b/Samples/SampleXrFramework/Src/GUI/AnimComponents.cpp new file mode 100755 index 0000000..aa2ebaf --- /dev/null +++ b/Samples/SampleXrFramework/Src/GUI/AnimComponents.cpp @@ -0,0 +1,229 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/************************************************************************************ + +Filename : SurfaceAnim_Component.cpp +Content : A reusable component for animating VR menu object surfaces. +Created : Sept 23, 2014 +Authors : Jonathan E. Wright + +*************************************************************************************/ + +#include "AnimComponents.h" + +#include "GuiSys.h" +#include "VRMenuObject.h" +#include "VRMenuMgr.h" +#include "System.h" + +#include "OVR_Math.h" + +using OVR::Bounds3f; +using OVR::Matrix4f; +using OVR::Posef; +using OVR::Quatf; +using OVR::Vector2f; +using OVR::Vector3f; +using OVR::Vector4f; + +namespace OVRFW { + +//================================ +// OvrAnimComponent::OvrAnimComponent +OvrAnimComponent::OvrAnimComponent(float const framesPerSecond, bool const looping) + : VRMenuComponent(VRMenuEventFlags_t()), + BaseTime(0.0), + BaseFrame(0), + CurFrame(0), + FramesPerSecond(framesPerSecond), + AnimState(ANIMSTATE_PAUSED), + Looping(looping), + ForceVisibilityUpdate(false), + FractionalFrame(0.0f), + FloatFrame(0.0) {} + +//================================ +// OvrAnimComponent::OvrAnimComponent +OvrAnimComponent::OvrAnimComponent() : OvrAnimComponent(30.0f, true) {} + +//================================ +// OvrAnimComponent::Frame +eMsgStatus OvrAnimComponent::Frame( + OvrGuiSys& guiSys, + ovrApplFrameIn const& vrFrame, + VRMenuObject* self, + VRMenuEvent const& event) { + // only recalculate the current frame if playing + if (AnimState == ANIMSTATE_PLAYING) { + double timePassed = vrFrame.PredictedDisplayTime - BaseTime; + FloatFrame = timePassed * FramesPerSecond; + int totalFrames = (int)floor(FloatFrame); + FractionalFrame = static_cast(FloatFrame - static_cast(totalFrames)); + int numFrames = GetNumFrames(self); + int frame = BaseFrame + totalFrames; + CurFrame = !Looping ? std::clamp(frame, 0, numFrames - 1) : frame % numFrames; + SetFrameVisibilities(guiSys, vrFrame, self); + } else if (ForceVisibilityUpdate) { + SetFrameVisibilities(guiSys, vrFrame, self); + ForceVisibilityUpdate = false; + // don't update any more + RemoveEventFlags(VRMENU_EVENT_FRAME_UPDATE); + } + + return MSG_STATUS_ALIVE; +} + +//================================ +// OvrAnimComponent::SetFrame +void OvrAnimComponent::SetFrame(VRMenuObject* self, int const frameNum) { + assert(self != NULL); + CurFrame = std::clamp(frameNum, 0, GetNumFrames(self) - 1); + // we must reset the base frame and the current time so that the frame calculation + // remains correct if we're playing. If we're not playing, this will cause the + // next Play() to start from this frame. + BaseFrame = frameNum; + BaseTime = GetTimeInSeconds(); + ForceVisibilityUpdate = true; // make sure visibilities are set next frame update + + // make sure we run one frame + AddEventFlags(VRMENU_EVENT_FRAME_UPDATE); +} + +//================================ +// OvrAnimComponent::Play +void OvrAnimComponent::Play() { + AnimState = ANIMSTATE_PLAYING; + BaseTime = GetTimeInSeconds(); + // on a play we offset the base frame to the current frame so a resume from pause doesn't + // restart + BaseFrame = CurFrame; + + AddEventFlags(VRMENU_EVENT_FRAME_UPDATE); +} + +//================================ +// OvrAnimComponent::Pause +void OvrAnimComponent::Pause() { + AnimState = ANIMSTATE_PAUSED; + RemoveEventFlags(VRMENU_EVENT_FRAME_UPDATE); +} + +eMsgStatus OvrAnimComponent::OnEvent_Impl( + OvrGuiSys& guiSys, + ovrApplFrameIn const& vrFrame, + VRMenuObject* self, + VRMenuEvent const& event) { + switch (event.EventType) { + case VRMENU_EVENT_FRAME_UPDATE: + return Frame(guiSys, vrFrame, self, event); + default: + assert(!(bool)"Event flags mismatch!"); // the constructor is specifying a flag that's + // not handled + return MSG_STATUS_ALIVE; + } +} + +//============================================================================================== +// OvrSurfaceAnimComponent +//============================================================================================== + +const char* OvrSurfaceAnimComponent::TYPE_NAME = "OvrSurfaceAnimComponent"; + +OvrSurfaceAnimComponent* OvrSurfaceAnimComponent::Create(void* placementBuffer) { + if (placementBuffer != NULL) { + return new (placementBuffer) OvrSurfaceAnimComponent; + } + return new OvrSurfaceAnimComponent(); +} + +//================================ +// OvrSurfaceAnimComponent::OvrSurfaceAnimComponent +OvrSurfaceAnimComponent::OvrSurfaceAnimComponent( + float const framesPerSecond, + bool const looping, + int const surfacesPerFrame) + : OvrAnimComponent(framesPerSecond, looping), SurfacesPerFrame(surfacesPerFrame) {} + +//================================ +// OvrSurfaceAnimComponent::OvrSurfaceAnimComponent +OvrSurfaceAnimComponent::OvrSurfaceAnimComponent() : OvrSurfaceAnimComponent(30.0f, true, 1) {} + +//================================ +// OvrSurfaceAnimComponent::SetFrameVisibilities +void OvrSurfaceAnimComponent::SetFrameVisibilities( + OvrGuiSys& guiSys, + ovrApplFrameIn const& vrFrame, + VRMenuObject* self) const { + int minIndex = GetCurFrame() * SurfacesPerFrame; + int maxIndex = (GetCurFrame() + 1) * SurfacesPerFrame; + for (int i = 0; i < self->NumSurfaces(); ++i) { + self->SetSurfaceVisible(i, i >= minIndex && i < maxIndex); + } +} + +//================================ +// OvrSurfaceAnimComponent::NumFrames +int OvrSurfaceAnimComponent::GetNumFrames(VRMenuObject* self) const { + return self->NumSurfaces() / SurfacesPerFrame; +} + +//============================================================== +// OvrChildrenAnimComponent +// +const char* OvrTrailsAnimComponent::TYPE_NAME = "OvrChildrenAnimComponent"; + +OvrTrailsAnimComponent::OvrTrailsAnimComponent( + float const framesPerSecond, + bool const looping, + int const numFrames, + int const numFramesAhead, + int const numFramesBehind) + : OvrAnimComponent(framesPerSecond, looping), + NumFrames(numFrames), + FramesAhead(numFramesAhead), + FramesBehind(numFramesBehind) {} + +float OvrTrailsAnimComponent::GetAlphaForFrame(const int frame) const { + const int currentFrame = GetCurFrame(); + if (frame == currentFrame) + return 1.0f; + + const float alpha = GetFractionalFrame(); + const float aheadFactor = 1.0f / FramesAhead; + for (int ahead = 1; ahead <= FramesAhead; ++ahead) { + if (frame == (currentFrame + ahead)) { + return (alpha * aheadFactor) + (aheadFactor * (FramesAhead - ahead)); + } + } + + const float invAlpha = 1.0f - alpha; + const float behindFactor = 1.0f / FramesBehind; + for (int behind = 1; behind < FramesBehind; ++behind) { + if (frame == (currentFrame - behind)) { + return (invAlpha * behindFactor) + (behindFactor * (FramesBehind - behind)); + } + } + + return 0.0f; +} + +void OvrTrailsAnimComponent::SetFrameVisibilities( + OvrGuiSys& guiSys, + ovrApplFrameIn const& vrFrame, + VRMenuObject* self) const { + // LOG( "FracFrame: %f", GetFractionalFrame() ); + for (int i = 0; i < self->NumChildren(); ++i) { + menuHandle_t childHandle = self->GetChildHandleForIndex(i); + if (VRMenuObject* childObject = guiSys.GetVRMenuMgr().ToObject(childHandle)) { + Vector4f color = childObject->GetColor(); + color.w = GetAlphaForFrame(i); + childObject->SetColor(color); + } + } +} + +int OvrTrailsAnimComponent::GetNumFrames(VRMenuObject* self) const { + return NumFrames; +} + +} // namespace OVRFW diff --git a/Samples/SampleXrFramework/Src/GUI/AnimComponents.h b/Samples/SampleXrFramework/Src/GUI/AnimComponents.h new file mode 100755 index 0000000..4a2f85e --- /dev/null +++ b/Samples/SampleXrFramework/Src/GUI/AnimComponents.h @@ -0,0 +1,169 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/************************************************************************************ + +Filename : SurfaceAnim_Component.h +Content : A reusable component for animating VR menu object surfaces. +Created : Sept 23, 2014 +Authors : Jonathan E. Wright + +*************************************************************************************/ + +#pragma once + +#include "VRMenuComponent.h" +#include "FrameParams.h" + +namespace OVRFW { + +//============================================================== +// OvrAnimComponent +// +// A component that contains the logic for animating a +class OvrAnimComponent : public VRMenuComponent { + public: + enum eAnimState { ANIMSTATE_PAUSED, ANIMSTATE_PLAYING, ANIMSTATE_MAX }; + + OvrAnimComponent(float const framesPerSecond, bool const looping); + virtual ~OvrAnimComponent() {} + + virtual eMsgStatus OnEvent_Impl( + OvrGuiSys& guiSys, + ovrApplFrameIn const& vrFrame, + VRMenuObject* self, + VRMenuEvent const& event); + + eMsgStatus Frame( + OvrGuiSys& guiSys, + ovrApplFrameIn const& vrFrame, + VRMenuObject* self, + VRMenuEvent const& event); + + void SetFrame(VRMenuObject* self, int const frameNum); + void Play(); + void Pause(); + void SetLooping(bool const loop) { + Looping = loop; + } + bool GetLooping() const { + return Looping; + } + void SetRate(float const fps) { + FramesPerSecond = fps; + } + float GetRate() const { + return FramesPerSecond; + } + int GetCurFrame() const { + return CurFrame; + } + + protected: + virtual void SetFrameVisibilities( + OvrGuiSys& guiSys, + ovrApplFrameIn const& vrFrame, + VRMenuObject* self) const = 0; + virtual int GetNumFrames(VRMenuObject* self) const = 0; + + double GetBaseTime() const { + return BaseTime; + } + double GetFloatFrame() const { + return FractionalFrame; + } + float GetFractionalFrame() const { + return FractionalFrame; + } + + OvrAnimComponent(); // only derived classes can default construct + + private: + double BaseTime; // time when animation started or resumed + int BaseFrame; // base frame animation started on (for unpausing, this can be > 0 ) + int CurFrame; // current frame the animation is on + float FramesPerSecond; // the playback rate of the animatoin + eAnimState AnimState; // the current animation state + bool Looping; // true if the animation should loop + bool ForceVisibilityUpdate; // set to force visibilities to update even when paused (used by + // SetFrame ) + float FractionalFrame; // 0-1 + double FloatFrame; // Animation floating point time +}; + +//============================================================== +// OvrSurfaceAnimComponent +// +class OvrSurfaceAnimComponent : public OvrAnimComponent { + public: + static const char* TYPE_NAME; + static const int TYPE_ID = 0x41908ADE; + + static OvrSurfaceAnimComponent* Create(void* placementBuffer); + + // Surfaces per frame must be set to the number of surfaces that should be renderered + // for each frame. If only a single surface is shown per frame, surfacesPerFrame should be 1. + // If multiple surfaces (for instance one diffuse and one additive) are shown then + // surfacesPerFrame should be set to 2. + // Frame numbers never + OvrSurfaceAnimComponent( + float const framesPerSecond, + bool const looping, + int const surfacesPerFrame); + + virtual char const* GetTypeName() const { + return TYPE_NAME; + } + virtual int GetTypeId() const { + return TYPE_ID; + } + + protected: + virtual void SetFrameVisibilities( + OvrGuiSys& guiSys, + ovrApplFrameIn const& vrFrame, + VRMenuObject* self) const; + virtual int GetNumFrames(VRMenuObject* self) const; + + private: + int SurfacesPerFrame; // how many surfaces are shown per frame + + OvrSurfaceAnimComponent(); +}; + +//============================================================== +// OvrChildrenAnimComponent +// +class OvrTrailsAnimComponent : public OvrAnimComponent { + public: + static const char* TYPE_NAME; + static const int TYPE_ID = 0xAD3402AF; + + OvrTrailsAnimComponent( + float const framesPerSecond, + bool const looping, + int const numFrames, + int const numFramesAhead, + int const numFramesBehind); + + virtual const char* GetTypeName() const { + return TYPE_NAME; + } + virtual int GetTypeId() const { + return TYPE_ID; + } + + protected: + virtual void SetFrameVisibilities( + OvrGuiSys& guiSys, + ovrApplFrameIn const& vrFrame, + VRMenuObject* self) const; + virtual int GetNumFrames(VRMenuObject* self) const; + + private: + float GetAlphaForFrame(const int frame) const; + int NumFrames; + int FramesAhead; + int FramesBehind; +}; + +} // namespace OVRFW diff --git a/Samples/SampleXrFramework/Src/GUI/CollisionPrimitive.cpp b/Samples/SampleXrFramework/Src/GUI/CollisionPrimitive.cpp new file mode 100755 index 0000000..f87d689 --- /dev/null +++ b/Samples/SampleXrFramework/Src/GUI/CollisionPrimitive.cpp @@ -0,0 +1,331 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/************************************************************************************ + +Filename : CollisionPrimitive.cpp +Content : Generic collision class supporting ray / triangle intersection. +Created : September 10, 2014 +Authors : Jonathan E. Wright + +*************************************************************************************/ + +#include "CollisionPrimitive.h" + +// #include "OVR_Geometry.h" +#include "Render/DebugLines.h" +#include "Misc/Log.h" + +#include + +using OVR::Bounds3f; +using OVR::Matrix4f; +using OVR::Posef; +using OVR::Quatf; +using OVR::Vector2f; +using OVR::Vector3f; +using OVR::Vector4f; + +inline bool Intersect_RayBounds( + const Vector3f& rayStart, + const Vector3f& rayDir, + const Vector3f& mins, + const Vector3f& maxs, + float& t0, + float& t1) { + const float rcpDirX = (fabsf(rayDir.x) > MATH_FLOAT_SMALLEST_NON_DENORMAL) + ? (1.0f / rayDir.x) + : MATH_FLOAT_HUGE_NUMBER; + const float rcpDirY = (fabsf(rayDir.y) > MATH_FLOAT_SMALLEST_NON_DENORMAL) + ? (1.0f / rayDir.y) + : MATH_FLOAT_HUGE_NUMBER; + const float rcpDirZ = (fabsf(rayDir.z) > MATH_FLOAT_SMALLEST_NON_DENORMAL) + ? (1.0f / rayDir.z) + : MATH_FLOAT_HUGE_NUMBER; + + const float sX = (mins.x - rayStart.x) * rcpDirX; + const float sY = (mins.y - rayStart.y) * rcpDirY; + const float sZ = (mins.z - rayStart.z) * rcpDirZ; + + const float tX = (maxs.x - rayStart.x) * rcpDirX; + const float tY = (maxs.y - rayStart.y) * rcpDirY; + const float tZ = (maxs.z - rayStart.z) * rcpDirZ; + + const float minX = std::min(sX, tX); + const float minY = std::min(sY, tY); + const float minZ = std::min(sZ, tZ); + + const float maxX = std::max(sX, tX); + const float maxY = std::max(sY, tY); + const float maxZ = std::max(sZ, tZ); + + t0 = std::max(minX, std::max(minY, minZ)); + t1 = std::min(maxX, std::min(maxY, maxZ)); + + return (t0 <= t1); +} + +bool Intersect_RayTriangle( + const Vector3f& rayStart, + const Vector3f& rayDir, + const Vector3f& v0, + const Vector3f& v1, + const Vector3f& v2, + float& t0, + float& u, + float& v) { + assert(rayDir.IsNormalized()); + + const Vector3f edge1 = v1 - v0; + const Vector3f edge2 = v2 - v0; + + const Vector3f tv = rayStart - v0; + const Vector3f pv = rayDir.Cross(edge2); + const Vector3f qv = tv.Cross(edge1); + const float det = edge1.Dot(pv); + + // If the determinant is negative then the triangle is backfacing. + if (det <= 0.0f) { + return false; + } + + // This code has been modified to only perform a floating-point + // division if the ray actually hits the triangle. If back facing + // triangles are not culled then the sign of 's' and 't' need to + // be flipped. This can be accomplished by multiplying the values + // with the determinant instead of the reciprocal determinant. + + const float s = tv.Dot(pv); + const float t = rayDir.Dot(qv); + + if (s >= 0.0f && s <= det) { + if (t >= 0.0f && s + t <= det) { + // If the determinant is almost zero then the ray lies in the triangle plane. + // This comparison is done last because it is usually rare for + // the ray to lay in the triangle plane. + if (fabsf(det) > MATH_FLOAT_SMALLEST_NON_DENORMAL) { + const float rcpDet = 1.0f / det; + t0 = edge2.Dot(qv) * rcpDet; + u = s * rcpDet; + v = t * rcpDet; + return true; + } + } + } + + return false; +} + +namespace OVRFW { + +//============================== +// OvrCollisionPrimitive::IntersectRayBounds +bool OvrCollisionPrimitive::IntersectRayBounds( + Vector3f const& start, + Vector3f const& dir, + Vector3f const& scale, + ContentFlags_t const testContents, + float& t0, + float& t1) const { + if (!(testContents & GetContents())) { + return false; + } + + return IntersectRayBounds(start, dir, scale, t0, t1); +} + +//============================== +// OvrCollisionPrimitive::IntersectRayBounds +bool OvrCollisionPrimitive::IntersectRayBounds( + Vector3f const& start, + Vector3f const& dir, + Vector3f const& scale, + float& t0, + float& t1) const { + Bounds3f scaledBounds = Bounds * scale; + if (scaledBounds.Contains(start, 0.1f)) { + return true; + } + + Intersect_RayBounds(start, dir, scaledBounds.GetMins(), scaledBounds.GetMaxs(), t0, t1); + + return t0 >= 0.0f && t1 >= 0.0f && t1 >= t0; +} + +//============================================================================================== +// OvrCollisionPrimitive + +//============================== +// OvrCollisionPrimitive::OvrCollisionPrimitive +OvrCollisionPrimitive::~OvrCollisionPrimitive() {} + +//============================================================================================== +// OvrTriCollisionPrimitive + +//============================== +// OvrTriCollisionPrimitive::OvrTriCollisionPrimitive +OvrTriCollisionPrimitive::OvrTriCollisionPrimitive() {} + +//============================== +// OvrTriCollisionPrimitive::OvrTriCollisionPrimitive +OvrTriCollisionPrimitive::OvrTriCollisionPrimitive( + std::vector const& vertices, + std::vector const& indices, + std::vector const& uvs, + ContentFlags_t const contents) + : OvrCollisionPrimitive(contents) { + Init(vertices, indices, uvs, contents); +} + +//============================== +// OvrTriCollisionPrimitive::~OvrTriCollisionPrimitive +OvrTriCollisionPrimitive::~OvrTriCollisionPrimitive() {} + +//============================== +// OvrTriCollisionPrimitive::Init +void OvrTriCollisionPrimitive::Init( + std::vector const& vertices, + std::vector const& indices, + std::vector const& uvs, + ContentFlags_t const contents) { + Vertices = vertices; + Indices = indices; + + if (uvs.size() != vertices.size()) { + UVs.resize(vertices.size()); + UVs.assign(vertices.size(), Vector2f(0.0f)); + } else { + UVs = uvs; + } + + SetContents(contents); + + // calculate the bounds + Bounds3f b; + b.Clear(); + for (const auto& v : vertices) { + b.AddPoint(v); + } + + SetBounds(b); +} + +//============================== +// OvrTriCollisionPrimitive::IntersectRay +bool OvrTriCollisionPrimitive::IntersectRay( + Vector3f const& start, + Vector3f const& dir, + Posef const& pose, + Vector3f const& scale, + ContentFlags_t const testContents, + OvrCollisionResult& result) const { + if (!(testContents & GetContents())) { + result = OvrCollisionResult(); + return false; + } + + // transform the ray into local space + Vector3f localStart = (start - pose.Translation) * pose.Rotation.Inverted(); + Vector3f localDir = dir * pose.Rotation.Inverted(); + + return IntersectRay(localStart, localDir, scale, testContents, result); +} + +//============================== +// OvrTriCollisionPrimitive::IntersectRay +// the ray should already be in local space +bool OvrTriCollisionPrimitive::IntersectRay( + Vector3f const& localStart, + Vector3f const& localDir, + Vector3f const& scale, + ContentFlags_t const testContents, + OvrCollisionResult& result) const { + if (!(testContents & GetContents())) { + result = OvrCollisionResult(); + return false; + } + + // test vs bounds + float t0; + float t1; + if (!IntersectRayBounds(localStart, localDir, scale, t0, t1)) { + return false; + } + + result.TriIndex = -1; + for (int i = 0; i < static_cast(Indices.size()); i += 3) { + float t_; + float u_; + float v_; + Vector3f verts[3]; + verts[0] = Vertices[Indices[i]] * scale; + verts[1] = Vertices[Indices[i + 1]] * scale; + verts[2] = Vertices[Indices[i + 2]] * scale; + + float diff = fabsf(localDir.LengthSq() - 1.0f); + if (diff > OVR::Mathf::Tolerance()) { + ALOG( + "!rayDir.IsNormalized() - ( %.4f, %.4f, %.4f ), len = %.8f, diff = %.8f", + localDir.x, + localDir.y, + localDir.z, + localDir.Length(), + diff); + assert(!(bool)"IsNormalized()"); + } + + if (Intersect_RayTriangle(localStart, localDir, verts[0], verts[1], verts[2], t_, u_, v_)) { + if (t_ < result.t) { + result.t = t_; + + result.TriIndex = i / 3; + result.uv = UVs[Indices[i + 0]] * (1.0f - u_ - v_) + UVs[Indices[i + 1]] * u_ + + UVs[Indices[i + 2]] * v_; + + result.Barycentric = Vector2f(u_, v_); + } + } + } + return result.TriIndex >= 0; +} + +//============================== +// OvrTriCollisionPrimitive::DebugRender +void OvrTriCollisionPrimitive::DebugRender( + OvrDebugLines& debugLines, + Posef& pose, + Vector3f const& scale, + bool const showNormals) const { + Bounds3f bounds = GetBounds() * scale; + debugLines.AddBounds(pose, bounds, Vector4f(1.0f, 0.5f, 0.0f, 1.0f)); + + Vector4f color(1.0f, 0.0f, 1.0f, 1.0f); + for (int i = 0; i < static_cast(Indices.size()); i += 3) { + int i1 = Indices[i + 0]; + int i2 = Indices[i + 1]; + int i3 = Indices[i + 2]; + Vector3f v1 = pose.Translation + (pose.Rotation * Vertices[i1] * scale); + Vector3f v2 = pose.Translation + (pose.Rotation * Vertices[i2] * scale); + Vector3f v3 = pose.Translation + (pose.Rotation * Vertices[i3] * scale); + debugLines.AddLine(v1, v2, color, color, 0, true); + debugLines.AddLine(v2, v3, color, color, 0, true); + debugLines.AddLine(v3, v1, color, color, 0, true); + + if (showNormals) { + Vector3f edge1 = v2 - v1; + Vector3f edge2 = v3 - v1; + Vector3f normal = edge1.Cross(edge2).Normalized(); + Vector3f edgeCenter = v1 + (edge1 * 0.5f); + Vector3f bisection = v3 - edgeCenter; + Vector3f center = edgeCenter + bisection * 0.5f; + debugLines.AddLine( + center, + center + normal * 0.08f, + Vector4f(0.0f, 1.0f, 0.0f, 1.0f), + Vector4f(0.0f, 1.0f, 0.0f, 1.0f), + 0, + false); + } + } +} + +} // namespace OVRFW diff --git a/Samples/SampleXrFramework/Src/GUI/CollisionPrimitive.h b/Samples/SampleXrFramework/Src/GUI/CollisionPrimitive.h new file mode 100755 index 0000000..e883b86 --- /dev/null +++ b/Samples/SampleXrFramework/Src/GUI/CollisionPrimitive.h @@ -0,0 +1,156 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/************************************************************************************ + +Filename : CollisionPrimitive.h +Content : Generic collision class supporting ray / triangle intersection. +Created : September 10, 2014 +Authors : Jonathan E. Wright + +*************************************************************************************/ + +#pragma once + +#include +#include + +#include "OVR_BitFlags.h" + +#include "Render/GlGeometry.h" // For TriangleIndex + +namespace OVRFW { + +class OvrDebugLines; + +enum eContentFlags { CONTENT_NONE = 0, CONTENT_SOLID, CONTENT_ALL = 0x7fffffff }; + +typedef OVR::BitFlagsT ContentFlags_t; + +//============================================================== +// OvrCollisionResult +// Structure that holds the result of a collision +class OvrCollisionResult { + public: + OvrCollisionResult() : t(FLT_MAX), uv(0.0f), TriIndex(-1) {} + + float t; // fraction along line where the intersection occurred + OVR::Vector2f uv; // texture coordinate of intersection + std::int64_t TriIndex; // index of triangle hit (local to collider) + OVR::Vector2f Barycentric; // barycentric coordinates intersection +}; + +//============================================================== +// OvrCollisionPrimitive +// Base class for a collision primitive. +class OvrCollisionPrimitive { + public: + OvrCollisionPrimitive() {} + OvrCollisionPrimitive(ContentFlags_t const contents) : Contents(contents) {} + virtual ~OvrCollisionPrimitive(); + + virtual bool IntersectRay( + OVR::Vector3f const& start, + OVR::Vector3f const& dir, + OVR::Posef const& pose, + OVR::Vector3f const& scale, + ContentFlags_t const testContents, + OvrCollisionResult& result) const = 0; + // the ray should already be in local space + virtual bool IntersectRay( + OVR::Vector3f const& localStart, + OVR::Vector3f const& localDir, + OVR::Vector3f const& scale, + ContentFlags_t const testContents, + OvrCollisionResult& result) const = 0; + + // test for ray intersection against only the AAB + bool IntersectRayBounds( + OVR::Vector3f const& start, + OVR::Vector3f const& dir, + OVR::Vector3f const& scale, + ContentFlags_t const testContents, + float& t0, + float& t1) const; + + virtual void DebugRender( + OvrDebugLines& debugLines, + OVR::Posef& pose, + OVR::Vector3f const& scale, + bool const showNormals) const = 0; + + ContentFlags_t GetContents() const { + return Contents; + } + void SetContents(ContentFlags_t const contents) { + Contents = contents; + } + + OVR::Bounds3f const& GetBounds() const { + return Bounds; + } + void SetBounds(OVR::Bounds3f const& bounds) { + Bounds = bounds; + } + + protected: + bool IntersectRayBounds( + OVR::Vector3f const& start, + OVR::Vector3f const& dir, + OVR::Vector3f const& scale, + float& t0, + float& t1) const; + + private: + ContentFlags_t Contents; // flags dictating what can hit this primitive + OVR::Bounds3f Bounds; // Axial-aligned bounds of the primitive +}; + +//============================================================== +// OvrTriCollisionPrimitive +// Collider that handles collision vs. polygons and stores those polygons itself. +class OvrTriCollisionPrimitive : public OvrCollisionPrimitive { + public: + OvrTriCollisionPrimitive(); + OvrTriCollisionPrimitive( + std::vector const& vertices, + std::vector const& indices, + std::vector const& uvs, + ContentFlags_t const contents); + + ~OvrTriCollisionPrimitive() override; + + void Init( + std::vector const& vertices, + std::vector const& indices, + std::vector const& uvs, + ContentFlags_t const contents); + + virtual bool IntersectRay( + OVR::Vector3f const& start, + OVR::Vector3f const& dir, + OVR::Posef const& pose, + OVR::Vector3f const& scale, + ContentFlags_t const testContents, + OvrCollisionResult& result) const override; + + // the ray should already be in local space + virtual bool IntersectRay( + OVR::Vector3f const& localStart, + OVR::Vector3f const& localDir, + OVR::Vector3f const& scale, + ContentFlags_t const testContents, + OvrCollisionResult& result) const override; + + virtual void DebugRender( + OvrDebugLines& debugLines, + OVR::Posef& pose, + OVR::Vector3f const& scale, + bool const showNormals) const override; + + private: + std::vector Vertices; // vertices for all triangles + std::vector Indices; // indices indicating which vertices make up each triangle + std::vector UVs; // uvs for each vertex +}; + +} // namespace OVRFW diff --git a/Samples/SampleXrFramework/Src/GUI/DefaultComponent.cpp b/Samples/SampleXrFramework/Src/GUI/DefaultComponent.cpp new file mode 100755 index 0000000..173e02a --- /dev/null +++ b/Samples/SampleXrFramework/Src/GUI/DefaultComponent.cpp @@ -0,0 +1,219 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/************************************************************************************ + +Filename : DefaultComponent.h +Content : A default menu component that handles basic actions most menu items need. +Created : July 25, 2014 +Authors : Jonathan E. Wright + +*************************************************************************************/ + +#include "DefaultComponent.h" + +#include "GuiSys.h" + +#include "AnimComponents.h" + +using OVR::Bounds3f; +using OVR::Matrix4f; +using OVR::Posef; +using OVR::Quatf; +using OVR::Vector2f; +using OVR::Vector3f; +using OVR::Vector4f; + +namespace OVRFW { + +//============================== +// OvrDefaultComponent:: +OvrDefaultComponent::OvrDefaultComponent( + Vector3f const& hilightOffset, + float const hilightScale, + float const fadeDuration, + float const fadeDelay, + Vector4f const& textNormalColor, + Vector4f const& textHilightColor, + bool const useSurfaceAnimator, + const bool noHilight) + : VRMenuComponent( + VRMenuEventFlags_t(VRMENU_EVENT_TOUCH_DOWN) | VRMENU_EVENT_TOUCH_UP | + VRMENU_EVENT_FOCUS_GAINED | VRMENU_EVENT_FOCUS_LOST | VRMENU_EVENT_INIT), + HilightFader(0.0f), + StartFadeInTime(-1.0), + StartFadeOutTime(-1.0), + HilightOffset(hilightOffset), + HilightScale(hilightScale), + FadeDuration(fadeDuration), + FadeDelay(fadeDelay), + TextNormalColor(textNormalColor), + TextHilightColor(textHilightColor), + SuppressText(false), + UseSurfaceAnimator(useSurfaceAnimator), + NoHilight(noHilight) {} + +//============================== +// OvrDefaultComponent::OnEvent_Impl +eMsgStatus OvrDefaultComponent::OnEvent_Impl( + OvrGuiSys& guiSys, + ovrApplFrameIn const& vrFrame, + VRMenuObject* self, + VRMenuEvent const& event) { + switch (event.EventType) { + case VRMENU_EVENT_INIT: + // run one frame to set the object initial state + return Frame(guiSys, vrFrame, self, event); + case VRMENU_EVENT_FRAME_UPDATE: + return Frame(guiSys, vrFrame, self, event); + case VRMENU_EVENT_FOCUS_GAINED: + return FocusGained(guiSys, vrFrame, self, event); + case VRMENU_EVENT_FOCUS_LOST: + return FocusLost(guiSys, vrFrame, self, event); + case VRMENU_EVENT_TOUCH_DOWN: + // DownSoundLimiter.PlaySoundEffect( guiSys, "", 0.1 ); + return MSG_STATUS_ALIVE; + case VRMENU_EVENT_TOUCH_UP: + UpSoundLimiter.PlaySoundEffect(guiSys, "sv_panel_touch_up", 0.1); + return MSG_STATUS_ALIVE; + default: + assert(!(bool)"Event flags mismatch!"); + return MSG_STATUS_ALIVE; + } +} + +//============================== +// OvrDefaultComponent::Frame +eMsgStatus OvrDefaultComponent::Frame( + OvrGuiSys& guiSys, + ovrApplFrameIn const& vrFrame, + VRMenuObject* self, + VRMenuEvent const& event) { + double t = vrFrame.PredictedDisplayTime; + if (StartFadeInTime >= 0.0 && t >= StartFadeInTime) { + HilightFader.StartFadeIn(); + StartFadeInTime = -1.0; + } else if (StartFadeOutTime >= 0.0 && t > StartFadeOutTime) { + HilightFader.StartFadeOut(); + StartFadeOutTime = -1.0; + } + + float const fadeRate = 1.0f / FadeDuration; + HilightFader.Update(fadeRate, vrFrame.DeltaSeconds); + if (HilightFader.GetFadeState() == Fader::FADE_NONE && StartFadeInTime <= 0.0 && + StartFadeOutTime <= 0.0) { + RemoveEventFlags(VRMENU_EVENT_FRAME_UPDATE); + } + + float const hilightAlpha = HilightFader.GetFinalAlpha(); + Vector3f offset = HilightOffset * hilightAlpha; + self->SetHilightPose(Posef(Quatf(), offset)); + + if (!NoHilight) { + if (UseSurfaceAnimator) { + OvrSurfaceAnimComponent* sac = self->GetComponentById(); + if (sac != NULL) { + sac->SetFrame(self, hilightAlpha >= 0.5f ? 1 : 0); + } + } else { + // set the highlight alpha on the first visible additive surface. If an animation + // component is also running, some surfaces may be set to invisible. + int additiveSurfIndex = + self->FindSurfaceWithTextureType(SURFACE_TEXTURE_ADDITIVE, true, true); + if (additiveSurfIndex >= 0) { + Vector4f surfColor = self->GetSurfaceColor(additiveSurfIndex); + surfColor.w = hilightAlpha; + self->SetSurfaceColor(additiveSurfIndex, surfColor); + } + } + } + + float const scale = ((HilightScale - 1.0f) * hilightAlpha) + 1.0f; + self->SetHilightScale(scale); + + if (SuppressText) { + self->SetTextColor(Vector4f(0.0f)); + } else { + Vector4f colorDelta = TextHilightColor - TextNormalColor; + Vector4f curColor = TextNormalColor + (colorDelta * hilightAlpha); + self->SetTextColor(curColor); + } + + return MSG_STATUS_ALIVE; +} + +//============================== +// OvrDefaultComponent::FocusGained +eMsgStatus OvrDefaultComponent::FocusGained( + OvrGuiSys& guiSys, + ovrApplFrameIn const& vrFrame, + VRMenuObject* self, + VRMenuEvent const& event) { + // set the hilight flag + self->SetHilighted(true); + GazeOverSoundLimiter.PlaySoundEffect(guiSys, "sv_focusgained", 0.1); + + StartFadeOutTime = -1.0; + StartFadeInTime = FadeDelay + vrFrame.PredictedDisplayTime; + + AddEventFlags(VRMENU_EVENT_FRAME_UPDATE); + + return MSG_STATUS_ALIVE; +} + +//============================== +// OvrDefaultComponent::FocusLost +eMsgStatus OvrDefaultComponent::FocusLost( + OvrGuiSys& guiSys, + ovrApplFrameIn const& vrFrame, + VRMenuObject* self, + VRMenuEvent const& event) { + // clear the hilight flag + self->SetHilighted(false); + + StartFadeInTime = -1.0; + StartFadeOutTime = FadeDelay + vrFrame.PredictedDisplayTime; + + AddEventFlags(VRMENU_EVENT_FRAME_UPDATE); + + return MSG_STATUS_ALIVE; +} + +const char* OvrSurfaceToggleComponent::TYPE_NAME = "OvrSurfaceToggleComponent"; + +//============================== +// OvrSurfaceToggleComponent::OnEvent_Impl +eMsgStatus OvrSurfaceToggleComponent::OnEvent_Impl( + OvrGuiSys& guiSys, + ovrApplFrameIn const& vrFrame, + VRMenuObject* self, + VRMenuEvent const& event) { + switch (event.EventType) { + case VRMENU_EVENT_FRAME_UPDATE: + return Frame(guiSys, vrFrame, self, event); + default: + assert(!(bool)"Event flags mismatch!"); + return MSG_STATUS_ALIVE; + } +} + +//============================== +// OvrSurfaceToggleComponent::FocusLost +eMsgStatus OvrSurfaceToggleComponent::Frame( + OvrGuiSys& guiSys, + ovrApplFrameIn const& vrFrame, + VRMenuObject* self, + VRMenuEvent const& event) { + const int numSurfaces = self->NumSurfaces(); + for (int i = 0; i < numSurfaces; ++i) { + self->SetSurfaceVisible(i, false); + } + + if (self->IsHilighted()) { + self->SetSurfaceVisible(GroupIndex + 1, true); + } else { + self->SetSurfaceVisible(GroupIndex, true); + } + return MSG_STATUS_ALIVE; +} + +} // namespace OVRFW diff --git a/Samples/SampleXrFramework/Src/GUI/DefaultComponent.h b/Samples/SampleXrFramework/Src/GUI/DefaultComponent.h new file mode 100755 index 0000000..5dde490 --- /dev/null +++ b/Samples/SampleXrFramework/Src/GUI/DefaultComponent.h @@ -0,0 +1,125 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/************************************************************************************ + +Filename : DefaultComponent.h +Content : A default menu component that handles basic actions most menu items need. +Created : July 25, 2014 +Authors : Jonathan E. Wright + +*************************************************************************************/ + +#pragma once + +#include "VRMenuComponent.h" +#include "Fader.h" +#include "FrameParams.h" + +namespace OVRFW { + +//============================================================== +// OvrDefaultComponent +class OvrDefaultComponent : public VRMenuComponent { + public: + static const int TYPE_ID = 1; + + OvrDefaultComponent( + OVR::Vector3f const& hilightOffset = OVR::Vector3f(0.0f, 0.0f, 0.05f), + float const hilightScale = 1.05f, + float const fadeDuration = 0.125f, + float const fadeDelay = 0.125f, + OVR::Vector4f const& textNormalColor = OVR::Vector4f(1.0f), + OVR::Vector4f const& textHilightColor = OVR::Vector4f(1.0f), + bool const useSurfaceAnimator = false, + const bool noHilight = false); + + virtual int GetTypeId() const { + return TYPE_ID; + } + + void SetSuppressText(bool const suppress) { + SuppressText = suppress; + } + + private: + // private variables + // We may actually want these to be static... + ovrSoundLimiter GazeOverSoundLimiter; + ovrSoundLimiter DownSoundLimiter; + ovrSoundLimiter UpSoundLimiter; + + SineFader HilightFader; + double StartFadeInTime; + double StartFadeOutTime; + OVR::Vector3f HilightOffset; + float HilightScale; + float FadeDuration; + float FadeDelay; + OVR::Vector4f TextNormalColor; + OVR::Vector4f TextHilightColor; + bool SuppressText; // true if text should not be faded in + bool UseSurfaceAnimator; // if true, use the surface animator to select a hilight surface + // instead of an additive pass + bool NoHilight; // don't change the hilight state -- something else will do it + + private: + virtual eMsgStatus OnEvent_Impl( + OvrGuiSys& guiSys, + ovrApplFrameIn const& vrFrame, + VRMenuObject* self, + VRMenuEvent const& event); + + eMsgStatus Frame( + OvrGuiSys& guiSys, + ovrApplFrameIn const& vrFrame, + VRMenuObject* self, + VRMenuEvent const& event); + eMsgStatus FocusGained( + OvrGuiSys& guiSys, + ovrApplFrameIn const& vrFrame, + VRMenuObject* self, + VRMenuEvent const& event); + eMsgStatus FocusLost( + OvrGuiSys& guiSys, + ovrApplFrameIn const& vrFrame, + VRMenuObject* self, + VRMenuEvent const& event); +}; + +//============================================================== +// OvrSurfaceToggleComponent +// Toggles surfaced based on pair index, the current is default state, +1 is hover +class OvrSurfaceToggleComponent : public VRMenuComponent { + public: + static const char* TYPE_NAME; + OvrSurfaceToggleComponent() + : VRMenuComponent(VRMenuEventFlags_t(VRMENU_EVENT_FRAME_UPDATE)), GroupIndex(0) {} + + void SetGroupIndex(const int index) { + GroupIndex = index; + } + int GetGroupIndex() const { + return GroupIndex; + } + + virtual const char* GetTypeName() const { + return TYPE_NAME; + } + + private: + virtual eMsgStatus OnEvent_Impl( + OvrGuiSys& guiSys, + ovrApplFrameIn const& vrFrame, + VRMenuObject* self, + VRMenuEvent const& event); + + eMsgStatus Frame( + OvrGuiSys& guiSys, + ovrApplFrameIn const& vrFrame, + VRMenuObject* self, + VRMenuEvent const& event); + + int GroupIndex; +}; + +} // namespace OVRFW diff --git a/Samples/SampleXrFramework/Src/GUI/Fader.cpp b/Samples/SampleXrFramework/Src/GUI/Fader.cpp new file mode 100755 index 0000000..edc779f --- /dev/null +++ b/Samples/SampleXrFramework/Src/GUI/Fader.cpp @@ -0,0 +1,151 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/************************************************************************************ + +Filename : Fader.cpp +Content : Utility classes for animation based on alpha values +Created : July 25, 2014 +Authors : Jonathan E. Wright + +*************************************************************************************/ + +#include "Fader.h" + +#include "OVR_Math.h" +#include "Misc/Log.h" + +#include + +namespace OVRFW { + +//====================================================================================== +// Fader +//====================================================================================== + +//============================== +// Fader::Fader +Fader::Fader(float const startAlpha) + : FadeState(FADE_NONE), + PrePauseState(FADE_NONE), + StartAlpha(startAlpha), + FadeAlpha(startAlpha) {} + +//============================== +// Fader::Update +void Fader::Update(float const fadeRate, double const deltaSeconds) { + if (FadeState > FADE_PAUSED && deltaSeconds > 0.0f) { + float const fadeDelta = + static_cast(fadeRate * deltaSeconds) * (FadeState == FADE_IN ? 1.0f : -1.0f); + FadeAlpha += fadeDelta; + assert(fabs(fadeDelta) > MATH_FLOAT_SMALLEST_NON_DENORMAL); + if (fabs(fadeDelta) < MATH_FLOAT_SMALLEST_NON_DENORMAL) { + ALOG("Fader::Update fabs( fadeDelta ) < MATH_FLOAT_SMALLEST_NON_DENORMAL !!!!"); + } + if (FadeAlpha < MATH_FLOAT_SMALLEST_NON_DENORMAL) { + FadeAlpha = 0.0f; + FadeState = FADE_NONE; + // ALOG( "FadeState = FADE_NONE" ); + } else if (FadeAlpha >= 1.0f - MATH_FLOAT_SMALLEST_NON_DENORMAL) { + FadeAlpha = 1.0f; + FadeState = FADE_NONE; + // ALOG( "FadeState = FADE_NONE" ); + } + // ALOG( "fadeState = %s, fadeDelta = %.4f, fadeAlpha = %.4f", GetFadeStateName( FadeState + // ), fadeDelta, FadeAlpha ); + } +} + +//============================== +// Fader::StartFadeIn +void Fader::StartFadeIn() { + // ALOG( "StartFadeIn" ); + FadeState = FADE_IN; +} + +//============================== +// Fader::StartFadeOut +void Fader::StartFadeOut() { + // ALOG( "StartFadeOut" ); + FadeState = FADE_OUT; +} + +//============================== +// Fader::PauseFade +void Fader::PauseFade() { + // ALOG( "PauseFade" ); + PrePauseState = FadeState; + FadeState = FADE_PAUSED; +} + +//============================== +// Fader::UnPause +void Fader::UnPause() { + FadeState = PrePauseState; +} + +//============================== +// Fader::GetFadeStateName +char const* Fader::GetFadeStateName(eFadeState const state) const { + char const* fadeStateNames[FADE_MAX] = {"FADE_NONE", "FADE_PAUSED", "FADE_IN", "FADE_OUT"}; + return fadeStateNames[state]; +} + +//============================== +// Fader::Reset +void Fader::Reset() { + FadeAlpha = StartAlpha; +} + +//============================== +// Fader::Reset +void Fader::ForceFinish() { + FadeAlpha = FadeState == FADE_IN ? 1.0f : 0.0f; + FadeState = FADE_NONE; +} + +//============================== +// Fader::IsFadingInOrFadedIn +bool Fader::IsFadingInOrFadedIn() const { + if (FadeState == FADE_PAUSED) { + return IsFadingInOrFadedIn(PrePauseState); + } + return IsFadingInOrFadedIn(FadeState); +} + +//============================== +// Fader::IsFadingInOrFadedIn +bool Fader::IsFadingInOrFadedIn(eFadeState const state) const { + switch (FadeState) { + case FADE_IN: + return true; + case FADE_OUT: + return false; + case FADE_NONE: + return FadeAlpha >= 1.0f; + default: + assert(false); // this should never be called with state FADE_PAUSE + return false; + } +} + +//====================================================================================== +// SineFader +//====================================================================================== + +//============================== +// SineFader::SineFader +SineFader::SineFader(float const startAlpha) : Fader(startAlpha) {} + +//============================== +// SineFader::GetFinalAlpha +float SineFader::GetFinalAlpha() const { + // NOTE: pausing will still re-calculate the + if (GetFadeState() == FADE_NONE) { + return GetFadeAlpha(); // already clamped + } + // map to sine wave + float radians = (1.0f - GetFadeAlpha()) * MATH_FLOAT_PI; // range 0 to pi + return (cosf(radians) + 1.0f) * 0.5f; // range 0 to 1 +} + +} // namespace OVRFW diff --git a/Samples/SampleXrFramework/Src/GUI/Fader.h b/Samples/SampleXrFramework/Src/GUI/Fader.h new file mode 100755 index 0000000..a7cba53 --- /dev/null +++ b/Samples/SampleXrFramework/Src/GUI/Fader.h @@ -0,0 +1,74 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/************************************************************************************ + +Filename : Fader.h +Content : Utility classes for animation based on alpha values +Created : July 25, 2014 +Authors : Jonathan E. Wright + +*************************************************************************************/ + +#pragma once + +namespace OVRFW { + +//============================================================== +// Fader +// Fades a value between 0 and 1 +class Fader { + public: + enum eFadeState { + FADE_NONE, // only the state when the alpha is either 1 or 0 + FADE_PAUSED, // value may be in the middle + FADE_IN, + FADE_OUT, + FADE_MAX + }; + + Fader(float const startAlpha); + + void Update(float const fadeRate, double const deltaSeconds); + + float GetFadeAlpha() const { + return FadeAlpha; + } + eFadeState GetFadeState() const { + return FadeState; + } + + void StartFadeIn(); + void StartFadeOut(); + void PauseFade(); + void UnPause(); + void Reset(); + void ForceFinish(); + void SetFadeAlpha(float const fa) { + FadeAlpha = fa; + } + + char const* GetFadeStateName(eFadeState const state) const; + + bool IsFadingInOrFadedIn() const; + + private: + eFadeState FadeState; + eFadeState PrePauseState; + float StartAlpha; + float FadeAlpha; + + private: + bool IsFadingInOrFadedIn(eFadeState const state) const; +}; + +//============================================================== +// SineFader +// Maps fade alpha to a sine curve +class SineFader : public Fader { + public: + SineFader(float const startAlpha); + + float GetFinalAlpha() const; +}; + +} // namespace OVRFW diff --git a/Samples/SampleXrFramework/Src/GUI/GazeCursor.cpp b/Samples/SampleXrFramework/Src/GUI/GazeCursor.cpp new file mode 100755 index 0000000..c6d741e --- /dev/null +++ b/Samples/SampleXrFramework/Src/GUI/GazeCursor.cpp @@ -0,0 +1,591 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/************************************************************************************ + +Filename : GazeCursor.cpp +Content : Global gaze cursor. +Created : June 6, 2014 +Authors : Jonathan E. Wright + +*************************************************************************************/ + +#include "GazeCursor.h" + +#include "Misc/Log.h" +#include "Render/GlTexture.h" +#include "Render/GlProgram.h" +#include "Render/GlGeometry.h" + +using OVR::Bounds3f; +using OVR::Matrix4f; +using OVR::Posef; +using OVR::Quatf; +using OVR::Vector2f; +using OVR::Vector3f; +using OVR::Vector4f; + +inline Vector3f GetViewMatrixPosition(Matrix4f const& m) { + return m.Inverted().GetTranslation(); +} + +inline Vector3f GetViewMatrixForward(Matrix4f const& m) { + return Vector3f(-m.M[2][0], -m.M[2][1], -m.M[2][2]).Normalized(); +} + +inline Vector3f GetViewMatrixUp(Matrix4f const& m) { + return Vector3f(-m.M[1][0], -m.M[1][1], -m.M[1][2]).Normalized(); +} + +namespace OVRFW { + +static const char* GazeCursorVertexSrc = R"glsl( + uniform vec4 UniformColor; + attribute vec4 Position; + attribute vec2 TexCoord; + attribute vec4 VertexColor; + varying highp vec2 oTexCoord; + varying lowp vec4 oColor; + void main() + { + gl_Position = TransformVertex( Position ); + oTexCoord = TexCoord; + oColor = VertexColor * UniformColor; + } +)glsl"; + +static const char* GazeCursorFragmentSrc = R"glsl( + uniform sampler2D Texture0; + varying highp vec2 oTexCoord; + varying lowp vec4 oColor; + void main() + { + gl_FragColor = oColor * texture2D( Texture0, oTexCoord ); + } +)glsl"; + +//============================================================== +// OvrGazeCursorLocal +// +// Private implementation of the gaze cursor interface. +class OvrGazeCursorLocal : public OvrGazeCursor { + public: + static const float CURSOR_MAX_DIST; + static const int TRAIL_GHOSTS = 1; + + OvrGazeCursorLocal(); + virtual ~OvrGazeCursorLocal(); + + // Initialize the gaze cursor system. + virtual void Init(ovrFileSys& fileSys); + + // Shutdown the gaze cursor system. + virtual void Shutdown(); + + // Updates the gaze cursor distance if ths distance passed is less than the current + // distance. System that use the gaze cursor should use this method so that they + // interact civilly with other systems using the gaze cursor. + virtual void UpdateDistance(float const d, eGazeCursorStateType const state); + + // Force the distance to a specific value -- this will set the distance even if + // it is further away than the current distance. Unless your intent is to overload + // the distance set by all other systems that use the gaze cursor, don't use this. + virtual void ForceDistance(float const d, eGazeCursorStateType const state); + + // Call when the scene changes or the camera moves a large amount to clear out the cursor trail + virtual void ClearGhosts(); + + // Called once per frame to update logic. + virtual void + Frame(Matrix4f const& viewMatrix, Matrix4f const& traceMatrix, float const deltaTime); + + // Generates the gaze cursor surfaces and appends to the surface render list. + virtual void AppendSurfaceList(std::vector& surfaceList) const; + + // Returns the current info about the gaze cursor. + virtual OvrGazeCursorInfo GetInfo() const; + + // Sets the rate at which the gaze cursor icon will spin. + virtual void SetRotationRate(float const degreesPerSec); + + // Sets the scale factor for the cursor's size. + virtual void SetCursorScale(float const scale); + + // Returns whether the gaze cursor will be drawn this frame + virtual bool IsVisible() const; + + // Hide the gaze cursor. + virtual void HideCursor() { + Hidden = true; + } + + // Show the gaze cursor. + virtual void ShowCursor() { + Hidden = false; + } + + // Hide the gaze cursor for specified frames + virtual void HideCursorForFrames(const int hideFrames) { + HiddenFrames = hideFrames; + } + + // Sets an addition distance to offset the cursor for rendering. This can help avoid + // z-fighting but also helps the cursor to feel more 3D by pushing it away from surfaces. + virtual void SetDistanceOffset(float const offset) { + DistanceOffset = offset; + } + + virtual void SetTrailEnabled(bool const enabled); + + private: + OvrGazeCursorInfo Info; // current cursor info + OvrGazeCursorInfo RenderInfo; // latched info for rendering + float CursorRotation; // current cursor rotation + float RotationRateRadians; // rotation rate in radians + float CursorScale; // scale of the cursor + float DistanceOffset; // additional distance to offset towards the camera. + int HiddenFrames; // Hide cursor for a number of frames + Matrix4f CursorTransform[TRAIL_GHOSTS]; // transform for each ghost + Matrix4f CursorScatterTransform[TRAIL_GHOSTS]; // transform for each depth-fail ghost + int CurrentTransform; // the next CursorTransform[] to fill + + // Since ovrDrawSurface takes a pointer to a ovrSurfaceDef, we need to store a + // ovrSurfaceDef somewhere. This means we have to update these surfaces inside of + // the AppendSurfaceList function, which is otherwise const. This could be avoided if + // we opt to copy the surface def instead of holding a pointer to it. + mutable ovrSurfaceDef ZPassCursorSurface; // VBO for the cursor geometry that passes z test + mutable ovrSurfaceDef ZFailCursorSurface; // VBO for the cursor geometry that fails z test + VertexAttribs ZPassVertexAttribs; // stored so we only generate once and never reallocate + VertexAttribs ZFailVertexAttribs; // stored so we only generate once and never reallocate + + GlTexture CursorTexture; // handle to the cursor's texture + GlProgram CursorProgram; // vertex and pixel shaders for the cursor + Vector4f CursorColorZPass; + Vector4f CursorColorZFail; + + bool Initialized; // true once initialized + bool Hidden; // true if the cursor should not render + + private: + void UpdateCursorPositions(VertexAttribs& attr, Bounds3f& bounds, Matrix4f const* transforms) + const; +}; + +GlGeometry CreateCursorGeometry(VertexAttribs& attr, int const numTrails, int const maxTrails); + +//============================== +// OvrGazeCursorLocal::OvrGazeCursorLocal +OvrGazeCursorLocal::OvrGazeCursorLocal() + : CursorRotation(0.0f), + RotationRateRadians(0.0f), + CursorScale(0.013f), + DistanceOffset(0.05f), + HiddenFrames(0), + CurrentTransform(0), + CursorTexture(), + CursorColorZPass(), + CursorColorZFail(), + Initialized(false), + Hidden(true) {} + +//============================== +// OvrGazeCursorLocal::OvrGazeCursorLocal +OvrGazeCursorLocal::~OvrGazeCursorLocal() {} + +//============================== +// OvrGazeCursorLocal:: +void OvrGazeCursorLocal::Init(ovrFileSys& fileSys) { + ALOG("OvrGazeCursorLocal::Init"); + // assert_WITH_TAG( Initialized == false, "GazeCursor" ); + + if (Initialized) { + ALOG("OvrGazeCursorLocal::Init - already initialized!"); + return; + } + + { + static ovrProgramParm cursorUniformParms[] = { + {"UniformColor", ovrProgramParmType::FLOAT_VECTOR4}, + {"Texture0", ovrProgramParmType::TEXTURE_SAMPLED}, + }; + // Initialize cursors + CursorProgram = GlProgram::Build( + GazeCursorVertexSrc, + GazeCursorFragmentSrc, + cursorUniformParms, + sizeof(cursorUniformParms) / sizeof(ovrProgramParm)); + + CursorColorZFail = Vector4f(1.0f, 0.0f, 0.0f, 0.15f); + CursorColorZPass = Vector4f(1.0f, 1.0f, 1.0f, 1.0f); + + int w = 0; + int h = 0; + CursorTexture = LoadTextureFromUri( + fileSys, "apk:///res/raw/gaze_cursor_circle_64x64.ktx", TextureFlags_t(), w, h); + + // Z-Pass Surface + ZPassCursorSurface.surfaceName = "gaze cursor Z-pass"; + ZPassCursorSurface.geo = + CreateCursorGeometry(ZPassVertexAttribs, TRAIL_GHOSTS, TRAIL_GHOSTS); + ZPassCursorSurface.graphicsCommand.Program = CursorProgram; + ZPassCursorSurface.graphicsCommand.UniformData[0].Data = &CursorColorZPass; + ZPassCursorSurface.graphicsCommand.UniformData[1].Data = &CursorTexture; + + ZPassCursorSurface.graphicsCommand.GpuState.blendEnable = + ovrGpuState::BLEND_ENABLE_SEPARATE; + ZPassCursorSurface.graphicsCommand.GpuState.blendMode = GL_FUNC_ADD; + ZPassCursorSurface.graphicsCommand.GpuState.blendSrc = GL_SRC_ALPHA; + ZPassCursorSurface.graphicsCommand.GpuState.blendDst = GL_ONE_MINUS_SRC_ALPHA; + ZPassCursorSurface.graphicsCommand.GpuState.blendSrcAlpha = GL_ONE; + ZPassCursorSurface.graphicsCommand.GpuState.blendDstAlpha = GL_ONE_MINUS_SRC_ALPHA; + ZPassCursorSurface.graphicsCommand.GpuState.blendModeAlpha = GL_FUNC_ADD; + ZPassCursorSurface.graphicsCommand.GpuState.depthFunc = GL_LEQUAL; + + ZPassCursorSurface.graphicsCommand.GpuState.frontFace = GL_CCW; + ZPassCursorSurface.graphicsCommand.GpuState.depthEnable = true; + ZPassCursorSurface.graphicsCommand.GpuState.depthMaskEnable = false; + ZPassCursorSurface.graphicsCommand.GpuState.polygonOffsetEnable = false; + ZPassCursorSurface.graphicsCommand.GpuState.cullEnable = false; + + // Z-Fail Surface + ZFailCursorSurface.surfaceName = "gaze cursor Z-fail"; + ZFailCursorSurface.geo = + CreateCursorGeometry(ZFailVertexAttribs, TRAIL_GHOSTS, TRAIL_GHOSTS); + ZFailCursorSurface.graphicsCommand.Program = CursorProgram; + ZFailCursorSurface.graphicsCommand.UniformData[0].Data = &CursorColorZFail; + ZFailCursorSurface.graphicsCommand.UniformData[1].Data = &CursorTexture; + + ZFailCursorSurface.graphicsCommand.GpuState.blendEnable = + ovrGpuState::BLEND_ENABLE_SEPARATE; + ZFailCursorSurface.graphicsCommand.GpuState.blendMode = GL_FUNC_ADD; + ZFailCursorSurface.graphicsCommand.GpuState.blendSrc = GL_SRC_ALPHA; + ZFailCursorSurface.graphicsCommand.GpuState.blendDst = GL_ONE_MINUS_SRC_ALPHA; + ZFailCursorSurface.graphicsCommand.GpuState.blendSrcAlpha = GL_ONE; + ZFailCursorSurface.graphicsCommand.GpuState.blendDstAlpha = GL_ONE_MINUS_SRC_ALPHA; + ZFailCursorSurface.graphicsCommand.GpuState.blendModeAlpha = GL_FUNC_ADD; + ZFailCursorSurface.graphicsCommand.GpuState.depthFunc = GL_GREATER; + + ZFailCursorSurface.graphicsCommand.GpuState.frontFace = GL_CCW; + ZFailCursorSurface.graphicsCommand.GpuState.depthEnable = true; + ZFailCursorSurface.graphicsCommand.GpuState.depthMaskEnable = false; + ZFailCursorSurface.graphicsCommand.GpuState.polygonOffsetEnable = false; + ZFailCursorSurface.graphicsCommand.GpuState.cullEnable = false; + } + + Initialized = true; +} + +//============================== +// OvrGazeCursorLocal:: +void OvrGazeCursorLocal::Shutdown() { + ALOG("OvrGazeCursorLocal::Shutdown"); + // assert_WITH_TAG( Initialized == true, "GazeCursor" ); + + ZPassCursorSurface.geo.Free(); + ZFailCursorSurface.geo.Free(); + + DeleteTexture(CursorTexture); + + GlProgram::Free(CursorProgram); + + Initialized = false; +} + +//============================== +// OvrGazeCursorLocal::UpdateDistance +void OvrGazeCursorLocal::UpdateDistance(float const d, eGazeCursorStateType const state) { + // ALOG( "OvrGazeCursorLocal::UpdateDistance %.4f", d ); + if (d < Info.Distance) { + // ALOG( "OvrGazeCursorLocal::UpdateDistance - new closest distace %.2f", d ); + Info.Distance = d; + Info.State = state; + } +} +//============================== +// OvrGazeCursorLocal::ForceDistance +void OvrGazeCursorLocal::ForceDistance(float const d, eGazeCursorStateType const state) { + Info.Distance = d; + Info.State = state; +} + +//============================== +// OvrGazeCursorLocal::ClearGhosts +void OvrGazeCursorLocal::ClearGhosts() { + CurrentTransform = 0; +} + +static float frand() { + return (rand() & 65535) / (65535.0f / 2.0f) - 1.0f; +} + +//============================== +// OvrGazeCursorLocal::Frame +void OvrGazeCursorLocal::Frame( + Matrix4f const& viewMatrix, + Matrix4f const& traceMatrix, + float const deltaTime) { + // ALOG( "OvrGazeCursorLocal::Frame" ); + HiddenFrames -= 1; + + if (RotationRateRadians != 0.0f) // comparison to exactly 0 is intentional + { + CursorRotation += deltaTime * RotationRateRadians; + if (CursorRotation > MATH_FLOAT_TWOPI) { + CursorRotation -= MATH_FLOAT_TWOPI; + } else if (CursorRotation < 0.0f) { + CursorRotation += MATH_FLOAT_TWOPI; + } + } + + const Vector3f viewPos(GetViewMatrixPosition(viewMatrix)); + const Vector3f tracePos(traceMatrix.GetTranslation()); + const Vector3f traceFwd(GetViewMatrixForward(traceMatrix.Inverted())); + + Vector3f position = tracePos + traceFwd * (Info.Distance - DistanceOffset); + + Matrix4f viewRot = Matrix4f::LookAtRH(viewPos, position, GetViewMatrixUp(viewMatrix)); + + viewRot.SetTranslation(Vector3f(0.0f)); + + // Add one ghost for every four milliseconds. + // Assume we are going to be at even multiples of vsync, so we don't need to bother + // keeping an accurate roundoff count. + const int lerps = static_cast(deltaTime / 0.004f); + + const Matrix4f& prev = CursorTransform[CurrentTransform % TRAIL_GHOSTS]; + Matrix4f& now = CursorTransform[(CurrentTransform + lerps) % TRAIL_GHOSTS]; + + now = Matrix4f::Translation(position) * viewRot.Inverted() * + Matrix4f::RotationZ(CursorRotation) * Matrix4f::Scaling(CursorScale, CursorScale, 1.0f); + + if (CurrentTransform > 0) { + for (int i = 1; i <= lerps; i++) { + const float f = (float)i / lerps; + Matrix4f& tween = CursorTransform[(CurrentTransform + i) % TRAIL_GHOSTS]; + + // We only need to build a scatter on the final point that is already set by now + if (i != lerps) { + tween = ((now * f) + (prev * (1.0f - f))); + } + + // When the cursor depth fails, draw a scattered set of ghosts + Matrix4f& scatter = CursorScatterTransform[(CurrentTransform + i) % TRAIL_GHOSTS]; + + // random point in circle + float rx, ry; + while (1) { + rx = frand(); + ry = frand(); + if ((rx * rx + ry * ry < 1.0f)) { + break; + } + } + scatter = tween * Matrix4f::Translation(rx, ry, 0.0f); + } + } else { + // When CurrentTransform is 0, reset "lerp" cursors to the now transform as these will be + // drawn in the next frame. If this is not done, only the cursor at pos 0 will have the now + // orientation, while the others up to lerps will have old data causing a brief "duplicate" + // to be on screen. + for (int i = 1; i < lerps; i++) { + Matrix4f& tween = CursorTransform[(CurrentTransform + i) % TRAIL_GHOSTS]; + tween = now; + } + } + CurrentTransform += lerps; + + RenderInfo = Info; + + // Update cursor geometry. + // Z-pass positions. + UpdateCursorPositions(ZPassVertexAttribs, ZPassCursorSurface.geo.localBounds, CursorTransform); + ZPassCursorSurface.geo.Update(ZPassVertexAttribs, false); + + // Z-fail positions. + UpdateCursorPositions( + ZFailVertexAttribs, ZFailCursorSurface.geo.localBounds, CursorScatterTransform); + ZFailCursorSurface.geo.Update(ZFailVertexAttribs, false); +} + +//============================== +// OvrGazeCursorLocal::AppendSurfaceList +void OvrGazeCursorLocal::AppendSurfaceList(std::vector& surfaceList) const { + // ALOG( "OvrGazeCursorLocal::AppendSurfaceList" ); + + if (HiddenFrames >= 0) { + return; + } + + if (Hidden) { + return; + } + + if (CursorScale <= 0.0f) { + ALOG("OvrGazeCursorLocal::AppendSurfaceList - scale 0"); + return; + } + + surfaceList.push_back(ovrDrawSurface(&ZPassCursorSurface)); + ZPassCursorSurface.graphicsCommand.UniformData[1].Data = (void*)&CursorTexture; + + surfaceList.push_back(ovrDrawSurface(&ZFailCursorSurface)); + ZFailCursorSurface.graphicsCommand.UniformData[1].Data = (void*)&CursorTexture; +} + +//============================== +// OvrGazeCursorLocal::GetInfo +OvrGazeCursorInfo OvrGazeCursorLocal::GetInfo() const { + return Info; +} + +//============================== +// OvrGazeCursorLocal::SetRotationRate +void OvrGazeCursorLocal::SetRotationRate(float const degreesPerSec) { + RotationRateRadians = degreesPerSec * MATH_FLOAT_DEGREETORADFACTOR; +} + +//============================== +// OvrGazeCursorLocal::SetCursorScale +void OvrGazeCursorLocal::SetCursorScale(float const scale) { + CursorScale = scale; +} + +//============================== +// OvrGazeCursorLocal::IsVisible +// Returns whether the gaze cursor will be drawn this frame +bool OvrGazeCursorLocal::IsVisible() const { + if (HiddenFrames >= 0) { + return false; + } + + if (Hidden) { + return false; + } + + if (CursorScale <= 0.0f) { + return false; + } + + return true; +} + +//============================== +// CreateCursorGeometry +GlGeometry CreateCursorGeometry(VertexAttribs& attr, int const numTrails, int const maxTrails) { + static const Vector2f GazeCursorUV0s[4] = { + Vector2f(0.0f, 0.0f), + Vector2f(1.0f, 0.0f), + Vector2f(0.0f, 1.0f), + Vector2f(1.0f, 1.0f), + }; + + static const GLushort GazeCursorIndices[6] = { + 0, + 1, + 2, + 3, + 2, + 1, + }; + + for (int ghost = 0; ghost < numTrails; ghost++) { + const float alpha = 1.0f - (ghost / (float)maxTrails); + for (int vertexIndex = 0; vertexIndex < 4; vertexIndex++) { + attr.position.push_back(Vector3f(0.0f, 0.0f, 0.0f)); + attr.uv0.push_back(GazeCursorUV0s[vertexIndex]); + attr.color.push_back(Vector4f(1.0f, 1.0f, 1.0f, alpha)); + } + } + + std::vector indices; + indices.resize(numTrails * 6); + for (int ghost = 0; ghost < numTrails; ++ghost) { + // push 6 indices for each quad "ghost" + indices[ghost * 6 + 0] = static_cast(ghost * 4 + GazeCursorIndices[0]); + indices[ghost * 6 + 1] = static_cast(ghost * 4 + GazeCursorIndices[1]); + indices[ghost * 6 + 2] = static_cast(ghost * 4 + GazeCursorIndices[2]); + indices[ghost * 6 + 3] = static_cast(ghost * 4 + GazeCursorIndices[3]); + indices[ghost * 6 + 4] = static_cast(ghost * 4 + GazeCursorIndices[4]); + indices[ghost * 6 + 5] = static_cast(ghost * 4 + GazeCursorIndices[5]); + } + + return GlGeometry(attr, indices); +} + +void OvrGazeCursorLocal::UpdateCursorPositions( + VertexAttribs& attr, + Bounds3f& bounds, + Matrix4f const* transforms) const { + const int numTrails = TRAIL_GHOSTS < CurrentTransform ? TRAIL_GHOSTS : CurrentTransform; + + // For missing trail transforms, draw degenerate triangles... + for (int slice = numTrails; slice < TRAIL_GHOSTS; slice++) { + for (int v = 0; v < 4; ++v) { + attr.position[slice * 4 + v] = Vector3f(0.0f, 0.0f, 0.0f); + } + } + + static const Vector4f GazeCursorPositions[4] = { + Vector4f(-1.0f, -1.0f, 0.0f, 1.0f), + Vector4f(1.0f, -1.0f, 0.0f, 1.0f), + Vector4f(-1.0f, 1.0f, 0.0f, 1.0f), + Vector4f(1.0f, 1.0f, 0.0f, 1.0f), + }; + + bounds.Clear(); + + // Transforming on the CPU shouldn't be too painful in this scenario since the vertex count is + // low and we would be uploading the same amount of data in the form of transforms if we used + // instancing anyways. So this costs us a few extra ops on the CPU, but allows us to bake the + // color fade into a static VBO and avoids instancing which may or maynot be fast on all + // hardware. + for (int slice = numTrails - 1; slice >= 0; slice--) { + const int index = (CurrentTransform - slice) % TRAIL_GHOSTS; + const Matrix4f transform = transforms[index]; + for (int v = 0; v < 4; ++v) { + Vector4f p(transform.Transform(GazeCursorPositions[v])); + attr.position[slice * 4 + v] = Vector3f(p.x, p.y, p.z); + bounds.AddPoint(Vector3f(p.x, p.y, p.z)); + } + } +} + +//============================== +// OvrGazeCursorLocal::SetTrailEnabled +void OvrGazeCursorLocal::SetTrailEnabled(bool const enabled) { + const int numTrails = enabled ? TRAIL_GHOSTS : 1; + if (ZPassCursorSurface.geo.vertexArrayObject != 0) { + ZPassCursorSurface.geo.Free(); + } + ZPassCursorSurface.geo = CreateCursorGeometry(ZPassVertexAttribs, numTrails, TRAIL_GHOSTS); + + if (ZFailCursorSurface.geo.vertexArrayObject != 0) { + ZFailCursorSurface.geo.Free(); + } + ZFailCursorSurface.geo = CreateCursorGeometry(ZFailVertexAttribs, numTrails, TRAIL_GHOSTS); +} + +//============================================================================================== +// +// OvrGazeCursor +// +//============================================================================================== + +//============================== +// OvrGazeCursor::Create +OvrGazeCursor* OvrGazeCursor::Create(ovrFileSys& fileSys) { + OvrGazeCursorLocal* gc = new OvrGazeCursorLocal(); + gc->Init(fileSys); + return gc; +} + +//============================== +// OvrGazeCursor::Destroy +void OvrGazeCursor::Destroy(OvrGazeCursor*& gazeCursor) { + if (gazeCursor != NULL) { + static_cast(gazeCursor)->Shutdown(); + delete gazeCursor; + gazeCursor = NULL; + } +} + +} // namespace OVRFW diff --git a/Samples/SampleXrFramework/Src/GUI/GazeCursor.h b/Samples/SampleXrFramework/Src/GUI/GazeCursor.h new file mode 100755 index 0000000..7f5aac0 --- /dev/null +++ b/Samples/SampleXrFramework/Src/GUI/GazeCursor.h @@ -0,0 +1,120 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/************************************************************************************ + +Filename : GazeCursor.h +Content : Global gaze cursor. +Created : June 6, 2014 +Authors : Jonathan E. Wright + +*************************************************************************************/ + +#pragma once + +#include + +#include "OVR_TypesafeNumber.h" +#include "OVR_Math.h" + +#include "OVR_FileSys.h" +#include "Render/SurfaceRender.h" + +namespace OVRFW { + +enum eGazeCursorStateType { + CURSOR_STATE_NORMAL, // tap will not activate, but might drag + CURSOR_STATE_HILIGHT, // tap will activate + CURSOR_STATE_PRESS, // release will activate + CURSOR_STATE_HAND, // dragging + CURSOR_STATE_MAX +}; + +//============================================================== +// OvrGazeCurorInfo +class OvrGazeCursorInfo { + public: + OvrGazeCursorInfo() : Distance(1.4f), State(CURSOR_STATE_NORMAL) {} + + void Reset(float const d) { + Distance = d; + State = CURSOR_STATE_NORMAL; + } + + float Distance; // distance from the view position, along the view direction, to the cursor + eGazeCursorStateType State; // state of the cursor +}; + +//============================================================== +// OvrGazeCursor +// +// Various systems can utilize the gaze cursor, but only the user that places the +// gaze cursor closest to the view (i.e. that detected the front-most item) should +// treat the cursor as active. All users behind the closest system should act as if +// the gaze cursor is disabled while they are not closest. +class OvrGazeCursor { + public: + friend class OvrGuiSysLocal; + + // Updates the gaze cursor distance if the distance passed is less than the current + // distance. Systems that use the gaze cursor should use this method so that they + // interact civilly with other systems using the gaze cursor. + virtual void UpdateDistance(float const d, eGazeCursorStateType const state) = 0; + + // Force the distance to a specific value -- this will set the distance even if + // it is further away than the current distance. Unless your intent is to overload + // the distance set by all other systems that use the gaze cursor, don't use this. + virtual void ForceDistance(float const d, eGazeCursorStateType const state) = 0; + + // Call when the scene changes or the camera moves a large amount to clear out the cursor trail + virtual void ClearGhosts() = 0; + + // Called once per frame to update logic. + virtual void Frame( + OVR::Matrix4f const& viewMatrix, + OVR::Matrix4f const& traceMatrix, + float const deltaTime) = 0; + + // Generates the gaze cursor draw surface list and appends to the surface list. + virtual void AppendSurfaceList(std::vector& surfaceList) const = 0; + + // Returns the current info about the gaze cursor. + virtual OvrGazeCursorInfo GetInfo() const = 0; + + // Sets the rate at which the gaze cursor icon will spin. + virtual void SetRotationRate(float const degreesPerSec) = 0; + + // Sets the scale factor for the cursor's size. + virtual void SetCursorScale(float const scale) = 0; + + // Returns whether the gaze cursor will be drawn this frame + virtual bool IsVisible() const = 0; + + // Hide the gaze cursor. + virtual void HideCursor() = 0; + + // Show the gaze cursor. + virtual void ShowCursor() = 0; + + // Hide the gaze cursor for a specified number of frames + // Used in App::Resume to hide the cursor interpolating to the gaze orientation from its initial + // orientation + virtual void HideCursorForFrames(const int hideFrames) = 0; + + // Sets an addition distance to offset the cursor for rendering. This can help avoid + // z-fighting but also helps the cursor to feel more 3D by pushing it away from surfaces. + // This is the amount to move towards the camera, so it should be positive to bring the + // cursor towards the viewer. + virtual void SetDistanceOffset(float const offset) = 0; + + // true to allow the gaze cursor to render trails. + virtual void SetTrailEnabled(bool const enabled) = 0; + + protected: + virtual ~OvrGazeCursor() {} + + private: + static OvrGazeCursor* Create(ovrFileSys& fileSys); + static void Destroy(OvrGazeCursor*& gazeCursor); +}; + +} // namespace OVRFW diff --git a/Samples/SampleXrFramework/Src/GUI/GuiSys.cpp b/Samples/SampleXrFramework/Src/GUI/GuiSys.cpp new file mode 100755 index 0000000..84f6eb5 --- /dev/null +++ b/Samples/SampleXrFramework/Src/GUI/GuiSys.cpp @@ -0,0 +1,918 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/************************************************************************************ + +Filename : OvrGuiSys.cpp +Content : Manager for native GUIs. +Created : June 6, 2014 +Authors : Jonathan E. Wright + +*************************************************************************************/ + +#include "GuiSys.h" + +#include "Render/GlProgram.h" +#include "Render/GlTexture.h" +#include "Render/GlGeometry.h" +#include "Render/TextureManager.h" + +#include "Misc/Log.h" + +#include "FrameParams.h" + +#include "VRMenu.h" +#include "VRMenuMgr.h" +#include "VRMenuComponent.h" +#include "SoundLimiter.h" +#include "VRMenuEventHandler.h" +#include "DefaultComponent.h" + +#include "JniUtils.h" +#include "OVR_JSON.h" + +#include "Reflection.h" +#include "ReflectionData.h" + +using OVR::Bounds3f; +using OVR::Matrix4f; +using OVR::Posef; +using OVR::Quatf; +using OVR::Vector2f; +using OVR::Vector3f; +using OVR::Vector4f; + +inline Vector3f GetViewMatrixForward(Matrix4f const& m) { + return Vector3f(-m.M[2][0], -m.M[2][1], -m.M[2][2]).Normalized(); +} + +inline Vector3f GetViewMatrixUp(Matrix4f const& m) { + return Vector3f(-m.M[1][0], -m.M[1][1], -m.M[1][2]).Normalized(); +} + +inline Vector3f GetViewMatrixLeft(Matrix4f const& m) { + return Vector3f(-m.M[0][0], -m.M[0][1], -m.M[0][2]).Normalized(); +} + +inline Vector3f GetViewMatrixPosition(Matrix4f const& m) { + return m.Inverted().GetTranslation(); +} + +namespace OVRFW { + +class OvrPointTracker { + public: + static const int DEFAULT_FRAME_RATE = 60; + + OvrPointTracker(float const rate = 0.1f) + : LastFrameTime(0.0), Rate(rate), CurPosition(0.0f), FirstFrame(true) {} + + void Update(double const curFrameTime, Vector3f const& newPos) { + double frameDelta = curFrameTime - LastFrameTime; + LastFrameTime = curFrameTime; + float const rateScale = + static_cast(frameDelta / (1.0 / static_cast(DEFAULT_FRAME_RATE))); + float const rate = Rate * rateScale; + if (FirstFrame) { + CurPosition = newPos; + } else { + Vector3f delta = (newPos - CurPosition) * rate; + if (delta.Length() < 0.001f) { + // don't allow a denormal to propagate from multiplications of very small numbers + delta = Vector3f(0.0f); + } + CurPosition += delta; + } + FirstFrame = false; + } + + void Reset() { + FirstFrame = true; + } + void SetRate(float const r) { + Rate = r; + } + + Vector3f const& GetCurPosition() const { + return CurPosition; + } + + private: + double LastFrameTime; + float Rate; + Vector3f CurPosition; + bool FirstFrame; +}; + +#define IMPL_CONSOLE_FUNC_BOOL(var_name) \ + ovrLexer lex(parms); \ + int v; \ + lex.ParseInt(v, 1); \ + var_name = v != 0; \ + ALOG(#var_name "( '%s' ) = %i", parms, var_name) + +//============================================================== +// ovrInfoText +class ovrInfoText { + public: + std::string Text; // informative text to show in front of the view + Vector4f Color; // color of info text + Vector3f Offset; // offset from center of screen in view space + long long EndFrame; // time to stop showing text + OvrPointTracker PointTracker; // smoothly tracks to text ideal location + OvrPointTracker FPSPointTracker; // smoothly tracks to ideal FPS text location +}; + +//============================================================== +// OvrGuiSysLocal +class OvrGuiSysLocal : public OvrGuiSys { + public: + OvrGuiSysLocal(const void* context); + ~OvrGuiSysLocal() override; + + virtual void Init( + ovrFileSys* fileSysArg, + OvrGuiSys::SoundEffectPlayer& soundEffectPlayer, + char const* fontName, + OvrDebugLines* debugLines, + int fontVertexBufferSize /* = 8192*/) override; + // Init with a custom font surface for larger-than-normal amounts of text. + virtual void Init( + ovrFileSys* fileSysArg, + OvrGuiSys::SoundEffectPlayer& soundEffectPlayer, + char const* fontName, + BitmapFontSurface* fontSurface, + OvrDebugLines* debugLines) override; + + virtual void Shutdown() override; + + virtual void Frame(ovrApplFrameIn const& vrFrame, Matrix4f const& viewMatrix) override; + virtual void Frame( + ovrApplFrameIn const& vrFrame, + Matrix4f const& viewMatrix, + Matrix4f const& traceMat) override; + + virtual void AppendSurfaceList( + Matrix4f const& centerViewMatrix, + std::vector* surfaceList) const override; + + virtual bool OnKeyEvent(int const keyCode, const int action) override; + + virtual void ResetMenuOrientations(Matrix4f const& viewMatrix) override; + + virtual HitTestResult TestRayIntersection(const Vector3f& start, const Vector3f& dir) + const override; + + virtual void AddMenu(VRMenu* menu) override; + virtual VRMenu* GetMenu(char const* menuName) const override; + virtual std::vector GetAllMenuNames() const override; + virtual void DestroyMenu(VRMenu* menu) override; + + virtual void OpenMenu(char const* name) override; + + virtual void CloseMenu(char const* menuName, bool const closeInstantly) override; + virtual void CloseMenu(VRMenu* menu, bool const closeInstantly) override; + + virtual bool IsMenuActive(char const* menuName) const override; + virtual bool IsAnyMenuActive() const override; + virtual bool IsAnyMenuOpen() const override; + + virtual void ShowInfoText(float const duration, const char* fmt, ...) override; + virtual void ShowInfoText( + float const duration, + Vector3f const& offset, + Vector4f const& color, + const char* fmt, + ...) override; + + virtual const void* GetContext() override { + return Context; + } + virtual ovrFileSys& GetFileSys() override { + return *FileSys; + } + virtual OvrVRMenuMgr& GetVRMenuMgr() override { + return *MenuMgr; + } + virtual OvrVRMenuMgr const& GetVRMenuMgr() const override { + return *MenuMgr; + }; + virtual OvrGazeCursor& GetGazeCursor() override { + return *GazeCursor; + } + virtual BitmapFont& GetDefaultFont() override { + return *DefaultFont; + } + virtual BitmapFont const& GetDefaultFont() const override { + return *DefaultFont; + } + virtual BitmapFontSurface& GetDefaultFontSurface() override { + return *DefaultFontSurface; + } + virtual OvrDebugLines& GetDebugLines() override { + return *DebugLines; + } + virtual SoundEffectPlayer& GetSoundEffectPlayer() override { + return *SoundEffectPlayer; + } + virtual ovrTextureManager& GetTextureManager() override { + return *TextureManager; + } // app's texture manager should always initialize before guisys + + virtual ovrReflection& GetReflection() override { + return *Reflection; + } + virtual ovrReflection const& GetReflection() const override { + return *Reflection; + } + + private: + const void* Context; + ovrFileSys* FileSys; + OvrVRMenuMgr* MenuMgr; + OvrGazeCursor* GazeCursor; + BitmapFont* DefaultFont; + BitmapFontSurface* DefaultFontSurface; + OvrDebugLines* DebugLines; + OvrGuiSys::SoundEffectPlayer* SoundEffectPlayer; + ovrReflection* Reflection; + ovrTextureManager* TextureManager; + + std::vector Menus; + std::vector ActiveMenus; + + ovrInfoText InfoText; + long long LastVrFrameNumber; + + int RecenterCount; + + bool IsInitialized; + + static bool SkipFrame; + static bool SkipRender; + static bool SkipSubmit; + static bool SkipFont; + static bool SkipCursor; + + private: + int FindMenuIndex(char const* menuName) const; + int FindMenuIndex(VRMenu const* menu) const; + int FindActiveMenuIndex(VRMenu const* menu) const; + int FindActiveMenuIndex(char const* menuName) const; + virtual void MakeActive(VRMenu* menu) override; + void MakeInactive(VRMenu* menu); + + std::vector GetDefaultComponents(); + + static void GUISkipFrame(void* appPtr, char const* parms) { + IMPL_CONSOLE_FUNC_BOOL(SkipFrame); + } + static void GUISkipRender(void* appPtr, char const* parms) { + IMPL_CONSOLE_FUNC_BOOL(SkipRender); + } + static void GUISkipSubmit(void* appPtr, char const* parms) { + IMPL_CONSOLE_FUNC_BOOL(SkipSubmit); + } + static void GUISkipFont(void* appPtr, char const* parms) { + IMPL_CONSOLE_FUNC_BOOL(SkipFont); + } + static void GUISkipCursor(void* appPtr, char const* parms) { + IMPL_CONSOLE_FUNC_BOOL(SkipCursor); + } +}; + +Vector4f const OvrGuiSys::BUTTON_DEFAULT_TEXT_COLOR(0.098f, 0.6f, 0.96f, 1.0f); +Vector4f const OvrGuiSys::BUTTON_HILIGHT_TEXT_COLOR(1.0f); + +//============================== +// OvrGuiSys::Create +OvrGuiSys* OvrGuiSys::Create(const void* context) { + return new OvrGuiSysLocal(context); +} + +//============================== +// OvrGuiSys::Destroy +void OvrGuiSys::Destroy(OvrGuiSys*& guiSys) { + if (guiSys != nullptr) { + guiSys->Shutdown(); + delete guiSys; + guiSys = nullptr; + } +} + +bool OvrGuiSysLocal::SkipFrame = false; +bool OvrGuiSysLocal::SkipRender = false; +bool OvrGuiSysLocal::SkipSubmit = false; +bool OvrGuiSysLocal::SkipFont = false; +bool OvrGuiSysLocal::SkipCursor = false; + +//============================== +// OvrGuiSysLocal:: +OvrGuiSysLocal::OvrGuiSysLocal(const void* context) + : Context(context), + FileSys(nullptr), + MenuMgr(nullptr), + GazeCursor(nullptr), + DefaultFont(nullptr), + DefaultFontSurface(nullptr), + DebugLines(nullptr), + SoundEffectPlayer(nullptr), + Reflection(nullptr), + TextureManager(nullptr), + LastVrFrameNumber(0), + RecenterCount(0), + IsInitialized(false) {} + +//============================== +// OvrGuiSysLocal:: +OvrGuiSysLocal::~OvrGuiSysLocal() { + assert(IsInitialized == false); // Shutdown should already have been called +} + +//============================== +// OvrGuiSysLocal::Init +void OvrGuiSysLocal::Init( + ovrFileSys* fileSysArg, + OvrGuiSys::SoundEffectPlayer& soundEffectPlayer, + char const* fontName, + BitmapFontSurface* fontSurface, + OvrDebugLines* debugLines) { + ALOG("OvrGuiSysLocal::Init"); + + this->FileSys = fileSysArg; + Reflection = ovrReflection::Create(); + + SoundEffectPlayer = &soundEffectPlayer; + DebugLines = debugLines; + + MenuMgr = OvrVRMenuMgr::Create(*this); + MenuMgr->Init(*this); + + GazeCursor = OvrGazeCursor::Create(*(this->FileSys)); + + DefaultFont = BitmapFont::Create(); + + assert(fontSurface->IsInitialized()); // if you pass a font surface in, you must initialized it + // before calling OvrGuiSysLocal::Init() + DefaultFontSurface = fontSurface; + + // choose a package to load the font from. + // select the System Activities package first + ALOG("GuiSys::Init - fontName is '%s'", fontName); + + if (OVR::OVR_strncmp(fontName, "apk:", 4) == 0) // if full apk path specified use that + { + if (!DefaultFont->Load(*(this->FileSys), fontName)) { + // we can't just do a fatal error here because the /lang/ host is supposed to be System + // Activities one case of the font failing to load is because System Activities is + // missing entirely. Instead, we + /// app->ShowDependencyError(); + } + } else { + char fontUri[1024]; + OVR::OVR_sprintf(fontUri, sizeof(fontUri), "apk://font/res/raw/%s", fontName); + if (!DefaultFont->Load(*(this->FileSys), fontUri)) { + // we can't just do a fatal error here because the /lang/ host is supposed to be System + // Activities one case of the font failing to load is because System Activities is + // missing entirely. Instead, we + /// app->ShowDependencyError(); + } + } + + TextureManager = ovrTextureManager::Create(); + + IsInitialized = true; + + /* + app->RegisterConsoleFunction( "GUISkipFrame", OvrGuiSysLocal::GUISkipFrame ); + app->RegisterConsoleFunction( "GUISkipRender", OvrGuiSysLocal::GUISkipRender ); + app->RegisterConsoleFunction( "GUISkipSubmit", OvrGuiSysLocal::GUISkipSubmit ); + app->RegisterConsoleFunction( "GUISkipFont", OvrGuiSysLocal::GUISkipFont ); + app->RegisterConsoleFunction( "GUISkipCursor", OvrGuiSysLocal::GUISkipCursor ); + */ +} + +//============================== +// OvrGuiSysLocal::Init +void OvrGuiSysLocal::Init( + ovrFileSys* fileSysArg, + OvrGuiSys::SoundEffectPlayer& soundEffectPlayer, + char const* fontName, + OvrDebugLines* debugLines, + int fontVertexBufferSize /* = 8192*/) { + BitmapFontSurface* fontSurface = BitmapFontSurface::Create(); + fontSurface->Init(fontVertexBufferSize); + Init(fileSysArg, soundEffectPlayer, fontName, fontSurface, debugLines); +} + +//============================== +// OvrGuiSysLocal::Shutdown +void OvrGuiSysLocal::Shutdown() { + if (!IsInitialized) { + assert(IsInitialized); + return; + } + + IsInitialized = false; + + // pointers in this list will always be in Menus list, too, so just clear it + for (int i = 0; i < static_cast(ActiveMenus.size()); ++i) { + ActiveMenus[i] = nullptr; + } + ActiveMenus.clear(); + + // We need to make sure we delete any child menus here -- it's not enough to just delete them + // in the destructor of the parent, because they'll be left in the menu list since the + // destructor has no way to call GuiSys->DestroyMenu() for them. + for (int i = 0; i < static_cast(Menus.size()); ++i) { + VRMenu* menu = Menus[i]; + Menus[i] = nullptr; + menu->Shutdown(*this); + delete menu; + } + Menus.clear(); + + BitmapFontSurface::Free(DefaultFontSurface); + BitmapFont::Free(DefaultFont); + OvrGazeCursor::Destroy(GazeCursor); + OvrVRMenuMgr::Destroy(MenuMgr); + ovrReflection::Destroy(Reflection); + ovrTextureManager::Destroy(TextureManager); + + DebugLines = nullptr; + SoundEffectPlayer = nullptr; +} + +//============================== +// OvrGuiSysLocal::RepositionMenus +// Reposition any open menus +void OvrGuiSysLocal::ResetMenuOrientations(Matrix4f const& centerViewMatrix) { + if (!IsInitialized) { + assert(IsInitialized); + return; + } + + for (int i = 0; i < static_cast(Menus.size()); ++i) { + if (VRMenu* menu = Menus.at(i)) { + ALOG("ResetMenuOrientation -> '%s'", menu->GetName()); + menu->ResetMenuOrientation(centerViewMatrix); + } + } +} + +//============================== +// OvrGuiSysLocal::AddMenu +void OvrGuiSysLocal::AddMenu(VRMenu* menu) { + if (menu == nullptr) { + ALOGW("Attempted to add null menu!"); + return; + } + if (!IsInitialized) { + assert(IsInitialized); + return; + } + + int menuIndex = FindMenuIndex(menu->GetName()); + if (menuIndex >= 0) { + ALOGW("Duplicate menu name '%s'", menu->GetName()); + assert(menuIndex < 0); + } + Menus.push_back(menu); +} + +//============================== +// OvrGuiSysLocal::GetMenu +VRMenu* OvrGuiSysLocal::GetMenu(char const* menuName) const { + int menuIndex = FindMenuIndex(menuName); + if (menuIndex >= 0) { + return Menus[menuIndex]; + } + return nullptr; +} + +//============================== +// OvrGuiSysLocal::GetAllMenuNames +std::vector OvrGuiSysLocal::GetAllMenuNames() const { + std::vector allMenuNames; + for (int i = 0; i < static_cast(Menus.size()); ++i) { + allMenuNames.push_back(std::string(Menus[i]->GetName())); + } + return allMenuNames; +} + +//============================== +// OvrGuiSysLocal::DestroyMenu +void OvrGuiSysLocal::DestroyMenu(VRMenu* menu) { + if (!IsInitialized) { + assert(IsInitialized); + return; + } + + if (menu == nullptr) { + return; + } + + MakeInactive(menu); + + menu->Shutdown(*this); + delete menu; + + int idx = FindMenuIndex(menu); + if (idx >= 0) { + Menus.erase(Menus.cbegin() + idx); + } +} + +//============================== +// OvrGuiSysLocal::FindMenuIndex +int OvrGuiSysLocal::FindMenuIndex(char const* menuName) const { + if (!IsInitialized) { + assert(IsInitialized); + return -1; + } + + for (int i = 0; i < static_cast(Menus.size()); ++i) { + if (OVR::OVR_stricmp(Menus[i]->GetName(), menuName) == 0) { + return i; + } + } + return -1; +} + +//============================== +// OvrGuiSysLocal::FindMenuIndex +int OvrGuiSysLocal::FindMenuIndex(VRMenu const* menu) const { + if (!IsInitialized) { + assert(IsInitialized); + return -1; + } + + for (int i = 0; i < static_cast(Menus.size()); ++i) { + if (Menus[i] == menu) { + return i; + } + } + return -1; +} + +//============================== +// OvrGuiSysLocal::FindActiveMenuIndex +int OvrGuiSysLocal::FindActiveMenuIndex(VRMenu const* menu) const { + if (!IsInitialized) { + assert(IsInitialized); + return -1; + } + + for (int i = 0; i < static_cast(ActiveMenus.size()); ++i) { + if (ActiveMenus[i] == menu) { + return i; + } + } + return -1; +} + +//============================== +// OvrGuiSysLocal::FindActiveMenuIndex +int OvrGuiSysLocal::FindActiveMenuIndex(char const* menuName) const { + if (!IsInitialized) { + assert(IsInitialized); + return -1; + } + + for (int i = 0; i < static_cast(ActiveMenus.size()); ++i) { + if (OVR::OVR_stricmp(ActiveMenus[i]->GetName(), menuName) == 0) { + return i; + } + } + return -1; +} + +//============================== +// OvrGuiSysLocal::MakeActive +void OvrGuiSysLocal::MakeActive(VRMenu* menu) { + if (!IsInitialized) { + assert(IsInitialized); + return; + } + + int idx = FindActiveMenuIndex(menu); + if (idx < 0) { + ActiveMenus.push_back(menu); + } +} + +//============================== +// OvrGuiSysLocal::MakeInactive +void OvrGuiSysLocal::MakeInactive(VRMenu* menu) { + if (!IsInitialized) { + assert(IsInitialized); + return; + } + + int idx = FindActiveMenuIndex(menu); + if (idx >= 0) { + ActiveMenus.erase(ActiveMenus.cbegin() + idx); + } +} + +//============================== +// OvrGuiSysLocal::OpenMenu +void OvrGuiSysLocal::OpenMenu(char const* menuName) { + if (!IsInitialized) { + assert(IsInitialized); + return; + } + + int menuIndex = FindMenuIndex(menuName); + if (menuIndex < 0) { + ALOGW("No menu named '%s'", menuName); + assert(menuIndex >= 0 && menuIndex < static_cast(Menus.size())); + return; + } + VRMenu* menu = Menus[menuIndex]; + assert(menu != nullptr); + + menu->Open(*this); +} + +//============================== +// OvrGuiSysLocal::CloseMenu +void OvrGuiSysLocal::CloseMenu(VRMenu* menu, bool const closeInstantly) { + if (!IsInitialized) { + assert(IsInitialized); + return; + } + + assert(menu != nullptr); + + menu->Close(*this, closeInstantly); +} + +//============================== +// OvrGuiSysLocal::CloseMenu +void OvrGuiSysLocal::CloseMenu(char const* menuName, bool const closeInstantly) { + if (!IsInitialized) { + assert(IsInitialized); + return; + } + + int menuIndex = FindMenuIndex(menuName); + if (menuIndex < 0) { + ALOGW("No menu named '%s'", menuName); + assert(menuIndex >= 0 && menuIndex < static_cast(Menus.size())); + return; + } + VRMenu* menu = Menus[menuIndex]; + CloseMenu(menu, closeInstantly); +} + +//============================== +// OvrGuiSysLocal::IsMenuActive +bool OvrGuiSysLocal::IsMenuActive(char const* menuName) const { + if (!IsInitialized) { + assert(IsInitialized); + return false; + } + + int idx = FindActiveMenuIndex(menuName); + return idx >= 0; +} + +//============================== +// OvrGuiSysLocal::IsAnyMenuOpen +bool OvrGuiSysLocal::IsAnyMenuActive() const { + if (!IsInitialized) { + assert(IsInitialized); + return false; + } + + return ActiveMenus.size() > 0; +} + +//============================== +// OvrGuiSysLocal::IsAnyMenuOpen +bool OvrGuiSysLocal::IsAnyMenuOpen() const { + if (!IsInitialized) { + assert(IsInitialized); + return false; + } + + for (int i = 0; i < static_cast(ActiveMenus.size()); ++i) { + if (ActiveMenus[i]->IsOpenOrOpening()) { + return true; + } + } + return false; +} + +//============================== +// OvrGuiSysLocal::Frame +void OvrGuiSysLocal::Frame(ovrApplFrameIn const& vrFrame, Matrix4f const& centerViewMatrix) { + Matrix4f traceMat(centerViewMatrix.Inverted()); + + Frame(vrFrame, centerViewMatrix, traceMat); +} + +//============================== +// OvrGuiSysLocal::Frame +void OvrGuiSysLocal::Frame( + const ovrApplFrameIn& vrFrame, + Matrix4f const& centerViewMatrix, + Matrix4f const& traceMat) { + /// OVR_PERF_TIMER( OvrGuiSys_Frame ); + + if (!IsInitialized || SkipFrame) { + assert(IsInitialized); + return; + } + + Matrix4f lastViewMatrix(vrFrame.HeadPose); + + const int currentRecenterCount = vrFrame.RecenterCount; + if (currentRecenterCount != RecenterCount) { + /// TODO - validate this + // ALOG( "OvrGuiSysLocal::Frame - reorienting menus" ); + /// app->RecenterLastViewMatrix(); + ResetMenuOrientations(lastViewMatrix); + RecenterCount = currentRecenterCount; + } + + // draw info text + if (InfoText.EndFrame >= LastVrFrameNumber) { + Vector3f viewPos = GetViewMatrixPosition(lastViewMatrix); + Vector3f viewFwd = GetViewMatrixForward(lastViewMatrix); + Vector3f viewUp(0.0f, 1.0f, 0.0f); + Vector3f viewLeft = viewUp.Cross(viewFwd); + Vector3f newPos = viewPos + viewFwd * InfoText.Offset.z + viewUp * InfoText.Offset.y + + viewLeft * InfoText.Offset.x; + InfoText.PointTracker.Update(vrFrame.PredictedDisplayTime, newPos); + + fontParms_t fp; + fp.AlignHoriz = HORIZONTAL_CENTER; + fp.AlignVert = VERTICAL_CENTER; + fp.Billboard = true; + fp.TrackRoll = false; + DefaultFontSurface->DrawTextBillboarded3Df( + *DefaultFont, + fp, + InfoText.PointTracker.GetCurPosition(), + 1.0f, + InfoText.Color, + InfoText.Text.c_str()); + } + + { + /// OVR_PERF_TIMER( OvrGuiSys_Frame_Menus_Frame ); + // go backwards through the list so we can use unordered remove when a menu finishes closing + for (int i = static_cast(ActiveMenus.size()) - 1; i >= 0; --i) { + VRMenu* curMenu = ActiveMenus[i]; + assert(curMenu != nullptr); + + curMenu->Frame(*this, vrFrame, centerViewMatrix, traceMat); + + if (curMenu->GetCurMenuState() == VRMenu::MENUSTATE_CLOSED) { + // remove from the active list + ActiveMenus[i] = ActiveMenus.back(); + ActiveMenus.pop_back(); + continue; + } + } + } + + { + /// OVR_PERF_TIMER( OvrGuiSys_GazeCursor_Frame ); + GazeCursor->Frame(centerViewMatrix, traceMat, vrFrame.DeltaSeconds); + } + + { + /// OVR_PERF_TIMER( OvrGuiSys_Frame_Font_Finish ); + DefaultFontSurface->Finish(centerViewMatrix); + } + + { + /// OVR_PERF_TIMER( OvrGuiSys_Frame_MenuMgr_Finish ); + MenuMgr->Finish(centerViewMatrix); + } + + LastVrFrameNumber = vrFrame.FrameIndex; +} + +//============================== +// OvrGuiSysLocal::AppendSurfaceList +void OvrGuiSysLocal::AppendSurfaceList( + Matrix4f const& centerViewMatrix, + std::vector* surfaceList) const { + if (!IsInitialized || SkipRender) { + assert(IsInitialized); + return; + } + + if (!SkipSubmit) { + MenuMgr->AppendSurfaceList(centerViewMatrix, *surfaceList); + } + + if (!SkipFont) { + DefaultFontSurface->AppendSurfaceList(*DefaultFont, *surfaceList); + } + + if (!SkipCursor) { + GazeCursor->AppendSurfaceList(*surfaceList); + } +} + +//============================== +// OvrGuiSysLocal::OnKeyEvent +bool OvrGuiSysLocal::OnKeyEvent(int const keyCode, const int action) { + if (!IsInitialized) { + assert(IsInitialized); + return false; + } + for (int i = 0; i < static_cast(ActiveMenus.size()); ++i) { + VRMenu* curMenu = ActiveMenus[i]; + if (curMenu && curMenu->OnKeyEvent(*this, keyCode, action)) { + ALOG("VRMenu '%s' consumed key event", curMenu->GetName()); + return true; + } + } + // we ignore other keys in the app menu for now + return false; +} + +void OvrGuiSysLocal::ShowInfoText(float const duration, const char* fmt, ...) { + char buffer[1024]; + va_list args; + va_start(args, fmt); + vsnprintf(buffer, sizeof(buffer), fmt, args); + va_end(args); + InfoText.Text = buffer; + InfoText.Color = Vector4f(1.0f); + InfoText.Offset = Vector3f(0.0f, 0.0f, 1.5f); + InfoText.PointTracker.Reset(); + InfoText.EndFrame = LastVrFrameNumber + (long long)(duration * 60.0f) + 1; +} + +void OvrGuiSysLocal::ShowInfoText( + float const duration, + Vector3f const& offset, + Vector4f const& color, + const char* fmt, + ...) { + char buffer[1024]; + va_list args; + va_start(args, fmt); + vsnprintf(buffer, sizeof(buffer), fmt, args); + va_end(args); + InfoText.Text = buffer; + InfoText.Color = color; + if (offset != InfoText.Offset || InfoText.EndFrame < LastVrFrameNumber) { + InfoText.PointTracker.Reset(); + } + InfoText.Offset = offset; + InfoText.EndFrame = LastVrFrameNumber + (long long)(duration * 60.0f) + 1; +} + +bool OvrGuiSys::ovrDummySoundEffectPlayer::Has(const char* name) const { + ALOG("ovrDummySoundEffectPlayer::Has( %s )", name); + return false; +} + +void OvrGuiSys::ovrDummySoundEffectPlayer::Play(const char* name) { + ALOG("ovrDummySoundEffectPlayer::Play( %s )", name); +} + +void OvrGuiSys::ovrDummySoundEffectPlayer::Stop(const char* name) { + ALOG("ovrDummySoundEffectPlayer::Stop( %s )", name); +} + +void OvrGuiSys::ovrDummySoundEffectPlayer::LoadSoundAsset(const char* name) { + ALOG("ovrDummySoundEffectPlayer::LoadSoundAsset( %s )", name); +} + +//============================== +// OvrGuiSysLocal::TestRayIntersection +HitTestResult OvrGuiSysLocal::TestRayIntersection(const Vector3f& start, const Vector3f& dir) + const { + HitTestResult result; + + for (int i = static_cast(ActiveMenus.size()) - 1; i >= 0; --i) { + VRMenu* curMenu = ActiveMenus[i]; + if (curMenu == nullptr) { + continue; + } + VRMenuObject* root = GetVRMenuMgr().ToObject(curMenu->GetRootHandle()); + if (root == nullptr) { + continue; + } + + HitTestResult r; + menuHandle_t hitHandle = root->HitTest( + *this, curMenu->GetMenuPose(), start, dir, ContentFlags_t(CONTENT_SOLID), r); + if (hitHandle.IsValid() && r.t < result.t) { + result = r; + result.RayStart = start; + result.RayDir = dir; + } + } + return result; +} + +} // namespace OVRFW diff --git a/Samples/SampleXrFramework/Src/GUI/GuiSys.h b/Samples/SampleXrFramework/Src/GUI/GuiSys.h new file mode 100755 index 0000000..6017831 --- /dev/null +++ b/Samples/SampleXrFramework/Src/GUI/GuiSys.h @@ -0,0 +1,158 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/************************************************************************************ + +Filename : GuiSys.h +Content : Manager for native GUIs. +Created : June 6, 2014 +Authors : Jonathan E. Wright + +*************************************************************************************/ + +#pragma once + +#include "VRMenuObject.h" +#include "OVR_FileSys.h" +#include "FrameParams.h" + +namespace OVRFW { + +class OvrGuiSys; +class OvrVRMenuMgr; +class VRMenuEvent; +class VRMenu; +class OvrGazeCursor; +class ovrTextureManager; +class ovrReflection; + +class ovrGuiFrameResult { + public: + HitTestResult HitResult; +}; + +//============================================================== +// OvrGuiSys +class OvrGuiSys { + public: + friend class VRMenu; + + static char const* APP_MENU_NAME; + static OVR::Vector4f const BUTTON_DEFAULT_TEXT_COLOR; + static OVR::Vector4f const BUTTON_HILIGHT_TEXT_COLOR; + + class SoundEffectPlayer { + public: + virtual ~SoundEffectPlayer() {} + virtual bool Has(const char* name) const = 0; + virtual void Play(const char* name) = 0; + virtual void Stop(const char* name) {} + virtual void LoadSoundAsset(const char* name) {} + }; + + class ovrDummySoundEffectPlayer : public SoundEffectPlayer { + public: + virtual bool Has(const char* name) const; + virtual void Play(const char* name); + virtual void Stop(const char* name); + virtual void LoadSoundAsset(const char* name); + }; + + virtual ~OvrGuiSys() {} + + static OvrGuiSys* Create(const void* context); + static void Destroy(OvrGuiSys*& guiSys); + + virtual void Init( + ovrFileSys* FileSys, + SoundEffectPlayer& soundEffectPlayer, + char const* fontName, + OvrDebugLines* debugLines, + int fontVertexBufferSize = 8192) = 0; + // Init with a custom font surface for larger-than-normal amounts of text. + virtual void Init( + ovrFileSys* FileSys, + SoundEffectPlayer& soundEffectPlayer, + char const* fontName, + BitmapFontSurface* fontSurface, + OvrDebugLines* debugLines) = 0; + + virtual void Shutdown() = 0; + + virtual void Frame(ovrApplFrameIn const& vrFrame, OVR::Matrix4f const& centerViewMatrix) = 0; + + virtual void Frame( + ovrApplFrameIn const& vrFrame, + OVR::Matrix4f const& centerViewMatrix, + OVR::Matrix4f const& traceMat) = 0; + + virtual void AppendSurfaceList( + OVR::Matrix4f const& centerViewMatrix, + std::vector* surfaceList) const = 0; + + // called when the app menu is up and a key event is received. Return true if the menu consumed + // the event. + virtual bool OnKeyEvent(int const keyCode, const int action) = 0; + + virtual void ResetMenuOrientations(OVR::Matrix4f const& viewMatrix) = 0; + + virtual HitTestResult TestRayIntersection(const OVR::Vector3f& start, const OVR::Vector3f& dir) + const = 0; + + //------------------------------------------------------------- + // Menu management + + // Add a new menu that can be opened to receive events + virtual void AddMenu(VRMenu* menu) = 0; + // Removes and frees a menu that was previously added + virtual void DestroyMenu(VRMenu* menu) = 0; + + // Return the menu with the matching name + virtual VRMenu* GetMenu(char const* menuName) const = 0; + + // Return a list of all the menu names + virtual std::vector GetAllMenuNames() const = 0; + + // Opens a menu and places it in the active list + virtual void OpenMenu(char const* name) = 0; + + // Closes a menu. It will be removed from the active list once it has finished closing. + virtual void CloseMenu(const char* name, bool const closeInstantly) = 0; + // Closes a menu. It will be removed from the active list once it has finished closing. + virtual void CloseMenu(VRMenu* menu, bool const closeInstantly) = 0; + + virtual bool IsMenuActive(char const* menuName) const = 0; + virtual bool IsAnyMenuActive() const = 0; + virtual bool IsAnyMenuOpen() const = 0; + + virtual void ShowInfoText(float const duration, const char* fmt, ...) = 0; + virtual void ShowInfoText( + float const duration, + OVR::Vector3f const& offset, + OVR::Vector4f const& color, + const char* fmt, + ...) = 0; + + //---------------------------------------------------------- + // interfaces + + virtual const void* GetContext() = 0; + virtual ovrFileSys& GetFileSys() = 0; + virtual OvrVRMenuMgr& GetVRMenuMgr() = 0; + virtual OvrVRMenuMgr const& GetVRMenuMgr() const = 0; + virtual OvrGazeCursor& GetGazeCursor() = 0; + virtual BitmapFont& GetDefaultFont() = 0; + virtual BitmapFont const& GetDefaultFont() const = 0; + virtual BitmapFontSurface& GetDefaultFontSurface() = 0; + virtual OvrDebugLines& GetDebugLines() = 0; + virtual SoundEffectPlayer& GetSoundEffectPlayer() = 0; + virtual ovrTextureManager& GetTextureManager() = 0; + virtual ovrReflection& GetReflection() = 0; + virtual ovrReflection const& GetReflection() const = 0; + + private: + //---------------------------------------------------------- + // private methods for VRMenu + virtual void MakeActive(VRMenu* menu) = 0; +}; + +} // namespace OVRFW diff --git a/Samples/SampleXrFramework/Src/GUI/Lerp.h b/Samples/SampleXrFramework/Src/GUI/Lerp.h new file mode 100755 index 0000000..911afe3 --- /dev/null +++ b/Samples/SampleXrFramework/Src/GUI/Lerp.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +/************************************************************************************ + +Filename : Lerp.h +Content : Simple floating point interpolation. +Created : 8/25/2014 +Authors : John Carmack + +*************************************************************************************/ + +#pragma once + +#include "OVR_Math.h" + +namespace OVRFW { + +class Lerp { + public: + Lerp() : startDomain(0.0), endDomain(0.0), startValue(0.0), endValue(0.0) {} + + void Set(double startDomain_, double startValue_, double endDomain_, double endValue_) { + startDomain = startDomain_; + endDomain = endDomain_; + startValue = startValue_; + endValue = endValue_; + } + + double Value(double domain) const { + const double f = + clamp((domain - startDomain) / (endDomain - startDomain), 0.0, 1.0); + return startValue * (1.0 - f) + endValue * f; + } + + double startDomain; + double endDomain; + double startValue; + double endValue; +}; + +} // namespace OVRFW diff --git a/Samples/SampleXrFramework/Src/GUI/MetaDataManager.cpp b/Samples/SampleXrFramework/Src/GUI/MetaDataManager.cpp new file mode 100755 index 0000000..deabb5b --- /dev/null +++ b/Samples/SampleXrFramework/Src/GUI/MetaDataManager.cpp @@ -0,0 +1,1171 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/************************************************************************************ + +Filename : MetaDataManager.cpp +Content : A class to manage metadata used by FolderBrowser +Created : January 26, 2015 +Authors : Jonathan E. Wright, Warsam Osman, Madhu Kalva + +*************************************************************************************/ + +#include "MetaDataManager.h" +#include "PackageFiles.h" +#include "OVR_FileSys.h" +#include "OVR_JSON.h" +#include "Misc/Log.h" + +#include +#include + +#if !defined(OVR_OS_WIN32) +#include +#include +#else +#include "windows.h" +#endif // !defined(OVR_OS_WIN32) + +using OVR::JSON; +using OVR::JsonReader; + +namespace OVRFW { + +void SortStringArray(std::vector& strings) { + std::sort(strings.begin(), strings.end()); +} + +// if pathToAppend is an empty string, this just adds a slash +void AppendPath(std::string& startPath, const char* pathToAppend) { + int const len = startPath.length(); + if (len == 0) { + startPath = pathToAppend; + return; + } + uint32_t lastCh = startPath[len - 1]; + if (lastCh != '/' && lastCh != '\\') { + // always append the linux path, assuming it will be corrected elsewhere if necessary for + // Windows + startPath += '/'; + } + startPath += pathToAppend; +} + +// DirPath should by a directory with a trailing slash. +// Returns all files in all search paths, as unique relative paths. +// Subdirectories will have a trailing slash. +// All files and directories that start with . are skipped. +std::unordered_map RelativeDirectoryFileList( + const std::vector& searchPaths, + const char* RelativeDirPath) { + // Check each of the mirrors in searchPaths and build up a list of unique strings + std::unordered_map uniqueStrings; + std::string relativeDirPathString = std::string(RelativeDirPath); + +#if defined(OVR_BUILD_DEBUG) + ALOG( + "RelativeDirectoryFileList searchPaths=%d relative='%s'", + (int)searchPaths.size(), + relativeDirPathString.c_str()); +#endif + + const int numSearchPaths = static_cast(searchPaths.size()); + for (int index = 0; index < numSearchPaths; ++index) { + const std::string fullPath = searchPaths[index] + relativeDirPathString; +#if !defined(OVR_OS_WIN32) + DIR* dir = opendir(fullPath.c_str()); + if (dir != NULL) { + struct dirent* entry; + while ((entry = readdir(dir)) != NULL) { + if (entry->d_name[0] == '.') { + continue; + } + if (entry->d_type == DT_DIR) { + std::string s = relativeDirPathString; + s += entry->d_name; + s += "/"; +#if defined(OVR_BUILD_DEBUG) + ALOG("RelativeDirectoryFileList adding - %s", s.c_str()); +#endif + + std::string lowerCaseS = s.c_str(); + std::transform( + lowerCaseS.begin(), lowerCaseS.end(), lowerCaseS.begin(), ::tolower); + uniqueStrings[lowerCaseS] = s; + } else if (entry->d_type == DT_REG) { + std::string s = relativeDirPathString; + s += entry->d_name; +#if defined(OVR_BUILD_DEBUG) + ALOG("RelativeDirectoryFileList adding - %s", s.c_str()); +#endif + + std::string lowerCaseS = s.c_str(); + std::transform( + lowerCaseS.begin(), lowerCaseS.end(), lowerCaseS.begin(), ::tolower); + uniqueStrings[lowerCaseS] = s; + } + } + closedir(dir); + } +#else + WIN32_FIND_DATAA findFileData; + HANDLE hFind = FindFirstFileA(fullPath.c_str(), &findFileData); + if (hFind != INVALID_HANDLE_VALUE) { + do { + if (findFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { + std::string s = relativeDirPathString; + s += findFileData.cFileName; + s += "\\"; +#if defined(OVR_BUILD_DEBUG) + ALOG("RelativeDirectoryFileList adding - %s", s.c_str()); +#endif + std::string lowerCaseS = s.c_str(); + std::transform( + lowerCaseS.begin(), lowerCaseS.end(), lowerCaseS.begin(), ::tolower); + uniqueStrings[lowerCaseS] = s; + } else { + std::string s = relativeDirPathString; + s += findFileData.cFileName; +#if defined(OVR_BUILD_DEBUG) + ALOG("RelativeDirectoryFileList adding - %s", s.c_str()); +#endif + std::string lowerCaseS = s.c_str(); + std::transform( + lowerCaseS.begin(), lowerCaseS.end(), lowerCaseS.begin(), ::tolower); + uniqueStrings[lowerCaseS] = s; + } + } while (FindNextFileA(hFind, &findFileData) != 0); + } + + FindClose(hFind); +#endif // !defined(OVR_OS_WIN32) + } + + return uniqueStrings; +} + +std::string ExtractFileBase(const std::string& s) { + const int l = static_cast(s.length()); + if (l == 0) { + return std::string(""); + } + + int end; + if (s[l - 1] == '/') { // directory ends in a slash + end = l - 1; + } else { + for (end = l - 1; end > 0 && s[end] != '.'; end--) + ; + if (end == 0) { + end = l; + } + } + int start; + for (start = end - 1; start > -1 && s[start] != '/'; start--) + ; + start++; + + return std::string(&s[start], end - start); +} + +bool MatchesExtension(const char* fileName, const char* ext) { + const int extLen = static_cast(OVR::OVR_strlen(ext)); + const int sLen = static_cast(OVR::OVR_strlen(fileName)); + if (sLen < extLen + 1) { + return false; + } + return (0 == strcmp(&fileName[sLen - extLen], ext)); +} + +//============================== +// OvrMetaData + +const char* const VERSION = "Version"; +const char* const CATEGORIES = "Categories"; +const char* const DATA = "Data"; +const char* const FAVORITES_TAG = "Favorites"; +const char* const TAG = "tag"; +const char* const LABEL = "label"; +const char* const TAGS = "tags"; +const char* const CATEGORY = "category"; +const char* const URL_INNER = "url"; + +void OvrMetaData::InitFromDirectory( + const char* relativePath, + const std::vector& searchPaths, + const OvrMetaDataFileExtensions& fileExtensions) { + ALOG("OvrMetaData::InitFromDirectory( %s )", relativePath); + + // Find all the files - checks all search paths + std::unordered_map uniqueFileList = + RelativeDirectoryFileList(searchPaths, relativePath); + std::vector fileList; + for (auto iter = uniqueFileList.begin(); iter != uniqueFileList.end(); ++iter) { +#if defined(OVR_BUILD_DEBUG) + /// We map the lowercase variant (for searching) as a key to the real MixedCase actual file + /// name make sure we use the real MixedCase name hence forth ( aka ->second ) + ALOG("OvrMetaData fileList[ '%s' ] = '%s'", iter->first.c_str(), iter->second.c_str()); +#endif + fileList.push_back(iter->second); + } + SortStringArray(fileList); + Category currentCategory; + currentCategory.CategoryTag = ExtractFileBase(relativePath); + // The label is the same as the tag by default. + // Will be replaced if definition found in loaded metadata + currentCategory.LocaleKey = currentCategory.CategoryTag; + + ALOG("OvrMetaData start category: %s", currentCategory.CategoryTag.c_str()); + std::vector subDirs; + // Grab the categories and loose files + for (const std::string& s : fileList) { + ALOG("OvrMetaData category: %s file: %s", currentCategory.CategoryTag.c_str(), s.c_str()); + + const std::string fileBase = ExtractFileBase(s); + // subdirectory - add category + if (MatchesExtension(s.c_str(), "/")) { + subDirs.push_back(s); + continue; + } + + // See if we want this loose-file + if (!ShouldAddFile(s.c_str(), fileExtensions)) { + continue; + } + + // Add loose file + const int dataIndex = static_cast(MetaData.size()); + OvrMetaDatum* datum = CreateMetaDatum(fileBase.c_str()); + if (datum) { + datum->Id = dataIndex; + datum->Tags.push_back(currentCategory.CategoryTag); + if (GetFullPath(searchPaths, s.c_str(), datum->Url)) { + // always use the lowercase version of the URL to search the map + std::string lowerCaseUrl = datum->Url.c_str(); + auto& loc = std::use_facet>(std::locale()); + loc.tolower(&lowerCaseUrl[0], &lowerCaseUrl[0] + lowerCaseUrl.length()); + + auto datumIter = UrlToIndex.find(lowerCaseUrl); + if (datumIter == UrlToIndex.end()) { + // always use the lowercase version of the URL as map key + UrlToIndex[lowerCaseUrl] = dataIndex; + MetaData.push_back(datum); + ALOG( + "OvrMetaData adding datum %s with index %d to %s", + datum->Url.c_str(), + dataIndex, + currentCategory.CategoryTag.c_str()); + // Register with category + currentCategory.DatumIndicies.push_back(dataIndex); + } else { + ALOGW( + "OvrMetaData::InitFromDirectory found duplicate url %s", + datum->Url.c_str()); + } + } else { + ALOGW("OvrMetaData::InitFromDirectory failed to find %s", s.c_str()); + } + } + } + + if (!currentCategory.DatumIndicies.empty()) { + Categories.push_back(currentCategory); + } + + // Recurse into subdirs + for (const std::string& subDir : subDirs) { + InitFromDirectory(subDir.c_str(), searchPaths, fileExtensions); + } +} + +void OvrMetaData::InitFromFileList( + const std::vector& fileList, + const OvrMetaDataFileExtensions& fileExtensions) { + // Create unique categories + std::unordered_map uniqueCategoryList; + for (int i = 0; i < static_cast(fileList.size()); ++i) { + const std::string& filePath = fileList[i]; + const std::string categoryTag = ExtractDirectory(fileList[i]); + auto iter = uniqueCategoryList.find(categoryTag); + int catIndex = -1; + if (iter == uniqueCategoryList.end()) { + ALOG(" category: %s", categoryTag.c_str()); + Category cat; + cat.CategoryTag = categoryTag; + // The label is the same as the tag by default. + // Will be replaced if definition found in loaded metadata + cat.LocaleKey = cat.CategoryTag; + catIndex = static_cast(Categories.size()); + Categories.push_back(cat); + uniqueCategoryList[categoryTag] = catIndex; + } else { + catIndex = iter->second; + } + + assert(catIndex > -1); + Category& currentCategory = Categories.at(catIndex); + + // See if we want this loose-file + if (!ShouldAddFile(filePath.c_str(), fileExtensions)) { + continue; + } + + // Add loose file + const int dataIndex = static_cast(MetaData.size()); + OvrMetaDatum* datum = CreateMetaDatum(filePath.c_str()); + if (datum != NULL) { + datum->Id = dataIndex; + datum->Url = filePath; + datum->Tags.push_back(currentCategory.CategoryTag); + + // always use the lowercase version of the URL + std::string lowerCaseUrl = datum->Url.c_str(); + auto& loc = std::use_facet>(std::locale()); + loc.tolower(&lowerCaseUrl[0], &lowerCaseUrl[0] + lowerCaseUrl.length()); + auto datumIter = UrlToIndex.find(lowerCaseUrl); + if (datumIter == UrlToIndex.end()) { + // keep the lowercase version in the map for insensitive find + UrlToIndex[lowerCaseUrl] = dataIndex; + MetaData.push_back(datum); + ALOG( + "OvrMetaData::InitFromFileList adding datum %s with index %d to %s", + datum->Url.c_str(), + dataIndex, + currentCategory.CategoryTag.c_str()); + // Register with category + currentCategory.DatumIndicies.push_back(dataIndex); + } else { + ALOGW("OvrMetaData::InitFromFileList found duplicate url %s", datum->Url.c_str()); + } + } + } +} + +void OvrMetaData::RenameCategory(const char* currentTag, const char* newName) { + for (Category& cat : Categories) { + if (cat.CategoryTag == currentTag) { + cat.LocaleKey = newName; + break; + } + } +} + +// Set a category label after construction +bool OvrMetaData::RenameCategoryTag(const char* currentTag, const char* newName) { + Category* category = GetCategory(currentTag); + if (!category) { + return false; + } + for (OvrMetaDatum* datum : MetaData) { + std::vector& tags = datum->Tags; + for (int t = 0; t < static_cast(tags.size()); ++t) { + if (tags.at(t) == currentTag) { + tags.at(t) = newName; + break; + } + } + } + category->CategoryTag = newName; + return true; +} + +std::shared_ptr LoadPackageMetaFile(const char* metaFile) { + int bufferLength = 0; + void* buffer = NULL; + std::string assetsMetaFile = "assets/"; + assetsMetaFile += metaFile; + ovr_ReadFileFromApplicationPackage(assetsMetaFile.c_str(), bufferLength, buffer); + if (!buffer) { + ALOGW("LoadPackageMetaFile failed to read %s", assetsMetaFile.c_str()); + } + return JSON::Parse(static_cast(buffer)); +} + +std::shared_ptr OvrMetaData::CreateOrGetStoredMetaFile( + const char* appFileStoragePath, + const char* metaFile) { + FilePath = appFileStoragePath; + FilePath += metaFile; + + ALOG("CreateOrGetStoredMetaFile FilePath: %s", FilePath.c_str()); + + std::shared_ptr dataFile = JSON::Load(FilePath.c_str()); + if (dataFile == NULL) { + // If this is the first run, or we had an error loading the file, we copy the meta file from + // assets to app's cache + WriteMetaFile(metaFile); + + // try loading it again + dataFile = JSON::Load(FilePath.c_str()); + if (dataFile == NULL) { + ALOGW("OvrMetaData failed to load JSON meta file: %s", metaFile); + } + } else { + ALOG("OvrMetaData::CreateOrGetStoredMetaFile found %s", FilePath.c_str()); + } + return dataFile; +} + +void OvrMetaData::WriteMetaFile(const char* metaFile) const { + ALOG("Writing metafile from apk"); + + if (FILE* newMetaFile = fopen(FilePath.c_str(), "w")) { + int bufferLength = 0; + void* buffer = NULL; + std::string assetsMetaFile = "assets/"; + assetsMetaFile += metaFile; + ovr_ReadFileFromApplicationPackage(assetsMetaFile.c_str(), bufferLength, buffer); + if (!buffer) { + ALOGW("OvrMetaData failed to read %s", assetsMetaFile.c_str()); + } else { + size_t writtenCount = fwrite(buffer, 1, bufferLength, newMetaFile); + if (writtenCount != static_cast(bufferLength)) { + ALOGE_FAIL("OvrMetaData::WriteMetaFile failed to write %s", metaFile); + } + free(buffer); + } + fclose(newMetaFile); + } else { + ALOGE_FAIL("OvrMetaData failed to create %s - check app permissions", FilePath.c_str()); + } +} + +void OvrMetaData::InitFromDirectoryMergeMeta( + const char* relativePath, + const std::vector& searchPaths, + const OvrMetaDataFileExtensions& fileExtensions, + const char* metaFile, + const char* packageName) { + ALOG("OvrMetaData::InitFromDirectoryMergeMeta"); + + std::string appFileStoragePath = "/data/data/"; + appFileStoragePath += packageName; + appFileStoragePath += "/files/"; + + FilePath = appFileStoragePath + metaFile; + + assert(HasPermission(FilePath.c_str(), permissionFlags_t(PERMISSION_READ))); + + std::shared_ptr dataFile = + CreateOrGetStoredMetaFile(appFileStoragePath.c_str(), metaFile); + + InitFromDirectory(relativePath, searchPaths, fileExtensions); + ProcessMetaData(dataFile, searchPaths, metaFile); +} + +void OvrMetaData::InitFromFileListMergeMeta( + const std::vector& fileList, + const std::vector& searchPaths, + const OvrMetaDataFileExtensions& fileExtensions, + const char* appFileStoragePath, + const char* metaFile, + std::shared_ptr storedMetaData) { + ALOG("OvrMetaData::InitFromFileListMergeMeta"); + + InitFromFileList(fileList, fileExtensions); + ProcessMetaData(storedMetaData, searchPaths, metaFile); +} + +void OvrMetaData::InsertCategoryList( + const int startIndex, + const std::vector& categoryList) { + // Merge in the remote categories + // if any duplicates exist, their order is based on the new list + std::vector finalCategoryList; + + std::unordered_map newCategorySet; + for (const Category& newCategory : categoryList) { + newCategorySet[newCategory.CategoryTag] = true; + } + + // Remove any duplicates in the existing categories + for (const Category& existingCategory : Categories) { + auto iter = newCategorySet.find(existingCategory.CategoryTag); + if (iter == newCategorySet.end()) { + finalCategoryList.push_back(existingCategory); + } + } + + // Insert the new category list starting at the startIndex if possible - otherwise just append + for (int remoteIndex = 0; remoteIndex < static_cast(categoryList.size()); ++remoteIndex) { + const Category& newCategory = categoryList[remoteIndex]; + + const int targetIndex = startIndex + remoteIndex; + ALOG( + "OvrMetaData::InsertCategoryList merging %s into category index %d", + newCategory.CategoryTag.c_str(), + targetIndex); + if (startIndex >= 0 && startIndex < static_cast(Categories.size())) { + finalCategoryList.insert(finalCategoryList.cbegin() + targetIndex, newCategory); + } else { + finalCategoryList.push_back(newCategory); + } + } + + std::swap(Categories, finalCategoryList); +} + +void OvrMetaData::ProcessRemoteMetaFile(const char* metaFileString, const int startIndex) { + char const* errorMsg = NULL; + std::shared_ptr remoteMetaFile = JSON::Parse(metaFileString, &errorMsg); + if (remoteMetaFile != NULL) { + // First grab the version + double remoteVersion = 0.0; + ExtractVersion(remoteMetaFile, remoteVersion); + + if (remoteVersion <= + Version) // We already have this metadata, don't need to process further + { + return; + } + + Version = remoteVersion; + + std::vector remoteCategories; + std::unordered_map remoteMetaData; + ExtractCategories(remoteMetaFile, remoteCategories); + ExtractRemoteMetaData(remoteMetaFile, remoteMetaData); + + InsertCategoryList(startIndex, remoteCategories); + + // Append the remote data + ReconcileMetaData(remoteMetaData); + + // Recreate indices which may have changed after reconciliation + RegenerateCategoryIndices(); + + // Serialize the new metadata + std::shared_ptr dataFile = MetaDataToJson(); + if (dataFile == NULL) { + ALOGE_FAIL("OvrMetaData::ProcessMetaData failed to generate JSON meta file"); + } + + dataFile->Save(FilePath.c_str()); + + ALOG("OvrMetaData::ProcessRemoteMetaFile updated %s", FilePath.c_str()); + } else { + ALOG("Meta file parse error '%s'", errorMsg != NULL ? "" : errorMsg); + } +} + +void OvrMetaData::ProcessMetaData( + std::shared_ptr dataFile, + const std::vector& searchPaths, + const char* metaFile) { + if (dataFile != NULL) { + // Grab the version from the loaded data + ExtractVersion(dataFile, Version); + + std::vector storedCategories; + std::unordered_map storedMetaData; + ExtractCategories(dataFile, storedCategories); + + // Read in package data first + std::shared_ptr packageMeta = LoadPackageMetaFile(metaFile); + if (packageMeta) { + // If we failed to find a version in the serialized data, need to set it from the assets + // version + if (Version < 0.0) { + ExtractVersion(packageMeta, Version); + if (Version < 0.0) { + Version = 0.0; + } + } + ExtractCategories(packageMeta, storedCategories); + ExtractMetaData(packageMeta, searchPaths, storedMetaData); + } else { + ALOGW("ProcessMetaData LoadPackageMetaFile failed for %s", metaFile); + } + + // Read in the stored data - overriding any found in the package + ExtractMetaData(dataFile, searchPaths, storedMetaData); + + // Reconcile the stored data vs the data read in + ReconcileCategories(storedCategories); + ReconcileMetaData(storedMetaData); + + // Recreate indices which may have changed after reconciliation + RegenerateCategoryIndices(); + + // Delete any newly empty categories except Favorites + if (!Categories.empty()) { + std::vector finalCategories; + finalCategories.push_back(Categories.at(0)); + for (Category& cat : Categories) { + if (!cat.DatumIndicies.empty()) { + finalCategories.push_back(cat); + } else { + ALOGW( + "OvrMetaData::ProcessMetaData discarding empty %s", + cat.CategoryTag.c_str()); + } + } + std::swap(finalCategories, Categories); + } + } else { + ALOGW("OvrMetaData::ProcessMetaData NULL dataFile"); + } + + // Rewrite new data + dataFile = MetaDataToJson(); + if (dataFile == NULL) { + ALOGE_FAIL("OvrMetaData::ProcessMetaData failed to generate JSON meta file"); + } + + dataFile->Save(FilePath.c_str()); + + ALOG("OvrMetaData::ProcessMetaData created %s", FilePath.c_str()); +} + +void OvrMetaData::ReconcileMetaData( + std::unordered_map& storedMetaData) { + if (storedMetaData.empty()) { + return; + } + DedupMetaData(MetaData, storedMetaData); + + // Now for any remaining stored data - check if it's remote and just add it, sorted by the + // assigned Id + std::vector sortedEntries; + auto storedIter = storedMetaData.begin(); + for (; storedIter != storedMetaData.end(); ++storedIter) { + OvrMetaDatum* storedDatum = storedIter->second; + if (IsRemote(storedDatum)) { + ALOG("ReconcileMetaData metadata adding remote %s", storedDatum->Url.c_str()); + sortedEntries.push_back(storedDatum); + } + } + std::sort( + sortedEntries.begin(), + sortedEntries.end(), + [=](const OvrMetaDatum* a, const OvrMetaDatum* b) { return a->Id < b->Id; }); + for (const auto& entry : sortedEntries) { + MetaData.push_back(entry); + } + storedMetaData.clear(); +} + +void OvrMetaData::DedupMetaData( + std::vector& existingData, + std::unordered_map& newData) { + // Fix the read in meta data using the stored + for (int i = 0; i < static_cast(existingData.size()); ++i) { + OvrMetaDatum* metaDatum = existingData[i]; + + // always use the lowercase version of the URL + std::string lowerCaseUrl = metaDatum->Url.c_str(); + auto& loc = std::use_facet>(std::locale()); + loc.tolower(&lowerCaseUrl[0], &lowerCaseUrl[0] + lowerCaseUrl.length()); + auto iter = newData.find(lowerCaseUrl); + if (iter != newData.end()) { + OvrMetaDatum* storedDatum = iter->second; + ALOG("DedupMetaData metadata for %s", storedDatum->Url.c_str()); + std::swap(storedDatum->Tags, metaDatum->Tags); + SwapExtendedData(storedDatum, metaDatum); + newData.erase(iter); + } + } +} + +void OvrMetaData::ReconcileCategories(std::vector& storedCategories) { + if (storedCategories.empty()) { + return; + } + + // Reconcile categories + // We want Favorites always at the top + // Followed by user created categories + // Finally we want to maintain the order of the retail categories (defined in assets/meta.json) + std::vector finalCategories; + + Category favorites = storedCategories.at(0); + if (favorites.CategoryTag != FAVORITES_TAG) { + ALOGW( + "OvrMetaData::ReconcileCategories failed to find expected category order -- missing assets/meta.json?"); + } + + finalCategories.push_back(favorites); + + std::unordered_map StoredCategoryMap; // using as set + for (const Category& storedCategory : storedCategories) { + ALOG( + "OvrMetaData::ReconcileCategories storedCategory: %s", + storedCategory.CategoryTag.c_str()); + StoredCategoryMap[storedCategory.CategoryTag] = true; + } + + // Now add the read in categories if they differ + for (const Category& readInCategory : Categories) { + auto iter = StoredCategoryMap.find(readInCategory.CategoryTag); + + if (iter == StoredCategoryMap.end()) { + ALOG("OvrMetaData::ReconcileCategories adding %s", readInCategory.CategoryTag.c_str()); + finalCategories.push_back(readInCategory); + } + } + + // Finally fill in the stored in categories after user made ones + for (const Category& storedCat : storedCategories) { + ALOG( + "OvrMetaData::ReconcileCategories adding stored category %s", + storedCat.CategoryTag.c_str()); + finalCategories.push_back(storedCat); + } + + // Now replace Categories + std::swap(Categories, finalCategories); +} + +void OvrMetaData::ExtractVersion(std::shared_ptr dataFile, double& outVersion) const { + if (dataFile == NULL) { + return; + } + + const JsonReader dataReader(dataFile); + if (dataReader.IsObject()) { + outVersion = dataReader.GetChildDoubleByName(VERSION); + } +} + +void OvrMetaData::ExtractCategories( + std::shared_ptr dataFile, + std::vector& outCategories) const { + if (dataFile == NULL) { + return; + } + + const JsonReader categories(dataFile->GetItemByName(CATEGORIES)); + + if (categories.IsArray()) { + while (const std::shared_ptr nextElement = categories.GetNextArrayElement()) { + const JsonReader category(nextElement); + if (category.IsObject()) { + Category extractedCategory; + extractedCategory.CategoryTag = category.GetChildStringByName(TAG); + extractedCategory.LocaleKey = category.GetChildStringByName(LABEL); + + // Check if we already have this category + bool exists = false; + for (const Category& existingCat : outCategories) { + if (extractedCategory.CategoryTag == existingCat.CategoryTag) { + exists = true; + break; + } + } + + if (!exists) { + ALOG("Extracting category: %s", extractedCategory.CategoryTag.c_str()); + outCategories.push_back(extractedCategory); + } + } + } + } +} + +void OvrMetaData::ExtractMetaData( + std::shared_ptr dataFile, + const std::vector& searchPaths, + std::unordered_map& outMetaData) const { + if (dataFile == NULL) { + return; + } + + const JsonReader data(dataFile->GetItemByName(DATA)); + if (data.IsArray()) { + int jsonIndex = static_cast(MetaData.size()); + while (const std::shared_ptr nextElement = data.GetNextArrayElement()) { + const JsonReader datum(nextElement); + if (datum.IsObject()) { + OvrMetaDatum* metaDatum = CreateMetaDatum(""); + if (!metaDatum) { + continue; + } + + metaDatum->Id = jsonIndex++; + const JsonReader tags(datum.GetChildByName(TAGS)); + if (tags.IsArray()) { + while (const std::shared_ptr tagElement = tags.GetNextArrayElement()) { + const JsonReader tag(tagElement); + if (tag.IsObject()) { + metaDatum->Tags.push_back(tag.GetChildStringByName(CATEGORY)); + } + } + } + + assert(!metaDatum->Tags.empty()); + + const std::string relativeUrl(datum.GetChildStringByName(URL_INNER)); + metaDatum->Url = relativeUrl; + bool foundPath = false; + const bool isRemote = IsRemote(metaDatum); + + // Get the absolute path if this is a local file + if (!isRemote) { + foundPath = GetFullPath(searchPaths, relativeUrl.c_str(), metaDatum->Url); + if (!foundPath) { + // if we fail to find the file, check for encrypted extension (TODO: Might + // put this into a virtual function if necessary, benign for now) + foundPath = GetFullPath( + searchPaths, std::string(relativeUrl + ".x").c_str(), metaDatum->Url); + } + } + + // if we fail to find the local file or it's a remote file, the Url is left as read + // in from the stored data + if (isRemote || !foundPath) { + metaDatum->Url = relativeUrl; + } + + ExtractExtendedData(datum, *metaDatum); + ALOG("OvrMetaData::ExtractMetaData adding datum %s", metaDatum->Url.c_str()); + + // always use the lowercase version of the URL + std::string lowerCaseUrl = metaDatum->Url.c_str(); + auto& loc = std::use_facet>(std::locale()); + loc.tolower(&lowerCaseUrl[0], &lowerCaseUrl[0] + lowerCaseUrl.length()); + auto iter = outMetaData.find(lowerCaseUrl); + if (iter == outMetaData.end()) { + // Always index by lowercase version of the URL + outMetaData[lowerCaseUrl] = metaDatum; + } else { + iter->second = metaDatum; + } + } + } + } +} + +void OvrMetaData::ExtractRemoteMetaData( + std::shared_ptr dataFile, + std::unordered_map& outMetaData) const { + if (dataFile == NULL) { + return; + } + + const JsonReader data(dataFile->GetItemByName(DATA)); + if (data.IsArray()) { + int jsonIndex = static_cast(MetaData.size()); + while (const std::shared_ptr nextElement = data.GetNextArrayElement()) { + const JsonReader jsonDatum(nextElement); + if (jsonDatum.IsObject()) { + OvrMetaDatum* metaDatum = CreateMetaDatum(""); + if (!metaDatum) { + continue; + } + metaDatum->Id = jsonIndex++; + const JsonReader tags(jsonDatum.GetChildByName(TAGS)); + if (tags.IsArray()) { + while (const std::shared_ptr tagElement = tags.GetNextArrayElement()) { + const JsonReader tag(tagElement); + if (tag.IsObject()) { + metaDatum->Tags.push_back(tag.GetChildStringByName(CATEGORY)); + } + } + } + + assert(!metaDatum->Tags.empty()); + + metaDatum->Url = jsonDatum.GetChildStringByName(URL_INNER); + ExtractExtendedData(jsonDatum, *metaDatum); + + // always use the lowercase version of the URL + std::string lowerCaseUrl = metaDatum->Url.c_str(); + auto& loc = std::use_facet>(std::locale()); + loc.tolower(&lowerCaseUrl[0], &lowerCaseUrl[0] + lowerCaseUrl.length()); + auto iter = outMetaData.find(lowerCaseUrl); + if (iter == outMetaData.end()) { + outMetaData[lowerCaseUrl] = metaDatum; + } else { + iter->second = metaDatum; + } + } + } + } +} + +void OvrMetaData::Serialize() { + // Serialize the new metadata + std::shared_ptr dataFile = MetaDataToJson(); + if (dataFile == NULL) { + ALOGE_FAIL("OvrMetaData::Serialize failed to generate JSON meta file"); + } + + dataFile->Save(FilePath.c_str()); + + ALOG("OvrMetaData::Serialize updated %s", FilePath.c_str()); +} + +void OvrMetaData::RegenerateCategoryIndices() { + for (Category& cat : Categories) { + cat.DatumIndicies.clear(); + } + + // Delete any data only tagged as "Favorite" - this is a fix for user created "Favorite" folder + // which is a special case Not doing this will show photos already favorited that the user + // cannot unfavorite + for (int metaDataIndex = 0; metaDataIndex < static_cast(MetaData.size()); + ++metaDataIndex) { + OvrMetaDatum& metaDatum = *MetaData.at(metaDataIndex); + std::vector& tags = metaDatum.Tags; + + assert(metaDatum.Tags.size() > 0); + if (tags.size() == 1) { + if (tags.at(0) == FAVORITES_TAG) { + ALOG("Removing broken metadatum %s", metaDatum.Url.c_str()); + MetaData.erase(MetaData.cbegin() + metaDataIndex); + } + } + } + + // Fix the indices + for (int metaDataIndex = 0; metaDataIndex < static_cast(MetaData.size()); + ++metaDataIndex) { + OvrMetaDatum& datum = *MetaData.at(metaDataIndex); + std::vector& tags = datum.Tags; + + assert(tags.size() > 0); + + if (tags.size() == 1) { + assert(tags[0] != FAVORITES_TAG); + } + + if (tags[0] == FAVORITES_TAG && tags.size() > 1) { + std::swap(tags[0], tags[1]); + } + + for (const std::string& tag : tags) { + if (!tag.empty()) { + if (Category* category = GetCategory(tag)) { + ALOG( + "OvrMetaData inserting index %d for datum %s to %s", + metaDataIndex, + datum.Url.c_str(), + category->CategoryTag.c_str()); + + // fix the metadata index itself + datum.Id = metaDataIndex; + + // Update the category with the new index + category->DatumIndicies.push_back(metaDataIndex); + category->Dirty = true; + } else { + ALOGW( + "OvrMetaData::RegenerateCategoryIndices failed to find category with tag %s for datum %s at index %d", + tag.c_str(), + datum.Url.c_str(), + metaDataIndex); + } + } + } + } +} + +std::shared_ptr OvrMetaData::MetaDataToJson() const { + std::shared_ptr DataFile = JSON::CreateObject(); + + // Add version + DataFile->AddNumberItem(VERSION, Version); + + // Add categories + std::shared_ptr newCategoriesObject = JSON::CreateArray(); + + for (const Category& cat : Categories) { + if (std::shared_ptr catObject = JSON::CreateObject()) { + catObject->AddStringItem(TAG, cat.CategoryTag.c_str()); + catObject->AddStringItem(LABEL, cat.LocaleKey.c_str()); + ALOG("OvrMetaData::MetaDataToJson adding category %s", cat.CategoryTag.c_str()); + newCategoriesObject->AddArrayElement(catObject); + } + } + DataFile->AddItem(CATEGORIES, newCategoriesObject); + + // Add meta data + std::shared_ptr newDataObject = JSON::CreateArray(); + + for (int i = 0; i < static_cast(MetaData.size()); ++i) { + const OvrMetaDatum& metaDatum = *MetaData.at(i); + + if (std::shared_ptr datumObject = JSON::CreateObject()) { + ExtendedDataToJson(metaDatum, datumObject); + datumObject->AddStringItem(URL_INNER, metaDatum.Url.c_str()); + ALOG("OvrMetaData::MetaDataToJson adding datum url %s", metaDatum.Url.c_str()); + if (std::shared_ptr newTagsObject = JSON::CreateArray()) { + for (const auto& tag : metaDatum.Tags) { + if (std::shared_ptr tagObject = JSON::CreateObject()) { + tagObject->AddStringItem(CATEGORY, tag.c_str()); + newTagsObject->AddArrayElement(tagObject); + } + } + + datumObject->AddItem(TAGS, newTagsObject); + } + newDataObject->AddArrayElement(datumObject); + } + } + DataFile->AddItem(DATA, newDataObject); + + return DataFile; +} + +TagAction OvrMetaData::ToggleTag(OvrMetaDatum* metaDatum, const std::string& newTag) { + ALOG("ToggleTag tag: %s on %s", newTag.c_str(), metaDatum->Url.c_str()); + + std::shared_ptr DataFile = JSON::Load(FilePath.c_str()); + if (DataFile == nullptr) { + ALOG("OvrMetaData failed to load JSON meta file: %s", FilePath.c_str()); + return TAG_ERROR; + } + + assert(DataFile); + assert(metaDatum); + + // First update the local data + TagAction action = TAG_ERROR; + for (int t = 0; t < static_cast(metaDatum->Tags.size()); ++t) { + if (metaDatum->Tags.at(t) == newTag) { + // Handle case which leaves us with no tags - ie. broken state + if (metaDatum->Tags.size() < 2) { + ALOGW( + "ToggleTag attempt to remove only tag: %s on %s", + newTag.c_str(), + metaDatum->Url.c_str()); + return TAG_ERROR; + } + ALOG("ToggleTag TAG_REMOVED tag: %s on %s", newTag.c_str(), metaDatum->Url.c_str()); + action = TAG_REMOVED; + metaDatum->Tags.erase(metaDatum->Tags.cbegin() + t); + break; + } + } + + if (action == TAG_ERROR) { + ALOG("ToggleTag TAG_ADDED tag: %s on %s", newTag.c_str(), metaDatum->Url.c_str()); + metaDatum->Tags.push_back(newTag); + action = TAG_ADDED; + } + + // Then serialize + std::shared_ptr newTagsObject = JSON::CreateArray(); + assert(newTagsObject); + + newTagsObject->Name = TAGS; + + for (const auto& tag : metaDatum->Tags) { + if (std::shared_ptr tagObject = JSON::CreateObject()) { + tagObject->AddStringItem(CATEGORY, tag.c_str()); + newTagsObject->AddArrayElement(tagObject); + } + } + + if (std::shared_ptr data = DataFile->GetItemByName(DATA)) { + if (std::shared_ptr datum = data->GetItemByIndex(metaDatum->Id)) { + if (std::shared_ptr tags = datum->GetItemByName(TAGS)) { + ALOG( + "ToggleTag tag: %s on %s - found node to replace", + newTag.c_str(), + metaDatum->Url.c_str()); + datum->ReplaceNodeWith(TAGS, newTagsObject); + ALOG( + "ToggleTag tag: %s on %s - node replaced", + newTag.c_str(), + metaDatum->Url.c_str()); + DataFile->Save(FilePath.c_str()); + ALOG( + "ToggleTag tag: %s on %s - file saved", newTag.c_str(), metaDatum->Url.c_str()); + } + } + } + return action; +} + +void OvrMetaData::AddCategory(const std::string& name) { + Category cat; + cat.CategoryTag = name; + cat.LocaleKey = name; + Categories.push_back(cat); +} + +void OvrMetaData::InsertCategoryAt(const int index, const std::string& name) { + if (Categories.empty()) { + AddCategory(name); + } else { + Category& targetCategory = Categories.at(index); + if (targetCategory.CategoryTag != name) { + Category cat; + cat.CategoryTag = name; + cat.LocaleKey = name; + Categories.insert(Categories.cbegin() + index, cat); + } else { + targetCategory.CategoryTag = name; + targetCategory.LocaleKey = name; + } + } +} + +OvrMetaData::Category* OvrMetaData::GetCategory(const std::string& categoryName) { + for (Category& category : Categories) { + if (category.CategoryTag == categoryName) { + return &category; + } + } + return NULL; +} + +const OvrMetaDatum& OvrMetaData::GetMetaDatum(const int index) const { + assert(index >= 0 && index < static_cast(MetaData.size())); + return *MetaData.at(index); +} + +bool OvrMetaData::GetMetaData( + const Category& category, + std::vector& outMetaData) const { + for (const int metaDataIndex : category.DatumIndicies) { + assert(metaDataIndex >= 0 && metaDataIndex < static_cast(MetaData.size())); + // const OvrMetaDatum * panoData = &MetaData.at( metaDataIndex ); + // ALOG( "Getting MetaData %d title %s from category %s", metaDataIndex, + // panoData->Title.c_str(), category.CategoryName.c_str() ); + outMetaData.push_back(MetaData.at(metaDataIndex)); + } + return true; +} + +bool OvrMetaData::ShouldAddFile( + const char* filename, + const OvrMetaDataFileExtensions& fileExtensions) const { + const size_t pathLen = OVR::OVR_strlen(filename); + for (const std::string& ext : fileExtensions.BadExtensions) { + const int extLen = ext.length(); + if (pathLen > static_cast(extLen) && + OVR::OVR_stricmp(filename + pathLen - extLen, ext.c_str()) == 0) { + return false; + } + } + + for (const std::string& ext : fileExtensions.GoodExtensions) { + const int extLen = ext.length(); + if (pathLen > static_cast(extLen) && + OVR::OVR_stricmp(filename + pathLen - extLen, ext.c_str()) == 0) { + return true; + } + } + + return false; +} + +void OvrMetaData::SetCategoryDatumIndicies(const int index, const std::vector& datumIndicies) { + assert(index < static_cast(Categories.size())); + + if (index < static_cast(Categories.size())) { + Categories[index].DatumIndicies = datumIndicies; + } +} + +void OvrMetaData::DumpToLog(bool const verbose) const { + if (verbose) { + for (int i = 0; i < static_cast(MetaData.size()); ++i) { + ALOG("MetaData - Url: %s", MetaData[i]->Url.c_str()); + } + } + ALOG("MetaData - Total: %i urls", static_cast(MetaData.size())); +} + +} // namespace OVRFW diff --git a/Samples/SampleXrFramework/Src/GUI/MetaDataManager.h b/Samples/SampleXrFramework/Src/GUI/MetaDataManager.h new file mode 100755 index 0000000..0ea4c48 --- /dev/null +++ b/Samples/SampleXrFramework/Src/GUI/MetaDataManager.h @@ -0,0 +1,195 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/************************************************************************************ + +Filename : MetaDataManager.h +Content : A class to manage metadata used by FolderBrowser +Created : January 26, 2015 +Authors : Jonathan E. Wright, Warsam Osman, Madhu Kalva + +*************************************************************************************/ + +#pragma once + +#include +#include +#include + +#include "OVR_JSON.h" + +namespace OVRFW { + +//============================================================== +// OvrMetaData +struct OvrMetaDatum { + mutable int FolderIndex; // index of the folder this meta data appears in (not serialized!) + mutable int PanelId; // panel id associated with this meta data (not serialized!) + int Id; // index into the array read from the JSON (not serialized!) + std::vector Tags; + std::string Url; + + protected: + OvrMetaDatum() {} +}; + +enum TagAction { TAG_ADDED, TAG_REMOVED, TAG_ERROR }; + +struct OvrMetaDataFileExtensions { + std::vector GoodExtensions; + std::vector BadExtensions; +}; + +class OvrMetaData { + public: + struct Category { + Category() : Dirty(true) {} + std::string CategoryTag; + std::string LocaleKey; + std::vector DatumIndicies; + bool Dirty; + }; + + OvrMetaData() : Version(-1.0) {} + + virtual ~OvrMetaData() {} + + // Init meta data from contents on disk + void InitFromDirectory( + const char* relativePath, + const std::vector& searchPaths, + const OvrMetaDataFileExtensions& fileExtensions); + + // Init meta data from a passed in list of files + void InitFromFileList( + const std::vector& fileList, + const OvrMetaDataFileExtensions& fileExtensions); + + // Check specific paths for media and reconcile against stored/new metadata (Maintained for SDK) + void InitFromDirectoryMergeMeta( + const char* relativePath, + const std::vector& searchPaths, + const OvrMetaDataFileExtensions& fileExtensions, + const char* metaFile, + const char* packageName); + + // File list passed in and we reconcile against stored/new metadata + void InitFromFileListMergeMeta( + const std::vector& fileList, + const std::vector& searchPaths, + const OvrMetaDataFileExtensions& fileExtensions, + const char* appFileStoragePath, + const char* metaFile, + std::shared_ptr storedMetaData); + + void ProcessRemoteMetaFile( + const char* metaFileString, + const int startInsertionIndex /* index to insert remote categories*/); + + // Extracts metadata from passed in JSON dataFile and merges it with the base one in assets if + // needed + void ProcessMetaData( + std::shared_ptr dataFile, + const std::vector& searchPaths, + const char* metaFile); + + // Rename a category after construction + void RenameCategory(const char* currentTag, const char* newName); + + // Rename a category tag after construction + bool RenameCategoryTag(const char* currentTag, const char* newName); + + // Adds or removes tag and returns action taken + TagAction ToggleTag(OvrMetaDatum* data, const std::string& tag); + + // Returns metaData file if one is found, otherwise creates one using the default meta.json in + // the assets folder + std::shared_ptr CreateOrGetStoredMetaFile( + const char* appFileStoragePath, + const char* metaFile); + void AddCategory(const std::string& name); + void InsertCategoryAt(const int index, const std::string& name); + + const std::vector& GetCategories() const { + return Categories; + } + const std::vector& GetMetaData() const { + return MetaData; + } + const Category& GetCategory(const int index) const { + return Categories[index]; + } + Category& GetCategory(const int index) { + return Categories[index]; + } + const OvrMetaDatum& GetMetaDatum(const int index) const; + bool GetMetaData(const Category& category, std::vector& outMetaData) const; + void SetCategoryDatumIndicies(const int index, const std::vector& datumIndicies); + void DumpToLog(bool const verbose) const; + void PrintCategories() const; + void RegenerateCategoryIndices(); + + protected: + // Overload to fill extended data during initialization + virtual OvrMetaDatum* CreateMetaDatum(const char* fileName) const = 0; + virtual void ExtractExtendedData(const OVR::JsonReader& jsonDatum, OvrMetaDatum& outDatum) + const = 0; + virtual void ExtendedDataToJson( + const OvrMetaDatum& datum, + std::shared_ptr outDatumObject) const = 0; + virtual void SwapExtendedData(OvrMetaDatum* left, OvrMetaDatum* right) const = 0; + + // Optional protected interface + virtual bool IsRemote(const OvrMetaDatum* /*datum*/) const { + return true; + } + + // Removes duplicate entries from newData + virtual void DedupMetaData( + std::vector& existingData, + std::unordered_map& newData); + + void InsertCategoryList(const int startIndex, const std::vector& categoryList); + + double GetVersion() { + return Version; + } + void SetVersion(const double val) { + Version = val; + } + std::vector& GetMetaData() { + return MetaData; + } + void SetMetaData(const std::vector& newData) { + MetaData = newData; + } + + Category* GetCategory(const std::string& categoryName); + + void ReconcileMetaData(std::unordered_map& storedMetaData); + void ReconcileCategories(std::vector& storedCategories); + + std::shared_ptr MetaDataToJson() const; + void WriteMetaFile(const char* metaFile) const; + bool ShouldAddFile(const char* filename, const OvrMetaDataFileExtensions& fileExtensions) const; + void ExtractVersion(std::shared_ptr dataFile, double& outVersion) const; + void ExtractCategories( + std::shared_ptr dataFile, + std::vector& outCategories) const; + void ExtractMetaData( + std::shared_ptr dataFile, + const std::vector& searchPaths, + std::unordered_map& outMetaData) const; + void ExtractRemoteMetaData( + std::shared_ptr dataFile, + std::unordered_map& outMetaData) const; + void Serialize(); + + private: + std::string FilePath; + std::vector Categories; + std::vector MetaData; + std::unordered_map UrlToIndex; + double Version; +}; + +} // namespace OVRFW diff --git a/Samples/SampleXrFramework/Src/GUI/Reflection.cpp b/Samples/SampleXrFramework/Src/GUI/Reflection.cpp new file mode 100755 index 0000000..831bb29 --- /dev/null +++ b/Samples/SampleXrFramework/Src/GUI/Reflection.cpp @@ -0,0 +1,801 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/************************************************************************************ + +Filename : Reflection.cpp +Content : Functions and declarations for introspection and reflection of C++ objects. +Created : 11/16/2015 +Authors : Jonathan E. Wright + +*************************************************************************************/ + +#include "Reflection.h" +#include "ReflectionData.h" + +#include "Misc/Log.h" +#include "Locale/OVR_Locale.h" + +#include "OVR_TypesafeNumber.h" + +#if !defined(WIN32) +#include +#else +#include +#endif // !defined(WIN32) + +#include // for strtoll + +namespace OVRFW { + +//============================================================================================== +// Parsing +//============================================================================================== + +template +bool EnumForName(ovrEnumInfo const* enumInfos, char const* const name, Type& out) { + int enumMax = INT_MIN; + for (int i = 0; enumInfos[i].Name != NULL; ++i) { + if (OVR::OVR_strcmp(enumInfos[i].Name, name) == 0) { + out = static_cast(enumInfos[i].Value); + return true; + } + if (enumInfos[i].Value > enumMax) { + enumMax = enumInfos[i].Value; + } + } + out = static_cast(enumMax); + return false; +} + +ovrParseResult ExpectPunctuation(const char* name, ovrLexer& lex, const char* expected) { + const int MAX_TOKEN = 128; + char token[MAX_TOKEN]; + ovrLexer::ovrResult res = lex.ExpectPunctuation(expected, token, MAX_TOKEN); + if (res != ovrLexer::LEX_RESULT_OK) { + return ovrParseResult( + ovrLexer::LEX_RESULT_UNEXPECTED_TOKEN, + "Error parsing '%s': Expected one of '%s', got '%s'", + name, + expected, + token); + } + return ovrParseResult(); +} + +ovrParseResult ParseBool( + ovrReflection& /*refl*/, + ovrLocale const& /*locale*/, + const char* name, + ovrLexer& lex, + ovrTypeInfo const* /*atomicInfo*/, + void* outPtr, + size_t const /*arraySize*/) { + bool& out = *static_cast(outPtr); + size_t const MAX_TOKEN = 128; + char token[MAX_TOKEN]; + + ovrLexer::ovrResult res = lex.NextToken(token, MAX_TOKEN); + if (res == ovrLexer::LEX_RESULT_OK) { + if (!OVR::OVR_strcmp(token, "false") || !OVR::OVR_strcmp(token, "0")) { + out = false; + return ovrParseResult(); + } else if (!OVR::OVR_strcmp(token, "true") || !OVR::OVR_strcmp(token, "1")) { + out = true; + return ovrParseResult(); + } + } + + return ovrParseResult(ovrLexer::LEX_RESULT_UNEXPECTED_TOKEN, "Error parsing '%s'", name); +} + +ovrParseResult ParseInt( + ovrReflection& /*refl*/, + ovrLocale const& /*locale*/, + const char* name, + ovrLexer& lex, + ovrTypeInfo const* /*atomicInfo*/, + void* outPtr, + size_t const /*arraySize*/) { + int& out = *static_cast(outPtr); + size_t const MAX_TOKEN = 128; + char token[MAX_TOKEN]; + + ovrLexer::ovrResult res = lex.ParseInt(out, 0); + if (res != ovrLexer::LEX_RESULT_OK) { + return ovrParseResult(res, "Error parsing '%s': expected int, got '%s'", name, token); + } + return ovrParseResult(); +} + +ovrParseResult ParseFloat( + ovrReflection& /*refl*/, + ovrLocale const& /*locale*/, + const char* name, + ovrLexer& lex, + ovrTypeInfo const* /*atomicInfo*/, + void* outPtr, + size_t const /*arraySize*/) { + float& out = *static_cast(outPtr); + size_t const MAX_TOKEN = 128; + char token[MAX_TOKEN]; + + ovrLexer::ovrResult res = lex.ParseFloat(out, 0.0f); + if (res != ovrLexer::LEX_RESULT_OK) { + return ovrParseResult(res, "Error parsing '%s': expected float, got '%s'", name, token); + } + return ovrParseResult(); +} + +ovrParseResult ParseDouble( + ovrReflection& /*refl*/, + ovrLocale const& /*locale*/, + const char* name, + ovrLexer& lex, + ovrTypeInfo const* /*atomicInfo*/, + void* outPtr, + size_t const /*arraySize*/) { + double& out = *static_cast(outPtr); + size_t const MAX_TOKEN = 128; + char token[MAX_TOKEN]; + + ovrLexer::ovrResult res = lex.ParseDouble(out, 0.0); + if (res != ovrLexer::LEX_RESULT_OK) { + return ovrParseResult(res, "Error parsing '%s': expected double, got '%s'", name, token); + } + return ovrParseResult(); +} + +ovrParseResult ParseEnum( + ovrReflection& /*refl*/, + ovrLocale const& /*locale*/, + const char* name, + ovrLexer& lex, + ovrTypeInfo const* atomicInfo, + void* outPtr, + size_t const /*arraySize*/) { + int& out = *static_cast(outPtr); + size_t const MAX_TOKEN = 128; + char token[MAX_TOKEN]; + + ovrLexer::ovrResult res = lex.NextToken(token, MAX_TOKEN); + if (res != ovrLexer::LEX_RESULT_OK) { + return ovrParseResult(res, "Error parsing '%s': expected enum, got '%s'", name, token); + } + + if (!EnumForName(atomicInfo->EnumInfos, token, out)) { + return ovrParseResult( + ovrLexer::LEX_RESULT_UNEXPECTED_TOKEN, + "Error parsing '%s': expected enum, got '%s'", + name, + token); + } + return ovrParseResult(); +} + +ovrParseResult ParseTypesafeNumber_int( + ovrReflection& /*refl*/, + ovrLocale const& /*locale*/, + const char* name, + ovrLexer& lex, + ovrTypeInfo const* /*atomicInfo*/, + void* outPtr, + size_t const /*arraySize*/) { + enum eTempEnum { INVALID_TEMP_ENUM = 0 }; + typedef OVR::TypesafeNumberT TempTypesafeNumber; + + TempTypesafeNumber& out = *static_cast(outPtr); + + size_t const MAX_TOKEN = 128; + char token[MAX_TOKEN]; + + int value; + ovrLexer::ovrResult res = lex.ParseInt(value, 0); + if (res != ovrLexer::LEX_RESULT_OK) { + return ovrParseResult(res, "Error parsing '%s': expected int, got '%s'", name, token); + } + out.Set(value); + + return ovrParseResult(); +} + +ovrParseResult ParseTypesafeNumber_long_long( + ovrReflection& /*refl*/, + ovrLocale const& /*locale*/, + const char* name, + ovrLexer& lex, + ovrTypeInfo const* /*atomicInfo*/, + void* outPtr, + size_t const /*arraySize*/) { + enum eTempEnum { INVALID_TEMP_ENUM = 0 }; + typedef OVR::TypesafeNumberT TempTypesafeNumber; + + TempTypesafeNumber& out = *static_cast(outPtr); + + size_t const MAX_TOKEN = 128; + char token[MAX_TOKEN]; + + ovrLexer::ovrResult res = lex.NextToken(token, MAX_TOKEN); + if (res != ovrLexer::LEX_RESULT_OK) { + return ovrParseResult(res, "Error parsing '%s': expected long long, got '%s'", name, token); + } + long long value = strtoll(token, nullptr, 10); + out.Set(value); + return ovrParseResult(); +} + +ovrParseResult ParseBitFlags( + ovrReflection& /*refl*/, + ovrLocale const& /*locale*/, + const char* name, + ovrLexer& lex, + ovrTypeInfo const* atomicInfo, + void* outPtr, + size_t const /*arraySize*/) { + int& out = *static_cast(outPtr); + size_t const MAX_TOKEN = 128; + char token[MAX_TOKEN]; + + out = 0; + + ovrLexer::ovrResult res; + for (;;) { + res = lex.NextToken(token, MAX_TOKEN); + if (res != ovrLexer::LEX_RESULT_OK) { + return ovrParseResult(res, "Error parsing '%s': expected enum, got '%s'", name, token); + } + + int e; + bool ok = EnumForName(atomicInfo->EnumInfos, token, e); + if (!ok) { + return ovrParseResult( + ovrLexer::LEX_RESULT_UNEXPECTED_TOKEN, + "Error parsing '%s': exepected enum, got '%s'", + name, + token); + } + out |= (1 << e); + + res = lex.PeekToken(token, MAX_TOKEN); + if (res != ovrLexer::LEX_RESULT_OK) { + return ovrParseResult( + ovrLexer::LEX_RESULT_UNEXPECTED_TOKEN, + "Error parsing '%s': expected '|' or ';', got '%s'", + name, + token); + } else if (!OVR::OVR_strcmp(token, ";")) { + return ovrParseResult(); + } else if (!OVR::OVR_strcmp(token, "|")) { + // consume the OR operator + lex.NextToken(token, MAX_TOKEN); + } + } +} + +ovrParseResult ParseString( + ovrReflection& /*refl*/, + ovrLocale const& locale, + const char* name, + ovrLexer& lex, + ovrTypeInfo const* /*atomicInfo*/, + void* outPtr, + size_t const /*arraySize*/) { + std::string& out = *static_cast(outPtr); + size_t const MAX_TOKEN = 1024; + char token[MAX_TOKEN]; + + ovrLexer::ovrResult res = lex.NextToken(token, MAX_TOKEN); + if (res != ovrLexer::LEX_RESULT_OK) { + return ovrParseResult(res, "Error parsing '%s': expected string, got '%s'", name, token); + } + + // we find the start of the string because it may be preceeded by a format specifier (~~w0, + // ~~RRGGBBAA, etc.) + char const* keyPtr = strstr(token, "@string/"); + if (keyPtr != nullptr) { + intptr_t const keyIndex = keyPtr - token; + std::string temp; + locale.GetLocalizedString(keyPtr, keyPtr, temp); + token[keyIndex] = '\0'; + out += token; + out += temp; + } else { + out = token; + } + return ovrParseResult(); +} + +ovrParseResult ParseIntVector( + ovrReflection& /*refl*/, + ovrLocale const& /*locale*/, + const char* name, + ovrLexer& lex, + ovrTypeInfo const* atomicInfo, + void* outPtr, + size_t const /*arraySize*/) { + int* out = static_cast(outPtr); + size_t const MAX_TOKEN = 1024; + char token[MAX_TOKEN]; + + ovrParseResult parseRes = ExpectPunctuation(name, lex, "("); + if (!parseRes) { + return parseRes; + } + + const int maxElements = static_cast(atomicInfo->Size / sizeof(int)); + for (int i = 0; i < 4; ++i) { + if (i >= maxElements) { + return ovrParseResult( + ovrLexer::LEX_RESULT_ERROR, "Error parsing '%s': too many vector elements", name); + } + + ovrLexer::ovrResult res = lex.ParseInt(out[i], 0); + if (res != ovrLexer::LEX_RESULT_OK) { + return ovrParseResult(res, "Error parsing '%s': expected int", name); + } + + res = lex.ExpectPunctuation(",)", token, MAX_TOKEN); + if (res != ovrLexer::LEX_RESULT_OK) { + return ovrParseResult( + res, "Error parsing '%s': expected ',' or '}', got '%s", name, token); + } + if (!OVR::OVR_strcmp(token, ")")) { + break; // end of vector + } + } + + return ovrParseResult(); +} + +ovrParseResult ParseFloatVector( + ovrReflection& /*refl*/, + ovrLocale const& /*locale*/, + const char* name, + ovrLexer& lex, + ovrTypeInfo const* atomicInfo, + void* outPtr, + size_t const /*arraySize*/) { + float* out = static_cast(outPtr); + size_t const MAX_TOKEN = 1024; + char token[MAX_TOKEN]; + + ovrParseResult parseRes = ExpectPunctuation(name, lex, "("); + if (!parseRes) { + return parseRes; + } + + const int maxElements = static_cast(atomicInfo->Size / sizeof(float)); + for (int i = 0; i < 4; ++i) { + if (i >= maxElements) { + return ovrParseResult( + ovrLexer::LEX_RESULT_ERROR, "Error parsing '%s': too many vector elements", name); + } + + ovrLexer::ovrResult res = lex.ParseFloat(out[i], 0.0f); + if (res != ovrLexer::LEX_RESULT_OK) { + return ovrParseResult(res, "Error parsing '%s': expected float", name); + } + + res = lex.ExpectPunctuation(",)", token, MAX_TOKEN); + if (res != ovrLexer::LEX_RESULT_OK) { + return ovrParseResult( + res, "Error parsing '%s': expected ',' or '}', got '%s", name, token); + } + if (!OVR::OVR_strcmp(token, ")")) { + break; // end of vector + } + } + + return ovrParseResult(); +} + +ovrParseResult::ovrParseResult(ovrLexer::ovrResult const result, char const* fmt, ...) + : Result(result), Error() { + char buffer[4096]; + va_list argPtr; + va_start(argPtr, fmt); + OVR::OVR_vsprintf(buffer, sizeof(buffer), fmt, argPtr); + va_end(argPtr); + +#if defined(OVR_BUILD_DEBUG) + if (result != ovrLexer::LEX_RESULT_OK) { + ALOG("ovrParseResult Error = %s ", &buffer[0]); + } +#endif + + Error = buffer; +} + +static bool IsInteger(char const* token) { + size_t const len = OVR::OVR_strlen(token); + for (size_t i = 0; i < len; ++i) { + if ((i == 0 && token[i] == '-') || (token[i] >= '0' && token[i] <= '9')) { + continue; + } + return false; + } + return true; +} + +ovrParseResult ParseArray( + ovrReflection& refl, + ovrLocale const& locale, + const char* name, + ovrLexer& lex, + ovrTypeInfo const* arrayTypeInfo, + void* arrayPtr, + size_t const arraySize) { + const int MAX_TOKEN = 1024; + char token[MAX_TOKEN]; + + // next token must be either the size of the array or an opening brace + ovrLexer::ovrResult result = lex.NextToken(token, MAX_TOKEN); + if (result != ovrLexer::LEX_RESULT_OK) { + return ovrParseResult(result, "Error parsing '%s'", name); + } + + int count; + if (!OVR::OVR_strcmp(token, "{")) { + // a count of 0 for dynamic arrays means grow as items are added + count = static_cast( + (arrayTypeInfo->ArrayType == ovrArrayType::OVR_POINTER || + arrayTypeInfo->ArrayType == ovrArrayType::OVR_OBJECT) + ? 0 + : arraySize); + } else { + if (arrayTypeInfo->ArrayType != ovrArrayType::OVR_POINTER && + arrayTypeInfo->ArrayType != ovrArrayType::OVR_OBJECT) { + return ovrParseResult( + ovrLexer::LEX_RESULT_ERROR, + "Error parsing '%s': size of array should not be specified for non-dynamic arrays.", + name); + } + + if (!IsInteger(token)) { + return ovrParseResult( + ovrLexer::LEX_RESULT_ERROR, "Error parsing '%s': expected integer", name); + } + assert(arrayTypeInfo->ResizeArrayFn != nullptr); + count = strtol(token, nullptr, 10); + if (count <= 0) { + return ovrParseResult( + ovrLexer::LEX_RESULT_ERROR, + "Error parsing '%s': invalid array size %i", + name, + count); + } + arrayTypeInfo->ResizeArrayFn(arrayPtr, count); + + ovrParseResult parseRes = ExpectPunctuation(name, lex, "{"); + if (!parseRes) { + return parseRes; + } + } + + // in an array, each entry is a type name + for (int index = 0;; ++index) { + ovrLexer::ovrResult res = lex.NextToken(token, MAX_TOKEN); + if (res == ovrLexer::LEX_RESULT_EOF) { + return ovrParseResult(); + } + if (res) { + return ovrParseResult(res, "Error %d parsing '%s'", name); + } + + if (!OVR::OVR_strcmp(token, "}")) { + return ovrParseResult(); + } + + if (index >= count) { + if (count == 0) { + // resize the dynamic array + arrayTypeInfo->ResizeArrayFn(arrayPtr, index + 1); + } else { + assert(index < count); + continue; + } + } + + const ovrTypeInfo* elementTypeInfo = refl.FindTypeInfo(token); + if (elementTypeInfo == nullptr) { + return ovrParseResult( + ovrLexer::LEX_RESULT_ERROR, + "Error %d parsing '%s': Unknown type '%s'", + name, + token); + } + + if (arrayTypeInfo->ArrayType == ovrArrayType::C_OBJECT || + arrayTypeInfo->ArrayType == ovrArrayType::C_POINTER) { + ovrParseResult parseRes = ExpectPunctuation(name, lex, "["); + if (!parseRes) { + return parseRes; + } + + int idx = 0; + res = lex.ParseInt(idx, 0); + if (res) { + return ovrParseResult( + res, "Error parsing '%s': expected array index, got '%s'", name, token); + } + + parseRes = ExpectPunctuation(name, lex, "]"); + if (!parseRes) { + return parseRes; + } + + if (idx != index) { + return ovrParseResult( + ovrLexer::LEX_RESULT_ERROR, + "Error parsing '%s': expected index %d, got %d", + name, + index, + idx); + } + } + + // if the array is not an array of pointers, do a placement new on the stack to avoid heap + // fragmentation + void* placementBuffer = nullptr; + if (arrayTypeInfo->ArrayType != ovrArrayType::OVR_POINTER && + arrayTypeInfo->ArrayType != ovrArrayType::C_POINTER) { + placementBuffer = alloca(elementTypeInfo->Size); + } + void* elementPtr = elementTypeInfo->CreateFn(placementBuffer); + + if (elementTypeInfo->MemberInfo != nullptr) { + ovrParseResult parseRes = + ParseObject(refl, locale, name, lex, elementTypeInfo, elementPtr, 0); + if (!parseRes) { + return parseRes; + } + } else { + assert(elementTypeInfo->ParseFn != nullptr); + + ovrParseResult parseRes = ExpectPunctuation(name, lex, "="); + if (!parseRes) { + return parseRes; + } + + parseRes = + elementTypeInfo->ParseFn(refl, locale, name, lex, elementTypeInfo, elementPtr, 0); + if (!parseRes) { + return parseRes; + } + + parseRes = ExpectPunctuation(name, lex, ";"); + if (!parseRes) { + return parseRes; + } + } + + // copy to the array + arrayTypeInfo->SetArrayElementFn(arrayPtr, index, elementPtr); + } +} + +void BuildScope(ovrReflection& refl, ovrTypeInfo const* typeInfo, std::string& scope) { + ovrTypeInfo const* parentTypeInfo = refl.FindTypeInfo(typeInfo->ParentTypeName); + if (parentTypeInfo != nullptr) { + BuildScope(refl, parentTypeInfo, scope); + } + if (!scope.empty()) { + scope += "::"; + } + scope += typeInfo->TypeName; +} + +ovrReflectionOverload const* ovrReflection::FindOverload(char const* scope) const { + for (ovrReflectionOverload const* o : Overloads) { + if (OVR::OVR_strcmp(o->GetScope(), scope) == 0) { + return o; + } + } + return nullptr; +} + +ovrParseResult ParseObject( + ovrReflection& refl, + ovrLocale const& locale, + const char* name, + ovrLexer& lex, + ovrTypeInfo const* objectTypeInfo, + void* objPtr, + const size_t /*arraySize*/) { + std::string scope; + BuildScope(refl, objectTypeInfo, scope); + ovrReflectionOverload const* o = refl.FindOverload(scope.c_str()); + if (o != nullptr && o->OverloadsMemberVar()) { + ovrMemberInfo const* overloadedMemberVar = + refl.FindMemberReflectionInfo(objectTypeInfo->MemberInfo, o->GetName()); + if (overloadedMemberVar != nullptr) { + uint8_t* memberPtr = static_cast(objPtr) + overloadedMemberVar->Offset; + switch (o->GetType()) { + case ovrReflectionOverload::OVERLOAD_FLOAT_DEFAULT_VALUE: + *reinterpret_cast(memberPtr) = + static_cast(o)->GetValue(); + break; + default: + assert(false); // unhandled overload type + break; + } + } + } + + const int MAX_TOKEN = 1024; + char token[MAX_TOKEN]; + + ovrLexer::ovrResult result = lex.ExpectPunctuation("{", token, MAX_TOKEN); + if (result) { + return ovrParseResult(result, "Error parsing '%s': Expected '{', got '%s'", name, token); + } + + // in an object, each entry is a member variable name + for (;;) { + ovrLexer::ovrResult res = lex.NextToken(token, MAX_TOKEN); + if (res == ovrLexer::LEX_RESULT_EOF) { + return ovrParseResult(); + } + if (res) { + return ovrParseResult(res, "Error %d parsing '%s'", name); + } + + if (!OVR::OVR_strcmp(token, "}")) { + break; + } + + ovrMemberInfo const* memberInfo = + refl.FindMemberReflectionInfoRecursive(objectTypeInfo, token); + if (memberInfo == nullptr) { + assert(memberInfo != nullptr); + return ovrParseResult(res, "Error parsing '%s': Unknown member '%s", name, token); + } + + void* memberPtr = static_cast(objPtr) + memberInfo->Offset; + + ovrTypeInfo const* memberTypeInfo = refl.FindTypeInfo(memberInfo->TypeName); + if (memberTypeInfo == nullptr) { + assert(memberTypeInfo != nullptr); + return ovrParseResult( + res, "Error parsing '%s': Unknown type '%s'", name, memberInfo->TypeName); + } + + if (memberTypeInfo->ParseFn != nullptr) // if we have a special-case parse function, use it + { + assert(memberTypeInfo->ParseFn != nullptr); + + if (memberInfo->Operator != ovrTypeOperator::ARRAY) { + ovrParseResult parseRes = ExpectPunctuation(name, lex, "="); + if (!parseRes) { + return parseRes; + } + } + + ovrParseResult parseRes = memberTypeInfo->ParseFn( + refl, locale, name, lex, memberTypeInfo, memberPtr, memberInfo->ArraySize); + if (!parseRes) { + return parseRes; + } + + if (memberInfo->Operator != ovrTypeOperator::ARRAY) { + parseRes = ExpectPunctuation(name, lex, ";"); + if (!parseRes) { + return parseRes; + } + } + } else // otherwise, this must be an object + { + assert(memberTypeInfo->MemberInfo != nullptr); + + ovrParseResult parseRes = + ParseObject(refl, locale, name, lex, memberTypeInfo, memberPtr, 0); + if (!parseRes) { + return parseRes; + } + } + } + + return ovrParseResult(); +} + +//============================================================================================= +// ovrReflection +//============================================================================================= + +ovrReflection* ovrReflection::Create() { + ovrReflection* r = new ovrReflection(); + if (r != nullptr) { + r->Init(); + } + return r; +} + +void ovrReflection::Destroy(ovrReflection*& r) { + if (r != nullptr) { + r->Shutdown(); + } + delete r; + r = nullptr; +} + +void ovrReflection::Init() { + AddTypeInfoList(TypeInfoList); +} + +void ovrReflection::Shutdown() { + for (int i = 0; i < static_cast(Overloads.size()); ++i) { + delete Overloads[i]; + Overloads[i] = nullptr; + } + Overloads.clear(); +} + +void ovrReflection::AddTypeInfoList(ovrTypeInfo const* list) { + TypeInfoLists.push_back(list); +} + +ovrMemberInfo const* ovrReflection::FindMemberReflectionInfoRecursive( + ovrTypeInfo const* objectTypeInfo, + const char* memberName) { + ovrMemberInfo const* arrayOfMemberType = objectTypeInfo->MemberInfo; + for (int i = 0; arrayOfMemberType[i].MemberName != nullptr; ++i) { + if (!OVR::OVR_strcmp(arrayOfMemberType[i].MemberName, memberName)) { + return &arrayOfMemberType[i]; + } + } + + if (objectTypeInfo->ParentTypeName == nullptr) { + return nullptr; + } + const ovrTypeInfo* parentTypeInfo = FindTypeInfo(objectTypeInfo->ParentTypeName); + if (parentTypeInfo == nullptr) { + return nullptr; + } + + return FindMemberReflectionInfoRecursive(parentTypeInfo, memberName); +} + +ovrMemberInfo const* ovrReflection::FindMemberReflectionInfo( + ovrMemberInfo const* arrayOfMemberType, + const char* memberName) { + for (int i = 0; arrayOfMemberType[i].MemberName != nullptr; ++i) { + if (!OVR::OVR_strcmp(arrayOfMemberType[i].MemberName, memberName)) { + return &arrayOfMemberType[i]; + } + } + return nullptr; +} + +ovrTypeInfo const* ovrReflection::FindTypeInfo(char const* typeName) { + assert(TypeInfoLists.size() > 0); + if (typeName == nullptr || typeName[0] == '\0') { + return nullptr; + } + + for (int i = 0; i < static_cast(TypeInfoLists.size()); ++i) { + // ALOG( "FindTypeInfo searching for %s ...", typeName ); + ovrTypeInfo const* ti = StaticFindTypeInfo(TypeInfoLists[i], typeName); + if (ti != nullptr) { + return ti; + } + } + ALOG("FindTypeInfo for '%s' could not be found! ERROR", typeName); + assert(false); + return nullptr; +} + +ovrTypeInfo const* ovrReflection::StaticFindTypeInfo( + ovrTypeInfo const* list, + char const* typeName) { + if (typeName == nullptr || typeName[0] == '\0') { + return nullptr; + } + + for (int i = 0; list[i].TypeName != nullptr; ++i) { + if (!OVR::OVR_strcmp(list[i].TypeName, typeName)) { + return &list[i]; + } + } + return nullptr; +} + +} // namespace OVRFW diff --git a/Samples/SampleXrFramework/Src/GUI/Reflection.h b/Samples/SampleXrFramework/Src/GUI/Reflection.h new file mode 100755 index 0000000..05eb932 --- /dev/null +++ b/Samples/SampleXrFramework/Src/GUI/Reflection.h @@ -0,0 +1,295 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/************************************************************************************ + +Filename : Reflection.h +Content : Functions and declarations for introspection and reflection of C++ objects. +Created : 11/16/2015 +Authors : Jonathan E. Wright + +*************************************************************************************/ + +#pragma once + +#include +#include +#include "OVR_Types.h" +#include "OVR_Lexer2.h" + +namespace OVRFW { + +struct ovrTypeInfo; +struct ovrMemberInfo; +class ovrLocale; +class ovrReflection; + +//============================================================================================== +// Parsing +//============================================================================================== + +class ovrParseResult { + public: + ovrParseResult() : Result(ovrLexer::LEX_RESULT_OK), Error() {} + + ovrParseResult(ovrLexer::ovrResult const result, char const* fmt, ...); + + operator bool() const { + return Result == ovrLexer::LEX_RESULT_OK; + } + + char const* GetErrorText() const { + return Error.c_str(); + } + + private: + ovrLexer::ovrResult Result; + std::string Error; +}; + +ovrParseResult ExpectPunctuation(const char* name, ovrLexer& lex, const char* expected); + +ovrParseResult ParseBool( + ovrReflection& refl, + ovrLocale const& locale, + const char* name, + ovrLexer& lex, + ovrTypeInfo const* /*atomicInfo*/, + void* outPtr, + size_t const arraySize); +ovrParseResult ParseInt( + ovrReflection& refl, + ovrLocale const& locale, + const char* name, + ovrLexer& lex, + ovrTypeInfo const* /*atomicInfo*/, + void* outPtr, + size_t const arraySize); +ovrParseResult ParseFloat( + ovrReflection& refl, + ovrLocale const& locale, + const char* name, + ovrLexer& lex, + ovrTypeInfo const* /*atomicInfo*/, + void* outPtr, + size_t const arraySize); +ovrParseResult ParseDouble( + ovrReflection& refl, + ovrLocale const& locale, + const char* name, + ovrLexer& lex, + ovrTypeInfo const* /*atomicInfo*/, + void* outPtr, + size_t const arraySize); +ovrParseResult ParseEnum( + ovrReflection& refl, + ovrLocale const& locale, + const char* name, + ovrLexer& lex, + ovrTypeInfo const* atomicInfo, + void* outPtr, + size_t const arraySize); +ovrParseResult ParseBitFlags( + ovrReflection& refl, + ovrLocale const& locale, + const char* name, + ovrLexer& lex, + ovrTypeInfo const* atomicInfo, + void* outPtr, + size_t const arraySize); +ovrParseResult ParseTypesafeNumber_int( + ovrReflection& refl, + ovrLocale const& locale, + const char* name, + ovrLexer& lex, + ovrTypeInfo const* atomicInfo, + void* outPtr, + size_t const arraySize); +ovrParseResult ParseTypesafeNumber_long_long( + ovrReflection& refl, + ovrLocale const& locale, + const char* name, + ovrLexer& lex, + ovrTypeInfo const* atomicInfo, + void* outPtr, + size_t const arraySize); +ovrParseResult ParseString( + ovrReflection& refl, + ovrLocale const& locale, + const char* name, + ovrLexer& lex, + ovrTypeInfo const* /*atomicInfo*/, + void* outPtr, + size_t const arraySize); +ovrParseResult ParseIntVector( + ovrReflection& refl, + ovrLocale const& locale, + const char* name, + ovrLexer& lex, + ovrTypeInfo const* /*atomicInfo*/, + void* outPtr, + size_t const arraySize); +ovrParseResult ParseFloatVector( + ovrReflection& refl, + ovrLocale const& locale, + const char* name, + ovrLexer& lex, + ovrTypeInfo const* /*atomicInfo*/, + void* outPtr, + size_t const arraySize); +ovrParseResult ParseArray( + ovrReflection& refl, + ovrLocale const& locale, + const char* name, + ovrLexer& lex, + ovrTypeInfo const* arrayTypeInfo, + void* objPtr, + size_t const arraySize); +ovrParseResult ParseObject( + ovrReflection& refl, + ovrLocale const& locale, + const char* name, + ovrLexer& lex, + ovrTypeInfo const* objectTypeInfo, + void* objPtr, + size_t const arraySize); + +//============================================================================================== +// Reflection data types +//============================================================================================== + +typedef ovrParseResult (*ParseFn_t)( + ovrReflection& refl, + ovrLocale const& locale, + char const* name, + ovrLexer& lex, + ovrTypeInfo const* typeInfo, + void* outPtr, + const size_t arraySize); +typedef void* (*CreateFn_t)(void* placementBuffer); +typedef void (*ResizeArrayFn_t)(void* objPtr, const int newSize); +typedef void (*SetArrayElementFn_t)(void* objPtr, const int index, void* elementPtr); + +enum class ovrArrayType : char { + NONE, + OVR_OBJECT, // std::vector< T* > + OVR_POINTER, // std::vector< T > + C_OBJECT, // T[] + C_POINTER // T*[] +}; + +struct ovrEnumInfo { + char const* Name; + int Value; +}; + +struct ovrTypeInfo { + char const* TypeName; + const char* ParentTypeName; // this would need to be an array to support multiple inheritance + size_t Size; + ovrEnumInfo const* EnumInfos; + ParseFn_t ParseFn; + CreateFn_t CreateFn; + ResizeArrayFn_t ResizeArrayFn; + SetArrayElementFn_t SetArrayElementFn; + ovrArrayType ArrayType; /// FIXME: only a member can have an array type... a type itself can't + /// be an array? NOT TRUE FOR CLASSES + bool Abstract; // true if class is abstract + ovrMemberInfo const* MemberInfo; +}; + +enum class ovrTypeOperator : char { NONE, POINTER, REFERENCE, ARRAY, MAX }; + +struct ovrMemberInfo { + char const* MemberName; + const char* TypeName; + ovrTypeInfo const* VarType; // cached pointer to type? + ovrTypeOperator Operator; + intptr_t Offset; + size_t ArraySize; // If an array, this is the number of items in the array, otherwise it's 0. +}; + +//============================================================================================== +// Reflection Functions +//============================================================================================== + +class ovrReflectionOverload { + public: + enum ovrOverloadType { OVERLOAD_FLOAT_DEFAULT_VALUE }; + + ovrReflectionOverload(ovrOverloadType const type, char const* scope, char const* name) + : Type(type), Scope(scope), Name(name) {} + virtual ~ovrReflectionOverload() {} + + ovrOverloadType GetType() const { + return Type; + } + char const* GetScope() const { + return Scope.c_str(); + } + char const* GetName() const { + return Name.c_str(); + } + + virtual bool OverloadsMemberVar() const { + return false; + } + + private: + ovrOverloadType Type; + std::string Scope; + std::string Name; +}; + +class ovrReflectionOverload_FloatDefaultValue : public ovrReflectionOverload { + public: + ovrReflectionOverload_FloatDefaultValue(char const* scope, char const* name, float const value) + : ovrReflectionOverload(ovrReflectionOverload::OVERLOAD_FLOAT_DEFAULT_VALUE, scope, name), + Value(value) {} + + float GetValue() const { + return Value; + } + virtual bool OverloadsMemberVar() const override { + return true; + } + + private: + float Value; +}; + +class ovrReflection { + public: + static ovrReflection* Create(); + static void Destroy(ovrReflection*& r); + + void Init(); + void Shutdown(); + // Add an additional list of types. The list must be terminated by a a + void AddTypeInfoList(ovrTypeInfo const* list); + + ovrMemberInfo const* FindMemberReflectionInfoRecursive( + ovrTypeInfo const* objectTypeInfo, + const char* memberName); + ovrMemberInfo const* FindMemberReflectionInfo( + ovrMemberInfo const* arrayOfMemberType, + const char* memberName); + ovrTypeInfo const* FindTypeInfo(char const* typeName); + + void AddOverload(ovrReflectionOverload* o) { + Overloads.push_back(o); + } + ovrReflectionOverload const* FindOverload(char const* scope) const; + + protected: + static ovrTypeInfo const* StaticFindTypeInfo(ovrTypeInfo const* list, char const* typeName); + + private: + std::vector TypeInfoLists; + std::vector Overloads; + + // can only be allocated and deleted by ovrReflection::Create and ovrReflection::Destroy + ovrReflection(){}; + virtual ~ovrReflection() {} +}; + +} // namespace OVRFW diff --git a/Samples/SampleXrFramework/Src/GUI/ReflectionData.cpp b/Samples/SampleXrFramework/Src/GUI/ReflectionData.cpp new file mode 100755 index 0000000..b8a6d6b --- /dev/null +++ b/Samples/SampleXrFramework/Src/GUI/ReflectionData.cpp @@ -0,0 +1,1136 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/************************************************************************************ + +Filename : ReflectionData.cpp +Content : Data for introspection and reflection of C++ objects. +Created : 11/16/2015 +Authors : Jonathan E. Wright + +*************************************************************************************/ + +// this is necessary so that offsetof() can work for private class members +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wkeyword-macro" +#endif // __clang__ + +#define _ALLOW_KEYWORD_MACROS +#undef private +#define private public + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif // __clang__ + +#include "ReflectionData.h" +#include "VRMenuObject.h" +#include "DefaultComponent.h" +#include "AnimComponents.h" + +using OVR::BitFlagsT; +using OVR::Bounds3f; +using OVR::Matrix4f; +using OVR::Posef; +using OVR::Quatf; +using OVR::TypesafeNumberT; +using OVR::Vector2f; +using OVR::Vector2i; +using OVR::Vector3f; +using OVR::Vector4f; + +namespace OVRFW { + +ovrEnumInfo HorizontalJustification_Enums[] = { + {"HORIZONTAL_LEFT", 0}, + {"HORIZONTAL_CENTER", 1}, + {"HORIZONTAL_RIGHT", 2}}; +OVR_VERIFY_ARRAY_SIZE(HorizontalJustification_Enums, 3); + +ovrEnumInfo VerticalJustification_Enums[] = { + {"VERTICAL_BASELINE", 0}, + {"VERTICAL_CENTER", 1}, + {"VERTICAL_CENTER_FIXEDHEIGHT", 2}, + {"VERTICAL_TOP", 3}}; +OVR_VERIFY_ARRAY_SIZE(VerticalJustification_Enums, 4); + +ovrEnumInfo eContentFlags_Enums[] = { + {"CONTENT_NONE", 0}, + {"CONTENT_SOLID", 1}, + {"CONTENT_ALL", 0x7fffffff}, +}; +OVR_VERIFY_ARRAY_SIZE(eContentFlags_Enums, 3); + +ovrEnumInfo VRMenuObjectType_Enums[] = { + {"VRMENU_CONTAINER", 0}, + {"VRMENU_STATIC", 1}, + {"VRMENU_BUTTON", 2}, + {"VRMENU_MAX", 3}}; +OVR_VERIFY_ARRAY_SIZE(VRMenuObjectType_Enums, VRMENU_MAX + 1); + +ovrEnumInfo VRMenuObjectFlag_Enums[] = { + {"VRMENUOBJECT_FLAG_NO_FOCUS_GAINED", 0}, + {"VRMENUOBJECT_DONT_HIT_ALL", 1}, + {"VRMENUOBJECT_DONT_HIT_TEXT", 2}, + {"VRMENUOBJECT_HIT_ONLY_BOUNDS", 3}, + {"VRMENUOBJECT_BOUND_ALL", 4}, + {"VRMENUOBJECT_FLAG_POLYGON_OFFSET", 5}, + {"VRMENUOBJECT_FLAG_NO_DEPTH", 6}, + {"VRMENUOBJECT_FLAG_NO_DEPTH_MASK", 7}, + {"VRMENUOBJECT_DONT_RENDER", 8}, + {"VRMENUOBJECT_DONT_RENDER_SURFACE", 9}, + {"VRMENUOBJECT_DONT_RENDER_TEXT", 10}, + {"VRMENUOBJECT_NO_GAZE_HILIGHT", 11}, + {"VRMENUOBJECT_RENDER_HIERARCHY_ORDER", 12}, + {"VRMENUOBJECT_FLAG_BILLBOARD", 13}, + {"VRMENUOBJECT_DONT_MOD_PARENT_COLOR", 14}, + {"VRMENUOBJECT_INSTANCE_TEXT", 15}}; +OVR_VERIFY_ARRAY_SIZE(VRMenuObjectFlag_Enums, VRMENUOBJECT_MAX); + +ovrEnumInfo VRMenuObjectInitFlag_Enums[] = { + {"VRMENUOBJECT_INIT_ALIGN_TO_VIEW", 0}, + {"VRMENUOBJECT_INIT_FORCE_POSITION", 1}}; +OVR_VERIFY_ARRAY_SIZE(VRMenuObjectInitFlag_Enums, 2); + +ovrEnumInfo SurfaceTextureType_Enums[] = { + {"SURFACE_TEXTURE_DIFFUSE", 0}, + {"SURFACE_TEXTURE_DIFFUSE_ALPHA_DISCARD", 1}, + {"SURFACE_TEXTURE_ADDITIVE", 2}, + {"SURFACE_TEXTURE_COLOR_RAMP", 3}, + {"SURFACE_TEXTURE_COLOR_RAMP_TARGET", 4}, + {"SURFACE_TEXTURE_ALPHA_MASK", 5}, + {"SURFACE_TEXTURE_MAX", 6}}; +OVR_VERIFY_ARRAY_SIZE(SurfaceTextureType_Enums, SURFACE_TEXTURE_MAX + 1); + +ovrEnumInfo VRMenuId_Enums[] = {{"INVALID_MENU_ID", INT_MIN}}; + +ovrEnumInfo VRMenuEventType_Enums[] = { + {"VRMENU_EVENT_FOCUS_GAINED", 0}, + {"VRMENU_EVENT_FOCUS_LOST", 1}, + {"VRMENU_EVENT_TOUCH_DOWN", 2}, + {"VRMENU_EVENT_TOUCH_UP", 3}, + {"VRMENU_EVENT_TOUCH_RELATIVE", 4}, + {"VRMENU_EVENT_TOUCH_ABSOLUTE", 5}, + {"VRMENU_EVENT_SWIPE_FORWARD", 6}, + {"VRMENU_EVENT_SWIPE_BACK", 7}, + {"VRMENU_EVENT_SWIPE_UP", 8}, + {"VRMENU_EVENT_SWIPE_DOWN", 9}, + {"VRMENU_EVENT_FRAME_UPDATE", 10}, + {"VRMENU_EVENT_SUBMIT_FOR_RENDERING", 11}, + {"VRMENU_EVENT_RENDER", 12}, + {"VRMENU_EVENT_OPENING", 13}, + {"VRMENU_EVENT_OPENED", 14}, + {"VRMENU_EVENT_CLOSING", 15}, + {"VRMENU_EVENT_CLOSED", 16}, + {"VRMENU_EVENT_INIT", 17}, + {"VRMENU_EVENT_SELECTED", 18}, + {"VRMENU_EVENT_DESELECTED", 19}, + {"VRMENU_EVENT_UPDATE_OBJECT", 20}, + {"VRMENU_EVENT_SWIPE_COMPLETE", 21}, + {"VRMENU_EVENT_ITEM_ACTION_COMPLETE", 22}, + {"VRMENU_EVENT_MAX", 23}}; +OVR_VERIFY_ARRAY_SIZE(VRMenuEventType_Enums, VRMENU_EVENT_MAX + 1); + +ovrEnumInfo AnimState_Enums[] = {{"ANIMSTATE_PAUSED", 0}, {"ANIMSTATE_PLAYING", 1}}; +OVR_VERIFY_ARRAY_SIZE(AnimState_Enums, OvrAnimComponent::ANIMSTATE_MAX); + +ovrEnumInfo EventDispatchType_Enums[] = { + {"EVENT_DISPATCH_TARGET", 0}, + {"EVENT_DISPATCH_FOCUS", 1}, + {"EVENT_DISPATCH_BROADCAST", 2}}; +OVR_VERIFY_ARRAY_SIZE(EventDispatchType_Enums, EVENT_DISPATCH_MAX); + +template +T* CreateObject(void* placementBuffer) { + if (placementBuffer != NULL) { + return new (placementBuffer) T(); + } + return new T(); +} + +void* Create_OvrDefaultComponent(void* placementBuffer) { + return CreateObject(placementBuffer); +} + +void* Create_OvrSurfaceAnimComponent(void* placementBuffer) { + return OvrSurfaceAnimComponent::Create(placementBuffer); +} + +void* Create_VRMenuSurfaceParms(void* placementBuffer) { + return CreateObject(placementBuffer); +} + +void* Create_VRMenuObjectParms(void* placementBuffer) { + return CreateObject(placementBuffer); +} + +void* Create_ovrSoundLimiter(void* placementBuffer) { + return CreateObject(placementBuffer); +} + +void* Create_string(void* placementBuffer) { + return CreateObject(placementBuffer); +} + +void* Create_bool(void* placementBuffer) { + return CreateObject(placementBuffer); +} + +void* Create_float(void* placementBuffer) { + return CreateObject(placementBuffer); +} + +void* Create_double(void* placementBuffer) { + return CreateObject(placementBuffer); +} + +void* Create_int(void* placementBuffer) { + return CreateObject(placementBuffer); +} + +template +void ResizeArray(void* arrayPtr, const int newSize) { + std::vector& a = *static_cast*>(arrayPtr); + a.resize(newSize); +} + +template +void ResizePtrArray(void* arrayPtr, const int newSize) { + std::vector& a = *static_cast*>(arrayPtr); + int oldSize = static_cast(a.size()); + a.resize(newSize); + + if (newSize > oldSize) { + // in-place construct since OVR::Array doesn't + for (int i = oldSize; i < newSize; ++i) { + ItemClass* item = static_cast(&a[i]); + new (item) ItemClass(); + } + } +} + +template +void SetArrayElementPtr(void* objPtr, const int index, ElementClass elementPtr) { + ArrayClass& a = *static_cast(objPtr); + a[index] = elementPtr; +} + +template +void SetArrayElement(void* objPtr, const int index, ElementClass* elementPtr) { + ArrayClass& a = *static_cast(objPtr); + a[index] = *elementPtr; +} + +void Resize_std_vector_VRMenuComponent_Ptr(void* objPtr, const int newSize) { + ResizePtrArray(objPtr, newSize); +} + +void SetArrayElementFn_std_vector_VRMenuComponent_Ptr( + void* objPtr, + const int index, + void* elementPtr) { + SetArrayElementPtr, VRMenuComponent*>( + objPtr, index, static_cast(elementPtr)); +} + +void Resize_std_vector_VRMenuSurfaceParms(void* objPtr, const int newSize) { + ResizeArray(objPtr, newSize); +} + +void Resize_std_vector_VRMenuObjectParms_Ptr(void* objPtr, const int newSize) { + ResizePtrArray(objPtr, newSize); +} + +void SetArrayElementFn_std_vector_VRMenuObjectParms_Ptr( + void* objPtr, + const int index, + void* elementPtr) { + SetArrayElementPtr, VRMenuObjectParms*>( + objPtr, index, static_cast(elementPtr)); +} + +void SetArrayElementFn_std_vector_VRMenuSurfaceParms( + void* objPtr, + const int index, + void* elementPtr) { + SetArrayElement, VRMenuSurfaceParms>( + objPtr, index, static_cast(elementPtr)); +} + +void SetArrayElementFn_string(void* objPtr, const int index, void* elementPtr) { + static_cast(objPtr)[index] = *static_cast(elementPtr); +} + +void SetArrayElementFn_int(void* objPtr, const int index, void* elementPtr) { + static_cast(objPtr)[index] = *static_cast(elementPtr); +} + +void SetArrayElementFn_GLuint(void* objPtr, const int index, void* elementPtr) { + static_cast(objPtr)[index] = *static_cast(elementPtr); +} + +void SetArrayElementFn_eSurfaceTextureType(void* objPtr, const int index, void* elementPtr) { + static_cast(objPtr)[index] = + *static_cast(elementPtr); +} + +//============================================================================================= +// Reflection Data +//============================================================================================= + +ovrMemberInfo Vector2i_Reflection[] = { + {"x", "int", NULL, ovrTypeOperator::NONE, offsetof(Vector2i, x), 0}, + {"y", "int", NULL, ovrTypeOperator::NONE, offsetof(Vector2i, y), 0}, + {}}; + +ovrMemberInfo Vector2f_Reflection[] = { + {"x", "float", NULL, ovrTypeOperator::NONE, offsetof(Vector2f, x), 0}, + {"y", "float", NULL, ovrTypeOperator::NONE, offsetof(Vector2f, y), 0}, + {}}; + +ovrMemberInfo Vector3f_Reflection[] = { + {"x", "float", NULL, ovrTypeOperator::NONE, offsetof(Vector3f, x), 0}, + {"y", "float", NULL, ovrTypeOperator::NONE, offsetof(Vector3f, y), 0}, + {"z", "float", NULL, ovrTypeOperator::NONE, offsetof(Vector3f, z), 0}, + {}}; + +ovrMemberInfo Vector4f_Reflection[] = { + {"x", "float", NULL, ovrTypeOperator::NONE, offsetof(Vector4f, x), 0}, + {"y", "float", NULL, ovrTypeOperator::NONE, offsetof(Vector4f, y), 0}, + {"z", "float", NULL, ovrTypeOperator::NONE, offsetof(Vector4f, z), 0}, + {"w", "float", NULL, ovrTypeOperator::NONE, offsetof(Vector4f, w), 0}, + {}}; + +ovrMemberInfo Quatf_Reflection[] = { + {"x", "float", NULL, ovrTypeOperator::NONE, offsetof(Quatf, x), 0}, + {"y", "float", NULL, ovrTypeOperator::NONE, offsetof(Quatf, y), 0}, + {"z", "float", NULL, ovrTypeOperator::NONE, offsetof(Quatf, z), 0}, + {"w", "float", NULL, ovrTypeOperator::NONE, offsetof(Quatf, w), 0}, + {}}; + +ovrMemberInfo TypesafeNumberT_longlong_eVRMenuId_INVALID_MENU_ID[] = { + {"Value", "long long", NULL, ovrTypeOperator::NONE, offsetof(VRMenuId_t, Value), 0}, + {}}; + +ovrMemberInfo Posef_Reflection[] = { + {"Position", "Vector3f", NULL, ovrTypeOperator::NONE, offsetof(Posef, Translation), 0}, + {"Orientation", "Quatf", NULL, ovrTypeOperator::NONE, offsetof(Posef, Rotation), 0}, + {}}; + +ovrMemberInfo VRMenuSurfaceParms_Reflection[] = { + {"SurfaceName", + "string", + NULL, + ovrTypeOperator::NONE, + offsetof(VRMenuSurfaceParms, SurfaceName), + 0}, + {"ImageNames", + "string[]", + NULL, + ovrTypeOperator::ARRAY, + offsetof(VRMenuSurfaceParms, ImageNames), + VRMENUSURFACE_IMAGE_MAX}, + {"ImageTexId", + "GLuint[]", + NULL, + ovrTypeOperator::ARRAY, + offsetof(VRMenuSurfaceParms, ImageTexId), + VRMENUSURFACE_IMAGE_MAX}, + {"ImageWidth", + "int[]", + NULL, + ovrTypeOperator::ARRAY, + offsetof(VRMenuSurfaceParms, ImageWidth), + VRMENUSURFACE_IMAGE_MAX}, + {"ImageHeight", + "int[]", + NULL, + ovrTypeOperator::ARRAY, + offsetof(VRMenuSurfaceParms, ImageHeight), + VRMENUSURFACE_IMAGE_MAX}, + {"TextureTypes", + "eSurfaceTextureType[]", + NULL, + ovrTypeOperator::ARRAY, + offsetof(VRMenuSurfaceParms, TextureTypes), + VRMENUSURFACE_IMAGE_MAX}, + {"Contents", + "BitFlagsT< eContentFlags >", + NULL, + ovrTypeOperator::NONE, + offsetof(VRMenuSurfaceParms, Contents), + 0}, + {"Color", "Vector4f", NULL, ovrTypeOperator::NONE, offsetof(VRMenuSurfaceParms, Color), 0}, + {"Anchors", "Vector2f", NULL, ovrTypeOperator::NONE, offsetof(VRMenuSurfaceParms, Anchors), 0}, + {"Border", "Vector4f", NULL, ovrTypeOperator::NONE, offsetof(VRMenuSurfaceParms, Border), 0}, + {"Dims", "Vector2f", NULL, ovrTypeOperator::NONE, offsetof(VRMenuSurfaceParms, Dims), 0}, + {"CropUV", "Vector4f", NULL, ovrTypeOperator::NONE, offsetof(VRMenuSurfaceParms, CropUV), 0}, + {"OffsetUVs", + "Vector2f", + NULL, + ovrTypeOperator::NONE, + offsetof(VRMenuSurfaceParms, OffsetUVs), + 0}, + {}}; + +ovrMemberInfo VRMenuFontParms_Reflection[] = { + {"AlignHoriz", + "HorizontalJustification", + NULL, + ovrTypeOperator::NONE, + offsetof(VRMenuFontParms, AlignHoriz), + 0}, + {"AlignVert", + "VerticalJustification", + NULL, + ovrTypeOperator::NONE, + offsetof(VRMenuFontParms, AlignVert), + 0}, + {"Billboard", "bool", NULL, ovrTypeOperator::NONE, offsetof(VRMenuFontParms, Billboard), 0}, + {"TrackRoll", "bool", NULL, ovrTypeOperator::NONE, offsetof(VRMenuFontParms, TrackRoll), 0}, + {"Outline", "bool", NULL, ovrTypeOperator::NONE, offsetof(VRMenuFontParms, Outline), 0}, + {"ColorCenter", "float", NULL, ovrTypeOperator::NONE, offsetof(VRMenuFontParms, ColorCenter)}, + {"AlphaCenter", "float", NULL, ovrTypeOperator::NONE, offsetof(VRMenuFontParms, AlphaCenter)}, + {"Scale", "float", NULL, ovrTypeOperator::NONE, offsetof(VRMenuFontParms, Scale), 0}, + {"WrapWidth", "float", NULL, ovrTypeOperator::NONE, offsetof(VRMenuFontParms, WrapWidth), 0}, + {"MaxLines", "int", NULL, ovrTypeOperator::NONE, offsetof(VRMenuFontParms, MaxLines), 0}, + {"MultiLine", "bool", NULL, ovrTypeOperator::NONE, offsetof(VRMenuFontParms, MultiLine), 0}, + {}}; + +ovrMemberInfo VRMenuObjectParms_Reflection[] = { + {"Type", + "eVRMenuObjectType", + NULL, + ovrTypeOperator::NONE, + offsetof(VRMenuObjectParms, Type), + 0}, + {"Flags", + "BitFlagsT< eVRMenuObjectFlags >", + NULL, + ovrTypeOperator::NONE, + offsetof(VRMenuObjectParms, Flags), + 0}, + {"InitFlags", + "BitFlagsT< eVRMenuObjectInitFlags >", + NULL, + ovrTypeOperator::NONE, + offsetof(VRMenuObjectParms, InitFlags), + 0}, + {"Components", + "std::vector< VRMenuComponent* >", + NULL, + ovrTypeOperator::ARRAY, + offsetof(VRMenuObjectParms, Components), + 0}, + {"SurfaceParms", + "std::vector< VRMenuSurfaceParms >", + NULL, + ovrTypeOperator::ARRAY, + offsetof(VRMenuObjectParms, SurfaceParms), + 0}, + {"Text", "string", NULL, ovrTypeOperator::NONE, offsetof(VRMenuObjectParms, Text), 0}, + {"LocalPose", "Posef", NULL, ovrTypeOperator::NONE, offsetof(VRMenuObjectParms, LocalPose), 0}, + {"LocalScale", + "Vector3f", + NULL, + ovrTypeOperator::NONE, + offsetof(VRMenuObjectParms, LocalScale), + 0}, + {"TextLocalPose", + "Posef", + NULL, + ovrTypeOperator::NONE, + offsetof(VRMenuObjectParms, TextLocalPose), + 0}, + {"TextLocalScale", + "Vector3f", + NULL, + ovrTypeOperator::NONE, + offsetof(VRMenuObjectParms, TextLocalScale), + 0}, + {"FontParms", + "VRMenuFontParms", + NULL, + ovrTypeOperator::NONE, + offsetof(VRMenuObjectParms, FontParms), + 0}, + {"Color", "Vector4f", NULL, ovrTypeOperator::NONE, offsetof(VRMenuObjectParms, Color), 0}, + {"TextColor", + "Vector4f", + NULL, + ovrTypeOperator::NONE, + offsetof(VRMenuObjectParms, TextColor), + 0}, + {"Id", + "TypesafeNumberT< long long, eVRMenuId, INVALID_MENU_ID >", + NULL, + ovrTypeOperator::NONE, + offsetof(VRMenuObjectParms, Id), + 0}, + {"ParentId", + "TypesafeNumberT< long long, eVRMenuId, INVALID_MENU_ID >", + NULL, + ovrTypeOperator::NONE, + offsetof(VRMenuObjectParms, ParentId), + 0}, + {"Contents", + "BitFlagsT< eContentFlags >", + NULL, + ovrTypeOperator::NONE, + offsetof(VRMenuObjectParms, Contents), + 0}, + {"Name", "string", NULL, ovrTypeOperator::NONE, offsetof(VRMenuObjectParms, Name), 0}, + {"ParentName", + "string", + NULL, + ovrTypeOperator::NONE, + offsetof(VRMenuObjectParms, ParentName), + 0}, + {"Tag", "string", NULL, ovrTypeOperator::NONE, offsetof(VRMenuObjectParms, Tag), 0}, + {"TexelCoords", + "bool", + NULL, + ovrTypeOperator::NONE, + offsetof(VRMenuObjectParms, TexelCoords), + 0}, + {"Selected", "bool", NULL, ovrTypeOperator::NONE, offsetof(VRMenuObjectParms, Selected), 0}, + {}}; + +ovrMemberInfo ovrSoundLimiter_Reflection[] = { + {"LastPlayTime", + "double", + NULL, + ovrTypeOperator::NONE, + offsetof(ovrSoundLimiter, LastPlayTime)}, + {}}; + +ovrMemberInfo Fader_Reflection[] = { + {"FadeState", "eFadeState", NULL, ovrTypeOperator::NONE, offsetof(Fader, FadeState)}, + {"PrePauseState", "eFadeState", NULL, ovrTypeOperator::NONE, offsetof(Fader, PrePauseState)}, + {"StartAlpha", "float", NULL, ovrTypeOperator::NONE, offsetof(Fader, StartAlpha)}, + {"FadeAlpha", "float", NULL, ovrTypeOperator::NONE, offsetof(Fader, FadeAlpha)}, + {}}; + +ovrMemberInfo SineFader_Reflection[] = {{}}; + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Winvalid-offsetof" +#endif + +ovrMemberInfo VRMenuComponent_Reflection[] = { + {"EventFlags", + "BitFlagsT< eVRMenuEventType, uint64_t >", + NULL, + ovrTypeOperator::NONE, + offsetof(VRMenuComponent, EventFlags)}, + {"Name", "string", NULL, ovrTypeOperator::NONE, offsetof(VRMenuComponent, Name)}, + {}}; + +ovrMemberInfo OvrDefaultComponent_Reflection[] = { + {"GazeOverSoundLimiter", + "ovrSoundLimiter", + NULL, + ovrTypeOperator::NONE, + offsetof(OvrDefaultComponent, GazeOverSoundLimiter)}, + {"DownSoundLimiter", + "ovrSoundLimiter", + NULL, + ovrTypeOperator::NONE, + offsetof(OvrDefaultComponent, DownSoundLimiter)}, + {"UpSoundLimiter", + "ovrSoundLimiter", + NULL, + ovrTypeOperator::NONE, + offsetof(OvrDefaultComponent, UpSoundLimiter)}, + {"HilightFader", + "SineFader", + NULL, + ovrTypeOperator::NONE, + offsetof(OvrDefaultComponent, HilightFader)}, + {"StartFadeInTime", + "double", + NULL, + ovrTypeOperator::NONE, + offsetof(OvrDefaultComponent, StartFadeInTime)}, + {"StartFadeOutTime", + "double", + NULL, + ovrTypeOperator::NONE, + offsetof(OvrDefaultComponent, StartFadeOutTime)}, + {"HilightOffset", + "Vector3f", + NULL, + ovrTypeOperator::NONE, + offsetof(OvrDefaultComponent, HilightOffset)}, + {"HilightScale", + "float", + NULL, + ovrTypeOperator::NONE, + offsetof(OvrDefaultComponent, HilightScale)}, + {"FadeDuration", + "float", + NULL, + ovrTypeOperator::NONE, + offsetof(OvrDefaultComponent, FadeDuration)}, + {"FadeDelay", "float", NULL, ovrTypeOperator::NONE, offsetof(OvrDefaultComponent, FadeDelay)}, + {"TextNormalColor", + "Vector4f", + NULL, + ovrTypeOperator::NONE, + offsetof(OvrDefaultComponent, TextNormalColor)}, + {"TextHilightColor", + "Vector4f", + NULL, + ovrTypeOperator::NONE, + offsetof(OvrDefaultComponent, TextHilightColor)}, + {"SuppressText", + "bool", + NULL, + ovrTypeOperator::NONE, + offsetof(OvrDefaultComponent, SuppressText)}, + {"UseSurfaceAnimator", + "bool", + NULL, + ovrTypeOperator::NONE, + offsetof(OvrDefaultComponent, UseSurfaceAnimator)}, + {"NoHilight", "bool", NULL, ovrTypeOperator::NONE, offsetof(OvrDefaultComponent, NoHilight)}, + {}}; + +ovrMemberInfo OvrAnimComponent_Reflection[] = { + {"BaseTime", "double", NULL, ovrTypeOperator::NONE, offsetof(OvrAnimComponent, BaseTime)}, + {"BaseFrame", "int", NULL, ovrTypeOperator::NONE, offsetof(OvrAnimComponent, BaseFrame)}, + {"CurFrame", "int", NULL, ovrTypeOperator::NONE, offsetof(OvrAnimComponent, CurFrame)}, + {"FramesPerSecond", + "float", + NULL, + ovrTypeOperator::NONE, + offsetof(OvrAnimComponent, FramesPerSecond)}, + {"AnimState", + "OvrAnimComponent::eAnimState", + NULL, + ovrTypeOperator::NONE, + offsetof(OvrAnimComponent, AnimState)}, + {"Looping", "bool", NULL, ovrTypeOperator::NONE, offsetof(OvrAnimComponent, Looping)}, + {"ForceVisibilityUpdate", + "bool", + NULL, + ovrTypeOperator::NONE, + offsetof(OvrAnimComponent, ForceVisibilityUpdate)}, + {"FractionalFrame", + "float", + NULL, + ovrTypeOperator::NONE, + offsetof(OvrAnimComponent, FractionalFrame)}, + {"FloatFrame", "double", NULL, ovrTypeOperator::NONE, offsetof(OvrAnimComponent, FloatFrame)}, + {}}; + +ovrMemberInfo OvrSurfaceAnimComponent_Reflection[] = { + {"SurfacesPerFrame", + "int", + NULL, + ovrTypeOperator::NONE, + offsetof(OvrSurfaceAnimComponent, SurfacesPerFrame)}, + {}}; + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +ovrTypeInfo TypeInfoList[] = { + {"bool", + NULL, + sizeof(bool), + NULL, + ParseBool, + Create_bool, + NULL, + NULL, + ovrArrayType::NONE, + false, + NULL}, + {"float", + NULL, + sizeof(float), + NULL, + ParseFloat, + Create_float, + NULL, + NULL, + ovrArrayType::NONE, + false, + NULL}, + {"double", + NULL, + sizeof(double), + NULL, + ParseDouble, + Create_double, + NULL, + NULL, + ovrArrayType::NONE, + false, + NULL}, + {"int", + NULL, + sizeof(int), + NULL, + ParseInt, + Create_int, + NULL, + NULL, + ovrArrayType::NONE, + false, + NULL}, + {"GLuint", + NULL, + sizeof(GLuint), + NULL, + ParseInt, + Create_int, + NULL, + NULL, + ovrArrayType::NONE, + false, + NULL}, + + {"eVRMenuObjectType", + NULL, + sizeof(eVRMenuObjectType), + VRMenuObjectType_Enums, + ParseEnum, + Create_int, + NULL, + NULL, + ovrArrayType::NONE, + false, + NULL}, + {"eVRMenuObjectFlags", + NULL, + sizeof(eVRMenuObjectFlags), + VRMenuObjectFlag_Enums, + ParseEnum, + Create_int, + NULL, + NULL, + ovrArrayType::NONE, + false, + NULL}, + {"eVRMenuObjectInitFlags", + NULL, + sizeof(eVRMenuObjectInitFlags), + VRMenuObjectInitFlag_Enums, + ParseEnum, + Create_int, + NULL, + NULL, + ovrArrayType::NONE, + false, + NULL}, + {"eVRMenuId", + NULL, + sizeof(eVRMenuId), + VRMenuId_Enums, + ParseEnum, + Create_int, + NULL, + NULL, + ovrArrayType::NONE, + false, + NULL}, + {"eSurfaceTextureType", + NULL, + sizeof(eSurfaceTextureType), + SurfaceTextureType_Enums, + ParseEnum, + Create_int, + NULL, + NULL, + ovrArrayType::NONE, + false, + NULL}, + {"HorizontalJustification", + NULL, + sizeof(HorizontalJustification), + HorizontalJustification_Enums, + ParseEnum, + Create_int, + NULL, + NULL, + ovrArrayType::NONE, + false, + NULL}, + {"VerticalJustification", + NULL, + sizeof(VerticalJustification), + VerticalJustification_Enums, + ParseEnum, + Create_int, + NULL, + NULL, + ovrArrayType::NONE, + false, + NULL}, + {"eContentFlags", + NULL, + sizeof(eContentFlags), + eContentFlags_Enums, + ParseEnum, + Create_int, + NULL, + NULL, + ovrArrayType::NONE, + false, + NULL}, + {"eVRMenuEventType", + NULL, + sizeof(eVRMenuEventType), + VRMenuEventType_Enums, + ParseEnum, + Create_int, + NULL, + NULL, + ovrArrayType::NONE, + false, + NULL}, + {"OvrAnimComponent::eAnimState", + NULL, + sizeof(OvrAnimComponent::eAnimState), + AnimState_Enums, + ParseEnum, + Create_int, + NULL, + NULL, + ovrArrayType::NONE, + false, + NULL}, + + {"BitFlagsT< eVRMenuObjectFlags >", + NULL, + sizeof(BitFlagsT), + VRMenuObjectFlag_Enums, + ParseBitFlags, + NULL, + NULL, + NULL, + ovrArrayType::NONE, + false, + NULL}, + {"BitFlagsT< eVRMenuObjectInitFlags >", + NULL, + sizeof(BitFlagsT), + VRMenuObjectInitFlag_Enums, + ParseBitFlags, + NULL, + NULL, + NULL, + ovrArrayType::NONE, + false, + NULL}, + {"BitFlagsT< eContentFlags >", + NULL, + sizeof(BitFlagsT), + eContentFlags_Enums, + ParseBitFlags, + NULL, + NULL, + NULL, + ovrArrayType::NONE, + false, + NULL}, + {"BitFlagsT< eVRMenuEventType, uint64_t >", + NULL, + sizeof(BitFlagsT), + VRMenuEventType_Enums, + ParseBitFlags, + NULL, + NULL, + NULL, + ovrArrayType::NONE, + false, + NULL}, + + {"string", + NULL, + sizeof(std::string), + NULL, + ParseString, + Create_string, + NULL, + NULL, + ovrArrayType::NONE, + false, + NULL}, + {"Vector2i", + NULL, + sizeof(Vector2i), + NULL, + ParseIntVector, + NULL, + NULL, + NULL, + ovrArrayType::NONE, + false, + Vector2i_Reflection}, + {"Vector2f", + NULL, + sizeof(Vector2f), + NULL, + ParseFloatVector, + NULL, + NULL, + NULL, + ovrArrayType::NONE, + false, + Vector2f_Reflection}, + {"Vector3f", + NULL, + sizeof(Vector3f), + NULL, + ParseFloatVector, + NULL, + NULL, + NULL, + ovrArrayType::NONE, + false, + Vector3f_Reflection}, + {"Vector4f", + NULL, + sizeof(Vector4f), + NULL, + ParseFloatVector, + NULL, + NULL, + NULL, + ovrArrayType::NONE, + false, + Vector4f_Reflection}, + {"Quatf", + NULL, + sizeof(Quatf), + NULL, + ParseFloatVector, + NULL, + NULL, + NULL, + ovrArrayType::NONE, + false, + Quatf_Reflection}, + {"Posef", + NULL, + sizeof(Posef), + NULL, + NULL, + NULL, + NULL, + NULL, + ovrArrayType::NONE, + false, + Posef_Reflection}, + + {"TypesafeNumberT< long long, eVRMenuId, INVALID_MENU_ID >", + NULL, + sizeof(TypesafeNumberT), + NULL, + ParseTypesafeNumber_long_long, + NULL, + NULL, + NULL, + ovrArrayType::NONE, + false, + TypesafeNumberT_longlong_eVRMenuId_INVALID_MENU_ID}, + + {"std::vector< VRMenuComponent* >", + NULL, + sizeof(std::vector), + NULL, + ParseArray, + NULL, + Resize_std_vector_VRMenuComponent_Ptr, + SetArrayElementFn_std_vector_VRMenuComponent_Ptr, + ovrArrayType::OVR_POINTER, + false, + NULL}, + {"std::vector< VRMenuSurfaceParms >", + NULL, + sizeof(std::vector), + NULL, + ParseArray, + NULL, + Resize_std_vector_VRMenuSurfaceParms, + SetArrayElementFn_std_vector_VRMenuSurfaceParms, + ovrArrayType::OVR_OBJECT, + false, + NULL}, + {"std::vector< VRMenuObjectParms* >", + NULL, + sizeof(std::vector), + NULL, + ParseArray, + NULL, + Resize_std_vector_VRMenuObjectParms_Ptr, + SetArrayElementFn_std_vector_VRMenuObjectParms_Ptr, + ovrArrayType::OVR_POINTER, + false, + NULL}, + + {"string[]", + NULL, + sizeof(std::string), + NULL, + ParseArray, + NULL, + NULL, + SetArrayElementFn_string, + ovrArrayType::C_OBJECT, + false, + NULL}, + {"int[]", + NULL, + sizeof(int), + NULL, + ParseArray, + NULL, + NULL, + SetArrayElementFn_int, + ovrArrayType::C_OBJECT, + false, + NULL}, + {"GLuint[]", + NULL, + sizeof(GLuint), + NULL, + ParseArray, + NULL, + NULL, + SetArrayElementFn_GLuint, + ovrArrayType::C_OBJECT, + false, + NULL}, + {"eSurfaceTextureType[]", + NULL, + sizeof(eSurfaceTextureType), + NULL, + ParseArray, + NULL, + NULL, + SetArrayElementFn_eSurfaceTextureType, + ovrArrayType::C_OBJECT, + false, + NULL}, + + {"VRMenuSurfaceParms", + NULL, + sizeof(VRMenuSurfaceParms), + NULL, + NULL, + Create_VRMenuSurfaceParms, + NULL, + NULL, + ovrArrayType::NONE, + false, + VRMenuSurfaceParms_Reflection}, + {"VRMenuFontParms", + NULL, + sizeof(VRMenuFontParms), + NULL, + NULL, + NULL, + NULL, + NULL, + ovrArrayType::NONE, + false, + VRMenuFontParms_Reflection}, + {"VRMenuObjectParms", + NULL, + sizeof(VRMenuObjectParms), + NULL, + NULL, + Create_VRMenuObjectParms, + NULL, + NULL, + ovrArrayType::NONE, + false, + VRMenuObjectParms_Reflection}, + + {"Fader", + NULL, + sizeof(Fader), + NULL, + NULL, + NULL, + NULL, + NULL, + ovrArrayType::NONE, + false, + Fader_Reflection}, + {"SineFader", + "Fader", + sizeof(SineFader), + NULL, + NULL, + NULL, + NULL, + NULL, + ovrArrayType::NONE, + false, + SineFader_Reflection}, + {"ovrSoundLimiter", + NULL, + sizeof(ovrSoundLimiter), + NULL, + NULL, + Create_ovrSoundLimiter, + NULL, + NULL, + ovrArrayType::NONE, + false, + ovrSoundLimiter_Reflection}, + {"VRMenuComponent", + NULL, + sizeof(VRMenuComponent), + NULL, + NULL, + NULL, + NULL, + NULL, + ovrArrayType::NONE, + true, + VRMenuComponent_Reflection}, + {"OvrDefaultComponent", + "VRMenuComponent", + sizeof(OvrDefaultComponent), + NULL, + NULL, + Create_OvrDefaultComponent, + NULL, + NULL, + ovrArrayType::NONE, + false, + OvrDefaultComponent_Reflection}, + {"OvrAnimComponent", + "VRMenuComponent", + sizeof(OvrAnimComponent), + NULL, + NULL, + NULL, + NULL, + NULL, + ovrArrayType::NONE, + true, + OvrAnimComponent_Reflection}, + {"OvrSurfaceAnimComponent", + "OvrAnimComponent", + sizeof(OvrSurfaceAnimComponent), + NULL, + NULL, + Create_OvrSurfaceAnimComponent, + NULL, + NULL, + ovrArrayType::NONE, + false, + OvrSurfaceAnimComponent_Reflection}, + + {}}; + +} // namespace OVRFW diff --git a/Samples/SampleXrFramework/Src/GUI/ReflectionData.h b/Samples/SampleXrFramework/Src/GUI/ReflectionData.h new file mode 100755 index 0000000..60d8621 --- /dev/null +++ b/Samples/SampleXrFramework/Src/GUI/ReflectionData.h @@ -0,0 +1,26 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/************************************************************************************ + +Filename : ReflectionData.h +Content : Data for introspection and reflection of C++ objects. +Created : 11/16/2015 +Authors : Jonathan E. Wright + +*************************************************************************************/ + +#pragma once + +#include "Reflection.h" + +#define MEMBER_SIZE(type_, member_) sizeof(((type_*)0)->member_) + +namespace OVRFW { + +//============================================================================================= +// Reflection Data +//============================================================================================= + +extern ovrTypeInfo TypeInfoList[]; + +} // namespace OVRFW diff --git a/Samples/SampleXrFramework/Src/GUI/SoundLimiter.cpp b/Samples/SampleXrFramework/Src/GUI/SoundLimiter.cpp new file mode 100755 index 0000000..6891e31 --- /dev/null +++ b/Samples/SampleXrFramework/Src/GUI/SoundLimiter.cpp @@ -0,0 +1,56 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/************************************************************************************ + +Filename : SoundLimiter.cpp +Content : Utility class for limiting how often sounds play. +Created : June 23, 2014 +Authors : Jonathan E. Wright + +*************************************************************************************/ + +#include "SoundLimiter.h" +#include "GuiSys.h" + +#include "OVR_Types.h" +#include "OVR_TypesafeNumber.h" +#include "OVR_BitFlags.h" +#include "OVR_Math.h" +#include "OVR_Std.h" + +#include "System.h" + +namespace OVRFW { + +//============================== +// ovrSoundLimiter::PlaySound +void ovrSoundLimiter::PlaySoundEffect( + OvrGuiSys& guiSys, + char const* soundName, + double const limitSeconds) { + double curTime = GetTimeInSeconds(); + double t = curTime - LastPlayTime; + // LOG_WITH_TAG( "VrMenu", "PlaySoundEffect( '%s', %.2f ) - t == %.2f : %s", soundName, + // limitSeconds, t, t >= limitSeconds ? "PLAYING" : "SKIPPING" ); + if (t >= limitSeconds) { + guiSys.GetSoundEffectPlayer().Play(soundName); + LastPlayTime = curTime; + } +} + +void ovrSoundLimiter::PlayMenuSound( + OvrGuiSys& guiSys, + char const* appendKey, + char const* soundName, + double const limitSeconds) { + char overrideSound[1024]; + OVR::OVR_sprintf(overrideSound, 1024, "%s_%s", appendKey, soundName); + + if (guiSys.GetSoundEffectPlayer().Has(overrideSound)) { + PlaySoundEffect(guiSys, overrideSound, limitSeconds); + } else { + PlaySoundEffect(guiSys, soundName, limitSeconds); + } +} + +} // namespace OVRFW diff --git a/Samples/SampleXrFramework/Src/GUI/SoundLimiter.h b/Samples/SampleXrFramework/Src/GUI/SoundLimiter.h new file mode 100755 index 0000000..4ec48e6 --- /dev/null +++ b/Samples/SampleXrFramework/Src/GUI/SoundLimiter.h @@ -0,0 +1,37 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/************************************************************************************ + +Filename : SoundLimiter.cpp +Content : Utility class for limiting how often sounds play. +Created : June 23, 2014 +Authors : Jonathan E. Wright + +*************************************************************************************/ + +#pragma once + +namespace OVRFW { + +class OvrGuiSys; + +//============================================================== +// ovrSoundLimiter +class ovrSoundLimiter { + public: + ovrSoundLimiter() : LastPlayTime(0.0) {} + + void PlaySoundEffect(OvrGuiSys& guiSys, char const* soundName, double const limitSeconds); + // Checks if menu specific sounds exists before playing the default vrappframework sound passed + // in. + void PlayMenuSound( + OvrGuiSys& guiSys, + char const* menuName, + char const* soundName, + double const limitSeconds); + + private: + double LastPlayTime; +}; + +} // namespace OVRFW diff --git a/Samples/SampleXrFramework/Src/GUI/VRMenu.cpp b/Samples/SampleXrFramework/Src/GUI/VRMenu.cpp new file mode 100755 index 0000000..35c064e --- /dev/null +++ b/Samples/SampleXrFramework/Src/GUI/VRMenu.cpp @@ -0,0 +1,716 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/************************************************************************************ + +Filename : VRMenu.cpp +Content : Class that implements the basic framework for a VR menu, holds all the + components for a single menu, and updates the VR menu event handler to + process menu events for a single menu. +Created : June 30, 2014 +Authors : Jonathan E. Wright + +*************************************************************************************/ + +// includes required for VRMenu.h + +// Make sure we get PRIu64 +#define __STDC_FORMAT_MACROS 1 + +#include "VRMenu.h" + +#include "VRMenuMgr.h" +#include "VRMenuEventHandler.h" +#include "GuiSys.h" +#include "Reflection.h" + +#include "OVR_FileSys.h" +#include "Misc/Log.h" + +using OVR::Bounds3f; +using OVR::Matrix4f; +using OVR::Posef; +using OVR::Quatf; +using OVR::Vector2f; +using OVR::Vector3f; +using OVR::Vector4f; + +inline Vector3f GetViewMatrixPosition(Matrix4f const& m) { + return m.Inverted().GetTranslation(); +} + +inline Vector3f GetViewMatrixForward(Matrix4f const& m) { + return Vector3f(-m.M[2][0], -m.M[2][1], -m.M[2][2]).Normalized(); +} + +namespace OVRFW { + +char const* VRMenu::MenuStateNames[MENUSTATE_MAX] = { + "MENUSTATE_OPENING", + "MENUSTATE_OPEN", + "MENUSTATE_CLOSING", + "MENUSTATE_CLOSED"}; + +// singleton so that other ids initialized during static initialization can be based off tis +VRMenuId_t VRMenu::GetRootId() { + VRMenuId_t ID_ROOT(-1); + return ID_ROOT; +} + +//============================== +// VRMenu::VRMenu +VRMenu::VRMenu(char const* name) + : CurMenuState(MENUSTATE_CLOSED), + NextMenuState(MENUSTATE_CLOSED), + EventHandler(NULL), + Name(name), + MenuDistance(1.45f), + IsInitialized(false), + ComponentsInitialized(false), + PostInitialized(false) { + EventHandler = new VRMenuEventHandler; +} + +//============================== +// VRMenu::~VRMenu +VRMenu::~VRMenu() { + delete EventHandler; + EventHandler = NULL; +} + +//============================== +// VRMenu::Create +VRMenu* VRMenu::Create(char const* menuName) { + return new VRMenu(menuName); +} + +//============================== +// VRMenu::Init +void VRMenu::Init( + OvrGuiSys& guiSys, + float const menuDistance, + VRMenuFlags_t const& flags, + std::vector comps /*= std::vector< VRMenuComponent* >()*/) { + assert(!RootHandle.IsValid()); + assert(!Name.empty()); + + Flags = flags; + MenuDistance = menuDistance; + + // create an empty root item + VRMenuObjectParms rootParms( + VRMENU_CONTAINER, + comps, + VRMenuSurfaceParms("root"), + "Root", + Posef(), + Vector3f(1.0f, 1.0f, 1.0f), + VRMenuFontParms(), + GetRootId(), + VRMenuObjectFlags_t(), + VRMenuObjectInitFlags_t()); + RootHandle = guiSys.GetVRMenuMgr().CreateObject(rootParms); + VRMenuObject* root = guiSys.GetVRMenuMgr().ToObject(RootHandle); + if (root == NULL) { + ALOGW("RootHandle (%" PRIu64 ") is invalid!", RootHandle.Get()); + return; + } + + IsInitialized = true; + ComponentsInitialized = false; +} + +void VRMenu::InitWithItems( + OvrGuiSys& guiSys, + float const menuDistance, + VRMenuFlags_t const& flags, + std::vector& itemParms) { + Init(guiSys, menuDistance, flags); + + if (Init_Impl(guiSys, menuDistance, flags, itemParms)) { + AddItems(guiSys, itemParms, GetRootHandle(), true); + } +} + +//============================================================== +// ChildParmsPair +class ChildParmsPair { + public: + ChildParmsPair(menuHandle_t const handle, VRMenuObjectParms const* parms) + : Handle(handle), Parms(parms) {} + ChildParmsPair() : Parms(NULL) {} + + menuHandle_t Handle; + VRMenuObjectParms const* Parms; +}; + +/// OVR_PERF_ACCUMULATOR_EXTERN( VerifyImageParms ); +/// OVR_PERF_ACCUMULATOR_EXTERN( FindSurfaceForGeoSizing ); +/// OVR_PERF_ACCUMULATOR_EXTERN( CreateImageGeometry ); +/// OVR_PERF_ACCUMULATOR_EXTERN( SelectProgramType ); + +//============================== +// VRMenu::AddItems +void VRMenu::AddItems( + OvrGuiSys& guiSys, + std::vector& itemParms, + menuHandle_t parentHandle_, + bool const recenter) { + // OVR_PERF_TIMER( AddItems ); + + const Vector3f up(0.0f, 1.0f, 0.0f); + const Vector3f left(1.0f, 0.0f, 0.0f); + + // create all items in the itemParms array, add each one to the parent, and position all items + // without the INIT_FORCE_POSITION flag vertically, one on top of the other + VRMenuObject* root = guiSys.GetVRMenuMgr().ToObject(parentHandle_); + assert(root != NULL); + +#if defined(OVR_USE_PERF_TIMER) + double const createStartTime = SystemClock::GetTimeInSeconds(); + double createObjectTotal = 0.0; +#endif + + std::vector pairs; + + Vector3f nextItemPos(0.0f); + int childIndex = 0; + for (int i = 0; i < static_cast(itemParms.size()); ++i) { + VRMenuObjectParms const* parms = itemParms[i]; + +#if defined(OVR_BUILD_DEBUG) + ALOG( + "## Menu[%d] Id=%d Text=%s", i, static_cast(parms->Id.Get()), parms->Text.c_str()); +#endif + +#if defined(OVR_BUILD_DEBUG) + // assure all ids are different + for (int j = 0; j < static_cast(itemParms.size()); ++j) { + if (j != i && parms->Id != VRMenuId_t() && parms->Id == itemParms[j]->Id) { + ALOG( + "Duplicate menu object ids for '%s' and '%s'!", + parms->Text.c_str(), + itemParms[j]->Text.c_str()); + } + } +#endif + +#if defined(OVR_USE_PERF_TIMER) + double const createObjectStartTime = SystemClock::GetTimeInSeconds(); +#endif + menuHandle_t handle = guiSys.GetVRMenuMgr().CreateObject(*parms); +#if defined(OVR_USE_PERF_TIMER) + createObjectTotal += SystemClock::GetTimeInSeconds() - createObjectStartTime; +#endif + + if (handle.IsValid() && root != NULL) { + if (parms->ParentId != root->GetId() && + (parms->ParentId.IsValid() || !parms->ParentName.empty())) { + pairs.push_back(ChildParmsPair(handle, parms)); + } + root->AddChild(guiSys.GetVRMenuMgr(), handle); + VRMenuObject* obj = + guiSys.GetVRMenuMgr().ToObject(root->GetChildHandleForIndex(childIndex++)); + if (obj != NULL && (parms->InitFlags & VRMENUOBJECT_INIT_FORCE_POSITION) == 0) { + Bounds3f const& lb = obj->GetLocalBounds(guiSys.GetDefaultFont()); + Vector3f size = lb.GetSize() * obj->GetLocalScale(); + Vector3f centerOfs(left * (size.x * -0.5f)); + if (!parms->ParentId.IsValid()) // only contribute to height if not being reparented + { + // stack the items + obj->SetLocalPose(Posef(Quatf(), nextItemPos + centerOfs)); + // calculate the total height + nextItemPos += up * size.y; + } else // otherwise center local to parent + { + obj->SetLocalPose(Posef(Quatf(), centerOfs)); + } + } + } + } + +#if defined(OVR_USE_PERF_TIMER) + ALOG("AddItems create took %f seconds", SystemClock::GetTimeInSeconds() - createStartTime); + ALOG("Creating Objects took %f seconds", createObjectTotal); +#endif + + { + // OVR_PERF_TIMER( Reparenting ); + + // reparent + std::vector reparented; + for (ChildParmsPair const& pair : pairs) { + menuHandle_t handle; + + if (!pair.Parms->ParentName.empty()) { + handle = HandleForName(guiSys.GetVRMenuMgr(), pair.Parms->ParentName.c_str()); + } else if (pair.Parms->ParentId.IsValid()) { + handle = HandleForId(guiSys.GetVRMenuMgr(), pair.Parms->ParentId); + } + VRMenuObject* parent = guiSys.GetVRMenuMgr().ToObject(handle); + assert(parent != nullptr); + if (parent != nullptr) { + root->RemoveChild(guiSys.GetVRMenuMgr(), pair.Handle); + parent->AddChild(guiSys.GetVRMenuMgr(), pair.Handle); + } + } + } + + if (recenter) { + // center the menu based on the height of the auto-placed children + float offset = nextItemPos.y * 0.5f; + Vector3f rootPos = root->GetLocalPosition(); + rootPos -= offset * up; + root->SetLocalPosition(rootPos); + } + + // make sure VRMENU_EVENT_INIT is sent for any new components + ComponentsInitialized = false; + +#if 0 + OVR_PERF_REPORT( VerifyImageParms ); + OVR_PERF_REPORT( FindSurfaceForGeoSizing ); + OVR_PERF_REPORT( CreateImageGeometry ); + OVR_PERF_REPORT( SelectProgramType ); +#endif +} + +//============================== +// VRMenu::Shutdown +void VRMenu::Shutdown(OvrGuiSys& guiSys) { + // OVR_ASSERT_WITH_TAG( IsInitialized, "VrMenu" ); + + Shutdown_Impl(guiSys); + + // this will free the root and all children + if (RootHandle.IsValid()) { + guiSys.GetVRMenuMgr().FreeObject(RootHandle); + RootHandle.Release(); + } +} + +//============================== +// VRMenu::RepositionMenu +void VRMenu::RepositionMenu(Matrix4f const& viewMatrix) { + if (Flags & VRMENU_FLAG_TRACK_GAZE) { + MenuPose = CalcMenuPosition(viewMatrix); + } else if (Flags & VRMENU_FLAG_PLACE_ON_HORIZON) { + MenuPose = CalcMenuPositionOnHorizon(viewMatrix); + } +} + +//============================== +// VRMenu::Frame +void VRMenu::Frame( + OvrGuiSys& guiSys, + ovrApplFrameIn const& vrFrame, + Matrix4f const& centerViewMatrix, + Matrix4f const& traceMat) { + // OVR_PERF_TIMER( VRMenu_Frame ); + + std::vector events; + events.reserve(1024); + // copy any pending events + for (const auto& pendingEvent : PendingEvents) { + events.push_back(pendingEvent); + } + PendingEvents.resize(0); + + if (!ComponentsInitialized) { + EventHandler->InitComponents(events); + ComponentsInitialized = true; + } + + // note we never enter this block unless our next state is different -- the switch statement + // inside this block is dependent on this. + if (NextMenuState != CurMenuState) { + ALOG("NextMenuState for '%s': %s", GetName(), MenuStateNames[NextMenuState]); + switch (NextMenuState) { + case MENUSTATE_OPENING: + assert(CurMenuState != NextMenuState); // logic below is dependent on this!! + if (CurMenuState == MENUSTATE_OPEN) { + // we're already open, so just set next to OPEN, too so we don't do any + // transition + NextMenuState = MENUSTATE_OPEN; + } else { + RepositionMenu(centerViewMatrix); + EventHandler->Opening(events); + Open_Impl(guiSys); + } + break; + case MENUSTATE_OPEN: { + assert(CurMenuState != NextMenuState); // logic below is dependent on this!! + if (CurMenuState != MENUSTATE_OPENING) { + ALOG("Instant open"); + } + OpenSoundLimiter.PlayMenuSound(guiSys, Name.c_str(), "sv_release_active", 0.1); + EventHandler->Opened(events); + } break; + case MENUSTATE_CLOSING: + assert(CurMenuState != NextMenuState); // logic below is dependent on this!! + if (CurMenuState == MENUSTATE_CLOSED) { + // we're already closed, so just set next to CLOSED, too so we don't do any + // transition + NextMenuState = MENUSTATE_CLOSED; + } else { + EventHandler->Closing(events); + } + break; + case MENUSTATE_CLOSED: { + assert(CurMenuState != NextMenuState); // logic below is dependent on this!! + if (CurMenuState != MENUSTATE_CLOSING) { + ALOG("Instant close"); + } + CloseSoundLimiter.PlayMenuSound(guiSys, Name.c_str(), "sv_deselect", 0.1); + EventHandler->Closed(events); + Close_Impl(guiSys); + } break; + default: + assert(!(bool)"Unhandled menu state!"); + break; + } + CurMenuState = NextMenuState; + } + + switch (CurMenuState) { + case MENUSTATE_OPENING: + if (IsFinishedOpening()) { + NextMenuState = MENUSTATE_OPEN; + } + break; + case MENUSTATE_OPEN: + break; + case MENUSTATE_CLOSING: + if (IsFinishedClosing()) { + NextMenuState = MENUSTATE_CLOSED; + } + break; + case MENUSTATE_CLOSED: + // handle remaining events -- note focus path is empty right now, but this may still + // broadcast messages to controls + EventHandler->HandleEvents(guiSys, vrFrame, RootHandle, events); + /// OVR_PERF_TIMER_STOP_MSG( VRMenu_Frame, Name.c_str() ); + return; + default: + assert(!(bool)"Unhandled menu state!"); + break; + } + + if (Flags & VRMENU_FLAG_TRACK_GAZE) { + MenuPose = CalcMenuPosition(centerViewMatrix); + } else if (Flags & VRMENU_FLAG_TRACK_GAZE_HORIZONTAL) { + MenuPose = CalcMenuPositionOnHorizon(centerViewMatrix); + } + + Frame_Impl(guiSys, vrFrame); + + { + // OVR_PERF_TIMER( VRMenu_Frame_EventHandler_Frame ); + EventHandler->Frame(guiSys, vrFrame, RootHandle, MenuPose, traceMat, events); + /// OVR_PERF_TIMER_STOP_MSG( VRMenu_Frame_EventHandler_Frame, Name.c_str() ); + } + { + // OVR_PERF_TIMER( VRMenu_Frame_EventHandler_HandleEvents ); + EventHandler->HandleEvents(guiSys, vrFrame, RootHandle, events); + /// OVR_PERF_TIMER_STOP_MSG( VRMenu_Frame_EventHandler_HandleEvents, Name.c_str() ); + } + + VRMenuObject* root = guiSys.GetVRMenuMgr().ToObject(RootHandle); + if (root != NULL) { + // OVR_PERF_TIMER( VRMenu_Frame_SubmitForRendering ); + VRMenuRenderFlags_t renderFlags; + guiSys.GetVRMenuMgr().SubmitForRendering( + guiSys, centerViewMatrix, RootHandle, MenuPose, renderFlags); + /// OVR_PERF_TIMER_STOP_MSG( VRMenu_Frame_SubmitForRendering, Name.c_str() ); + } + + if (!PostInitialized) { + PostInit_Impl(guiSys, vrFrame); + PostInitialized = true; + } + + /// OVR_PERF_TIMER_STOP_MSG( VRMenu_Frame, Name.c_str() ); +} + +//============================== +// VRMenu::OnKeyEvent +bool VRMenu::OnKeyEvent(OvrGuiSys& guiSys, int const keyCode, const int action) { + if (OnKeyEvent_Impl(guiSys, keyCode, action)) { + // consumed by sub class + return true; + } + return false; +} + +//============================== +// VRMenu::Open +void VRMenu::Open(OvrGuiSys& guiSys) { + ALOG( + "VRMenu::Open - '%s', pre - c: %s n: %s", + GetName(), + MenuStateNames[CurMenuState], + MenuStateNames[NextMenuState]); + if (CurMenuState == MENUSTATE_OPENING) { + // this is a NOP, never allow transitioning back to the same state + return; + } + NextMenuState = MENUSTATE_OPENING; + guiSys.MakeActive(this); + ALOG( + "VRMenu::Open - %s, post - c: %s n: %s", + GetName(), + MenuStateNames[CurMenuState], + MenuStateNames[NextMenuState]); +} + +//============================== +// VRMenu::Close +void VRMenu::Close(OvrGuiSys& guiSys, bool const instant) { + ALOG( + "VRMenu::Close - %s, pre - c: %s n: %s", + GetName(), + MenuStateNames[CurMenuState], + MenuStateNames[NextMenuState]); + if (CurMenuState == MENUSTATE_CLOSING) { + // this is a NOP, never allow transitioning back to the same state + return; + } + NextMenuState = instant ? MENUSTATE_CLOSED : MENUSTATE_CLOSING; + ALOG( + "VRMenu::Close - %s, post - c: %s n: %s", + GetName(), + MenuStateNames[CurMenuState], + MenuStateNames[NextMenuState]); +} + +//============================== +// VRMenu::HandleForId +menuHandle_t VRMenu::HandleForId(OvrVRMenuMgr const& menuMgr, VRMenuId_t const id) const { + VRMenuObject* root = menuMgr.ToObject(RootHandle); + assert(root != NULL); + return root->ChildHandleForId(menuMgr, id); +} + +//============================== +// VRMenu::ObjectForId +VRMenuObject* VRMenu::ObjectForId(OvrGuiSys const& guiSys, VRMenuId_t const id) const { + VRMenuObject* root = guiSys.GetVRMenuMgr().ToObject(RootHandle); + assert(root != NULL); + menuHandle_t handle = root->ChildHandleForId(guiSys.GetVRMenuMgr(), id); + return guiSys.GetVRMenuMgr().ToObject(handle); +} + +//============================== +// VRMenu::HandleForName +menuHandle_t VRMenu::HandleForName(OvrVRMenuMgr const& menuMgr, char const* name) const { + VRMenuObject* root = menuMgr.ToObject(RootHandle); + assert(root != NULL); + return root->ChildHandleForName(menuMgr, name); +} + +//============================== +// VRMenu::ObjectForName +VRMenuObject* VRMenu::ObjectForName(OvrGuiSys const& guiSys, char const* name) const { + VRMenuObject* root = guiSys.GetVRMenuMgr().ToObject(RootHandle); + assert(root != NULL); + menuHandle_t handle = root->ChildHandleForName(guiSys.GetVRMenuMgr(), name); + return guiSys.GetVRMenuMgr().ToObject(handle); +} + +//============================== +// VRMenu::IdForName +VRMenuId_t VRMenu::IdForName(OvrGuiSys const& guiSys, char const* name) const { + VRMenuObject* obj = ObjectForName(guiSys, name); + if (obj == nullptr) { + return VRMenuId_t(); + } + return obj->GetId(); +} + +//============================== +// VRMenu::CalcMenuPosition +Posef VRMenu::CalcMenuPosition(Matrix4f const& viewMatrix) const { + const Matrix4f invViewMatrix = viewMatrix.Inverted(); + const Vector3f viewPos(GetViewMatrixPosition(viewMatrix)); + const Vector3f viewFwd(GetViewMatrixForward(viewMatrix)); + + // spawn directly in front + Quatf rotation(-viewFwd, 0.0f); + Quatf viewRot(invViewMatrix); + Quatf fullRotation = rotation * viewRot; + fullRotation.Normalize(); + + Vector3f position(viewPos + viewFwd * MenuDistance); + + return Posef(fullRotation, position); +} + +//============================== +// VRMenu::CalcMenuPositionOnHorizon +Posef VRMenu::CalcMenuPositionOnHorizon(Matrix4f const& viewMatrix) const { + const Vector3f viewPos(GetViewMatrixPosition(viewMatrix)); + const Vector3f viewFwd(GetViewMatrixForward(viewMatrix)); + + // project the forward view onto the horizontal plane + Vector3f const up(0.0f, 1.0f, 0.0f); + float dot = viewFwd.Dot(up); + Vector3f horizontalFwd = + (dot < -0.99999f || dot > 0.99999f) ? Vector3f(1.0f, 0.0f, 0.0f) : viewFwd - (up * dot); + horizontalFwd.Normalize(); + + Matrix4f horizontalViewMatrix = Matrix4f::LookAtRH(Vector3f(0), horizontalFwd, up); + horizontalViewMatrix + .Transpose(); // transpose because we want the rotation opposite of where we're looking + + // this was only here to test rotation about the local axis + // Quatf rotation( -horizontalFwd, 0.0f ); + + Quatf viewRot(horizontalViewMatrix); + Quatf fullRotation = /*rotation * */ viewRot; + fullRotation.Normalize(); + + Vector3f position(viewPos + horizontalFwd * MenuDistance); + + return Posef(fullRotation, position); +} + +//============================== +// VRMenu::OnItemEvent +void VRMenu::OnItemEvent( + OvrGuiSys& guiSys, + ovrApplFrameIn const& vrFrame, + VRMenuId_t const itemId, + VRMenuEvent const& event) { + OnItemEvent_Impl(guiSys, vrFrame, itemId, event); +} + +//============================== +// VRMenu::Init_Impl +bool VRMenu::Init_Impl( + OvrGuiSys& guiSys, + float const menuDistance, + VRMenuFlags_t const& flags, + std::vector& itemParms) { + return true; +} + +//============================== +// VRMenu::PostInit_Impl +void VRMenu::PostInit_Impl(OvrGuiSys& guiSys, ovrApplFrameIn const& vrFrame) {} + +//============================== +// VRMenu::Shutdown_Impl +void VRMenu::Shutdown_Impl(OvrGuiSys& guiSys) {} + +//============================== +// VRMenu::Frame_Impl +void VRMenu::Frame_Impl(OvrGuiSys& guiSys, ovrApplFrameIn const& vrFrame) {} + +//============================== +// VRMenu::OnKeyEvent_Impl +bool VRMenu::OnKeyEvent_Impl(OvrGuiSys& guiSys, int const keyCode, const int action) { + return false; +} + +//============================== +// VRMenu::Open_Impl +void VRMenu::Open_Impl(OvrGuiSys& guiSys) {} + +//============================== +// VRMenu::Close_Impl +void VRMenu::Close_Impl(OvrGuiSys& guiSys) {} + +//============================== +// VRMenu::OnItemEvent_Impl +void VRMenu::OnItemEvent_Impl( + OvrGuiSys& guiSys, + ovrApplFrameIn const& vrFrame, + VRMenuId_t const itemId, + VRMenuEvent const& event) {} + +//============================== +// VRMenu::GetFocusedHandle() +menuHandle_t VRMenu::GetFocusedHandle() const { + if (EventHandler != NULL) { + return EventHandler->GetFocusedHandle(); + } + return menuHandle_t(); +} + +//============================== +// VRMenu::ResetMenuOrientation +void VRMenu::ResetMenuOrientation(Matrix4f const& viewMatrix) { + ALOG("ResetMenuOrientation for '%s'", GetName()); + ResetMenuOrientation_Impl(viewMatrix); +} + +//============================== +// VRMenu::ResetMenuOrientation_Impl +void VRMenu::ResetMenuOrientation_Impl(Matrix4f const& viewMatrix) { + RepositionMenu(viewMatrix); +} + +//============================== +// VRMenu::SetSelected +void VRMenu::SetSelected(VRMenuObject* obj, bool const selected) { + if (obj != NULL) { + if (obj->IsSelected() != selected) { + obj->SetSelected(selected); + VRMenuEvent ev( + selected ? VRMENU_EVENT_SELECTED : VRMENU_EVENT_DESELECTED, + EVENT_DISPATCH_TARGET, + obj->GetHandle(), + Vector3f(0.0f), + HitTestResult(), + ""); + PendingEvents.push_back(ev); + } + } +} + +//============================== +// VRMenu::SetSelected +void VRMenu::SetSelected(OvrGuiSys& guiSys, VRMenuId_t const itemId, bool const selected) { + VRMenuObject* obj = ObjectForId(guiSys, itemId); + return SetSelected(obj, selected); +} + +//============================== +// VRMenu::InitFromReflectionData +bool VRMenu::InitFromReflectionData( + OvrGuiSys& guiSys, + ovrFileSys& fileSys, + ovrReflection& refl, + ovrLocale const& locale, + char const* fileNames[], + float const menuDistance, + VRMenuFlags_t const& flags) { + std::vector itemParms; + for (int i = 0; fileNames[i] != nullptr; ++i) { + std::vector parmBuffer; + if (!fileSys.ReadFile(fileNames[i], parmBuffer)) { + DeletePointerArray(itemParms); + ALOG("Failed to load reflection file '%s'.", fileNames[i]); + return false; + } + + // Add a null terminator + parmBuffer.push_back('\0'); + +#if defined(OVR_BUILD_DEBUG) +/// ALOG( "Loaded reflection file:\n==============\n%s\n=================\n", &parmBuffer[0] ); +#endif + + ovrParseResult parseResult = + VRMenuObject::ParseItemParms(refl, locale, fileNames[i], parmBuffer, itemParms); + if (!parseResult) { + DeletePointerArray(itemParms); + ALOG("%s", parseResult.GetErrorText()); + return false; + } + } + + InitWithItems(guiSys, menuDistance, flags, itemParms); + return true; +} + +} // namespace OVRFW diff --git a/Samples/SampleXrFramework/Src/GUI/VRMenu.h b/Samples/SampleXrFramework/Src/GUI/VRMenu.h new file mode 100755 index 0000000..05edb92 --- /dev/null +++ b/Samples/SampleXrFramework/Src/GUI/VRMenu.h @@ -0,0 +1,272 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/************************************************************************************ + +Filename : VRMenu.h +Content : Class that implements the basic framework for a VR menu, holds all the + components for a single menu, and updates the VR menu event handler to + process menu events for a single menu. +Created : June 30, 2014 +Authors : Jonathan E. Wright + +*************************************************************************************/ + +#pragma once + +#include +#include +#include + +#include "OVR_BitFlags.h" +#include "OVR_Std.h" + +#include "FrameParams.h" +#include "Misc/Log.h" + +#include "VRMenuObject.h" +#include "SoundLimiter.h" +#include "GazeCursor.h" + +namespace OVRFW { + +class BitmapFont; +class BitmapFontSurface; +class VRMenuEventHandler; +class OvrGuiSys; + +enum eVRMenuFlags { + // initially place the menu in front of the user's view on the horizon plane but do not move to + // follow the user's gaze. + VRMENU_FLAG_PLACE_ON_HORIZON, + // place the menu directly in front of the user's view on the horizon plane each frame. The user + // will sill be able to gaze track vertically. + VRMENU_FLAG_TRACK_GAZE_HORIZONTAL, + // place the menu directly in front of the user's view each frame -- this means gaze tracking + // won't be available since the user can't look at different parts of the menu. + VRMENU_FLAG_TRACK_GAZE, + // If set, just consume the back key but do nothing with it (for warning menu that must be + // accepted) + VRMENU_FLAG_BACK_KEY_DOESNT_EXIT, + // If set, a short-press of the back key will exit the app when in this menu + VRMENU_FLAG_BACK_KEY_EXITS_APP, + // If set, return false so short press is passed to app + VRMENU_FLAG_SHORT_PRESS_HANDLED_BY_APP +}; + +typedef OVR::BitFlagsT VRMenuFlags_t; + +//============================================================== +// VRMenu +class VRMenu { + public: + friend class OvrGuiSysLocal; + + enum eMenuState { + MENUSTATE_OPENING, + MENUSTATE_OPEN, + MENUSTATE_CLOSING, + MENUSTATE_CLOSED, + MENUSTATE_MAX + }; + + static char const* MenuStateNames[MENUSTATE_MAX]; + + static VRMenuId_t GetRootId(); + + static VRMenu* Create(char const* menuName); + + bool InitFromReflectionData( + OvrGuiSys& guiSys, + ovrFileSys& fileSys, + ovrReflection& refl, + ovrLocale const& locale, + char const* fileNames[], + float const menuDistance, + VRMenuFlags_t const& flags); + + void Init( + OvrGuiSys& guiSys, + float const menuDistance, + VRMenuFlags_t const& flags, + std::vector comps = std::vector()); + void InitWithItems( + OvrGuiSys& guiSys, + float const menuDistance, + VRMenuFlags_t const& flags, + std::vector& itemParms); + + void AddItems( + OvrGuiSys& guiSys, + std::vector& itemParms, + menuHandle_t parentHandle, + bool const recenter); + void Shutdown(OvrGuiSys& guiSys); + void Frame( + OvrGuiSys& guiSys, + ovrApplFrameIn const& vrFrame, + OVR::Matrix4f const& viewMatrix, + OVR::Matrix4f const& traceMat); + + bool OnKeyEvent(OvrGuiSys& guiSys, int const keyCode, const int action); + + void Open(OvrGuiSys& guiSys); + void Close(OvrGuiSys& guiSys, bool const instant = false); + + bool IsOpen() const { + return CurMenuState == MENUSTATE_OPEN; + } + bool IsOpenOrOpening() const { + return CurMenuState == MENUSTATE_OPEN || CurMenuState == MENUSTATE_OPENING || + NextMenuState == MENUSTATE_OPEN || NextMenuState == MENUSTATE_OPENING; + } + + bool IsClosed() const { + return CurMenuState == MENUSTATE_CLOSED; + } + bool IsClosedOrClosing() const { + return CurMenuState == MENUSTATE_CLOSED || CurMenuState == MENUSTATE_CLOSING || + NextMenuState == MENUSTATE_CLOSED || NextMenuState == MENUSTATE_CLOSING; + } + + void OnItemEvent( + OvrGuiSys& guiSys, + ovrApplFrameIn const& vrFrame, + VRMenuId_t const itemId, + class VRMenuEvent const& event); + + virtual void OnJobCompleted(OvrGuiSys&, class ovrJob&) {} + + // Clients can query the current menu state but to change it they must use + // SetNextMenuState() and allow the menu to switch states when it can. + eMenuState GetCurMenuState() const { + return CurMenuState; + } + + // Returns the next menu state. + eMenuState GetNextMenuState() const { + return NextMenuState; + } + // Sets the next menu state. The menu will switch to that state at the next + // opportunity. + void SetNextMenuState(eMenuState const s) { + NextMenuState = s; + } + + menuHandle_t GetRootHandle() const { + return RootHandle; + } + menuHandle_t GetFocusedHandle() const; + OVR::Posef const& GetMenuPose() const { + return MenuPose; + } + void SetMenuPose(OVR::Posef const& pose) { + MenuPose = pose; + } + + menuHandle_t HandleForName(OvrVRMenuMgr const& menuMgr, char const* name) const; + menuHandle_t HandleForId(OvrVRMenuMgr const& menuMgr, VRMenuId_t const id) const; + + VRMenuObject* ObjectForName(OvrGuiSys const& guiSys, char const* name) const; + VRMenuObject* ObjectForId(OvrGuiSys const& guiSys, VRMenuId_t const id) const; + + VRMenuId_t IdForName(OvrGuiSys const& guiSys, char const* name) const; + + char const* GetName() const { + return Name.c_str(); + } + bool IsMenu(char const* menuName) const { + return OVR::OVR_stricmp(Name.c_str(), menuName) == 0; + } + + // Use an arbitrary view matrix. This is used when updating the menus and passing the current + // matrix + void RepositionMenu(OVR::Matrix4f const& viewMatrix); + + // Reset the MenuPose orientation - for now we assume identity orientation as the basis for all + // menus + void ResetMenuOrientation(OVR::Matrix4f const& viewMatrix); + + VRMenuFlags_t const& GetFlags() const { + return Flags; + } + void SetFlags(VRMenuFlags_t const& flags) { + Flags = flags; + } + + OVR::Posef CalcMenuPosition(OVR::Matrix4f const& viewMatrix) const; + + OVR::Posef CalcMenuPositionOnHorizon(OVR::Matrix4f const& viewMatrix) const; + + float GetMenuDistance() const { + return MenuDistance; + } + void SetMenuDistance(float const& menuDistance) { + MenuDistance = menuDistance; + } + + // Set the selected state of an object AND send an event for selection + void SetSelected(VRMenuObject* obj, bool const selected); + void SetSelected(OvrGuiSys& guiSys, VRMenuId_t const itemId, bool const selected); + + protected: + // only derived classes can instance a VRMenu + VRMenu(char const* name); + // only GuiSysLocal can free a VRMenu + virtual ~VRMenu(); + + private: + menuHandle_t + RootHandle; // handle to the menu's root item (to which all other items must be attached) + + eMenuState CurMenuState; // the current menu state + eMenuState NextMenuState; // the state the menu should move to next + + OVR::Posef MenuPose; // world-space position and orientation of this menu's root item + + ovrSoundLimiter OpenSoundLimiter; // prevents the menu open sound from playing too often + ovrSoundLimiter CloseSoundLimiter; // prevents the menu close sound from playing too often + + VRMenuEventHandler* EventHandler; + std::vector PendingEvents; // events pending since the last frame + + std::string Name; // name of the menu + + VRMenuFlags_t Flags; // various flags that dictate menu behavior + float MenuDistance; // distance from eyes + bool IsInitialized; // true if Init was called + bool ComponentsInitialized; // true if init message has been sent to components + bool PostInitialized; // true if post init was run + + private: + // return true to continue with normal initialization (adding items) or false to skip. + virtual bool Init_Impl( + OvrGuiSys& guiSys, + float const menuDistance, + VRMenuFlags_t const& flags, + std::vector& itemParms); + // called once from Frame() after all child items have received the init event. + virtual void PostInit_Impl(OvrGuiSys& guiSys, ovrApplFrameIn const& vrFrame); + virtual void Shutdown_Impl(OvrGuiSys& guiSys); + virtual void Frame_Impl(OvrGuiSys& guiSys, ovrApplFrameIn const& vrFrame); + // return true if the key was consumed. + virtual bool OnKeyEvent_Impl(OvrGuiSys& guiSys, int const keyCode, const int action); + virtual void Open_Impl(OvrGuiSys& guiSys); + virtual void Close_Impl(OvrGuiSys& guiSys); + virtual void OnItemEvent_Impl( + OvrGuiSys& guiSys, + ovrApplFrameIn const& vrFrame, + VRMenuId_t const itemId, + class VRMenuEvent const& event); + virtual void ResetMenuOrientation_Impl(OVR::Matrix4f const& viewMatrix); + + // return true when finished opening/closing - allowing derived menus to animate etc. during + // open/close + virtual bool IsFinishedOpening() const { + return true; + } + virtual bool IsFinishedClosing() const { + return true; + } +}; + +} // namespace OVRFW diff --git a/Samples/SampleXrFramework/Src/GUI/VRMenuComponent.cpp b/Samples/SampleXrFramework/Src/GUI/VRMenuComponent.cpp new file mode 100755 index 0000000..697b497 --- /dev/null +++ b/Samples/SampleXrFramework/Src/GUI/VRMenuComponent.cpp @@ -0,0 +1,52 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/************************************************************************************ + +Filename : VRMenuComponent.h +Content : Menuing system for VR apps. +Created : June 23, 2014 +Authors : Jonathan E. Wright + +*************************************************************************************/ + +#include "VRMenuComponent.h" +#include "VRMenuMgr.h" + +namespace OVRFW { + +const char* VRMenuComponent::TYPE_NAME = ""; + +//============================== +// VRMenuComponent::OnEvent +eMsgStatus VRMenuComponent::OnEvent( + OvrGuiSys& guiSys, + ovrApplFrameIn const& vrFrame, + VRMenuObject* self, + VRMenuEvent const& event) { + assert(self != NULL); + + //------------------- + // do any pre work that every event handler must do + //------------------- + + // LOG_WITH_TAG( "VrMenu", "OnEvent '%s' for '%s'", VRMenuEventTypeNames[event.EventType], + // self->GetText().ToCStr() ); + + // call the overloaded implementation + eMsgStatus status = OnEvent_Impl(guiSys, vrFrame, self, event); + + //------------------- + // do any post work that every event handle must do + //------------------- + + // When new items are added to a menu, the menu sends VRMENU_EVENT_INIT again so that those + // components will be initialized. In order to prevent components from getting initialized + // a second time, components clear their VRMENU_EVENT_INIT flag after each init event. + if (event.EventType == VRMENU_EVENT_INIT) { + EventFlags &= ~VRMenuEventFlags_t(VRMENU_EVENT_INIT); + } + + return status; +} + +} // namespace OVRFW diff --git a/Samples/SampleXrFramework/Src/GUI/VRMenuComponent.h b/Samples/SampleXrFramework/Src/GUI/VRMenuComponent.h new file mode 100755 index 0000000..a9c2ffa --- /dev/null +++ b/Samples/SampleXrFramework/Src/GUI/VRMenuComponent.h @@ -0,0 +1,166 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/************************************************************************************ + +Filename : VRMenuComponent.h +Content : Menuing system for VR apps. +Created : June 23, 2014 +Authors : Jonathan E. Wright + +*************************************************************************************/ + +#pragma once + +#include "VRMenuObject.h" +#include "VRMenuEvent.h" +#include "SoundLimiter.h" +#include "FrameParams.h" +#include + +namespace OVRFW { + +enum eMsgStatus { + MSG_STATUS_CONSUMED, // message was consumed, don't pass to anything else + MSG_STATUS_ALIVE // continue passing up +}; + +enum eVRMenuComponentFlags { + VRMENU_COMPONENT_EVENT_HANDLER, // handles events + VRMENU_COMPONENT_FRAME_UPDATE, // gets Frame updates +}; + +typedef OVR::BitFlagsT VRMenuComponentFlags_t; + +class VRMenuEvent; + +//============================================================== +// VRMenuComponent +// +// Base class for menu components +class VRMenuComponent { + public: + friend class VRMenuObject; + + static const int TYPE_ID = -1; + static const char* TYPE_NAME; + + explicit VRMenuComponent(VRMenuEventFlags_t const& eventFlags) : EventFlags(eventFlags) {} + virtual ~VRMenuComponent() {} + + bool HandlesEvent(VRMenuEventFlags_t const eventFlags) const { + return (EventFlags & eventFlags) != 0; + } + + // only called if the event's type flag is set in the component's EventFlags. + eMsgStatus OnEvent( + OvrGuiSys& guiSys, + ovrApplFrameIn const& vrFrame, + VRMenuObject* self, + VRMenuEvent const& event); + + VRMenuEventFlags_t GetEventFlags() const { + return EventFlags; + } + + virtual int GetTypeId() const { + return TYPE_ID; + } + virtual const char* GetTypeName() const { + return TYPE_NAME; + } + + const char* GetName() const { + return Name.c_str(); + } + void SetName(char const* name) { + Name = name; + } + + virtual void SetEnabled(const bool /*enabled*/) { + assert(false); + } + + protected: + void RemoveEventFlags(VRMenuEventFlags_t const& flags) { + EventFlags &= ~flags; + } + void AddEventFlags(VRMenuEventFlags_t const& flags) { + EventFlags |= flags; + } + void ClearEventFlags() { + EventFlags &= ~EventFlags; + } + + private: + virtual eMsgStatus OnEvent_Impl( + OvrGuiSys& guiSys, + ovrApplFrameIn const& vrFrame, + VRMenuObject* self, + VRMenuEvent const& event) = 0; + + private: + VRMenuEventFlags_t EventFlags; // used to dispatch events to the correct handler + std::string Name; // only needs to be set if the component will be searched by name +}; + +//============================================================== +// VRMenuComponent_OnFocusGained +class VRMenuComponent_OnFocusGained : public VRMenuComponent { + public: + VRMenuComponent_OnFocusGained() + : VRMenuComponent(VRMenuEventFlags_t(VRMENU_EVENT_FOCUS_GAINED)) {} +}; + +//============================================================== +// VRMenuComponent_OnFocusLost +class VRMenuComponent_OnFocusLost : public VRMenuComponent { + public: + VRMenuComponent_OnFocusLost() : VRMenuComponent(VRMenuEventFlags_t(VRMENU_EVENT_FOCUS_LOST)) {} +}; + +//============================================================== +// VRMenuComponent_OnTouchDown +class VRMenuComponent_OnTouchDown : public VRMenuComponent { + public: + VRMenuComponent_OnTouchDown() : VRMenuComponent(VRMenuEventFlags_t(VRMENU_EVENT_TOUCH_DOWN)) {} +}; + +//============================================================== +// VRMenuComponent_OnTouchUp +class VRMenuComponent_OnTouchUp : public VRMenuComponent { + public: + VRMenuComponent_OnTouchUp() : VRMenuComponent(VRMenuEventFlags_t(VRMENU_EVENT_TOUCH_UP)) {} +}; + +//============================================================== +// VRMenuComponent_OnSubmitForRendering +class VRMenuComponent_OnSubmitForRendering : public VRMenuComponent { + public: + VRMenuComponent_OnSubmitForRendering() + : VRMenuComponent(VRMenuEventFlags_t(VRMENU_EVENT_SUBMIT_FOR_RENDERING)) {} +}; + +//============================================================== +// VRMenuComponent_OnRender +class VRMenuComponent_OnRender : public VRMenuComponent { + public: + VRMenuComponent_OnRender() : VRMenuComponent(VRMenuEventFlags_t(VRMENU_EVENT_RENDER)) {} +}; + +//============================================================== +// VRMenuComponent_OnTouchRelative +class VRMenuComponent_OnTouchRelative : public VRMenuComponent { + public: + VRMenuComponent_OnTouchRelative() + : VRMenuComponent(VRMenuEventFlags_t(VRMENU_EVENT_TOUCH_RELATIVE)) {} +}; + +//============================================================== +// VRMenuComponent_OnTouchAbsolute +class VRMenuComponent_OnTouchAbsolute : public VRMenuComponent { + public: + VRMenuComponent_OnTouchAbsolute() + : VRMenuComponent(VRMenuEventFlags_t(VRMENU_EVENT_TOUCH_ABSOLUTE)) {} +}; + +} // namespace OVRFW diff --git a/Samples/SampleXrFramework/Src/GUI/VRMenuEvent.cpp b/Samples/SampleXrFramework/Src/GUI/VRMenuEvent.cpp new file mode 100755 index 0000000..7c34a0b --- /dev/null +++ b/Samples/SampleXrFramework/Src/GUI/VRMenuEvent.cpp @@ -0,0 +1,41 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/************************************************************************************ + +Filename : VRMenuEvent.cpp +Content : Event class for menu events. +Created : June 23, 2014 +Authors : Jonathan E. Wright + +*************************************************************************************/ + +#include "VRMenuEvent.h" + +namespace OVRFW { + +char const* VRMenuEvent::EventTypeNames[] = { + "VRMENU_EVENT_FOCUS_GAINED", + "VRMENU_EVENT_FOCUS_LOST", + "VRMENU_EVENT_TOUCH_DOWN", + "VRMENU_EVENT_TOUCH_UP", + "VRMENU_EVENT_TOUCH_RELATIVE", + "VRMENU_EVENT_TOUCH_ABSOLUTE", + "VRMENU_EVENT_SWIPE_FORWARD", + "VRMENU_EVENT_SWIPE_BACK", + "VRMENU_EVENT_SWIPE_UP", + "VRMENU_EVENT_SWIPE_DOWN", + "VRMENU_EVENT_FRAME_UPDATE", + "VRMENU_EVENT_SUBMIT_FOR_RENDERING", + "VRMENU_EVENT_RENDER", + "VRMENU_EVENT_OPENING", + "VRMENU_EVENT_OPENED", + "VRMENU_EVENT_CLOSING", + "VRMENU_EVENT_CLOSED", + "VRMENU_EVENT_INIT", + "VRMENU_EVENT_SELECTED", + "VRMENU_EVENT_DESELECTED", + "VRMENU_EVENT_UPDATE_OBJECT", + "VRMENU_EVENT_SWIPE_COMPLETE", + "VRMENU_EVENT_ITEM_ACTION_COMPLETE"}; + +} // namespace OVRFW diff --git a/Samples/SampleXrFramework/Src/GUI/VRMenuEvent.h b/Samples/SampleXrFramework/Src/GUI/VRMenuEvent.h new file mode 100755 index 0000000..d41b9a4 --- /dev/null +++ b/Samples/SampleXrFramework/Src/GUI/VRMenuEvent.h @@ -0,0 +1,111 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/************************************************************************************ + +Filename : VRMenuEvent.h +Content : Event class for menu events. +Created : June 23, 2014 +Authors : Jonathan E. Wright + +*************************************************************************************/ + +#pragma once + +#include "VRMenuObject.h" + +namespace OVRFW { + +enum eVRMenuEventType { + VRMENU_EVENT_FOCUS_GAINED, // TargetHandle is the handle of the object gaining focus, other + // handle is previous focus + VRMENU_EVENT_FOCUS_LOST, // TargetHandle is the handle of the object losing focus + VRMENU_EVENT_TOUCH_DOWN, // TargetHandle will be the focused handle + VRMENU_EVENT_TOUCH_UP, // TargetHandle will be the focused handle + VRMENU_EVENT_TOUCH_RELATIVE, // sent when a touch position changes, TargetHandle will be the + // focused handle + VRMENU_EVENT_TOUCH_ABSOLUTE, // sent whenever touch is down, TargetHandle will be the focused + // handle + VRMENU_EVENT_SWIPE_FORWARD, // sent for a forward swipe event, TargetHandle will be the focused + // handle + VRMENU_EVENT_SWIPE_BACK, // TargetHandle will be the focused handle + VRMENU_EVENT_SWIPE_UP, // TargetHandle will be the focused handle + VRMENU_EVENT_SWIPE_DOWN, // TargetHandle will be the focused handle + VRMENU_EVENT_FRAME_UPDATE, // TargetHandle should be empty + VRMENU_EVENT_SUBMIT_FOR_RENDERING, + VRMENU_EVENT_RENDER, + VRMENU_EVENT_OPENING, // sent when a menu starts to open + VRMENU_EVENT_OPENED, // sent when a menu opens + VRMENU_EVENT_CLOSING, // sent when a menu starts to close + VRMENU_EVENT_CLOSED, // sent when a menu closes + VRMENU_EVENT_INIT, // sent when the owning menu initializes + VRMENU_EVENT_SELECTED, // sent when a menu selects an object + VRMENU_EVENT_DESELECTED, // sent when a menu deselected an object + VRMENU_EVENT_UPDATE_OBJECT, // sent when a menu object needs to be updated + VRMENU_EVENT_SWIPE_COMPLETE, // sent when a swipe operation has been completed and touch up has + // triggered + + VRMENU_EVENT_ITEM_ACTION_COMPLETE, // item -> menu : item has completed some action + + VRMENU_EVENT_MAX +}; + +typedef OVR::BitFlagsT VRMenuEventFlags_t; + +enum eEventDispatchType { + EVENT_DISPATCH_TARGET, // send only to target + EVENT_DISPATCH_FOCUS, // send to all in focus path + EVENT_DISPATCH_BROADCAST, // send to all + EVENT_DISPATCH_MAX +}; + +//============================================================== +// VRMenuEvent +class VRMenuEvent { + public: + static char const* EventTypeNames[VRMENU_EVENT_MAX]; + + VRMenuEvent() + : EventType(VRMENU_EVENT_MAX), DispatchType(EVENT_DISPATCH_MAX), FloatValue(0.0f) {} + + // non-target + VRMenuEvent( + eVRMenuEventType const eventType, + eEventDispatchType const dispatchType, + menuHandle_t const& targetHandle, + OVR::Vector3f const& floatValue, + HitTestResult const& hitResult, + char const* message) + : EventType(eventType), + DispatchType(dispatchType), + TargetHandle(targetHandle), + FloatValue(floatValue), + HitResult(hitResult), + Message(message) {} + + // non-target with other handle + VRMenuEvent( + eVRMenuEventType const eventType, + eEventDispatchType const dispatchType, + menuHandle_t const& targetHandle, + menuHandle_t const& otherHandle, + OVR::Vector3f const& floatValue, + HitTestResult const& hitResult, + char const* message) + : EventType(eventType), + DispatchType(dispatchType), + TargetHandle(targetHandle), + OtherHandle(otherHandle), + FloatValue(floatValue), + HitResult(hitResult), + Message(message) {} + + eVRMenuEventType EventType; + eEventDispatchType DispatchType; + menuHandle_t TargetHandle; // valid only if targeted to a specific object + menuHandle_t OtherHandle; // varies dependent on event type + OVR::Vector3f FloatValue; + HitTestResult HitResult; + std::string Message; +}; + +} // namespace OVRFW diff --git a/Samples/SampleXrFramework/Src/GUI/VRMenuEventHandler.cpp b/Samples/SampleXrFramework/Src/GUI/VRMenuEventHandler.cpp new file mode 100755 index 0000000..df88303 --- /dev/null +++ b/Samples/SampleXrFramework/Src/GUI/VRMenuEventHandler.cpp @@ -0,0 +1,392 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/************************************************************************************ + +Filename : VRMenuEventHandler.cpp +Content : Menu component for handling hit tests and dispatching events. +Created : June 23, 2014 +Authors : Jonathan E. Wright + +*************************************************************************************/ + +#include "VRMenuEventHandler.h" + +#include "OVR_Std.h" + +#include "Misc/Log.h" + +#include "GazeCursor.h" +#include "VRMenuMgr.h" +#include "GuiSys.h" +#include "VRMenuComponent.h" + +using OVR::Bounds3f; +using OVR::Matrix4f; +using OVR::Posef; +using OVR::Quatf; +using OVR::Vector2f; +using OVR::Vector3f; +using OVR::Vector4f; + +namespace OVRFW { + +//============================== +// VRMenuEventHandler::VRMenuEventHandler +VRMenuEventHandler::VRMenuEventHandler() {} + +//============================== +// VRMenuEventHandler::~VRMenuEventHandler +VRMenuEventHandler::~VRMenuEventHandler() {} + +//============================== +// VRMenuEventHandler::Frame +void VRMenuEventHandler::Frame( + OvrGuiSys& guiSys, + ovrApplFrameIn const& vrFrame, + menuHandle_t const& rootHandle, + Posef const& menuPose, + Matrix4f const& traceMat, + std::vector& events) { + VRMenuObject* root = guiSys.GetVRMenuMgr().ToObject(rootHandle); + if (root == NULL) { + return; + } + + // find the object the gaze is touching and update gaze focus +#if 1 + const Vector3f viewPos(traceMat.GetTranslation()); + Matrix4f m(traceMat); + m.SetTranslation(Vector3f(0.0f)); + const Vector3f viewFwd = m.Transform(Vector3f(0.0f, 0.0f, -1.0f)).Normalized(); +#else + const Matrix4f viewMatrix = guiSys.GetApp()->GetLastViewMatrix(); + const Vector3f viewPos(GetViewMatrixPosition(viewMatrix)); + const Vector3f viewFwd(GetViewMatrixForward(viewMatrix)); +#endif + + HitTestResult result; + menuHandle_t hitHandle = + root->HitTest(guiSys, menuPose, viewPos, viewFwd, ContentFlags_t(CONTENT_SOLID), result); + result.RayStart = viewPos; + result.RayDir = viewFwd; + + VRMenuObject* hit = hitHandle.IsValid() ? guiSys.GetVRMenuMgr().ToObject(hitHandle) : NULL; + /* + if ( hit != NULL ) + { + guiSys.GetApp()->ShowInfoText( 0.0f, "%s", hit->GetText().c_str() ); + } + */ + bool focusChanged = (hitHandle != FocusedHandle); + if (focusChanged) { + // focus changed + VRMenuObject* oldFocus = guiSys.GetVRMenuMgr().ToObject(FocusedHandle); + if (oldFocus != NULL) { + // setup event for item losing the focus + VRMenuEvent event( + VRMENU_EVENT_FOCUS_LOST, + EVENT_DISPATCH_TARGET, + FocusedHandle, + Vector3f(0.0f), + result, + ""); + events.push_back(event); + } + if (hit != NULL) { + if ((hit->GetFlags() & VRMENUOBJECT_FLAG_NO_FOCUS_GAINED) == 0) { + // set up event for item gaining the focus + VRMenuEvent event( + VRMENU_EVENT_FOCUS_GAINED, + EVENT_DISPATCH_FOCUS, + hitHandle, + oldFocus != nullptr ? oldFocus->GetHandle() : menuHandle_t(), + Vector3f(0.0f), + result, + ""); + events.push_back(event); + } + } + FocusedHandle = hitHandle; + } + + { + // always post the frame event to the root + VRMenuEvent event( + VRMENU_EVENT_FRAME_UPDATE, + EVENT_DISPATCH_BROADCAST, + menuHandle_t(), + Vector3f(0.0f), + result, + ""); + events.push_back(event); + } +} + +//============================== +// VRMenuEventHandler::InitComponents +void VRMenuEventHandler::InitComponents(std::vector& events) { + VRMenuEvent event( + VRMENU_EVENT_INIT, + EVENT_DISPATCH_BROADCAST, + menuHandle_t(), + Vector3f(0.0f), + HitTestResult(), + ""); + events.push_back(event); +} + +//============================== +// VRMenuEventHandler::Opening +void VRMenuEventHandler::Opening(std::vector& events) { + ALOG("Opening"); + // broadcast the opening event + VRMenuEvent event( + VRMENU_EVENT_OPENING, + EVENT_DISPATCH_BROADCAST, + menuHandle_t(), + Vector3f(0.0f), + HitTestResult(), + ""); + events.push_back(event); +} + +//============================== +// VRMenuEventHandler::Opened +void VRMenuEventHandler::Opened(std::vector& events) { + ALOG("Opened"); + // broadcast the opened event + VRMenuEvent event( + VRMENU_EVENT_OPENED, + EVENT_DISPATCH_BROADCAST, + menuHandle_t(), + Vector3f(0.0f), + HitTestResult(), + ""); + events.push_back(event); +} + +//============================== +// VRMenuEventHandler::Closing +void VRMenuEventHandler::Closing(std::vector& events) { + ALOG("Closing"); + // broadcast the closing event + VRMenuEvent event( + VRMENU_EVENT_CLOSING, + EVENT_DISPATCH_BROADCAST, + menuHandle_t(), + Vector3f(0.0f), + HitTestResult(), + ""); + events.push_back(event); +} + +//============================== +// VRMenuEventHandler::Closed +void VRMenuEventHandler::Closed(std::vector& events) { + ALOG("Closed"); + // broadcast the closed event + VRMenuEvent closedEvent( + VRMENU_EVENT_CLOSED, + EVENT_DISPATCH_BROADCAST, + menuHandle_t(), + Vector3f(0.0f), + HitTestResult(), + ""); + events.push_back(closedEvent); + + if (FocusedHandle.IsValid()) { + VRMenuEvent focusLostEvent( + VRMENU_EVENT_FOCUS_LOST, + EVENT_DISPATCH_TARGET, + FocusedHandle, + Vector3f(0.0f), + HitTestResult(), + ""); + events.push_back(focusLostEvent); + FocusedHandle.Release(); + ALOG("Released FocusHandle"); + } +} + +//============================== +// LogEventType +static inline void LogEventType(VRMenuEvent const& event, char const* fmt, ...) { +#if defined(OVR_OS_ANDROID) + if (event.EventType != VRMENU_EVENT_TOUCH_RELATIVE) { + return; + } + + char fmtBuff[256]; + va_list args; + va_start(args, fmt); + vsnprintf(fmtBuff, sizeof(fmtBuff), fmt, args); + va_end(args); + + char buffer[512]; + OVR::OVR_sprintf( + buffer, sizeof(buffer), "%s: %s", VRMenuEvent::EventTypeNames[event.EventType], fmtBuff); + + __android_log_write(ANDROID_LOG_WARN, "VrMenu", buffer); +#endif +} + +//============================== +// FindTargetPath +static void FindTargetPath( + OvrGuiSys& guiSys, + menuHandle_t const curHandle, + std::vector& targetPath) { + VRMenuObject* obj = guiSys.GetVRMenuMgr().ToObject(curHandle); + if (obj != NULL) { + FindTargetPath(guiSys, obj->GetParentHandle(), targetPath); + targetPath.push_back(curHandle); + } +} + +//============================== +// FindTargetPath +static void FindTargetPath( + OvrGuiSys& guiSys, + menuHandle_t const rootHandle, + menuHandle_t const curHandle, + std::vector& targetPath) { + FindTargetPath(guiSys, curHandle, targetPath); + if (targetPath.size() == 0) { + targetPath.push_back(rootHandle); // ensure at least root is in the path + } +} + +//============================== +// VRMenuEventHandler::HandleEvents +void VRMenuEventHandler::HandleEvents( + OvrGuiSys& guiSys, + ovrApplFrameIn const& vrFrame, + menuHandle_t const rootHandle, + std::vector const& events) const { + VRMenuObject* root = guiSys.GetVRMenuMgr().ToObject(rootHandle); + if (root == NULL) { + return; + } + + // find the list of all objects that are in the focused path + std::vector focusPath; + focusPath.reserve(256); + FindTargetPath(guiSys, rootHandle, FocusedHandle, focusPath); + + std::vector targetPath; + + for (VRMenuEvent const& event : events) { + switch (event.DispatchType) { + case EVENT_DISPATCH_BROADCAST: { + // broadcast to everything + BroadcastEvent(guiSys, vrFrame, event, root); + } break; + case EVENT_DISPATCH_FOCUS: + // send to the focus path only -- this list should be parent -> child order + DispatchToPath(guiSys, vrFrame, event, focusPath, false); + break; + case EVENT_DISPATCH_TARGET: + if (targetPath.size() == 0 || event.TargetHandle != targetPath.back()) { + targetPath.clear(); + FindTargetPath(guiSys, rootHandle, event.TargetHandle, targetPath); + } + DispatchToPath(guiSys, vrFrame, event, targetPath, false); + break; + default: + assert(!(bool)"unknown dispatch type"); + break; + } + } +} + +//============================== +// VRMenuEventHandler::DispatchToComponents +bool VRMenuEventHandler::DispatchToComponents( + OvrGuiSys& guiSys, + ovrApplFrameIn const& vrFrame, + VRMenuEvent const& event, + VRMenuObject* receiver) const { + /// assert_WITH_TAG( receiver != NULL, "VrMenu" ); + + std::vector const& list = receiver->GetComponentList(); + int componentIndex = 0; + for (VRMenuComponent* menuComponent : list) { + if (menuComponent->HandlesEvent(VRMenuEventFlags_t(event.EventType))) { + LogEventType(event, "DispatchEvent: to '%s'", receiver->GetText().c_str()); + + if (menuComponent->OnEvent(guiSys, vrFrame, receiver, event) == MSG_STATUS_CONSUMED) { + LogEventType( + event, + "DispatchEvent: receiver '%s', component %i consumed event.", + receiver->GetText().c_str(), + componentIndex); + return true; // consumed by component + } + } + componentIndex++; + } + return false; +} + +//============================== +// VRMenuEventHandler::DispatchToPath +bool VRMenuEventHandler::DispatchToPath( + OvrGuiSys& guiSys, + ovrApplFrameIn const& vrFrame, + VRMenuEvent const& event, + std::vector const& path, + bool const log) const { + // send to the focus path only -- this list should be parent -> child order + for (int i = 0; i < static_cast(path.size()); ++i) { + VRMenuObject* obj = guiSys.GetVRMenuMgr().ToObject(path[i]); + char const* const indent = + " "; + // set to + if (obj != NULL && DispatchToComponents(guiSys, vrFrame, event, obj)) { + if (log) { + ALOG( + "%sDispatchToPath: %s, object '%s' consumed event.", + &indent[64 - i * 2], + VRMenuEvent::EventTypeNames[event.EventType], + (obj != NULL ? obj->GetText().c_str() : "")); + } + return true; // consumed by a component + } + if (log) { + ALOG( + "%sDispatchToPath: %s, object '%s' passed event.", + &indent[64 - i * 2], + VRMenuEvent::EventTypeNames[event.EventType], + obj != NULL ? obj->GetText().c_str() : ""); + } + } + return false; +} + +//============================== +// VRMenuEventHandler::BroadcastEvent +bool VRMenuEventHandler::BroadcastEvent( + OvrGuiSys& guiSys, + ovrApplFrameIn const& vrFrame, + VRMenuEvent const& event, + VRMenuObject* receiver) const { + /// assert_WITH_TAG( receiver != NULL, "VrMenu" ); + + // allow parent components to handle first + if (DispatchToComponents(guiSys, vrFrame, event, receiver)) { + return true; + } + + // if the parent did not consume, dispatch to children + int numChildren = receiver->NumChildren(); + for (int i = 0; i < numChildren; ++i) { + menuHandle_t childHandle = receiver->GetChildHandleForIndex(i); + VRMenuObject* child = guiSys.GetVRMenuMgr().ToObject(childHandle); + if (child != NULL && BroadcastEvent(guiSys, vrFrame, event, child)) { + return true; // consumed by child + } + } + return false; +} + +} // namespace OVRFW diff --git a/Samples/SampleXrFramework/Src/GUI/VRMenuEventHandler.h b/Samples/SampleXrFramework/Src/GUI/VRMenuEventHandler.h new file mode 100755 index 0000000..b3874d5 --- /dev/null +++ b/Samples/SampleXrFramework/Src/GUI/VRMenuEventHandler.h @@ -0,0 +1,79 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/************************************************************************************ + +Filename : VRMenuFrame.h +Content : Menu component for handling hit tests and dispatching events. +Created : June 23, 2014 +Authors : Jonathan E. Wright + +*************************************************************************************/ + +#pragma once + +#include "VRMenuObject.h" +#include "VRMenuEvent.h" +#include "GazeCursor.h" +#include "SoundLimiter.h" +#include "FrameParams.h" + +namespace OVRFW { + +//============================================================== +// VRMenuEventHandler +class VRMenuEventHandler { + public: + VRMenuEventHandler(); + ~VRMenuEventHandler(); + + void Frame( + OvrGuiSys& guiSys, + const ovrApplFrameIn& vrFrame, + menuHandle_t const& rootHandle, + OVR::Posef const& menuPose, + OVR::Matrix4f const& traceMat, + std::vector& events); + + void HandleEvents( + OvrGuiSys& guiSys, + const ovrApplFrameIn& vrFrame, + menuHandle_t const rootHandle, + std::vector const& events) const; + + void InitComponents(std::vector& events); + void Opening(std::vector& events); + void Opened(std::vector& events); + void Closing(std::vector& events); + void Closed(std::vector& events); + + menuHandle_t GetFocusedHandle() const { + return FocusedHandle; + } + + private: + menuHandle_t FocusedHandle; + + ovrSoundLimiter GazeOverSoundLimiter; + ovrSoundLimiter DownSoundLimiter; + ovrSoundLimiter UpSoundLimiter; + + private: + bool DispatchToComponents( + OvrGuiSys& guiSys, + ovrApplFrameIn const& vrFrame, + VRMenuEvent const& event, + VRMenuObject* receiver) const; + bool DispatchToPath( + OvrGuiSys& guiSys, + ovrApplFrameIn const& vrFrame, + VRMenuEvent const& event, + std::vector const& path, + bool const log) const; + bool BroadcastEvent( + OvrGuiSys& guiSys, + ovrApplFrameIn const& vrFrame, + VRMenuEvent const& event, + VRMenuObject* receiver) const; +}; + +} // namespace OVRFW diff --git a/Samples/SampleXrFramework/Src/GUI/VRMenuMgr.cpp b/Samples/SampleXrFramework/Src/GUI/VRMenuMgr.cpp new file mode 100755 index 0000000..81a277c --- /dev/null +++ b/Samples/SampleXrFramework/Src/GUI/VRMenuMgr.cpp @@ -0,0 +1,1397 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/************************************************************************************ + +Filename : OvrMenuMgr.cpp +Content : Menuing system for VR apps. +Created : May 23, 2014 +Authors : Jonathan E. Wright + +*************************************************************************************/ + +#include "VRMenuMgr.h" + +#include "Render/DebugLines.h" +#include "Render/BitmapFont.h" +#include "Misc/Log.h" + +#include "VRMenuObject.h" +#include "GuiSys.h" + +#include "OVR_Lexer2.h" + +using OVR::Bounds3f; +using OVR::Matrix4f; +using OVR::Posef; +using OVR::Quatf; +using OVR::Vector2f; +using OVR::Vector3f; +using OVR::Vector4f; + +namespace OVRFW { + +// diffuse-only programs +static const char* GUIDiffuseOnlyVertexShaderSrc = R"glsl( +uniform lowp vec4 UniformColor; +uniform lowp vec4 UniformFadeDirection; +uniform lowp vec2 UniformUVOffset; + +attribute vec4 Position; +attribute vec2 TexCoord; +attribute vec4 VertexColor; + +varying highp vec2 oTexCoord; +varying lowp vec4 oColor; + +void main() +{ + gl_Position = TransformVertex( Position ); + oTexCoord = vec2( TexCoord.x + UniformUVOffset.x, TexCoord.y + UniformUVOffset.y ); + oColor = UniformColor * VertexColor; + // Fade out vertices if direction is positive + if ( dot(UniformFadeDirection.xyz, UniformFadeDirection.xyz) > 0.0 ) + { + if ( dot(UniformFadeDirection.xyz, Position.xyz ) > 0.0 ) { oColor[3] = 0.0; } + } +} +)glsl"; + +static const char* GUIDiffuseOnlyFragmentShaderSrc = R"glsl( +uniform sampler2D Texture0; +uniform highp vec4 ClipUVs; + +varying highp vec2 oTexCoord; +varying lowp vec4 oColor; + +void main() +{ + if ( oTexCoord.x < ClipUVs.x || oTexCoord.y < ClipUVs.y || oTexCoord.x > ClipUVs.z || oTexCoord.y > ClipUVs.w ) + { + gl_FragColor = vec4( 0.0, 0.0, 0.0, 0.0 ); + } + else + { + gl_FragColor = oColor * texture2D( Texture0, oTexCoord ); + } +} +)glsl"; + +static const char* GUIDiffuseAlphaDiscardFragmentShaderSrc = R"glsl( +uniform sampler2D Texture0; +uniform highp vec4 ClipUVs; + +varying highp vec2 oTexCoord; +varying lowp vec4 oColor; + +void main() +{ + if ( oTexCoord.x < ClipUVs.x || oTexCoord.y < ClipUVs.y || oTexCoord.x > ClipUVs.z || oTexCoord.y > ClipUVs.w ) + { + gl_FragColor = vec4( 0.0, 0.0, 0.0, 0.0 ); + } + else + { + lowp vec4 texColor = texture2D( Texture0, oTexCoord ); + if ( texColor.w < 0.01 ) discard; + gl_FragColor = oColor * texColor; + } +} +)glsl"; + +// diffuse color ramped programs +static const char* GUIColorRampFragmentSrc = R"glsl( +uniform sampler2D Texture0; +uniform sampler2D Texture1; +uniform mediump vec2 ColorTableOffset; + +varying highp vec2 oTexCoord; +varying lowp vec4 oColor; + +void main() +{ + lowp vec4 texel = texture2D( Texture0, oTexCoord ); + lowp vec2 colorIndex = vec2( ColorTableOffset.x + texel.x, ColorTableOffset.y ); + lowp vec4 remappedColor = texture2D( Texture1, colorIndex.xy ); + gl_FragColor = oColor * vec4( remappedColor.xyz, texel.a ); +} +)glsl"; + +// diffuse + color ramped + target programs +static const char* GUIDiffuseColorRampTargetVertexShaderSrc = R"glsl( +uniform lowp vec4 UniformColor; +uniform lowp vec4 UniformFadeDirection; + +attribute vec4 Position; +attribute vec2 TexCoord; +attribute vec2 TexCoord1; +attribute vec4 VertexColor; + +varying highp vec2 oTexCoord; +varying highp vec2 oTexCoord1; +varying lowp vec4 oColor; + +void main() +{ + gl_Position = TransformVertex( Position ); + oTexCoord = TexCoord; + oTexCoord1 = TexCoord1; + oColor = UniformColor * VertexColor; + // Fade out vertices if direction is positive + if ( dot(UniformFadeDirection.xyz, UniformFadeDirection.xyz) > 0.0 ) + { + if ( dot(UniformFadeDirection.xyz, Position.xyz ) > 0.0 ) { oColor[3] = 0.0; } + } +} +)glsl"; + +static const char* GUIColorRampTargetFragmentSrc = R"glsl( +uniform sampler2D Texture0; +uniform sampler2D Texture1; // color ramp target +uniform sampler2D Texture2; // color ramp +uniform mediump vec2 ColorTableOffset; + +varying highp vec2 oTexCoord; +varying highp vec2 oTexCoord1; +varying lowp vec4 oColor; + +void main() +{ + mediump vec4 lookup = texture2D( Texture1, oTexCoord1 ); + mediump vec2 colorIndex = vec2( ColorTableOffset.x + lookup.x, ColorTableOffset.y ); + mediump vec4 remappedColor = texture2D( Texture2, colorIndex.xy ); + mediump vec4 texel = texture2D( Texture0, oTexCoord ); + mediump vec3 blended = ( texel.xyz * ( 1.0 - lookup.a ) ) + ( remappedColor.xyz * lookup.a ); + gl_FragColor = oColor * vec4( blended.xyz, texel.a ); +} +)glsl"; + +// diffuse + additive programs +static const char* GUITwoTextureColorModulatedShaderSrc = R"glsl( +uniform lowp vec4 UniformColor; +uniform lowp vec4 UniformFadeDirection; + +attribute vec4 Position; +attribute vec2 TexCoord; +attribute vec2 TexCoord1; +attribute vec4 VertexColor; +attribute vec4 Parms; + +varying highp vec2 oTexCoord; +varying highp vec2 oTexCoord1; +varying lowp vec4 oColor; + +void main() +{ + gl_Position = TransformVertex( Position ); + oTexCoord = TexCoord; + oTexCoord1 = TexCoord1; + oColor = UniformColor * VertexColor; + // Fade out vertices if direction is positive + if ( dot(UniformFadeDirection.xyz, UniformFadeDirection.xyz) > 0.0 ) + { + if ( dot(UniformFadeDirection.xyz, Position.xyz ) > 0.0 ) { oColor[3] = 0.0; } + } +} +)glsl"; + +static const char* GUIDiffusePlusAdditiveFragmentShaderSrc = R"glsl( +uniform sampler2D Texture0; +uniform sampler2D Texture1; + +varying highp vec2 oTexCoord; +varying highp vec2 oTexCoord1; +varying lowp vec4 oColor; + +void main() +{ + lowp vec4 diffuseTexel = texture2D( Texture0, oTexCoord ); + lowp vec4 additiveTexel = texture2D( Texture1, oTexCoord1 ); + lowp vec4 additiveModulated = vec4( additiveTexel.xyz * additiveTexel.a, 0.0 ); + gl_FragColor = min( diffuseTexel + additiveModulated, 1.0 ) * oColor; +} +)glsl"; + +// diffuse + diffuse program +// the alpha for the second diffuse is used to composite the color to the first diffuse and +// the alpha of the first diffuse is used to composite to the fragment. +static const char* GUIDiffuseCompositeFragmentShaderSrc = R"glsl( +uniform sampler2D Texture0; +uniform sampler2D Texture1; + +varying highp vec2 oTexCoord; +varying highp vec2 oTexCoord1; +varying lowp vec4 oColor; + +void main() +{ + lowp vec4 diffuse1Texel = texture2D( Texture0, oTexCoord ); + lowp vec4 diffuse2Texel = texture2D( Texture1, oTexCoord1 ); + gl_FragColor = vec4( diffuse1Texel.xyz * ( 1.0 - diffuse2Texel.a ) + diffuse2Texel.xyz * diffuse2Texel.a, diffuse1Texel.a ) * oColor; +} +)glsl"; + +// alpha map + diffuse program +// the alpha channel of first texture is to composite to the fragment +static const char* GUIAlphaDiffuseFragmentShaderSrc = R"glsl( +uniform sampler2D Texture0; +uniform sampler2D Texture1; + +varying highp vec2 oTexCoord; +varying highp vec2 oTexCoord1; +varying lowp vec4 oColor; + +void main() +{ + lowp vec4 diffuse1Texel = texture2D( Texture0, oTexCoord ); + lowp vec4 diffuse2Texel = texture2D( Texture1, oTexCoord1 ); + gl_FragColor = vec4( diffuse2Texel.xyz, diffuse1Texel.a ) * oColor; +} +)glsl"; + +//================================== +// ComposeHandle +menuHandle_t ComposeHandle(int const index, std::uint32_t const id) { + std::uint64_t handle = (((std::uint64_t)id) << 32ULL) | (std::uint64_t)index; + return menuHandle_t(handle); +} + +//================================== +// DecomposeHandle +void DecomposeHandle(menuHandle_t const handle, int& index, std::uint32_t& id) { + index = (int)(handle.Get() & 0xFFFFFFFF); + id = (std::uint32_t)(handle.Get() >> 32ULL); +} + +//================================== +// HandleComponentsAreValid +static bool HandleComponentsAreValid(int const index, std::uint32_t const id) { + if (id == INVALID_MENU_OBJECT_ID) { + return false; + } + if (index < 0) { + return false; + } + return true; +} + +//============================================================== +// SurfSort +class SurfSort { + public: + int64_t Key; + + bool operator<(SurfSort const& other) const { + return Key - other.Key > 0; // inverted because we want to render furthest-to-closest + } +}; + +//============================================================== +// VRMenuMgrLocal +class VRMenuMgrLocal : public OvrVRMenuMgr { + public: + static int const MAX_SUBMITTED = 256; + + VRMenuMgrLocal(OvrGuiSys& guiSys); + virtual ~VRMenuMgrLocal(); + + // Initialize the VRMenu system + virtual void Init(OvrGuiSys& guiSys); + // Shutdown the VRMenu syatem + virtual void Shutdown(); + + // creates a new menu object + virtual menuHandle_t CreateObject(VRMenuObjectParms const& parms); + // Frees a menu object. If the object is a child of a parent object, this will + // also remove the child from the parent. + virtual void FreeObject(menuHandle_t const handle); + // Returns true if the handle is valid. + virtual bool IsValid(menuHandle_t const handle) const; + // Return the object for a menu handle or NULL if the object does not exist or the + // handle is invalid; + virtual VRMenuObject* ToObject(menuHandle_t const handle) const; + + // Submits the specified menu object to be renderered + virtual void SubmitForRendering( + OvrGuiSys& guiSys, + Matrix4f const& centerViewMatrix, + menuHandle_t const handle, + Posef const& worldPose, + VRMenuRenderFlags_t const& flags); + + // Call once per frame before rendering to sort surfaces. + virtual void Finish(Matrix4f const& viewMatrix); + + virtual void AppendSurfaceList( + Matrix4f const& centerViewMatrix, + std::vector& surfaceList); + + virtual GlProgram const* GetGUIGlProgram(eGUIProgramType const programType) const; + + static VRMenuMgrLocal& ToLocal(OvrVRMenuMgr& menuMgr) { + return *(VRMenuMgrLocal*)&menuMgr; + } + + private: + //-------------------------------------------------------------- + // private methods + //-------------------------------------------------------------- + + // make a private assignment operator to prevent warning C4512: assignment operator could not be + // generated + VRMenuMgrLocal& operator=(const VRMenuMgrLocal&); + + void AddComponentToDeletionList(menuHandle_t const ownerHandle, VRMenuComponent* component); + void ExecutePendingComponentDeletions(); + + void CondenseList(); + void SubmitForRenderingRecursive( + OvrGuiSys& guiSys, + Matrix4f const& centerViewMatrix, + VRMenuRenderFlags_t const& flags, + VRMenuObject const* obj, + Posef const& parentModelPose, + Vector4f const& parentColor, + Vector3f const& parentScale, + Bounds3f& cullBounds, + SubmittedMenuObject* submitted, + int const maxIndices, + int& curIndex, + int const distanceIndex) const; + + //-------------------------------------------------------------- + // private members + //-------------------------------------------------------------- + OvrGuiSys& GuiSys; // reference to the GUI sys that owns this menu manager + std::uint32_t CurrentId; // ever-incrementing object ID (well... up to 4 billion or so :) + std::vector ObjectList; // list of all menu objects + std::vector FreeList; // list of free slots in the array + + std::vector + PendingDeletions; // list of components (and owning objects) that are pending deletion + + bool Initialized; // true if Init has been called + + SubmittedMenuObject Submitted[MAX_SUBMITTED]; // all objects that have been submitted for + // rendering on the current frame + std::vector + SortKeys; // sort key consisting of distance from view and submission index + int NumSubmitted; // number of currently submitted menu objects + mutable int NumToRender; // number of submitted objects to render + + GlProgram GUIProgramDiffuseOnly; // has a diffuse only + GlProgram GUIProgramDiffuseAlphaDiscard; // diffuse, but discard fragments with 0 alpha + GlProgram GUIProgramDiffusePlusAdditive; // has a diffuse and an additive + GlProgram GUIProgramDiffuseComposite; // has a two diffuse maps + GlProgram GUIProgramDiffuseColorRamp; // has a diffuse and color ramp, and color ramp target is + // the diffuse + GlProgram GUIProgramDiffuseColorRampTarget; // has diffuse, color ramp, and a separate color + // ramp target + GlProgram GUIProgramAlphaDiffuse; // alpha map + diffuse map + + static bool ShowCollision; // show collision bounds only + static bool ShowDebugBounds; // true to show the menu items' debug bounds. This is static so + // that the console command will turn on bounds for all activities. + static bool + ShowDebugHierarchy; // true to show the menu items' hierarchy. This is static so that the + // console command will turn on bounds for all activities. + static bool ShowDebugNormals; + static bool ShowPoses; + static bool ShowStats; // show stats like number of draw calls + static bool ShowWrapWidths; + + static void DebugCollision(void* appPtr, const char* cmdLine); + static void DebugMenuBounds(void* appPtr, const char* cmdLine); + static void DebugMenuHierarchy(void* appPtr, const char* cmdLine); + static void DebugMenuPoses(void* appPtr, const char* cmdLine); + static void DebugShowStats(void* appPtr, const char* cmdLine); + static void DebugWordWrap(void* appPtr, const char* cmdLine); +}; + +bool VRMenuMgrLocal::ShowCollision = false; +bool VRMenuMgrLocal::ShowDebugBounds = false; +bool VRMenuMgrLocal::ShowDebugHierarchy = false; +bool VRMenuMgrLocal::ShowDebugNormals = false; +bool VRMenuMgrLocal::ShowPoses = false; +bool VRMenuMgrLocal::ShowStats = false; +bool VRMenuMgrLocal::ShowWrapWidths = false; + +void VRMenuMgrLocal::DebugCollision(void* appPtr, const char* parms) { + ovrLexer lex(parms); + int show; + lex.ParseInt(show, 0); + ShowCollision = show != 0; + ALOG("DebugCollision( '%s' ): show = %i", parms, show); +} + +void VRMenuMgrLocal::DebugMenuBounds(void* appPtr, const char* parms) { + ovrLexer lex(parms); + int show; + lex.ParseInt(show, 0); + ShowDebugBounds = show != 0; + ALOG("DebugMenuBounds( '%s' ): show = %i", parms, show); +} + +void VRMenuMgrLocal::DebugMenuHierarchy(void* appPtr, const char* parms) { + ovrLexer lex(parms); + int show; + lex.ParseInt(show, 0); + ShowDebugHierarchy = show != 0; + ALOG("DebugMenuHierarchy( '%s' ): show = %i", parms, show); +} + +void VRMenuMgrLocal::DebugMenuPoses(void* appPtr, const char* parms) { + ovrLexer lex(parms); + int show; + lex.ParseInt(show, 0); + ShowPoses = show != 0; + ALOG("DebugMenuPoses( '%s' ): show = %i", parms, show); +} + +void VRMenuMgrLocal::DebugShowStats(void* appPtr, const char* parms) { + ovrLexer lex(parms); + int show; + lex.ParseInt(show, 0); + ShowStats = show != 0; + ALOG("ShowStats( '%s' ): show = %i", parms, show); +} + +void VRMenuMgrLocal::DebugWordWrap(void* appPtr, const char* parms) { + ovrLexer lex(parms); + int show; + lex.ParseInt(show, 0); + ShowWrapWidths = show != 0; + ALOG("ShowWrapWidths( '%s' ): show = %i", parms, show); +} + +//================================== +// VRMenuMgrLocal::VRMenuMgrLocal +VRMenuMgrLocal::VRMenuMgrLocal(OvrGuiSys& guiSys) + : GuiSys(guiSys), CurrentId(0), Initialized(false), NumSubmitted(0), NumToRender(0) {} + +//================================== +// VRMenuMgrLocal::~VRMenuMgrLocal +VRMenuMgrLocal::~VRMenuMgrLocal() {} + +//================================== +// VRMenuMgrLocal::Init +// +// Initialize the VRMenu system +void VRMenuMgrLocal::Init(OvrGuiSys& guiSys) { + ALOG("VRMenuMgrLocal::Init"); + if (Initialized) { + return; + } + + // diffuse only + if (GUIProgramDiffuseOnly.VertexShader == 0 || GUIProgramDiffuseOnly.FragmentShader == 0) { + static OVRFW::ovrProgramParm uniformParms[] = { + /// Vertex + {"UniformColor", OVRFW::ovrProgramParmType::FLOAT_VECTOR4}, + {"UniformFadeDirection", OVRFW::ovrProgramParmType::FLOAT_VECTOR4}, + {"UniformUVOffset", OVRFW::ovrProgramParmType::FLOAT_VECTOR2}, + /// Fragment + {"Texture0", OVRFW::ovrProgramParmType::TEXTURE_SAMPLED}, + {"ClipUVs", OVRFW::ovrProgramParmType::FLOAT_VECTOR4}, + }; + const int uniformCount = sizeof(uniformParms) / sizeof(OVRFW::ovrProgramParm); + ALOG("VRMenuMgrLocal::Init - compiling ... GUIProgramDiffuseOnly "); + GUIProgramDiffuseOnly = OVRFW::GlProgram::Build( + GUIDiffuseOnlyVertexShaderSrc, + GUIDiffuseOnlyFragmentShaderSrc, + uniformParms, + uniformCount); + } + // diffuse alpha discard only + if (GUIProgramDiffuseAlphaDiscard.VertexShader == 0 || + GUIProgramDiffuseAlphaDiscard.FragmentShader == 0) { + static OVRFW::ovrProgramParm uniformParms[] = { + /// Vertex + {"UniformColor", OVRFW::ovrProgramParmType::FLOAT_VECTOR4}, + {"UniformFadeDirection", OVRFW::ovrProgramParmType::FLOAT_VECTOR4}, + {"UniformUVOffset", OVRFW::ovrProgramParmType::FLOAT_VECTOR2}, + /// Fragment + {"Texture0", OVRFW::ovrProgramParmType::TEXTURE_SAMPLED}, + {"ClipUVs", OVRFW::ovrProgramParmType::FLOAT_VECTOR4}, + }; + const int uniformCount = sizeof(uniformParms) / sizeof(OVRFW::ovrProgramParm); + ALOG("VRMenuMgrLocal::Init - compiling ... GUIProgramDiffuseAlphaDiscard "); + GUIProgramDiffuseAlphaDiscard = OVRFW::GlProgram::Build( + GUIDiffuseOnlyVertexShaderSrc, + GUIDiffuseAlphaDiscardFragmentShaderSrc, + uniformParms, + uniformCount); + } + // diffuse + additive + if (GUIProgramDiffusePlusAdditive.VertexShader == 0 || + GUIProgramDiffusePlusAdditive.FragmentShader == 0) { + static OVRFW::ovrProgramParm uniformParms[] = { + /// Vertex + {"UniformColor", OVRFW::ovrProgramParmType::FLOAT_VECTOR4}, + {"UniformFadeDirection", OVRFW::ovrProgramParmType::FLOAT_VECTOR4}, + /// Fragment + {"Texture0", OVRFW::ovrProgramParmType::TEXTURE_SAMPLED}, + {"Texture1", OVRFW::ovrProgramParmType::TEXTURE_SAMPLED}, + }; + const int uniformCount = sizeof(uniformParms) / sizeof(OVRFW::ovrProgramParm); + ALOG("VRMenuMgrLocal::Init - compiling ... GUIProgramDiffusePlusAdditive "); + GUIProgramDiffusePlusAdditive = OVRFW::GlProgram::Build( + GUITwoTextureColorModulatedShaderSrc, + GUIDiffusePlusAdditiveFragmentShaderSrc, + uniformParms, + uniformCount); + } + // diffuse + diffuse + if (GUIProgramDiffuseComposite.VertexShader == 0 || + GUIProgramDiffuseComposite.FragmentShader == 0) { + static OVRFW::ovrProgramParm uniformParms[] = { + /// Vertex + {"UniformColor", OVRFW::ovrProgramParmType::FLOAT_VECTOR4}, + {"UniformFadeDirection", OVRFW::ovrProgramParmType::FLOAT_VECTOR4}, + /// Fragment + {"Texture0", OVRFW::ovrProgramParmType::TEXTURE_SAMPLED}, + {"Texture1", OVRFW::ovrProgramParmType::TEXTURE_SAMPLED}, + }; + const int uniformCount = sizeof(uniformParms) / sizeof(OVRFW::ovrProgramParm); + ALOG("VRMenuMgrLocal::Init - compiling ... GUIProgramDiffuseComposite "); + GUIProgramDiffuseComposite = OVRFW::GlProgram::Build( + GUITwoTextureColorModulatedShaderSrc, + GUIDiffuseCompositeFragmentShaderSrc, + uniformParms, + uniformCount); + } + // diffuse color ramped + if (GUIProgramDiffuseColorRamp.VertexShader == 0 || + GUIProgramDiffuseColorRamp.FragmentShader == 0) { + static OVRFW::ovrProgramParm uniformParms[] = { + /// Vertex + {"UniformColor", OVRFW::ovrProgramParmType::FLOAT_VECTOR4}, + {"UniformFadeDirection", OVRFW::ovrProgramParmType::FLOAT_VECTOR4}, + {"UniformUVOffset", OVRFW::ovrProgramParmType::FLOAT_VECTOR2}, + /// Fragment + {"Texture0", OVRFW::ovrProgramParmType::TEXTURE_SAMPLED}, + {"Texture1", OVRFW::ovrProgramParmType::TEXTURE_SAMPLED}, + {"ColorTableOffset", OVRFW::ovrProgramParmType::FLOAT_VECTOR2}, + }; + const int uniformCount = sizeof(uniformParms) / sizeof(OVRFW::ovrProgramParm); + ALOG("VRMenuMgrLocal::Init - compiling ... GUIProgramDiffuseColorRamp "); + GUIProgramDiffuseColorRamp = OVRFW::GlProgram::Build( + GUIDiffuseOnlyVertexShaderSrc, GUIColorRampFragmentSrc, uniformParms, uniformCount); + } + // diffuse, color ramp, and a specific target for the color ramp + if (GUIProgramDiffuseColorRampTarget.VertexShader == 0 || + GUIProgramDiffuseColorRampTarget.FragmentShader == 0) { + static OVRFW::ovrProgramParm uniformParms[] = { + /// Vertex + {"UniformColor", OVRFW::ovrProgramParmType::FLOAT_VECTOR4}, + {"UniformFadeDirection", OVRFW::ovrProgramParmType::FLOAT_VECTOR4}, + /// Fragment + {"Texture0", OVRFW::ovrProgramParmType::TEXTURE_SAMPLED}, + {"Texture1", OVRFW::ovrProgramParmType::TEXTURE_SAMPLED}, + {"Texture2", OVRFW::ovrProgramParmType::TEXTURE_SAMPLED}, + {"ColorTableOffset", OVRFW::ovrProgramParmType::FLOAT_VECTOR2}, + }; + const int uniformCount = sizeof(uniformParms) / sizeof(OVRFW::ovrProgramParm); + ALOG("VRMenuMgrLocal::Init - compiling ... GUIProgramDiffuseColorRampTarget "); + GUIProgramDiffuseColorRampTarget = OVRFW::GlProgram::Build( + GUIDiffuseColorRampTargetVertexShaderSrc, + GUIColorRampTargetFragmentSrc, + uniformParms, + uniformCount); + } + if (GUIProgramAlphaDiffuse.VertexShader == 0 || GUIProgramAlphaDiffuse.FragmentShader == 0) { + static OVRFW::ovrProgramParm uniformParms[] = { + /// Vertex + {"UniformColor", OVRFW::ovrProgramParmType::FLOAT_VECTOR4}, + {"UniformFadeDirection", OVRFW::ovrProgramParmType::FLOAT_VECTOR4}, + /// Fragment + {"Texture0", OVRFW::ovrProgramParmType::TEXTURE_SAMPLED}, + {"Texture1", OVRFW::ovrProgramParmType::TEXTURE_SAMPLED}, + }; + const int uniformCount = sizeof(uniformParms) / sizeof(OVRFW::ovrProgramParm); + ALOG("VRMenuMgrLocal::Init - compiling ... GUIProgramAlphaDiffuse "); + GUIProgramAlphaDiffuse = OVRFW::GlProgram::Build( + GUITwoTextureColorModulatedShaderSrc, + GUIAlphaDiffuseFragmentShaderSrc, + uniformParms, + uniformCount); + } + + Initialized = true; +} + +//================================== +// VRMenuMgrLocal::Shutdown +// +// Shutdown the VRMenu syatem +void VRMenuMgrLocal::Shutdown() { + if (!Initialized) { + return; + } + + GlProgram::Free(GUIProgramDiffuseOnly); + GlProgram::Free(GUIProgramDiffuseAlphaDiscard); + GlProgram::Free(GUIProgramDiffusePlusAdditive); + GlProgram::Free(GUIProgramDiffuseComposite); + GlProgram::Free(GUIProgramDiffuseColorRamp); + GlProgram::Free(GUIProgramDiffuseColorRampTarget); + GlProgram::Free(GUIProgramAlphaDiffuse); + + Initialized = false; +} + +//================================== +// VRMenuMgrLocal::CreateObject +// creates a new menu object +menuHandle_t VRMenuMgrLocal::CreateObject(VRMenuObjectParms const& parms) { + /// OVR_PERF_TIMER( CreatObject ); + + if (!Initialized) { + ALOGW("VRMenuMgrLocal::CreateObject - manager has not been initialized!"); + return menuHandle_t(); + } + + // validate parameters + if (parms.Type >= VRMENU_MAX) { + ALOGW("VRMenuMgrLocal::CreateObject - Invalid menu object type: %i", parms.Type); + return menuHandle_t(); + } + + // create the handle first so we can enforce setting it be requiring it to be passed to the + // constructor + int index = -1; + if (FreeList.size() > 0) { + index = FreeList.back(); + FreeList.pop_back(); + } else { + index = static_cast(ObjectList.size()); + } + + std::uint32_t id = ++CurrentId; + menuHandle_t handle = ComposeHandle(index, id); + // ALOG( "VRMenuMgrLocal::CreateObject - handle is %llu", handle.Get() ); + + VRMenuObject* obj = new VRMenuObject(parms, handle); + if (obj == NULL) { + ALOGW("VRMenuMgrLocal::CreateObject - failed to allocate menu object!"); + assert( + obj != NULL); // this would be bad -- but we're likely just going to explode elsewhere + return menuHandle_t(); + } + + obj->Init(GuiSys, parms); + + if (index == static_cast(ObjectList.size())) { + // we have to grow the array + ObjectList.push_back(obj); + } else { + // insert in existing slot + assert(ObjectList[index] == NULL); + ObjectList[index] = obj; + } + + return handle; +} + +//================================== +// VRMenuMgrLocal::FreeObject +// Frees a menu object. If the object is a child of a parent object, this will +// also remove the child from the parent. +void VRMenuMgrLocal::FreeObject(menuHandle_t const handle) { + int index; + std::uint32_t id; + DecomposeHandle(handle, index, id); + if (!HandleComponentsAreValid(index, id)) { + return; + } + if (ObjectList[index] == NULL) { + // already freed + return; + } + + VRMenuObject* obj = ObjectList[index]; + // remove this object from its parent's child list + if (obj->GetParentHandle().IsValid()) { + VRMenuObject* parentObj = ToObject(obj->GetParentHandle()); + if (parentObj != NULL) { + parentObj->RemoveChild(*this, handle); + } + } + + // free all of this object's children + obj->FreeChildren(*this); + + delete obj; + + // empty the slot + ObjectList[index] = NULL; + // add the index to the free list + FreeList.push_back(index); + + CondenseList(); +} + +//================================== +// VRMenuMgrLocal::CondenseList +// keeps the free list from growing too large when items are removed +void VRMenuMgrLocal::CondenseList() { + // we can only condense the array if we have a significant number of items at the end of the + // array buffer that are empty (because we cannot move an existing object around without + // changing its handle, too, which would invalidate any existing references to it). This is the + // difference between the current size and the array capacity. + int const MIN_FREE = 64; // very arbitray number + if (ObjectList.capacity() - ObjectList.size() < MIN_FREE) { + return; + } + + // shrink to current size + ObjectList.resize(ObjectList.size()); + + // create a new free list of just indices < the new size + std::vector newFreeList; + for (int i = 0; i < static_cast(FreeList.size()); ++i) { + if (FreeList[i] <= static_cast(ObjectList.size())) { + newFreeList.push_back(FreeList[i]); + } + } + FreeList = newFreeList; +} + +//================================== +// VRMenuMgrLocal::IsValid +bool VRMenuMgrLocal::IsValid(menuHandle_t const handle) const { + int index; + std::uint32_t id; + DecomposeHandle(handle, index, id); + return HandleComponentsAreValid(index, id); +} + +//================================== +// VRMenuMgrLocal::ToObject +// Return the object for a menu handle. +VRMenuObject* VRMenuMgrLocal::ToObject(menuHandle_t const handle) const { + int index; + std::uint32_t id; + DecomposeHandle(handle, index, id); + if (id == INVALID_MENU_OBJECT_ID) { + return NULL; + } + if (!HandleComponentsAreValid(index, id)) { + ALOGW("VRMenuMgrLocal::ToObject - invalid handle."); + return NULL; + } + if (index >= static_cast(ObjectList.size())) { + ALOGW("VRMenuMgrLocal::ToObject - index out of range."); + return NULL; + } + VRMenuObject* object = ObjectList[index]; + if (object == NULL) { + ALOGW("VRMenuMgrLocal::ToObject - slot empty."); + return NULL; // this can happen if someone is holding onto the handle of an object that's + // been freed + } + if (object->GetHandle() != handle) { + // if the handle of the object in the slot does not match, then the object the handle refers + // to was deleted and a new object is in the slot + ALOGW("VRMenuMgrLocal::ToObject - slot mismatch."); + return NULL; + } + return object; +} + +/* +static void LogBounds( const char * name, char const * prefix, Bounds3f const & bounds ) +{ + ALOG_WITH_TAG( "Spam", "'%s' %s: min( %.2f, %.2f, %.2f ) - max( %.2f, %.2f, %.2f )", + name, prefix, + bounds.GetMins().x, bounds.GetMins().y, bounds.GetMins().z, + bounds.GetMaxs().x, bounds.GetMaxs().y, bounds.GetMaxs().z ); +} +*/ + +/// OVR_PERF_ACCUMULATOR( SubmitForRenderingRecursive_DrawText3D ); +/// OVR_PERF_ACCUMULATOR( SubmitForRenderingRecursive_submit ); + +//============================== +// VRMenuMgrLocal::SubmitForRenderingRecursive +void VRMenuMgrLocal::SubmitForRenderingRecursive( + OvrGuiSys& guiSys, + Matrix4f const& centerViewMatrix, + VRMenuRenderFlags_t const& flags, + VRMenuObject const* obj, + Posef const& parentModelPose, + Vector4f const& parentColor, + Vector3f const& parentScale, + Bounds3f& cullBounds, + SubmittedMenuObject* submitted, + int const maxIndices, + int& curIndex, + int const distanceIndex) const { + if (curIndex >= maxIndices) { + // If this happens we're probably not correctly clearing the submitted surfaces each frame + // OR we've got a LOT of surfaces. + ALOG("maxIndices = %i, curIndex = %i", maxIndices, curIndex); + /// assert_WITH_TAG( curIndex < maxIndices, "VrMenu" ); + return; + } + + // check if this object is hidden + VRMenuObjectFlags_t const oFlags = obj->GetFlags(); + if (oFlags & VRMENUOBJECT_DONT_RENDER) { + return; + } + + Posef const& localPose = obj->GetLocalPose(); + Vector3f const& localScale = obj->GetLocalScale(); + Vector4f const& localColor = obj->GetColor(); + Posef curModelPose; + Vector4f curColor; + Vector3f scale; + + VRMenuObject::TransformByParent( + parentModelPose, + parentScale, + parentColor, + localPose, + localScale, + localColor, + oFlags, + curModelPose, + scale, + curColor); + + assert(obj != NULL); + + cullBounds = obj->GetLocalBounds(guiSys.GetDefaultFont()) * parentScale; + + int submissionIndex = -1; + if (obj->GetType() != VRMENU_CONTAINER) // containers never render, but their children may + { + Posef const& hilightPose = obj->GetHilightPose(); + Posef itemPose( + curModelPose.Rotation * hilightPose.Rotation, + curModelPose.Translation + + (curModelPose.Rotation * parentScale.EntrywiseMultiply(hilightPose.Translation))); + Matrix4f poseMat(itemPose.Rotation); + curModelPose = itemPose; // so children like the slider bar caret use our hilight offset and + // don't end up clipping behind us! + VRMenuRenderFlags_t rFlags = flags; + if (oFlags & VRMENUOBJECT_FLAG_POLYGON_OFFSET) { + rFlags |= VRMENU_RENDER_POLYGON_OFFSET; + } + if (oFlags & VRMENUOBJECT_FLAG_NO_DEPTH) { + rFlags |= VRMENU_RENDER_NO_DEPTH; + } + if (oFlags & VRMENUOBJECT_FLAG_NO_DEPTH_MASK) { + rFlags |= VRMENU_RENDER_NO_DEPTH_MASK; + } + if (oFlags & VRMENUOBJECT_INSTANCE_TEXT) { + rFlags |= VRMENU_RENDER_SUBMIT_TEXT_SURFACE; + } + + if (oFlags & VRMENUOBJECT_FLAG_BILLBOARD) { + Matrix4f invViewMatrix = centerViewMatrix.Transposed(); + itemPose.Rotation = Quatf(invViewMatrix); + } + + if (ShowPoses) { + Matrix4f const itemPoseMat(itemPose); + guiSys.GetDebugLines().AddLine( + itemPose.Translation, + itemPose.Translation + itemPoseMat.GetXBasis() * 0.05f, + Vector4f(0.0f, 1.0f, 0.0f, 1.0f), + Vector4f(0.0f, 1.0f, 0.0f, 1.0f), + 0, + false); + guiSys.GetDebugLines().AddLine( + itemPose.Translation, + itemPose.Translation + itemPoseMat.GetYBasis() * 0.05f, + Vector4f(1.0f, 0.0f, 0.0f, 1.0f), + Vector4f(1.0f, 0.0f, 0.0f, 1.0f), + 0, + false); + guiSys.GetDebugLines().AddLine( + itemPose.Translation, + itemPose.Translation + itemPoseMat.GetZBasis() * 0.05f, + Vector4f(0.0f, 0.0f, 1.0f, 1.0f), + Vector4f(0.0f, 0.0f, 1.0f, 1.0f), + 0, + false); + } + + if ((oFlags & VRMENUOBJECT_DONT_RENDER_SURFACE) == 0) { + // the menu object may have zero or more renderable surfaces (if 0, it may draw only + // text) + /// OVR_PERF_ACCUMULATE( SubmitForRenderingRecursive_submit ); + submissionIndex = curIndex; + std::vector const& surfaces = obj->GetSurfaces(); + for (int i = 0; i < static_cast(surfaces.size()); ++i) { + VRMenuSurface const& surf = surfaces[i]; + if (surf.IsRenderable()) { + SubmittedMenuObject& sub = submitted[curIndex]; + sub.SurfaceIndex = i; + sub.DistanceIndex = distanceIndex >= 0 ? distanceIndex : curIndex; + sub.Pose = itemPose; + sub.Scale = scale; + sub.Flags = rFlags; + sub.ColorTableOffset = obj->GetColorTableOffset(); + sub.SkipAdditivePass = !obj->IsHilighted(); + sub.Handle = obj->GetHandle(); + // modulate surface color with parent's current color + sub.Color = surf.GetColor() * curColor; + sub.Offsets = surf.GetAnchorOffsets(); + sub.FadeDirection = obj->GetFadeDirection(); + sub.ClipUVs = surf.GetClipUVs(); + sub.OffsetUVs = surf.GetOffsetUVs(); +#if defined(OVR_BUILD_DEBUG) + sub.SurfaceName = surf.GetName(); +#endif + sub.LocalBounds = cullBounds; + curIndex++; + } + } + /// OVR_PERF_TIMER_STOP( SubmitForRenderingRecursive_submit ); + } + + std::string const& text = obj->GetText(); + if ((oFlags & VRMENUOBJECT_DONT_RENDER_TEXT) == 0 && text.length() > 0) { + Posef const& textLocalPose = obj->GetTextLocalPose(); + Posef curTextPose; + curTextPose.Translation = + itemPose.Translation + (itemPose.Rotation * textLocalPose.Translation * scale); + curTextPose.Rotation = itemPose.Rotation * textLocalPose.Rotation; + + Matrix4f textMat(curTextPose); + Vector3f textUp = textMat.GetYBasis(); + Vector3f textNormal = textMat.GetZBasis(); + Vector3f position = curTextPose.Translation + + textNormal * 0.001f; // this is simply to prevent z-fighting right now + Vector3f textScale = scale * obj->GetTextLocalScale() * obj->GetWrapScale(); + + Vector4f textColor = obj->GetTextColor(); + // Apply parent's alpha influence + textColor.w *= parentColor.w; + VRMenuFontParms const& fp = obj->GetFontParms(); + fontParms_t fontParms; + fontParms.AlignHoriz = fp.AlignHoriz; + fontParms.AlignVert = fp.AlignVert; + fontParms.Billboard = fp.Billboard; + fontParms.TrackRoll = fp.TrackRoll; + fontParms.ColorCenter = fp.ColorCenter; + fontParms.AlphaCenter = fp.AlphaCenter; + + // We could re-create the text surface here every frame to handle all cases where any + // font parameters change, but that would be needlessly expensive. + if (rFlags & VRMENU_RENDER_SUBMIT_TEXT_SURFACE) { + assert(obj->TextSurface != nullptr); + Matrix4f scaleMatrix; + scaleMatrix.M[0][0] = parentScale.x; + scaleMatrix.M[1][1] = parentScale.y; + scaleMatrix.M[2][2] = parentScale.z; + obj->TextSurface->ModelMatrix = scaleMatrix * Matrix4f(curTextPose.Rotation); + obj->TextSurface->ModelMatrix.SetTranslation(curTextPose.Translation); + + // if we didn't submit anything but we have an instanced text surface, submit an + // invalid surface so that the text surface will be added to the surface list in + // BuildDrawSurface + if (curIndex - submissionIndex == 0) { + SubmittedMenuObject& sub = submitted[curIndex]; + sub.SurfaceIndex = -1; + sub.Pose = itemPose; + sub.Scale = scale; + sub.Flags = rFlags; + sub.Handle = obj->GetHandle(); + sub.Color = parentColor; + sub.LocalBounds = cullBounds; + curIndex++; + } + } else { + /// OVR_PERF_ACCUMULATE( SubmitForRenderingRecursive_DrawText3D ); + guiSys.GetDefaultFontSurface().DrawText3D( + guiSys.GetDefaultFont(), + fontParms, + position, + textNormal, + textUp, + textScale.x * fp.Scale, + textColor, + text.c_str()); + } + + if (ShowWrapWidths) { + // this shows a ruler for the wrap width when rendering text + Vector3f yofs(0.0f, 0.05f, 0.0f); + Vector3f xofs(fp.WrapWidth * 0.5f, 0.0f, 0.0f); + + guiSys.GetDebugLines().AddLine( + position - xofs - yofs, + position - xofs + yofs, + Vector4f(0.0f, 1.0f, 0.0f, 1.0f), + Vector4f(1.0f, 0.0f, 0.0f, 1.0f), + 0, + false); + + guiSys.GetDebugLines().AddLine( + position + xofs - yofs, + position + xofs + yofs, + Vector4f(0.0f, 1.0f, 0.0f, 1.0f), + Vector4f(1.0f, 0.0f, 0.0f, 1.0f), + 0, + false); + + guiSys.GetDebugLines().AddLine( + position - xofs, + position + xofs, + Vector4f(0.0f, 1.0f, 0.0f, 1.0f), + Vector4f(1.0f, 0.0f, 0.0f, 1.0f), + 0, + false); + } + } + // ALOG_WITH_TAG( "Spam", "AddPoint for '%s'", text.c_str() ); + // GetDebugLines().AddPoint( curModelPose.Position, 0.05f, 1, true ); + } + + // submit all children + if (obj->Children.size() > 0) { + // If this object has the render hierarchy order flag, then it and all its children should + // be depth sorted based on this object's distance + the inverse of the submission index. + // (inverted because we want a higher submission index to render after a lower submission + // index) + int di = distanceIndex; + if (di < 0 && (oFlags & VRMenuObjectFlags_t(VRMENUOBJECT_RENDER_HIERARCHY_ORDER))) { + di = curIndex; + } + + for (int i = 0; i < static_cast(obj->Children.size()); ++i) { + menuHandle_t childHandle = obj->Children[i]; + VRMenuObject const* child = static_cast(ToObject(childHandle)); + if (child == NULL) { + continue; + } + + Bounds3f childCullBounds; + SubmitForRenderingRecursive( + guiSys, + centerViewMatrix, + flags, + child, + curModelPose, + curColor, + scale, + childCullBounds, + submitted, + maxIndices, + curIndex, + di); + + Posef pose = child->GetLocalPose(); + pose.Translation = pose.Translation * scale; + childCullBounds = Bounds3f::Transform(pose, childCullBounds); + cullBounds = Bounds3f::Union(cullBounds, childCullBounds); + } + } + + obj->SetCullBounds(cullBounds); + + // VRMenuId_t debugId( 297 ); + if (ShowCollision) { + OvrCollisionPrimitive const* cp = obj->GetCollisionPrimitive(); + if (cp != NULL) { + cp->DebugRender(guiSys.GetDebugLines(), curModelPose, scale, ShowDebugNormals); + } + // if ( obj->GetId() == debugId ) + { + std::vector const& surfaces = obj->GetSurfaces(); + for (int i = 0; i < static_cast(surfaces.size()); ++i) { + VRMenuSurface const& surf = surfaces[i]; + surf.GetTris().DebugRender( + guiSys.GetDebugLines(), curModelPose, scale, ShowDebugNormals); + } + } + } + if (ShowDebugBounds) //&& obj->GetId() == debugId ) + { + { + // for debug drawing, put the cull bounds in world space + // LogBounds( obj->GetText().c_str(), "Transformed CullBounds", myCullBounds ); + guiSys.GetDebugLines().AddBounds( + curModelPose, cullBounds, Vector4f(0.0f, 1.0f, 1.0f, 1.0f)); + } + { + Bounds3f localBounds = obj->GetLocalBounds(guiSys.GetDefaultFont()) * parentScale; + // LogBounds( obj->GetText().c_str(), "localBounds", localBounds ); + guiSys.GetDebugLines().AddBounds( + curModelPose, localBounds, Vector4f(1.0f, 0.0f, 0.0f, 1.0f)); + Bounds3f textLocalBounds = obj->GetTextLocalBounds(guiSys.GetDefaultFont()); + Posef hilightPose = obj->GetHilightPose(); + textLocalBounds = Bounds3f::Transform( + Posef(hilightPose.Rotation, hilightPose.Translation * scale), textLocalBounds); + guiSys.GetDebugLines().AddBounds( + curModelPose, textLocalBounds * parentScale, Vector4f(1.0f, 1.0f, 0.0f, 1.0f)); + } + } + + // draw the hierarchy + if (ShowDebugHierarchy) { + fontParms_t fp; + fp.AlignHoriz = HORIZONTAL_CENTER; + fp.AlignVert = VERTICAL_CENTER; + fp.Billboard = true; +#if 0 + VRMenuObject const * parent = ToObject( obj->GetParentHandle() ); + if ( parent != NULL ) + { + Vector3f itemUp = curModelPose.Rotation * Vector3f( 0.0f, 1.0f, 0.0f ); + Vector3f itemNormal = curModelPose.Rotation * Vector3f( 0.0f, 0.0f, 1.0f ); + fontSurface.DrawTextBillboarded3D( font, fp, curModelPose.Translation, itemNormal, itemUp, + 0.5f, Vector4f( 1.0f, 0.0f, 1.0f, 1.0f ), obj->GetSurfaces()[0] ); //parent->GetText().c_str() ); + } +#endif + guiSys.GetDebugLines().AddLine( + parentModelPose.Translation, + curModelPose.Translation, + Vector4f(1.0f, 0.0f, 0.0f, 1.0f), + Vector4f(0.0f, 0.0f, 1.0f, 1.0f), + 5, + false); + if (obj->GetSurfaces().size() > 0) { + guiSys.GetDefaultFontSurface().DrawTextBillboarded3D( + guiSys.GetDefaultFont(), + fp, + curModelPose.Translation, + 0.5f, + Vector4f(0.8f, 0.8f, 0.8f, 1.0f), + obj->GetSurfaces()[0].GetName().c_str()); + } + } +} + +//============================== +// VRMenuMgrLocal::SubmitForRendering +// Submits the specified menu object and it's children +void VRMenuMgrLocal::SubmitForRendering( + OvrGuiSys& guiSys, + Matrix4f const& centerViewMatrix, + menuHandle_t const handle, + Posef const& worldPose, + VRMenuRenderFlags_t const& flags) { + // ALOG( "VRMenuMgrLocal::SubmitForRendering" ); + if (NumSubmitted >= MAX_SUBMITTED) { + ALOGW("Too many menu objects submitted!"); + return; + } + VRMenuObject* obj = static_cast(ToObject(handle)); + if (obj == NULL) { + return; + } + + Bounds3f cullBounds; + SubmitForRenderingRecursive( + guiSys, + centerViewMatrix, + flags, + obj, + worldPose, + Vector4f(1.0f), + Vector3f(1.0f), + cullBounds, + Submitted, + MAX_SUBMITTED, + NumSubmitted, + -1); + + /// OVR_PERF_REPORT( SubmitForRenderingRecursive_submit ); + /// OVR_PERF_REPORT( SubmitForRenderingRecursive_DrawText3D ); +} + +//============================== +// VRMenuMgrLocal::Finish +void VRMenuMgrLocal::Finish(Matrix4f const& viewMatrix) { + // free any deleted component objects + ExecutePendingComponentDeletions(); + + if (NumSubmitted == 0) { + NumToRender = 0; + return; + } + + Matrix4f invViewMatrix = viewMatrix.Inverted(); // if the view is never scaled or sheared we + // could use Transposed() here instead + Vector3f viewPos = invViewMatrix.GetTranslation(); + + // sort surfaces + SortKeys.resize(NumSubmitted); + for (int i = 0; i < NumSubmitted; ++i) { + // The sort key is a combination of the distance squared, reinterpreted as an integer, and + // the submission index. This sorts on distance while still allowing submission order to + // contribute in the equal case. The DistanceIndex is used to force a submitted object to + // use some other object's distance instead of its own, allowing a group of objects to sort + // against all other object's based on a single distance. Objects using the same + // DistanceIndex will then be sorted against each other based only on their submission + // index. + float const distSq = + (Submitted[Submitted[i].DistanceIndex].Pose.Translation - viewPos).LengthSq(); + int64_t sortKey = *reinterpret_cast(&distSq); + SortKeys[i].Key = (sortKey << 32ULL) | + (NumSubmitted - + i); // invert i because we want items submitted sooner to be considered "further away" + } + + std::sort(SortKeys.begin(), SortKeys.end()); + + NumToRender = NumSubmitted; + NumSubmitted = 0; +} + +//============================== +// VRMenuMgrLocal::AppendSurfaceList +void VRMenuMgrLocal::AppendSurfaceList( + Matrix4f const& centerViewMatrix, + std::vector& surfaceList) { + if (NumToRender == 0) { + return; + } + + for (int i = 0; i < NumToRender; ++i) { + int idx = abs(static_cast(SortKeys[i].Key & 0xFFFFFFFF) - NumToRender); + SubmittedMenuObject const& cur = Submitted[idx]; + + VRMenuObject* obj = static_cast(ToObject(cur.Handle)); + if (obj != NULL) { + Vector3f translation( + cur.Pose.Translation.x + cur.Offsets.x, + cur.Pose.Translation.y + cur.Offsets.y, + cur.Pose.Translation.z); + + Matrix4f transform(cur.Pose.Rotation); + if (cur.Flags & VRMENU_RENDER_BILLBOARD) { + Matrix4f invViewMatrix = centerViewMatrix.Inverted(); + Vector3f viewPos = invViewMatrix.GetTranslation(); + + Vector3f normal = viewPos - cur.Pose.Translation; + Vector3f up(0.0f, 1.0f, 0.0f); + float length = normal.Length(); + if (length > MATH_FLOAT_SMALLEST_NON_DENORMAL) { + normal.Normalize(); + if (normal.Dot(up) > MATH_FLOAT_SMALLEST_NON_DENORMAL) { + transform = + Matrix4f::CreateFromBasisVectors(normal, Vector3f(0.0f, 1.0f, 0.0f)); + } + } + } + + Matrix4f scaleMatrix; + scaleMatrix.M[0][0] = cur.Scale.x; + scaleMatrix.M[1][1] = cur.Scale.y; + scaleMatrix.M[2][2] = cur.Scale.z; + + transform *= scaleMatrix; + transform.SetTranslation(translation); + + // TODO: do we need to use SubmittedMenuObject at all now that we can use + // ovrSurfaceDef? We still need to sort for now but ideally SurfaceRenderer + // would sort all surfaces before rendering. + + obj->BuildDrawSurface( + *this, + transform, + cur.SurfaceName.c_str(), + cur.SurfaceIndex, + cur.Color, + cur.FadeDirection, + cur.ColorTableOffset, + cur.ClipUVs, + cur.OffsetUVs, + cur.SkipAdditivePass, + cur.Flags, + cur.LocalBounds, + surfaceList); + } + } + + glDisable(GL_POLYGON_OFFSET_FILL); + + if (ShowStats) { + ALOG("VRMenuMgr: submitted %i surfaces", NumToRender); + } +} + +//============================== +// VRMenuMgrLocal::GetGUIGlProgram +GlProgram const* VRMenuMgrLocal::GetGUIGlProgram(eGUIProgramType const programType) const { + switch (programType) { + case PROGRAM_DIFFUSE_ONLY: + return &GUIProgramDiffuseOnly; + case PROGRAM_DIFFUSE_ALPHA_DISCARD: + return &GUIProgramDiffuseAlphaDiscard; + case PROGRAM_ADDITIVE_ONLY: + return &GUIProgramDiffuseOnly; + case PROGRAM_DIFFUSE_PLUS_ADDITIVE: + return &GUIProgramDiffusePlusAdditive; + case PROGRAM_DIFFUSE_COMPOSITE: + return &GUIProgramDiffuseComposite; + case PROGRAM_DIFFUSE_COLOR_RAMP: + return &GUIProgramDiffuseColorRamp; + case PROGRAM_DIFFUSE_COLOR_RAMP_TARGET: + return &GUIProgramDiffuseColorRampTarget; + case PROGRAM_ALPHA_DIFFUSE: + return &GUIProgramAlphaDiffuse; + default: + /// assert_WITH_TAG( !"Invalid gui program type", "VrMenu" ); + break; + } + return NULL; +} + +//============================== +// VRMenuMgrLocal::AddComponentToDeletionList +void VRMenuMgrLocal::AddComponentToDeletionList( + menuHandle_t const ownerHandle, + VRMenuComponent* component) { + int index = -1; + for (int i = 0; i < static_cast(PendingDeletions.size()); ++i) { + if (PendingDeletions[i].GetOwnerHandle() == ownerHandle) { + index = i; + break; + } + } + + if (index < 0) { + index = static_cast(PendingDeletions.size()); + PendingDeletions.push_back(ovrComponentList(ownerHandle)); + } + + PendingDeletions[index].AddComponent(component); +} + +//============================== +// VRMenuMgrLocal::ExecutePendingComponentDeletions +void VRMenuMgrLocal::ExecutePendingComponentDeletions() { + for (int i = static_cast(PendingDeletions.size()) - 1; i >= 0; --i) { + ovrComponentList& list = PendingDeletions[i]; + VRMenuObject* object = ToObject(list.GetOwnerHandle()); + if (object != nullptr) { + object->FreeComponents(list); + } + } + PendingDeletions.clear(); +} + +//============================== +// OvrVRMenuMgr::Create +OvrVRMenuMgr* OvrVRMenuMgr::Create(OvrGuiSys& guiSys) { + VRMenuMgrLocal* mgr = new VRMenuMgrLocal(guiSys); + return mgr; +} + +//============================== +// OvrVRMenuMgr::Free +void OvrVRMenuMgr::Destroy(OvrVRMenuMgr*& mgr) { + if (mgr != NULL) { + mgr->Shutdown(); + delete mgr; + mgr = NULL; + } +} + +} // namespace OVRFW diff --git a/Samples/SampleXrFramework/Src/GUI/VRMenuMgr.h b/Samples/SampleXrFramework/Src/GUI/VRMenuMgr.h new file mode 100755 index 0000000..242f917 --- /dev/null +++ b/Samples/SampleXrFramework/Src/GUI/VRMenuMgr.h @@ -0,0 +1,74 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/************************************************************************************ + +Filename : VRMenuMgr.h +Content : Menuing system for VR apps. +Created : May 23, 2014 +Authors : Jonathan E. Wright + +*************************************************************************************/ + +#pragma once + +#include "VRMenuObject.h" + +namespace OVRFW { + +class BitmapFont; +class BitmapFontSurface; +struct GlProgram; +class OvrDebugLines; + +//============================================================== +// OvrVRMenuMgr +class OvrVRMenuMgr { + public: + friend class VRMenuObject; + + virtual ~OvrVRMenuMgr() {} + + static OvrVRMenuMgr* Create(OvrGuiSys& guiSys); + static void Destroy(OvrVRMenuMgr*& mgr); + + // Initialize the VRMenu system + virtual void Init(OvrGuiSys& guiSys) = 0; + // Shutdown the VRMenu syatem + virtual void Shutdown() = 0; + + // creates a new menu object + virtual menuHandle_t CreateObject(VRMenuObjectParms const& parms) = 0; + // Frees a menu object. If the object is a child of a parent object, this will + // also remove the child from the parent. + virtual void FreeObject(menuHandle_t const handle) = 0; + // Returns true if the handle is valid. + virtual bool IsValid(menuHandle_t const handle) const = 0; + // Return the object for a menu handle or NULL if the object does not exist or the + // handle is invalid; + virtual VRMenuObject* ToObject(menuHandle_t const handle) const = 0; + + // Submits the specified menu object and its children + virtual void SubmitForRendering( + OvrGuiSys& guiSys, + OVR::Matrix4f const& centerViewMatrix, + menuHandle_t const handle, + OVR::Posef const& worldPose, + VRMenuRenderFlags_t const& flags) = 0; + + // Call once per frame before rendering to sort surfaces. + virtual void Finish(OVR::Matrix4f const& viewMatrix) = 0; + + virtual void AppendSurfaceList( + OVR::Matrix4f const& centerViewMatrix, + std::vector& surfaceList) = 0; + + virtual GlProgram const* GetGUIGlProgram(eGUIProgramType const programType) const = 0; + + private: + // Called only from VRMenuObject. + virtual void AddComponentToDeletionList( + menuHandle_t const ownerHandle, + VRMenuComponent* component) = 0; +}; + +} // namespace OVRFW diff --git a/Samples/SampleXrFramework/Src/GUI/VRMenuObject.cpp b/Samples/SampleXrFramework/Src/GUI/VRMenuObject.cpp new file mode 100755 index 0000000..5a2feb6 --- /dev/null +++ b/Samples/SampleXrFramework/Src/GUI/VRMenuObject.cpp @@ -0,0 +1,2366 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/************************************************************************************ + +Filename : VRMenuObject.cpp +Content : Menuing system for VR apps. +Created : May 23, 2014 +Authors : Jonathan E. Wright + +*************************************************************************************/ + +#include "VRMenuObject.h" + +#include "Misc/Log.h" +#include "Render/Egl.h" +#include "Render/GlTexture.h" +#include "Render/BitmapFont.h" +#include "Render/TextureManager.h" + +#include "Locale/OVR_Locale.h" + +#include "OVR_FileSys.h" + +#include "OVR_Math.h" + +#include "VRMenuMgr.h" +#include "GuiSys.h" +#include "VRMenuComponent.h" +#include "ui_default.h" // embedded default UI texture (loaded as a placeholder when something doesn't load) +#include "Reflection.h" + +using OVR::Bounds3f; +using OVR::Matrix4f; +using OVR::Posef; +using OVR::Quatf; +using OVR::Vector2f; +using OVR::Vector3f; +using OVR::Vector4f; + +#define USE_TEXTURE_MANAGER + +inline bool Intersect_RayBounds( + const Vector3f& rayStart, + const Vector3f& rayDir, + const Vector3f& mins, + const Vector3f& maxs, + float& t0, + float& t1) { + const float rcpDirX = (fabsf(rayDir.x) > MATH_FLOAT_SMALLEST_NON_DENORMAL) + ? (1.0f / rayDir.x) + : MATH_FLOAT_HUGE_NUMBER; + const float rcpDirY = (fabsf(rayDir.y) > MATH_FLOAT_SMALLEST_NON_DENORMAL) + ? (1.0f / rayDir.y) + : MATH_FLOAT_HUGE_NUMBER; + const float rcpDirZ = (fabsf(rayDir.z) > MATH_FLOAT_SMALLEST_NON_DENORMAL) + ? (1.0f / rayDir.z) + : MATH_FLOAT_HUGE_NUMBER; + + const float sX = (mins.x - rayStart.x) * rcpDirX; + const float sY = (mins.y - rayStart.y) * rcpDirY; + const float sZ = (mins.z - rayStart.z) * rcpDirZ; + + const float tX = (maxs.x - rayStart.x) * rcpDirX; + const float tY = (maxs.y - rayStart.y) * rcpDirY; + const float tZ = (maxs.z - rayStart.z) * rcpDirZ; + + const float minX = std::min(sX, tX); + const float minY = std::min(sY, tY); + const float minZ = std::min(sZ, tZ); + + const float maxX = std::max(sX, tX); + const float maxY = std::max(sY, tY); + const float maxZ = std::max(sZ, tZ); + + t0 = std::max(minX, std::max(minY, minZ)); + t1 = std::min(maxX, std::min(maxY, maxZ)); + + return (t0 <= t1); +} + +namespace OVRFW { + +const float VRMenuSurface::Z_BOUNDS = 0.05f; + +//====================================================================================== +// VRMenuSurfaceTexture + +//============================== +// VRMenuSurfaceTexture::VRMenuSurfaceTexture:: +VRMenuSurfaceTexture::VRMenuSurfaceTexture() : Type(SURFACE_TEXTURE_MAX), OwnsTexture(false) {} + +//============================== +// VRMenuSurfaceTexture::LoadTexture +bool VRMenuSurfaceTexture::LoadTexture( + OvrGuiSys& guiSys, + eSurfaceTextureType const type, + char const* imageName, + bool const allowDefault) { + Free(); + + assert(type >= 0 && type < SURFACE_TEXTURE_MAX); + + Type = type; + + if (imageName != NULL && imageName[0] != '\0') { +#if defined(USE_TEXTURE_MANAGER) + textureHandle_t const h = + guiSys.GetTextureManager().LoadTexture(guiSys.GetFileSys(), imageName); + Texture = guiSys.GetTextureManager().GetGlTexture(h); +#else + std::vector buffer; + if (guiSys.GetFileSys().ReadFile(imageName, buffer)) { + int w; + int h; + Texture = LoadTextureFromBuffer( + imageName, + MemBuffer(buffer, static_cast(buffer.GetSize())), + TextureFlags_t(TEXTUREFLAG_NO_DEFAULT), + w, + h); + } +#endif + } + + if (!Texture.IsValid() && allowDefault) { +#if defined(USE_TEXTURE_MANAGER) + textureHandle_t const h = + guiSys.GetTextureManager().LoadTexture("", uiDefaultTgaData, uiDefaultTgaSize); + Texture = guiSys.GetTextureManager().GetGlTexture(h); +#else + int w; + int h; + Texture = LoadTextureFromBuffer( + imageName, MemBuffer(uiDefaultTgaData, uiDefaultTgaSize), TextureFlags_t(), w, h); +#endif + ALOGW( + "VRMenuSurfaceTexture::CreateFromImage: failed to load image '%s' - default loaded instead!", + imageName); + } + + // if allocated via the texture manager we cannot "own" the texture -- since the texture manager + // may have given the same handle to anyone else who asked. +#if !defined(USE_TEXTURE_MANAGER) + OwnsTexture = true; +#endif + return Texture.IsValid(); +} + +//============================== +// VRMenuSurfaceTexture::LoadTexture +void VRMenuSurfaceTexture::LoadTexture( + eSurfaceTextureType const type, + const GLuint texId, + const int width, + const int height) { + Free(); + + assert(type >= 0 && type < SURFACE_TEXTURE_MAX); + + Type = type; + OwnsTexture = false; + Texture = GlTexture(texId, width, height); +} + +//============================== +// VRMenuSurfaceTexture::Free +void VRMenuSurfaceTexture::Free() { + if (Texture.IsValid()) { + if (OwnsTexture) { + DeleteTexture(Texture); + } + Type = SURFACE_TEXTURE_MAX; + OwnsTexture = false; + } +} + +//====================================================================================== +// VRMenuSurfaceTris + +//====================================================================================== +// VRMenuSurface + +#if 0 +static void PrintBounds( const char * name, char const * prefix, Bounds3f const & bounds ) +{ + ALOG( "'%s' %s: min( %.2f, %.2f, %.2f ) - max( %.2f, %.2f, %.2f )", + name, prefix, + bounds.GetMins().x, bounds.GetMins().y, bounds.GetMins().z, + bounds.GetMaxs().x, bounds.GetMaxs().y, bounds.GetMaxs().z ); +} +#endif + +//============================== +// VRMenuSurface::VRMenuSurface +VRMenuSurface::VRMenuSurface() + : Color(1.0f) + //, TextureDims( 0, 0 ) + //, Dims( 0.0f, 0.0f ) + //, Anchors( 0.0f, 0.0f, 1.0f + , + Border(0.0f, 0.0f, 0.0f, 0.0f), + ClipUVs( + -1.0f, + -1.0f, + 2.0f, + 2.0f) // if we clip exactly at the edges we get sharp, aliased edges + , + Contents(CONTENT_SOLID), + Visible(true), + ProgramType(PROGRAM_MAX) {} + +//============================== +// VRMenuSurface::~VRMenuSurface +VRMenuSurface::~VRMenuSurface() { + Free(); +} + +//============================== +// VRMenuSurface::CreateImageGeometry +// +// This creates a quad for mapping the texture. +void VRMenuSurface::CreateImageGeometry( + int const textureWidth, + int const textureHeight, + const Vector2f& dims, + const Vector4f& border, + const Vector4f& cropUV, + ContentFlags_t const contents) { + // assert( SurfaceDef.geo.vertexBuffer == 0 && SurfaceDef.geo.indexBuffer == 0 && + // SurfaceDef.geo.vertexArrayObject == 0 ); + + TriangleIndex vertsX = 0; + TriangleIndex vertsY = 0; + float vertUVX[4] = {0.0f, 0.0f, 0.0f, 0.0f}; + float vertUVY[4] = {1.0f, 0.0f, 0.0f, 0.0f}; + float vertPosX[4] = {0.0f, 0.0f, 0.0f, 0.0f}; + float vertPosY[4] = {0.0f, 0.0f, 0.0f, 0.0f}; + + // x components + vertPosX[vertsX] = 0.0f; + vertUVX[vertsX++] = 0.0f; + + if (border[BORDER_LEFT] > 0.0f) { + vertPosX[vertsX] = border[BORDER_LEFT] / dims.x; + vertUVX[vertsX++] = border[BORDER_LEFT] / (float)textureWidth; + } + + if (border[BORDER_RIGHT] > 0.0f) { + vertPosX[vertsX] = 1.0f - border[BORDER_RIGHT] / dims.x; + vertUVX[vertsX++] = 1.0f - border[BORDER_RIGHT] / (float)textureWidth; + } + + vertPosX[vertsX] = 1.0f; + vertUVX[vertsX++] = 1.0f; + + // y components + vertPosY[vertsY] = 0.0f; + vertUVY[vertsY++] = 0.0f; + + if (border[BORDER_BOTTOM] > 0.0f) { + vertPosY[vertsY] = border[BORDER_BOTTOM] / dims.y; + vertUVY[vertsY++] = border[BORDER_BOTTOM] / (float)textureHeight; + } + + if (border[BORDER_TOP] > 0.0f) { + vertPosY[vertsY] = 1.0f - border[BORDER_TOP] / dims.y; + vertUVY[vertsY++] = 1.0f - border[BORDER_TOP] / (float)textureHeight; + } + + vertPosY[vertsY] = 1.0f; + vertUVY[vertsY++] = 1.0f; + + for (int i = 0; i < vertsX; i++) { + vertUVX[i] = (cropUV.z - cropUV.x) * vertUVX[i] + cropUV.x; + } + + for (int i = 0; i < vertsY; i++) { + vertUVY[i] = (cropUV.w - cropUV.y) * vertUVY[i] + cropUV.y; + } + + // create the vertices + const int vertexCount = vertsX * vertsY; + const TriangleIndex horizontal = vertsX - 1; + const TriangleIndex vertical = vertsY - 1; + + VertexAttribs attribs; + attribs.position.resize(vertexCount); + attribs.uv0.resize(vertexCount); + attribs.uv1.resize(vertexCount); + attribs.color.resize(vertexCount); + + Vector4f color(1.0f, 1.0f, 1.0f, 1.0f); + + for (int y = 0; y <= vertical; y++) { + const float yPos = + (-1 + vertPosY[y] * 2) * (dims.y * VRMenuObject::DEFAULT_TEXEL_SCALE * 0.5f); + const float uvY = 1.0f - vertUVY[y]; + + for (int x = 0; x <= horizontal; x++) { + const int index = y * (horizontal + 1) + x; + attribs.position[index].x = + (-1 + vertPosX[x] * 2) * (dims.x * VRMenuObject::DEFAULT_TEXEL_SCALE * 0.5f); + attribs.position[index].z = 0; + attribs.position[index].y = yPos; + attribs.uv0[index].x = vertUVX[x]; + attribs.uv0[index].y = uvY; + attribs.uv1[index] = attribs.uv0[index]; + attribs.color[index] = color; + } + } + + std::vector indices; + indices.resize(horizontal * vertical * 6); + + // If this is to be used to draw a linear format texture, like + // a surface texture, it is better for cache performance that + // the triangles be drawn to follow the side to side linear order. + int index = 0; + for (TriangleIndex y = 0; y < vertical; y++) { + for (TriangleIndex x = 0; x < horizontal; x++) { + indices[index + 0] = y * (horizontal + 1) + x; + indices[index + 1] = y * (horizontal + 1) + x + 1; + indices[index + 2] = (y + 1) * (horizontal + 1) + x; + indices[index + 3] = (y + 1) * (horizontal + 1) + x; + indices[index + 4] = y * (horizontal + 1) + x + 1; + indices[index + 5] = (y + 1) * (horizontal + 1) + x + 1; + index += 6; + } + } + + Tris.Init(attribs.position, indices, attribs.uv0, contents); + + if (SurfaceDef.geo.vertexBuffer == 0 && SurfaceDef.geo.indexBuffer == 0 && + SurfaceDef.geo.vertexArrayObject == 0) { + SurfaceDef.geo.Create(attribs, indices); + } else { + SurfaceDef.geo.Update(attribs); + } +} + +//============================== +// VRMenuSurface::BuildDrawSurface +void VRMenuObject::BuildDrawSurface( + OvrVRMenuMgr const& menuMgr, + Matrix4f const& modelMatrix, + const char* surfaceName, + int const surfaceIndex, + Vector4f const& color, + Vector3f const& fadeDirection, + Vector2f const& colorTableOffset, + Vector4f const& clipUVs, + Vector2f const& offsetUVs, + bool const skipAdditivePass, + VRMenuRenderFlags_t const& flags, + Bounds3f const& localBounds, + std::vector& surfaceList) { + int n; + if (surfaceIndex < 0) { + // this means we're only submitting an instanced text surface + n = 1; + surfaceList.resize(surfaceList.size() + 1); + } else { + // add one draw surface + n = (flags & VRMenuRenderFlags_t(VRMENU_RENDER_SUBMIT_TEXT_SURFACE)) != 0 ? 2 : 1; + surfaceList.resize(surfaceList.size() + n); + + Surfaces[surfaceIndex].BuildDrawSurface( + menuMgr, + modelMatrix, + surfaceName, + color, + fadeDirection, + colorTableOffset, + clipUVs, + offsetUVs, + skipAdditivePass, + flags, + localBounds, + surfaceList[surfaceList.size() - n]); + n--; + } + + if (n >= 1) { + assert(TextSurface != nullptr); + /// TextSurface->SurfaceDef.graphicsCommand.uniformValues[0][3] = color.w; + surfaceList[surfaceList.size() - n].modelMatrix = TextSurface->ModelMatrix; + surfaceList[surfaceList.size() - n].surface = &TextSurface->SurfaceDef; + } +} + +//============================== +// VRMenuSurface::BuildDrawSurface +// TODO: Ideally the materialDef only needs to be set up once unless it's been changed, but +// some menu items can have their surfaces changed on the fly (such as background-loaded thumbnails) +void VRMenuSurface::BuildDrawSurface( + OvrVRMenuMgr const& menuMgr, + Matrix4f const& modelMatrix, + const char* surfaceName, + Vector4f const& color, + Vector3f const& fadeDirection, + Vector2f const& colorTableOffset, + Vector4f const& clipUVs, + Vector2f const& offsetUVs, + bool const skipAdditivePass, + VRMenuRenderFlags_t const& flags, + Bounds3f const& localBounds, + ovrDrawSurface& outSurf) { + outSurf.modelMatrix = modelMatrix; +#if defined(OVR_BUILD_DEBUG) // skip these allocations in release builds + SurfaceDef.surfaceName = surfaceName; +#endif + SurfaceDef.geo.localBounds = localBounds; + outSurf.surface = &SurfaceDef; + + ovrGraphicsCommand& gc = SurfaceDef.graphicsCommand; + + GlProgram const* program = NULL; + + eGUIProgramType pt = ProgramType; + if (skipAdditivePass) { + if (pt == PROGRAM_DIFFUSE_PLUS_ADDITIVE || pt == PROGRAM_DIFFUSE_COMPOSITE) { + pt = PROGRAM_DIFFUSE_ONLY; // this is used to not render the gaze-over hilights + } + } + + program = menuMgr.GetGUIGlProgram(pt); + if (program == NULL) { + assert(program != NULL); + return; + } + + gc.Program = *program; + + /// Update local parameters + Color = color; + FadeDirection = fadeDirection; + ClipUVs = clipUVs; + OffsetUVs = offsetUVs; + ColorTableOffset = colorTableOffset; + + /// uniform binding - match the uniforms to what they were setup in VRMenuMgrLocal::Init + int additiveIndex = IndexForTextureType(SURFACE_TEXTURE_ADDITIVE, 1); + int diffuseIndex = IndexForTextureType(SURFACE_TEXTURE_DIFFUSE, 1); + int diffuseADIndex = IndexForTextureType(SURFACE_TEXTURE_DIFFUSE_ALPHA_DISCARD, 1); + int alphaIndex = IndexForTextureType(SURFACE_TEXTURE_ALPHA_MASK, 1); + int diffuse2Index = IndexForTextureType(SURFACE_TEXTURE_DIFFUSE, 2); + int rampIndex = IndexForTextureType(SURFACE_TEXTURE_COLOR_RAMP, 1); + int targetIndex = IndexForTextureType(SURFACE_TEXTURE_COLOR_RAMP_TARGET, 1); + switch (pt) { + case PROGRAM_DIFFUSE_ONLY: + gc.Textures[0] = Textures[diffuseIndex].GetTexture(); + gc.UniformData[0].Data = &Color; + gc.UniformData[1].Data = &FadeDirection; + gc.UniformData[2].Data = &OffsetUVs; + gc.UniformData[3].Data = &gc.Textures[0]; + gc.UniformData[4].Data = &ClipUVs; + break; + + case PROGRAM_ADDITIVE_ONLY: + gc.Textures[0] = Textures[additiveIndex].GetTexture(); + gc.UniformData[0].Data = &Color; + gc.UniformData[1].Data = &FadeDirection; + gc.UniformData[2].Data = &OffsetUVs; + gc.UniformData[3].Data = &gc.Textures[0]; + break; + + case PROGRAM_DIFFUSE_ALPHA_DISCARD: + gc.Textures[0] = Textures[diffuseADIndex].GetTexture(); + gc.UniformData[0].Data = &Color; + gc.UniformData[1].Data = &FadeDirection; + gc.UniformData[2].Data = &OffsetUVs; + gc.UniformData[3].Data = &gc.Textures[0]; + gc.UniformData[4].Data = &ClipUVs; + break; + + case PROGRAM_DIFFUSE_PLUS_ADDITIVE: + gc.Textures[0] = Textures[diffuseIndex].GetTexture(); + gc.Textures[1] = Textures[additiveIndex].GetTexture(); + gc.UniformData[0].Data = &Color; + gc.UniformData[1].Data = &FadeDirection; + gc.UniformData[2].Data = &gc.Textures[0]; + gc.UniformData[3].Data = &gc.Textures[1]; + break; + + case PROGRAM_DIFFUSE_COMPOSITE: + gc.Textures[0] = Textures[diffuseIndex].GetTexture(); + gc.Textures[1] = Textures[diffuse2Index].GetTexture(); + gc.UniformData[0].Data = &Color; + gc.UniformData[1].Data = &FadeDirection; + gc.UniformData[2].Data = &gc.Textures[0]; + gc.UniformData[3].Data = &gc.Textures[1]; + break; + + case PROGRAM_DIFFUSE_COLOR_RAMP: + gc.Textures[0] = Textures[diffuseIndex].GetTexture(); + gc.Textures[1] = Textures[targetIndex].GetTexture(); + gc.Textures[2] = Textures[rampIndex].GetTexture(); + gc.UniformData[0].Data = &Color; + gc.UniformData[1].Data = &FadeDirection; + gc.UniformData[2].Data = &OffsetUVs; + gc.UniformData[3].Data = &gc.Textures[0]; + gc.UniformData[4].Data = &gc.Textures[1]; + gc.UniformData[5].Data = &gc.Textures[2]; + gc.UniformData[6].Data = &ColorTableOffset; + break; + + case PROGRAM_DIFFUSE_COLOR_RAMP_TARGET: + gc.Textures[0] = Textures[diffuseIndex].GetTexture(); + gc.Textures[1] = Textures[targetIndex].GetTexture(); + gc.Textures[2] = Textures[rampIndex].GetTexture(); + gc.UniformData[0].Data = &Color; + gc.UniformData[1].Data = &FadeDirection; + gc.UniformData[2].Data = &gc.Textures[0]; + gc.UniformData[3].Data = &gc.Textures[1]; + gc.UniformData[4].Data = &gc.Textures[2]; + gc.UniformData[5].Data = &ColorTableOffset; + break; + + case PROGRAM_ALPHA_DIFFUSE: + gc.Textures[0] = Textures[alphaIndex].GetTexture(); + gc.Textures[1] = Textures[diffuseIndex].GetTexture(); + gc.UniformData[0].Data = &Color; + gc.UniformData[1].Data = &FadeDirection; + gc.UniformData[2].Data = &gc.Textures[0]; + gc.UniformData[3].Data = &gc.Textures[1]; + break; + + default: + /// assert_WITH_TAG( !"Invalid gui program type", "VrMenu" ); + break; + } + + // most programs use normal blending + gc.GpuState.blendEnable = ovrGpuState::BLEND_ENABLE; + gc.GpuState.depthEnable = (flags & VRMENU_RENDER_NO_DEPTH) != 0 ? false : true; + gc.GpuState.depthMaskEnable = (flags & VRMENU_RENDER_NO_DEPTH_MASK) != 0 ? false : true; + gc.GpuState.polygonOffsetEnable = (flags & VRMENU_RENDER_POLYGON_OFFSET) != 0 ? true : false; + gc.GpuState.blendSrc = GL_SRC_ALPHA; + gc.GpuState.blendDst = pt == PROGRAM_ADDITIVE_ONLY ? GL_ONE : GL_ONE_MINUS_SRC_ALPHA; + gc.GpuState.cullEnable = true; +} + +//============================== +// VRMenuSurface::SetTextureSampling +//============================== +void VRMenuSurface::SetTextureSampling(eGUIProgramType const pt) { + switch (pt) { + case PROGRAM_DIFFUSE_ONLY: + case PROGRAM_DIFFUSE_ALPHA_DISCARD: { + int diffuseIndex = IndexForTextureType( + pt == PROGRAM_DIFFUSE_ONLY ? SURFACE_TEXTURE_DIFFUSE + : SURFACE_TEXTURE_DIFFUSE_ALPHA_DISCARD, + 1); + /// assert_WITH_TAG( diffuseIndex >= 0, "VrMenu" ); // surface setup should have + /// detected this! + // bind the texture + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, Textures[diffuseIndex].GetTexture().texture); + 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 PROGRAM_DIFFUSE_COMPOSITE: { + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + int diffuseIndex = IndexForTextureType(SURFACE_TEXTURE_DIFFUSE, 1); + /// assert_WITH_TAG( diffuseIndex >= 0, "VrMenu" ); // surface setup should have + /// detected this! + int diffuse2Index = IndexForTextureType(SURFACE_TEXTURE_DIFFUSE, 2); + /// assert_WITH_TAG( diffuse2Index >= 0, "VrMenu" ); // surface setup should have + /// detected this! + // bind both textures + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, Textures[diffuseIndex].GetTexture().texture); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_2D, Textures[diffuse2Index].GetTexture().texture); + 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 PROGRAM_ALPHA_DIFFUSE: { + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + int alphaIndex = IndexForTextureType(SURFACE_TEXTURE_ALPHA_MASK, 1); + /// assert_WITH_TAG( alphaIndex >= 0, "VrMenu" ); // surface setup should have + /// detected this! + int diffuseIndex = IndexForTextureType(SURFACE_TEXTURE_DIFFUSE, 1); + /// assert_WITH_TAG( diffuseIndex >= 0, "VrMenu" ); // surface setup should have + /// detected this! + // bind both textures + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, Textures[alphaIndex].GetTexture().texture); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_2D, Textures[diffuseIndex].GetTexture().texture); + 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 PROGRAM_ADDITIVE_ONLY: { + glBlendFunc(GL_SRC_ALPHA, GL_ONE); + int additiveIndex = IndexForTextureType(SURFACE_TEXTURE_ADDITIVE, 1); + /// assert_WITH_TAG( additiveIndex >= 0, "VrMenu" ); // surface setup should have + /// detected this! + // bind the texture + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, Textures[additiveIndex].GetTexture().texture); + 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 PROGRAM_DIFFUSE_PLUS_ADDITIVE: // has a diffuse and an additive + { + // glBlendFunc( GL_ONE, GL_ONE ); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + int diffuseIndex = IndexForTextureType(SURFACE_TEXTURE_DIFFUSE, 1); + /// assert_WITH_TAG( diffuseIndex >= 0, "VrMenu" ); // surface setup should have + /// detected this! + int additiveIndex = IndexForTextureType(SURFACE_TEXTURE_ADDITIVE, 1); + /// assert_WITH_TAG( additiveIndex >= 0, "VrMenu" ); // surface setup should have + /// detected this! + // bind both textures + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, Textures[diffuseIndex].GetTexture().texture); + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_2D, Textures[additiveIndex].GetTexture().texture); + break; + } + case PROGRAM_DIFFUSE_COLOR_RAMP: // has a diffuse and color ramp, and color ramp target is + // the diffuse + { + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + int diffuseIndex = IndexForTextureType(SURFACE_TEXTURE_DIFFUSE, 1); + /// assert_WITH_TAG( diffuseIndex >= 0, "VrMenu" ); // surface setup should have + /// detected this! + int rampIndex = IndexForTextureType(SURFACE_TEXTURE_COLOR_RAMP, 1); + /// assert_WITH_TAG( rampIndex >= 0, "VrMenu" ); // surface setup should have + /// detected this! + // bind both textures + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, Textures[diffuseIndex].GetTexture().texture); + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_2D, Textures[rampIndex].GetTexture().texture); + // do not do any filtering on the "palette" texture + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1.0f); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + 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 PROGRAM_DIFFUSE_COLOR_RAMP_TARGET: // has diffuse, color ramp, and a separate color + // ramp target + { + // ALOG( "Surface '%s' - PROGRAM_COLOR_RAMP_TARGET", SurfaceName ); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + int diffuseIndex = IndexForTextureType(SURFACE_TEXTURE_DIFFUSE, 1); + /// assert_WITH_TAG( diffuseIndex >= 0, "VrMenu" ); // surface setup should have + /// detected this! + int rampIndex = IndexForTextureType(SURFACE_TEXTURE_COLOR_RAMP, 1); + /// assert_WITH_TAG( rampIndex >= 0, "VrMenu" ); // surface setup should have + /// detected this! + int targetIndex = IndexForTextureType(SURFACE_TEXTURE_COLOR_RAMP_TARGET, 1); + /// assert_WITH_TAG( targetIndex >= 0, "VrMenu" ); // surface setup should have + /// detected this! + // bind both textures + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, Textures[diffuseIndex].GetTexture().texture); + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_2D, Textures[targetIndex].GetTexture().texture); + glActiveTexture(GL_TEXTURE2); + glBindTexture(GL_TEXTURE_2D, Textures[rampIndex].GetTexture().texture); + // do not do any filtering on the "palette" texture + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1.0f); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + 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 PROGRAM_MAX: { + ALOGW("Unsupported texture map combination."); + return; + } + default: { + /// assert_WITH_TAG( !"Unhandled ProgramType", "Uhandled ProgramType" ); + return; + } + } +} + +/// OVR_PERF_ACCUMULATOR( VerifyImageParms ); +/// OVR_PERF_ACCUMULATOR( FindSurfaceForGeoSizing ); +/// OVR_PERF_ACCUMULATOR( CreateImageGeometry ); +/// OVR_PERF_ACCUMULATOR( SelectProgramType ); +/// OVR_PERF_ACCUMULATOR( CreateFromSurfaceParms ); + +//============================== +// VRMenuSurface::CreateFromSurfaceParms +void VRMenuSurface::CreateFromSurfaceParms(OvrGuiSys& guiSys, VRMenuSurfaceParms const& parms) { + /// OVR_PERF_TIMER( CreateFromSurfaceParms ); + + Free(); + + SurfaceName = parms.SurfaceName; + + { + /// OVR_PERF_TIMER( VerifyImageParms ); + // verify the input parms have a valid image name and texture type + bool isValid = false; + for (int i = 0; i < VRMENUSURFACE_IMAGE_MAX; ++i) { + if (!parms.ImageNames[i].empty() && + (parms.TextureTypes[i] >= SURFACE_TEXTURE_DIFFUSE && + parms.TextureTypes[i] < SURFACE_TEXTURE_MAX)) { + isValid = true; + Textures[i].LoadTexture( + guiSys, parms.TextureTypes[i], parms.ImageNames[i].c_str(), true); + } else if ( + (parms.ImageTexId[i] != 0) && + (parms.TextureTypes[i] >= SURFACE_TEXTURE_DIFFUSE && + parms.TextureTypes[i] < SURFACE_TEXTURE_MAX)) { + isValid = true; + Textures[i].LoadTexture( + parms.TextureTypes[i], + parms.ImageTexId[i], + parms.ImageWidth[i], + parms.ImageHeight[i]); + } + } + /// OVR_PERF_ACCUMULATE( VerifyImageParms ); + if (!isValid) { + // ALOG( "VRMenuSurfaceParms '%s' - no valid images - skipping", + // parms.SurfaceName.c_str() ); + return; + } + } + + int surfaceIdx = -1; + { + /// OVR_PERF_TIMER( FindSurfaceForGeoSizing ); + // make sure we have a surface for sizing the geometry + for (int i = 0; i < VRMENUSURFACE_IMAGE_MAX; ++i) { + if (Textures[i].GetTexture().texture != 0) { + surfaceIdx = i; + break; + } + } + /// OVR_PERF_ACCUMULATE( FindSurfaceForGeoSizing ); + if (surfaceIdx < 0) { + // ALOG( "VRMenuSurface::CreateFromImageParms - no suitable image for surface + // creation" ); + return; + } + } + + TextureDims.x = Textures[surfaceIdx].GetWidth(); + TextureDims.y = Textures[surfaceIdx].GetHeight(); + + if ((parms.Dims.x == 0) || (parms.Dims.y == 0)) { + Dims.x = static_cast(TextureDims.x); + Dims.y = static_cast(TextureDims.y); + } else { + Dims = parms.Dims; + } + + Border = parms.Border; + CropUV = parms.CropUV; + OffsetUVs = parms.OffsetUVs; + Anchors = parms.Anchors; + Contents = parms.Contents; + Color = parms.Color; + + { + /// OVR_PERF_TIMER( CreateImageGeometry ); + CreateImageGeometry(TextureDims.x, TextureDims.y, Dims, Border, CropUV, Contents); + /// OVR_PERF_ACCUMULATE( CreateImageGeometry ); + } + + { + /// OVR_PERF_TIMER( SelectProgramType ); + // now, based on the combination of surfaces, determine the render prog to use + if (HasTexturesOfType(SURFACE_TEXTURE_DIFFUSE, 1) && + HasTexturesOfType(SURFACE_TEXTURE_COLOR_RAMP, 1) && + HasTexturesOfType(SURFACE_TEXTURE_COLOR_RAMP_TARGET, 1)) { + ProgramType = PROGRAM_DIFFUSE_COLOR_RAMP_TARGET; + } else if ( + HasTexturesOfType(SURFACE_TEXTURE_DIFFUSE, 1) && + HasTexturesOfType(SURFACE_TEXTURE_MAX, 2)) { + ProgramType = PROGRAM_DIFFUSE_ONLY; + } else if ( + HasTexturesOfType(SURFACE_TEXTURE_DIFFUSE_ALPHA_DISCARD, 1) && + HasTexturesOfType(SURFACE_TEXTURE_MAX, 2)) { + ProgramType = PROGRAM_DIFFUSE_ALPHA_DISCARD; + } else if ( + HasTexturesOfType(SURFACE_TEXTURE_ADDITIVE, 1) && + HasTexturesOfType(SURFACE_TEXTURE_MAX, 2)) { + ProgramType = PROGRAM_ADDITIVE_ONLY; + } else if ( + HasTexturesOfType(SURFACE_TEXTURE_DIFFUSE, 2) && + HasTexturesOfType(SURFACE_TEXTURE_MAX, 1)) { + ProgramType = PROGRAM_DIFFUSE_COMPOSITE; + } else if ( + HasTexturesOfType(SURFACE_TEXTURE_DIFFUSE, 1) && + HasTexturesOfType(SURFACE_TEXTURE_COLOR_RAMP, 1) && + HasTexturesOfType(SURFACE_TEXTURE_MAX, 1)) { + ProgramType = PROGRAM_DIFFUSE_COLOR_RAMP; + } else if ( + HasTexturesOfType(SURFACE_TEXTURE_DIFFUSE, 1) && + HasTexturesOfType(SURFACE_TEXTURE_ADDITIVE, 1) && + HasTexturesOfType(SURFACE_TEXTURE_MAX, 1)) { + ProgramType = PROGRAM_DIFFUSE_PLUS_ADDITIVE; + } else if ( + HasTexturesOfType(SURFACE_TEXTURE_ALPHA_MASK, 1) && + HasTexturesOfType(SURFACE_TEXTURE_DIFFUSE, 1) && + HasTexturesOfType(SURFACE_TEXTURE_MAX, 1)) { + ProgramType = PROGRAM_ALPHA_DIFFUSE; + } else { + ALOGW("Invalid material combination -- either add a shader to support it or fix it."); + ProgramType = PROGRAM_MAX; + } + /// OVR_PERF_ACCUMULATE( SelectProgramType ); + } + + SetTextureSampling(ProgramType); + + /// OVR_PERF_ACCUMULATE( CreateFromSurfaceParms ); +} + +//============================== +// VRMenuSurface::RegenerateSurfaceGeometry +void VRMenuSurface::RegenerateSurfaceGeometry() { + CreateImageGeometry(TextureDims.x, TextureDims.y, Dims, Border, CropUV, Contents); +} + +//============================== +// VRMenuSurface:: +bool VRMenuSurface::HasTexturesOfType(eSurfaceTextureType const t, int const requiredCount) const { + int count = 0; + for (int i = 0; i < VRMENUSURFACE_IMAGE_MAX; ++i) { + if (Textures[i].GetType() == t) { + count++; + } + } + return (requiredCount == count); // must be the exact same number +} + +int VRMenuSurface::IndexForTextureType(eSurfaceTextureType const t, int const occurenceCount) + const { + int count = 0; + for (int i = 0; i < VRMENUSURFACE_IMAGE_MAX; ++i) { + if (Textures[i].GetType() == t) { + count++; + if (count == occurenceCount) { + return i; + } + } + } + return -1; +} + +//============================== +// VRMenuSurface::Free +void VRMenuSurface::Free() { + for (int i = 0; i < VRMENUSURFACE_IMAGE_MAX; ++i) { + Textures[i].Free(); + } +} + +//============================== +// VRMenuSurface::IntersectRay +bool VRMenuSurface::IntersectRay( + Vector3f const& start, + Vector3f const& dir, + Posef const& pose, + Vector3f const& scale, + ContentFlags_t const testContents, + OvrCollisionResult& result) const { + return Tris.IntersectRay(start, dir, pose, scale, testContents, result); +} + +//============================== +// VRMenuSurface::IntersectRay +bool VRMenuSurface::IntersectRay( + Vector3f const& localStart, + Vector3f const& localDir, + Vector3f const& scale, + ContentFlags_t const testContents, + OvrCollisionResult& result) const { + return Tris.IntersectRay(localStart, localDir, scale, testContents, result); +} + +//============================== +// VRMenuSurface::LoadTexture +void VRMenuSurface::LoadTexture( + OvrGuiSys& guiSys, + int const textureIndex, + eSurfaceTextureType const type, + char const* imageName) { + if (textureIndex < 0 || textureIndex >= VRMENUSURFACE_IMAGE_MAX) { + /// assert_WITH_TAG( textureIndex >= 0 && textureIndex < VRMENUSURFACE_IMAGE_MAX, "VrMenu" + /// ); + return; + } + Textures[textureIndex].LoadTexture(guiSys, type, imageName, true); +} + +//============================== +// VRMenuSurface::LoadTexture +void VRMenuSurface::LoadTexture( + int const textureIndex, + eSurfaceTextureType const type, + const GLuint texId, + const int width, + const int height) { + if (textureIndex < 0 || textureIndex >= VRMENUSURFACE_IMAGE_MAX) { + /// assert_WITH_TAG( textureIndex >= 0 && textureIndex < VRMENUSURFACE_IMAGE_MAX, "VrMenu" + /// ); + return; + } + Textures[textureIndex].LoadTexture(type, texId, width, height); +} + +//============================== +// VRMenuSurface::GetAnchorOffsets +Vector2f VRMenuSurface::GetAnchorOffsets() const { + return Vector2f( + ((1.0f - Anchors.x) - 0.5f) * Dims.x * + VRMenuObject::DEFAULT_TEXEL_SCALE, // inverted so that 0.0 is left-aligned + (Anchors.y - 0.5f) * Dims.y * VRMenuObject::DEFAULT_TEXEL_SCALE); +} + +void VRMenuSurface::SetOwnership(int const index, bool const isOwner) { + Textures[index].SetOwnership(isOwner); +} + +//====================================================================================== +// VRMenuObject + +//================================== +// VRMenuObject::VRMenuObject +VRMenuObject::VRMenuObject(VRMenuObjectParms const& parms, menuHandle_t const handle) + : Type(parms.Type), + Handle(handle), + Id(parms.Id), + Flags(parms.Flags), + Name(parms.Name), + Tag(parms.Tag), + LocalPose(parms.LocalPose), + LocalScale(parms.LocalScale), + HilightPose(Quatf(), Vector3f(0.0f, 0.0f, 0.0f)), + HilightScale(1.0f), + WrapScale(1.0f), + TextLocalPose(parms.TextLocalPose), + TextLocalScale(parms.TextLocalScale), + Text(parms.Text), + CollisionPrimitive(NULL), + Contents(parms.Contents), + Color(parms.Color), + TextColor(parms.TextColor), + ColorTableOffset(0.0f), + FontParms(parms.FontParms), + Hilighted(false), + Selected(false), + TextDirty(true), + MinsBoundsExpand(0.0f), + MaxsBoundsExpand(0.0f), + TextMetrics(), + TextSurface(nullptr) { + CullBounds.Clear(); +} + +//================================== +// VRMenuObject::~VRMenuObject +VRMenuObject::~VRMenuObject() { + if (CollisionPrimitive != nullptr) { + delete CollisionPrimitive; + CollisionPrimitive = nullptr; + } + + // all components must be dynamically allocated + // this is critical + for (int i = 0; i < static_cast(Components.size()); ++i) { + if (Components[i]) { + delete Components[i]; + } + Components[i] = nullptr; + } + Components.clear(); + Handle.Release(); + ParentHandle.Release(); + FreeTextSurface(); + Type = VRMENU_MAX; +} + +/// OVR_PERF_ACCUMULATOR( VRMenuObjectInit ); + +//================================== +// VRMenuObject::Init +void VRMenuObject::Init(OvrGuiSys& guiSys, VRMenuObjectParms const& parms) { + /// OVR_PERF_TIMER( VRMenuObjectInit ); + for (int i = 0; i < static_cast(parms.SurfaceParms.size()); ++i) { + int idx = AllocSurface(); + Surfaces[idx].CreateFromSurfaceParms(guiSys, parms.SurfaceParms[i]); + } + + // bounds are nothing submitted for rendering + CullBounds = Bounds3f(0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f); + FontParms = parms.FontParms; + for (int i = 0; i < static_cast(parms.Components.size()); ++i) { + AddComponent(parms.Components[i]); + } + + if (parms.TexelCoords) { + LocalPose.Translation = { + LocalPose.Translation.x * DEFAULT_TEXEL_SCALE, + LocalPose.Translation.y * DEFAULT_TEXEL_SCALE, + LocalPose.Translation.z}; + TextLocalPose.Translation = { + TextLocalPose.Translation.x * DEFAULT_TEXEL_SCALE, + TextLocalPose.Translation.y * DEFAULT_TEXEL_SCALE, + TextLocalPose.Translation.z}; + if (Surfaces.size() > 0) { + Vector2f dims = Surfaces[0].GetDims(); + LocalScale = Vector3f(LocalScale.x / dims.x, LocalScale.y / dims.y, LocalScale.z); + } + FontParms.WrapWidth *= DEFAULT_TEXEL_SCALE; + } + Selected = parms.Selected; + + /// OVR_PERF_ACCUMULATE( VRMenuObjectInit ); +} + +//================================== +// VRMenuObject::FreeChildren +void VRMenuObject::FreeChildren(OvrVRMenuMgr& menuMgr) { + for (int i = 0; i < static_cast(Children.size()); ++i) { + menuMgr.FreeObject(Children[i]); + } + Children.resize(0); + // NOTE! bounds will be incorrect now until submitted for rendering +} + +//================================== +// VRMenuObject::IsDescendant +bool VRMenuObject::IsDescendant(OvrVRMenuMgr& menuMgr, menuHandle_t const handle) const { + for (int i = 0; i < static_cast(Children.size()); ++i) { + if (Children[i] == handle) { + return true; + } + } + + for (int i = 0; i < static_cast(Children.size()); ++i) { + VRMenuObject* child = menuMgr.ToObject(Children[i]); + if (child != NULL) { + bool r = child->IsDescendant(menuMgr, handle); + if (r) { + return true; + } + } + } + + return false; +} + +//============================== +// VRMenuObject::AddChild +void VRMenuObject::AddChild(OvrVRMenuMgr& menuMgr, menuHandle_t const handle) { + Children.push_back(handle); + + VRMenuObject* child = menuMgr.ToObject(handle); + if (child != NULL) { + child->SetParentHandle(this->Handle); + } + // NOTE: bounds will be incorrect until submitted for rendering +} +void VRMenuObject::AddChild(VRMenuObject* child) { + Children.push_back(child->GetHandle()); + if (child != nullptr) { + child->SetParentHandle(this->Handle); + } +} + +//============================== +// VRMenuObject::RemoveChild +void VRMenuObject::RemoveChild(OvrVRMenuMgr& menuMgr, menuHandle_t const handle) { + for (int i = 0; i < static_cast(Children.size()); ++i) { + if (Children[i] == handle) { + Children.erase(Children.cbegin() + i); + return; + } + } +} + +//============================== +// VRMenuObject::FreeChild +void VRMenuObject::FreeChild(OvrVRMenuMgr& menuMgr, menuHandle_t const handle) { + for (int i = 0; i < static_cast(Children.size()); ++i) { + menuHandle_t childHandle = Children[i]; + if (childHandle == handle) { + Children.erase(Children.cbegin() + i); + menuMgr.FreeObject(childHandle); + return; + } + } +} + +//============================== +// VRMenuObject::Frame +void VRMenuObject::Frame(OvrVRMenuMgr& menuMgr, Matrix4f const& viewMatrix) { + for (int i = 0; i < static_cast(Children.size()); ++i) { + VRMenuObject* child = menuMgr.ToObject(Children[i]); + if (child != NULL) { + child->Frame(menuMgr, viewMatrix); + } + } +} + +//============================== +// IntersectRayBounds +// Reports true if the hit was at or beyond start in the ray direction, +// or if the start point was inside of the bounds. +bool VRMenuObject::IntersectRayBounds( + Vector3f const& start, + Vector3f const& dir, + Vector3f const& mins, + Vector3f const& maxs, + ContentFlags_t const testContents, + float& t0, + float& t1) const { + if (!(testContents & GetContents())) { + return false; + } + + if (Bounds3f(mins, maxs).Contains(start, 0.1f)) { + return true; + } + Intersect_RayBounds(start, dir, mins, maxs, t0, t1); + return t0 >= 0.0f && t1 >= 0.0f && t1 >= t0; +} + +//============================== +// VRMenuObject::IntersectRay +bool VRMenuObject::IntersectRay( + Vector3f const& localStart, + Vector3f const& localDir, + Vector3f const& parentScale, + Bounds3f const& bounds, + float& bounds_t0, + float& bounds_t1, + ContentFlags_t const testContents, + OvrCollisionResult& result) const { + result = OvrCollisionResult(); + + // bounds are already computed with scale applied + if (!IntersectRayBounds( + localStart, + localDir, + bounds.GetMins(), + bounds.GetMaxs(), + testContents, + bounds_t0, + bounds_t1)) { + bounds_t0 = FLT_MAX; + bounds_t1 = FLT_MAX; + return false; + } + + // if marked to check only the bounds, then we've hit the object + if (Flags & VRMENUOBJECT_HIT_ONLY_BOUNDS) { + result.t = bounds_t0; + return true; + } + + // vertices have not had the scale applied yet + Vector3f const scale = GetLocalScale() * parentScale; + + // test vs. collision primitive + if (CollisionPrimitive != NULL) { + CollisionPrimitive->IntersectRay(localStart, localDir, scale, testContents, result); + } + + // test vs. surfaces + if (GetType() != VRMENU_CONTAINER && (Flags & VRMENUOBJECT_DONT_RENDER_SURFACE) == 0) { + for (const auto& surface : Surfaces) { + if (surface.IsRenderable()) { + OvrCollisionResult localResult; + if (surface.IntersectRay(localStart, localDir, scale, testContents, localResult)) { + if (localResult.t < result.t) { + result = localResult; + } + } + } + } + } + + return result.TriIndex >= 0; +} + +static void TransformByParentPose( + Posef const& parentPose, + Vector3f const& parentScale, + Posef const& localPose, + Vector3f const& localScale, + Posef& outPose, + Vector3f& outScale) { + outPose.Translation = parentPose.Translation + + (parentPose.Rotation * parentScale.EntrywiseMultiply(localPose.Translation)); + outPose.Rotation = parentPose.Rotation * localPose.Rotation; + outScale = parentScale.EntrywiseMultiply(localScale); +} + +//============================== +// VRMenuObject::HitTest_r +bool VRMenuObject::HitTest_r( + OvrGuiSys const& guiSys, + Posef const& parentPose, + Vector3f const& parentScale, + Vector3f const& rayStart, + Vector3f const& rayDir, + ContentFlags_t const testContents, + HitTestResult& result) const { + if (Flags & VRMENUOBJECT_DONT_RENDER) { + return false; + } + + if (Flags & VRMENUOBJECT_DONT_HIT_ALL) { + return false; + } + + // transform ray into local space + Vector3f scale; + Posef modelPose; + TransformByParentPose(parentPose, parentScale, LocalPose, GetLocalScale(), modelPose, scale); + + Vector3f localStart = modelPose.Rotation.Inverted().Rotate(rayStart - modelPose.Translation); + Vector3f localDir = modelPose.Rotation.Inverted().Rotate(rayDir).Normalized(); + /* + LOG_WITH_TAG( "Spam", "Hit test vs '%s', start: (%.2f, %.2f, %.2f ) cull bounds( %.2f, %.2f, + %.2f ) -> ( %.2f, %.2f, %.2f )", GetText().c_str(), localStart.x, localStart.y, localStart.z, + CullBounds.b[0].x, CullBounds.b[0].y, CullBounds.b[0].z, + CullBounds.b[1].x, CullBounds.b[1].y, CullBounds.b[1].z ); + */ + // test against cull bounds if we have children ... otherwise cullBounds == localBounds + if (Children.size() > 0) { + if (CullBounds.IsInverted()) { + ALOG("CullBounds are inverted!!"); + return false; + } + float cullT0; + float cullT1; + // any contents will hit cull bounds + ContentFlags_t allContents(OVR::ALL_BITS); + bool hitCullBounds = IntersectRayBounds( + localStart, + localDir, + CullBounds.GetMins(), + CullBounds.GetMaxs(), + allContents, + cullT0, + cullT1); + + // LOG_WITH_TAG( "Spam", "Cull hit = %s, t0 = %.2f t1 = %.2f", hitCullBounds ? "true" + // : "false", cullT0, cullT1 ); + + if (!hitCullBounds) { + return false; + } + } + + // test against self first, if not a container + if (GetContents() & testContents) { + if (Flags & VRMENUOBJECT_BOUND_ALL) { + // local bounds are the union of surface bounds and text bounds + Bounds3f localBounds = GetLocalBounds(guiSys.GetDefaultFont()) * parentScale; + float t0; + float t1; + bool hit = IntersectRayBounds( + localStart, + localDir, + localBounds.GetMins(), + localBounds.GetMaxs(), + testContents, + t0, + t1); + if (hit) { + result.HitHandle = Handle; + result.t = t1; + result.uv = Vector2f(0.0f); // unknown + } + } else { + float selfT0; + float selfT1; + OvrCollisionResult cresult; + Bounds3f const& localBounds = GetLocalBounds(guiSys.GetDefaultFont()) * parentScale; + assert(!localBounds.IsInverted()); + + bool hit = IntersectRay( + localStart, + localDir, + parentScale, + localBounds, + selfT0, + selfT1, + testContents, + cresult); + if (hit) { + // app->ShowInfoText( 0.0f, "tri: %i", (int)cresult.TriIndex ); + result = cresult; + result.HitHandle = Handle; + } + + // also check vs. the text bounds if there is any text + if (!Text.empty() && GetType() != VRMENU_CONTAINER && + (Flags & VRMENUOBJECT_DONT_HIT_TEXT) == 0) { + float textT0; + float textT1; + Bounds3f bounds = GetTextLocalBounds(guiSys.GetDefaultFont()) * parentScale; + bool textHit = IntersectRayBounds( + localStart, + localDir, + bounds.GetMins(), + bounds.GetMaxs(), + testContents, + textT0, + textT1); + if (textHit && textT1 < result.t) { + result.HitHandle = Handle; + result.t = textT1; + result.uv = Vector2f(0.0f); // unknown + } + } + } + } + + // test against children + for (int i = 0; i < static_cast(Children.size()); ++i) { + VRMenuObject* child = + static_cast(guiSys.GetVRMenuMgr().ToObject(Children[i])); + if (child != NULL) { + HitTestResult childResult; + bool intersected = child->HitTest_r( + guiSys, modelPose, scale, rayStart, rayDir, testContents, childResult); + if (intersected && childResult.t < result.t) { + result = childResult; + } + } + } + return result.HitHandle.IsValid(); +} + +//============================== +// VRMenuObject::HitTest +menuHandle_t VRMenuObject::HitTest( + OvrGuiSys const& guiSys, + Posef const& worldPose, + Vector3f const& rayStart, + Vector3f const& rayDir, + ContentFlags_t const testContents, + HitTestResult& result) const { + HitTest_r(guiSys, worldPose, Vector3f(1.0f), rayStart, rayDir, testContents, result); + + return result.HitHandle; +} + +//============================== +// VRMenuObject::GetLocalBounds +Bounds3f VRMenuObject::GetLocalBounds(BitmapFont const& font) const { + Bounds3f bounds; + bounds.Clear(); + Vector3f const localScale = GetLocalScale(); + for (const auto& surface : Surfaces) { + Bounds3f const& surfaceBounds = surface.GetLocalBounds() * localScale; + bounds = Bounds3f::Union(bounds, surfaceBounds); + } + + if (CollisionPrimitive != NULL) { + bounds = Bounds3f::Union(bounds, CollisionPrimitive->GetBounds()); + } + + // transform surface bounds by whatever the hilight pose is + if (!bounds.IsInverted()) { + bounds = Bounds3f::Transform(HilightPose, bounds); + } + + // also union the text bounds, as long as we're not a container (containers don't render + // anything) + if (!Text.empty() && GetType() != VRMENU_CONTAINER) { + bounds = Bounds3f::Union(bounds, GetTextLocalBounds(font)); + } + + // if no valid surface bounds, then the local bounds is the local translation + if (bounds.IsInverted()) { + bounds.AddPoint(LocalPose.Translation); + bounds = Bounds3f::Transform(HilightPose, bounds); + } + + // after everything is calculated, expand (or contract) the bounds some custom amount + bounds = Bounds3f::Expand(bounds, MinsBoundsExpand, MaxsBoundsExpand); + + return bounds; +} + +//============================== +// VRMenuObject::GetTextLocalBounds +Bounds3f VRMenuObject::GetTextLocalBounds(BitmapFont const& font) const { + // NOTE: despite being 3 scalars, text scaling only uses the x component since + // DrawText3D doesn't take separate x and y scales right now. + Vector3f const localScale = GetLocalScale(); + Vector3f const textLocalScale = GetTextLocalScale(); + float scale = localScale.x * textLocalScale.x * FontParms.Scale * WrapScale; + + if (TextDirty) { + TextDirty = false; + + // word-wrap the text if wrapping is specified + if (FontParms.WrapWidth >= 0.0f && FontParms.MultiLine) { + font.WordWrapText( + Text, + FontParms.WrapWidth * localScale.x * textLocalScale.x, + localScale.x * textLocalScale.x * FontParms.Scale); + } + + // also union the text bounds + if (Text.empty()) { + TextMetrics = textMetrics_t(); + } else { + size_t len; + + int const MAX_LINES = 16; + float lineWidths[MAX_LINES]; + int requestedLines = std::clamp(FontParms.MaxLines, 1, 16); + int numLines = 0; + + font.CalcTextMetrics( + Text.c_str(), + len, + TextMetrics.w, + TextMetrics.h, + TextMetrics.ascent, + TextMetrics.descent, + TextMetrics.fontHeight, + lineWidths, + requestedLines, + numLines); + + // for the time being if we exceed the number of lines we truncate the last few lines + // and add a ... to indicate more text is there we'll do this until we support scrolling + if (numLines > requestedLines) { + font.TruncateText(Text, requestedLines); + } + + if (FontParms.WrapWidth >= 0.0f && + TextMetrics.w * FontParms.Scale > FontParms.WrapWidth) { + WrapScale = FontParms.WrapWidth / (TextMetrics.w * FontParms.Scale); + scale = localScale.x * textLocalScale.x * FontParms.Scale * WrapScale; + } else { + WrapScale = 1.0f; // reset scale back to 1.0f in case it was shrunk down previously + } + } + + if (Flags & VRMenuObjectFlags_t(VRMENUOBJECT_INSTANCE_TEXT)) { + FreeTextSurface(); + TextSurface = new ovrTextSurface(); + fontParms_t fp; + fp.AlignHoriz = FontParms.AlignHoriz; + fp.AlignVert = FontParms.AlignVert; + fp.TrackRoll = FontParms.TrackRoll; + fp.ColorCenter = FontParms.ColorCenter; + fp.AlphaCenter = FontParms.AlphaCenter; + TextSurface->SurfaceDef = font.TextSurface( + Text.c_str(), scale, TextColor, FontParms.AlignHoriz, FontParms.AlignVert, &fp); + } + } + + // this seems overly complex because font characters are rendered so that their origin + // is on their baseline and not on one of the corners of the glyph. Because of this + // we must treat the initial ascent (amount the font goes above the first baseline) and + // final descent (amount the font goes below the final baseline) independently from the + // lines in between when centering. + Bounds3f textBounds( + Vector3f(0.0f, (TextMetrics.h - TextMetrics.ascent) * -1.0f, 0.0f) * scale, + Vector3f(TextMetrics.w, TextMetrics.ascent, 0.0f) * scale); + + Vector3f trans = Vector3f::ZERO; + switch (FontParms.AlignVert) { + case VERTICAL_BASELINE: + trans.y = 0.0f; + break; + + case VERTICAL_CENTER: { + trans.y = (TextMetrics.h * 0.5f) - TextMetrics.ascent; + break; + } + + case VERTICAL_CENTER_FIXEDHEIGHT: { + trans.y = (TextMetrics.fontHeight * -0.5f); + break; + } + + case VERTICAL_TOP: { + trans.y = TextMetrics.h - TextMetrics.ascent; + break; + } + } + + switch (FontParms.AlignHoriz) { + case HORIZONTAL_LEFT: + trans.x = 0.0f; + break; + + case HORIZONTAL_CENTER: { + trans.x = TextMetrics.w * -0.5f; + break; + } + case HORIZONTAL_RIGHT: { + trans.x = -TextMetrics.w; + break; + } + } + + textBounds.Translate(trans * scale); + + Bounds3f textLocalBounds = Bounds3f::Transform(GetTextLocalPose(), textBounds); + // transform by hilightpose here since surfaces are transformed by it before unioning the bounds + textLocalBounds = Bounds3f::Transform(HilightPose, textLocalBounds); + + return textLocalBounds; +} + +//============================== +// VRMenuObject::CalcLocalBoundsForText +Bounds3f VRMenuObject::CalcLocalBoundsForText(BitmapFont const& font, std::string& text) const { + if (text.empty()) { + return Bounds3f(); + } + + // NOTE: despite being 3 scalars, text scaling only uses the x component since + // DrawText3D doesn't take separate x and y scales right now. + Vector3f const localScale = GetLocalScale(); + Vector3f const textLocalScale = GetTextLocalScale(); + + float localWrapScale = 1.0f; + float scale = localScale.x * textLocalScale.x * FontParms.Scale * localWrapScale; + + size_t len; + int const MAX_LINES = 16; + float lineWidths[MAX_LINES]; + int numLines = 0; + int requestedLines = std::clamp(FontParms.MaxLines, 1, 16); + + float w; + float h; + float ascent; + float descent; + float fontHeight; + + // word-wrap the text if wrapping is specified + if (FontParms.WrapWidth >= 0.0f && FontParms.MultiLine) { + font.WordWrapText( + text, + FontParms.WrapWidth * localScale.x * textLocalScale.x, + localScale.x * textLocalScale.x * FontParms.Scale); + } + + font.CalcTextMetrics( + text.c_str(), len, w, h, ascent, descent, fontHeight, lineWidths, requestedLines, numLines); + + // for the time being if we exceed the number of lines we truncate the last few lines and add a + // ... to indicate more text is there we'll do this until we support scrolling + if (numLines > requestedLines) { + font.TruncateText(text, requestedLines); + } + + if (FontParms.WrapWidth >= 0.0f && w * FontParms.Scale > FontParms.WrapWidth) { + localWrapScale = FontParms.WrapWidth / (w * FontParms.Scale); + scale = localScale.x * textLocalScale.x * FontParms.Scale * localWrapScale; + } + + // this seems overly complex because font characters are rendered so that their origin + // is on their baseline and not on one of the corners of the glyph. Because of this + // we must treat the initial ascent (amount the font goes above the first baseline) and + // final descent (amount the font goes below the final baseline) independently from the + // lines in between when centering. + Bounds3f textBounds( + Vector3f(0.0f, (h - ascent) * -1.0f, 0.0f) * scale, Vector3f(w, ascent, 0.0f) * scale); + + Vector3f trans = Vector3f::ZERO; + switch (FontParms.AlignVert) { + case VERTICAL_BASELINE: + trans.y = 0.0f; + break; + + case VERTICAL_CENTER: { + trans.y = (TextMetrics.h * 0.5f) - TextMetrics.ascent; + break; + } + + case VERTICAL_CENTER_FIXEDHEIGHT: { + trans.y = (TextMetrics.fontHeight * -0.5f); + break; + } + + case VERTICAL_TOP: { + trans.y = TextMetrics.h - TextMetrics.ascent; + break; + } + } + + switch (FontParms.AlignHoriz) { + case HORIZONTAL_LEFT: + trans.x = 0.0f; + break; + + case HORIZONTAL_CENTER: { + trans.x = TextMetrics.w * -0.5f; + break; + } + case HORIZONTAL_RIGHT: { + trans.x = -TextMetrics.w; + break; + } + } + + textBounds.Translate(trans * scale); + + Bounds3f textLocalBounds = Bounds3f::Transform(GetTextLocalPose(), textBounds); + // transform by hilightpose here since surfaces are transformed by it before unioning the bounds + textLocalBounds = Bounds3f::Transform(HilightPose, textLocalBounds); + + return textLocalBounds; +} + +//============================== +// VRMenuObject::AddComponent +void VRMenuObject::AddComponent(VRMenuComponent* component) { + if (component == NULL) { + return; // this is fine... makes submitting VRMenuComponentParms easier. + } + + int componentIndex = GetComponentIndex(component); + if (componentIndex >= 0) { + // cannot add the same component twice! + /// assert_WITH_TAG( componentIndex < 0, "VRMenu" ); + return; + } + Components.push_back(component); +} + +//============================== +// VRMenuObject::DeleteComponent +void VRMenuObject::DeleteComponent(OvrGuiSys& guiSys, VRMenuComponent* component) { +#if defined(OVR_BUILD_DEBUG) + [[maybe_unused]] int componentIndex = GetComponentIndex(component); + assert(componentIndex >= 0); +#endif + // stop the component from getting any events between now and the time it's freed. + component->ClearEventFlags(); + + // add the object and the component to a pending deletion list + guiSys.GetVRMenuMgr().AddComponentToDeletionList(GetHandle(), component); +} + +//============================== +// VRMenuObject::FreeComponents +void VRMenuObject::FreeComponents(ovrComponentList& list) { + std::vector& comps = list.GetComponents(); + for (int i = static_cast(comps.size()) - 1; i >= 0; --i) { + VRMenuComponent* component = comps[i]; + comps[i] = nullptr; + + for (int j = 0; j < static_cast(Components.size()); ++j) { + if (Components[j] == component) { + Components.erase(Components.cbegin() + j); + break; + } + } + + delete component; + } +} + +//============================== +// VRMenuObject::GetComponentIndex +int VRMenuObject::GetComponentIndex(VRMenuComponent* component) const { + for (int i = 0; i < static_cast(Components.size()); ++i) { + if (Components[i] == component) { + return i; + } + } + return -1; +} + +//============================== +// VRMenuObject::GetComponentById +VRMenuComponent* VRMenuObject::GetComponentById_Impl(int const id, const char* name) const { + std::vector const& comps = GetComponentList(); + for (int c = 0; c < static_cast(comps.size()); ++c) { + if (VRMenuComponent* comp = comps[c]) { + if (comp->GetTypeId() == id) { + if (name == NULL || !OVR::OVR_strcmp(comp->GetName(), name)) { + return comp; + } + } + } else { + assert(comp); + } + } + + return NULL; +} + +//============================== +// VRMenuObject::GetComponentByTypeName +VRMenuComponent* VRMenuObject::GetComponentByTypeName_Impl(const char* typeName) const { + std::vector const& comps = GetComponentList(); + for (int c = 0; c < static_cast(comps.size()); ++c) { + if (VRMenuComponent* comp = comps[c]) { + if (comp->GetTypeName() == typeName) { + return comp; + } + } else { + assert(comp); + } + } + + return NULL; +} + +//============================== +// VRMenuObject::GetColorTableOffset +Vector2f const& VRMenuObject::GetColorTableOffset() const { + return ColorTableOffset; +} + +//============================== +// VRMenuObject::SetColorTableOffset +void VRMenuObject::SetColorTableOffset(Vector2f const& ofs) { + ColorTableOffset = ofs; +} + +//============================== +// VRMenuObject::GetColor +Vector4f const& VRMenuObject::GetColor() const { + return Color; +} + +//============================== +// VRMenuObject::SetColor +void VRMenuObject::SetColor(Vector4f const& c) { + Color = c; +} + +void VRMenuObject::SetVisible(bool visible) { + if (visible) { + Flags &= ~VRMenuObjectFlags_t(VRMENUOBJECT_DONT_RENDER); + } else { + Flags |= VRMenuObjectFlags_t(VRMENUOBJECT_DONT_RENDER); + } +} + +//============================== +// VRMenuObject::ChildForId +VRMenuObject* VRMenuObject::ChildForId(OvrVRMenuMgr const& menuMgr, VRMenuId_t const id) const { + int n = NumChildren(); + for (int i = 0; i < n; ++i) { + VRMenuObject const* child = + static_cast(menuMgr.ToObject(GetChildHandleForIndex(i))); + if (child != nullptr) { + if (child->GetId() == id) { + return const_cast(child); + } else { + VRMenuObject* c = child->ChildForId(menuMgr, id); + if (c != nullptr) { + return c; + } + } + } + } + return nullptr; +} + +//============================== +// VRMenuObject::ChildHandleForId +menuHandle_t VRMenuObject::ChildHandleForId(OvrVRMenuMgr const& menuMgr, VRMenuId_t const id) + const { + int n = NumChildren(); + for (int i = 0; i < n; ++i) { + menuHandle_t childHandle = GetChildHandleForIndex(i); + VRMenuObject const* child = static_cast(menuMgr.ToObject(childHandle)); + if (child != nullptr) { + if (child->GetId() == id) { + return childHandle; + } else { + menuHandle_t handle = child->ChildHandleForId(menuMgr, id); + if (handle.IsValid()) { + return handle; + } + } + } + } + return menuHandle_t(); +} + +//============================== +// VRMenuObject::ChildHandleForName +menuHandle_t VRMenuObject::ChildHandleForName(OvrVRMenuMgr const& menuMgr, char const* name) const { + if (name == NULL || name[0] == '\0') { + return menuHandle_t(); + } + + int n = NumChildren(); + for (int i = 0; i < n; ++i) { + VRMenuObject const* child = + static_cast(menuMgr.ToObject(GetChildHandleForIndex(i))); + if (child != NULL) { + if (OVR::OVR_stricmp(child->GetName().c_str(), name) == 0) { + return child->GetHandle(); + } else { + menuHandle_t handle = child->ChildHandleForName(menuMgr, name); + if (handle.IsValid()) { + return handle; + } + } + } + } + return menuHandle_t(); +} + +//============================== +// VRMenuObject::ChildHandleForTag +menuHandle_t VRMenuObject::ChildHandleForTag(OvrVRMenuMgr const& menuMgr, char const* tag) const { + if (tag == NULL || tag[0] == '\0') { + return menuHandle_t(); + } + + int n = NumChildren(); + for (int i = 0; i < n; ++i) { + VRMenuObject const* child = + static_cast(menuMgr.ToObject(GetChildHandleForIndex(i))); + if (child != NULL) { + if (OVR::OVR_stricmp(child->GetTag().c_str(), tag) == 0) { + return child->GetHandle(); + } else { + menuHandle_t handle = child->ChildHandleForTag(menuMgr, tag); + if (handle.IsValid()) { + return handle; + } + } + } + } + return menuHandle_t(); +} + +//============================== +// VRMenuObject::GetLocalScale +Vector3f VRMenuObject::GetLocalScale() const { + return Vector3f( + LocalScale.x * HilightScale, LocalScale.y * HilightScale, LocalScale.z * HilightScale); +} + +//============================== +// VRMenuObject::GetTextLocalScale +Vector3f VRMenuObject::GetTextLocalScale() const { + return Vector3f( + TextLocalScale.x * HilightScale, + TextLocalScale.y * HilightScale, + TextLocalScale.z * HilightScale); +} + +//============================== +// VRMenuObject::SetSurfaceTexture +void VRMenuObject::SetSurfaceTexture( + OvrGuiSys& guiSys, + int const surfaceIndex, + int const textureIndex, + eSurfaceTextureType const type, + char const* imageName) { + if (surfaceIndex < 0 || surfaceIndex >= static_cast(Surfaces.size())) { + /// assert_WITH_TAG( surfaceIndex >= 0 && surfaceIndex < static_cast< int >( Surfaces.size() + /// ), "VrMenu" ); + return; + } + Surfaces[surfaceIndex].LoadTexture(guiSys, textureIndex, type, imageName); +} + +//============================== +// VRMenuObject::SetSurfaceTexture +void VRMenuObject::SetSurfaceTexture( + int const surfaceIndex, + int const textureIndex, + eSurfaceTextureType const type, + GLuint const texId, + int const width, + int const height) { + if (surfaceIndex < 0 || surfaceIndex >= static_cast(Surfaces.size())) { + /// assert_WITH_TAG( surfaceIndex >= 0 && surfaceIndex < static_cast< int >( Surfaces.size() + /// ), "VrMenu" ); + return; + } + Surfaces[surfaceIndex].LoadTexture(textureIndex, type, texId, width, height); +} + +//============================== +// VRMenuObject::SetSurfaceTexture +void VRMenuObject::SetSurfaceTexture( + int const surfaceIndex, + int const textureIndex, + eSurfaceTextureType const type, + GlTexture const& texture) { + if (surfaceIndex < 0 || surfaceIndex >= static_cast(Surfaces.size())) { + /// assert_WITH_TAG( surfaceIndex >= 0 && surfaceIndex < static_cast< int >( Surfaces.size() + /// ), "VrMenu" ); + return; + } + Surfaces[surfaceIndex].LoadTexture( + textureIndex, type, texture.texture, texture.Width, texture.Height); +} + +//============================== +// VRMenuObject::SetSurfaceTexture +void VRMenuObject::SetSurfaceTextureTakeOwnership( + int const surfaceIndex, + int const textureIndex, + eSurfaceTextureType const type, + GLuint const texId, + int const width, + int const height) { + if (surfaceIndex < 0 || surfaceIndex >= static_cast(Surfaces.size())) { + /// assert_WITH_TAG( surfaceIndex >= 0 && surfaceIndex < static_cast< int >( Surfaces.size() + /// ), "VrMenu" ); + return; + } + Surfaces[surfaceIndex].LoadTexture(textureIndex, type, texId, width, height); + Surfaces[surfaceIndex].SetOwnership(textureIndex, true); +} + +//============================== +// VRMenuObject::SetSurfaceTextureTakeOwnership +void VRMenuObject::SetSurfaceTextureTakeOwnership( + int const surfaceIndex, + int const textureIndex, + eSurfaceTextureType const type, + GlTexture const& texture) { + if (surfaceIndex < 0 || surfaceIndex >= static_cast(Surfaces.size())) { + /// assert_WITH_TAG( surfaceIndex >= 0 && surfaceIndex < static_cast< int >( Surfaces.size() + /// ), "VrMenu" ); + return; + } + Surfaces[surfaceIndex].LoadTexture( + textureIndex, type, texture.texture, texture.Width, texture.Height); + Surfaces[surfaceIndex].SetOwnership(textureIndex, true); +} + +//============================== +// VRMenuObject::RegenerateSurfaceGeometry +void VRMenuObject::RegenerateSurfaceGeometry( + int const surfaceIndex, + const bool freeSurfaceGeometry) { + if (surfaceIndex < 0 || surfaceIndex >= static_cast(Surfaces.size())) { + /// assert_WITH_TAG( surfaceIndex >= 0 && surfaceIndex < static_cast< int >( Surfaces.size() + /// ), "VrMenu" ); + return; + } + + if (freeSurfaceGeometry) { + Surfaces[surfaceIndex].Free(); + } + + Surfaces[surfaceIndex].RegenerateSurfaceGeometry(); +} + +//============================== +// VRMenuObject::GetSurfaceDims +Vector2f const& VRMenuObject::GetSurfaceDims(int const surfaceIndex) const { + if (surfaceIndex < 0 || surfaceIndex >= static_cast(Surfaces.size())) { + /// assert_WITH_TAG( surfaceIndex >= 0 && surfaceIndex < static_cast< int >( Surfaces.size() + /// ), "VrMenu" ); + return Vector2f::ZERO; + } + + return Surfaces[surfaceIndex].GetDims(); +} + +//============================== +// VRMenuObject::SetSurfaceDims +void VRMenuObject::SetSurfaceDims(int const surfaceIndex, Vector2f const& dims) { + if (surfaceIndex < 0 || surfaceIndex >= static_cast(Surfaces.size())) { + /// assert_WITH_TAG( surfaceIndex >= 0 && surfaceIndex < static_cast< int >( Surfaces.size() + /// ), "VrMenu" ); + return; + } + + Surfaces[surfaceIndex].SetDims(dims); +} + +//============================== +// VRMenuObject::GetSurfaceBorder +Vector4f const& VRMenuObject::GetSurfaceBorder(int const surfaceIndex) { + if (surfaceIndex < 0 || surfaceIndex >= static_cast(Surfaces.size())) { + /// assert_WITH_TAG( surfaceIndex >= 0 && surfaceIndex < static_cast< int >( Surfaces.size() + /// ), "VrMenu" ); + return Vector4f::ZERO; + } + + return Surfaces[surfaceIndex].GetBorder(); +} + +//============================== +// VRMenuObject::SetSurfaceBorder +void VRMenuObject::SetSurfaceBorder(int const surfaceIndex, Vector4f const& border) { + if (surfaceIndex < 0 || surfaceIndex >= static_cast(Surfaces.size())) { + /// assert_WITH_TAG( surfaceIndex >= 0 && surfaceIndex < static_cast< int >( Surfaces.size() + /// ), "VrMenu" ); + return; + } + + Surfaces[surfaceIndex].SetBorder(border); +} + +//============================== +// VRMenuObject::SetLocalBoundsExpand +void VRMenuObject::SetLocalBoundsExpand(Vector3f const mins, Vector3f const& maxs) { + MinsBoundsExpand = mins; + MaxsBoundsExpand = maxs; +} + +//============================== +// VRMenuObject::SetCollisionPrimitive +void VRMenuObject::SetCollisionPrimitive(OvrCollisionPrimitive* c) { + if (CollisionPrimitive != NULL) { + delete CollisionPrimitive; + } + CollisionPrimitive = c; +} + +//============================== +// VRMenuObject::FindSurfaceWithTextureType +int VRMenuObject::FindSurfaceWithTextureType(eSurfaceTextureType const type, bool const singular) + const { + return FindSurfaceWithTextureType(type, singular, false); +} + +//============================== +// VRMenuObject::FindSurfaceWithTextureType +int VRMenuObject::FindSurfaceWithTextureType( + eSurfaceTextureType const type, + bool const singular, + bool const visibleOnly) const { + for (int i = 0; i < static_cast(Surfaces.size()); ++i) { + VRMenuSurface const& surf = Surfaces[i]; + if (visibleOnly && !surf.GetVisible()) { + continue; + } + + int numTextures = 0; + bool hasType = false; + // we have to look through the surface images because we don't know how many are valid + for (int j = 0; j < VRMENUSURFACE_IMAGE_MAX; j++) { + VRMenuSurfaceTexture const& texture = surf.GetTexture(j); + if (texture.GetType() != SURFACE_TEXTURE_MAX) { + numTextures++; + } + if (texture.GetType() == type) { + hasType = true; + } + } + if (hasType) { + if (!singular || (singular && numTextures == 1)) { + return i; + } + } + } + return -1; +} + +//============================== +// VRMenuObject::SetSurfaceColor +void VRMenuObject::SetSurfaceColor(int const surfaceIndex, Vector4f const& color) { + VRMenuSurface& surf = Surfaces[surfaceIndex]; + surf.SetColor(color); +} + +//============================== +// VRMenuObject::GetSurfaceColor +Vector4f const& VRMenuObject::GetSurfaceColor(int const surfaceIndex) const { + VRMenuSurface const& surf = Surfaces[surfaceIndex]; + return surf.GetColor(); +} + +//============================== +// VRMenuObject::SetSurfaceVisible +void VRMenuObject::SetSurfaceVisible(int const surfaceIndex, bool const v) { + VRMenuSurface& surf = Surfaces[surfaceIndex]; + surf.SetVisible(v); +} + +//============================== +// VRMenuObject::GetSurfaceVisible +bool VRMenuObject::GetSurfaceVisible(int const surfaceIndex) const { + VRMenuSurface const& surf = Surfaces[surfaceIndex]; + return surf.GetVisible(); +} + +//============================== +// VRMenuObject::NumSurfaces +int VRMenuObject::NumSurfaces() const { + return static_cast(Surfaces.size()); +} + +//============================== +// VRMenuObject::AllocSurface +int VRMenuObject::AllocSurface() { + int newIndex = static_cast(Surfaces.size()); + Surfaces.emplace_back(VRMenuSurface()); + return newIndex; +} + +//============================== +// VRMenuObject::CreateFromSurfaceParms +void VRMenuObject::CreateFromSurfaceParms( + OvrGuiSys& guiSys, + int const surfaceIndex, + VRMenuSurfaceParms const& parms) { + VRMenuSurface& surf = Surfaces[surfaceIndex]; + surf.CreateFromSurfaceParms(guiSys, parms); +} + +//============================== +// VRMenuObject::SetText +void VRMenuObject::SetText(char const* text) { + Text = text; + TextDirty = true; +} + +//============================== +// VRMenuObject::SetTextWordWrapped +void VRMenuObject::SetTextWordWrapped( + char const* text, + BitmapFont const& font, + float const widthInMeters) { + FontParms.WrapWidth = widthInMeters; + SetText(text); + font.WordWrapText(Text, widthInMeters, FontParms.Scale); +} + +//============================== +// VRMenuObject::TransformByParent +void VRMenuObject::TransformByParent( + Posef const& parentPose, + Vector3f const& parentScale, + Vector4f const& parentColor, + Posef const& localPose, + Vector3f const& localScale, + Vector4f const& localColor, + VRMenuObjectFlags_t const& objectFlags, + Posef& outPose, + Vector3f& outScale, + Vector4f& outColor) { + TransformByParentPose(parentPose, parentScale, localPose, localScale, outPose, outScale); + outColor = (objectFlags & VRMENUOBJECT_DONT_MOD_PARENT_COLOR) ? localColor * parentColor.w + : parentColor * localColor; +} + +//============================== +// VRMenuObject::GetWorldTransform +void VRMenuObject::GetWorldTransform( + OvrVRMenuMgr& menuMgr, + Posef const& menuPose, + Posef& outPose, + Vector3f& outScale, + Vector4f& outColor) const { + std::vector objects; + objects.push_back(this); + + // collect all of the parents + menuHandle_t parentHandle = ParentHandle; + while (parentHandle.IsValid()) { + VRMenuObject* obj = menuMgr.ToObject(parentHandle); + objects.push_back(obj); + parentHandle = obj->GetParentHandle(); + } + + // now go through them in parent -> child order to compute the transformation + + outPose = menuPose; // start as identity + outScale = Vector3f(1.0f); + outColor = Vector4f(1.0f); + for (int i = static_cast(objects.size()) - 1; i >= 0; --i) { + VRMenuObject const* obj = objects[i]; + TransformByParent( + outPose, + outScale, + outColor, + obj->GetLocalPose(), + obj->GetLocalScale(), + obj->GetColor(), + obj->GetFlags(), + outPose, + outScale, + outColor); + } +} + +//============================== +// VRMenuObject::Recurse +void VRMenuObject::Recurse( + OvrVRMenuMgr const& menuMgr, + ovrRecursionFunctor& functor, + VRMenuObject* obj) { + functor.AtNode(obj); + + for (int i = 0; i < obj->NumChildren(); ++i) { + VRMenuObject* child = menuMgr.ToObject(obj->GetChildHandleForIndex(i)); + if (child != nullptr) { + child->Recurse(menuMgr, functor, child); + } + } +} + +//============================== +// VRMenuObject::FreeTextSurface +void VRMenuObject::FreeTextSurface() const { + if (TextSurface != nullptr) { + TextSurface->SurfaceDef.geo.Free(); + delete TextSurface; + TextSurface = nullptr; + } +} + +static void DumpText(char const* token, char const* text, int const size) { + // DUMP THE ENITER BUFFER TO THE LOG TO CATCH MEMORY CORRUPTION + ALOG("VRGUI PARSE ERROR!"); + ALOG("lex token: %s", token); + ALOG("lex buffer:"); + const int BLOCK_SIZE = 256; + int blocks = size / BLOCK_SIZE; + char const* bufferStart = text; + for (int i = 0; i <= blocks; i++) { + char const* cur = bufferStart + i * BLOCK_SIZE; + int count = (i < blocks) ? BLOCK_SIZE : (size - (blocks * BLOCK_SIZE)); + char temp[BLOCK_SIZE + 1]; + memcpy(temp, cur, count); + temp[count] = '\0'; + ALOG("block %i: %s", i, temp); + } +} + +//============================== +// VRMenuObject::ParseItemParms +ovrParseResult VRMenuObject::ParseItemParms( + ovrReflection& refl, + ovrLocale const& locale, + char const* fileName, + std::vector const& buffer, + std::vector& itemParms) { + ovrLexer lex(buffer, ":;|[],()/*\\#"); + + char token[128]; + ovrLexer::ovrResult res = lex.NextToken(token, sizeof(token)); + if (res == ovrLexer::LEX_RESULT_EOF) { + return ovrParseResult(res, "Error parsing reflection file '%s'.", fileName); + } + + do { + if (token[0] == '#') { + // preprocessor token + res = lex.NextToken(token, sizeof(token)); + if (res != ovrLexer::LEX_RESULT_OK) { + DumpText( + token, + (const char*)(static_cast(buffer.data())), + (int)buffer.size()); + return ovrParseResult(res, "Expected key word after #."); + } + if (OVR::OVR_strcmp(token, "pragma") == 0) { + res = lex.NextToken(token, sizeof(token)); + if (res != ovrLexer::LEX_RESULT_OK) { + return ovrParseResult(res, "Expected pragma type."); + } + if (OVR::OVR_strcmp(token, "overload_float_default_value") == 0) { + res = lex.ExpectPunctuation("(", token, sizeof(token)); + if (res != ovrLexer::LEX_RESULT_OK) { + return ovrParseResult(res, "Expected '('."); + } + + // parse overload parameters + std::string scope; + std::string name; + for (;;) { + char nameToken[128]; + res = lex.NextToken(nameToken, sizeof(nameToken)); + if (res != ovrLexer::LEX_RESULT_OK) { + return ovrParseResult(res, "Expected identifier name."); + } + char puncToken[16]; + res = lex.NextToken(puncToken, sizeof(puncToken)); + if (res != ovrLexer::LEX_RESULT_OK) { + return ovrParseResult(res, "Expected ':'"); + } + if (puncToken[0] == ',') { + name = nameToken; + break; + } + if (puncToken[0] != ':') { + return ovrParseResult(res, "Expected ':'"); + } + res = lex.ExpectPunctuation(":", puncToken, sizeof(puncToken)); + if (res != ovrLexer::LEX_RESULT_OK) { + return ovrParseResult(res, "Expected ':'"); + } + if (!scope.empty()) { + scope += "::"; + } + scope += nameToken; + } + + float value; + res = lex.ParseFloat(value, 0.0f); + if (res != ovrLexer::LEX_RESULT_OK) { + return ovrParseResult(res, "Expected float value."); + } + + res = lex.ExpectPunctuation(")", token, sizeof(token)); + if (res != ovrLexer::LEX_RESULT_OK) { + return ovrParseResult(res, "Expected ')'."); + } + + refl.AddOverload(new ovrReflectionOverload_FloatDefaultValue( + scope.c_str(), name.c_str(), value)); + } + } else { + // unknown pragmas are errors for now + return ovrParseResult(res, "Unknown pragma %s", token); + } + } else if (OVR::OVR_strcmp(token, "itemParms") == 0) { + std::vector parms; + ovrTypeInfo const* typeInfo = refl.FindTypeInfo("std::vector< VRMenuObjectParms* >"); + if (typeInfo != nullptr) { + ovrParseResult parseRes = + ParseArray(refl, locale, fileName, lex, typeInfo, &parms, 0); + if (!parseRes) { + DeletePointerArray(parms); + return parseRes; + } + } + + itemParms.insert(itemParms.cend(), parms.cbegin(), parms.cend()); + parms.resize(0); + + } else { + return ovrParseResult(res, "Unknown token '%s'", token); + } + + res = lex.NextToken(token, sizeof(token)); + if (res == ovrLexer::LEX_RESULT_EOF) { + break; + } else if (res != ovrLexer::LEX_RESULT_OK) { + return ovrParseResult(res, "Error parsing reflection file '%s'.", fileName); + } + } while (res == ovrLexer::LEX_RESULT_OK); + + return ovrParseResult(); +} + +} // namespace OVRFW diff --git a/Samples/SampleXrFramework/Src/GUI/VRMenuObject.h b/Samples/SampleXrFramework/Src/GUI/VRMenuObject.h new file mode 100755 index 0000000..c29bb71 --- /dev/null +++ b/Samples/SampleXrFramework/Src/GUI/VRMenuObject.h @@ -0,0 +1,1447 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/************************************************************************************ + +Filename : VRMenuObject.h +Content : Menuing system for VR apps. +Created : May 23, 2014 +Authors : Jonathan E. Wright + +*************************************************************************************/ + +#pragma once + +#include +#include + +#include "OVR_BitFlags.h" +#include "OVR_TypesafeNumber.h" +#include "OVR_Math.h" + +#include "Render/Egl.h" // GLuint +#include "Render/BitmapFont.h" // HorizontalJustification & VerticalJustification +#include "Misc/Log.h" + +#include "CollisionPrimitive.h" + +#include "OVR_Lexer2.h" // ovrLexer + +namespace OVRFW { + +class App; +class OvrVRMenuMgr; +class OvrGuiSys; +class ovrReflection; +class ovrLocale; +class ovrParseResult; + +//============================== +// DeletePointerArray +// helper function for cleaning up an array of pointers +template +void DeletePointerArray(std::vector& a) { + for (T* item : a) { + delete item; + } + a.clear(); +} + +enum eGUIProgramType { + PROGRAM_DIFFUSE_ONLY, // has a diffuse only + PROGRAM_DIFFUSE_ALPHA_DISCARD, // discard 0-alpha fragments + PROGRAM_ADDITIVE_ONLY, + PROGRAM_DIFFUSE_PLUS_ADDITIVE, // has a diffuse and an additive + PROGRAM_DIFFUSE_COLOR_RAMP, // has a diffuse and color ramp, and color ramp target is the + // diffuse + PROGRAM_DIFFUSE_COLOR_RAMP_TARGET, // has diffuse, color ramp, and a separate color ramp target + PROGRAM_DIFFUSE_COMPOSITE, // has two diffuse, with the second composited onto the first using + // it's alpha mask + PROGRAM_ALPHA_DIFFUSE, // first texture is the alpha map, second texture is the d + PROGRAM_MAX // some other combo not supported, or no texture maps at all +}; + +enum eVRMenuObjectType { + VRMENU_CONTAINER, // a container for other controls + VRMENU_STATIC, // non-interactable item, may have text + VRMENU_BUTTON, // "clickable" + VRMENU_MAX // invalid +}; + +enum eVRMenuSurfaceImage { + VRMENUSURFACE_IMAGE_0, + VRMENUSURFACE_IMAGE_1, + VRMENUSURFACE_IMAGE_2, + VRMENUSURFACE_IMAGE_MAX +}; + +enum eSurfaceTextureType { + SURFACE_TEXTURE_DIFFUSE, + SURFACE_TEXTURE_DIFFUSE_ALPHA_DISCARD, // discard 0-alpha fragments + SURFACE_TEXTURE_ADDITIVE, + SURFACE_TEXTURE_COLOR_RAMP, + SURFACE_TEXTURE_COLOR_RAMP_TARGET, + SURFACE_TEXTURE_ALPHA_MASK, + SURFACE_TEXTURE_MAX // also use to indicate an uninitialized type +}; + +enum eVRMenuObjectFlags { + VRMENUOBJECT_FLAG_NO_FOCUS_GAINED, // object will never get focus gained event + VRMENUOBJECT_DONT_HIT_ALL, // do not collide with object in hit testing + VRMENUOBJECT_DONT_HIT_TEXT, // do not collide with text in hit testing + VRMENUOBJECT_HIT_ONLY_BOUNDS, // only collide against the bounds, not the collision surface + VRMENUOBJECT_BOUND_ALL, // for hit testing, merge all bounds into a single AABB + VRMENUOBJECT_FLAG_POLYGON_OFFSET, // render with polygon offset + VRMENUOBJECT_FLAG_NO_DEPTH, // render without depth test + VRMENUOBJECT_FLAG_NO_DEPTH_MASK, // render without writing to the z buffer + VRMENUOBJECT_DONT_RENDER, // don't render this object nor its children + VRMENUOBJECT_DONT_RENDER_SURFACE, // don't render this object's non text surfaces + VRMENUOBJECT_DONT_RENDER_TEXT, // don't render this object's text (useful for naming an object + // just for debugging) + VRMENUOBJECT_NO_GAZE_HILIGHT, // when hit this object won't change the cursor hilight state + VRMENUOBJECT_RENDER_HIERARCHY_ORDER, // this object and all its children will use this object's + // position for depth sorting, and sort vs. each other by + // submission index (i.e. parent's render first) + VRMENUOBJECT_FLAG_BILLBOARD, // always face view plane normal + VRMENUOBJECT_DONT_MOD_PARENT_COLOR, // don't apply the parent's color (but do apply alpha) + VRMENUOBJECT_INSTANCE_TEXT, // text on this object will be rendered to its own surface for + // sorting (this has performance penalty so only use if if you have + // sorting issues with text). + + VRMENUOBJECT_MAX // not an actual flag, just used for counting the number of enums +}; + +typedef OVR::BitFlagsT VRMenuObjectFlags_t; + +enum eVRMenuObjectInitFlags { + VRMENUOBJECT_INIT_ALIGN_TO_VIEW, // align to the camera position on init + VRMENUOBJECT_INIT_FORCE_POSITION // use the position in the parms instead of an auto-calculated + // position +}; + +typedef OVR::BitFlagsT VRMenuObjectInitFlags_t; + +enum eVRMenuId { INVALID_MENU_ID = INT_MIN }; +typedef OVR::TypesafeNumberT VRMenuId_t; + +// menu object handles +enum eMenuIdType { INVALID_MENU_OBJECT_ID = 0 }; +typedef OVR::TypesafeNumberT menuHandle_t; + +// menu render flags +enum eVRMenuRenderFlags { + VRMENU_RENDER_NO_DEPTH, + VRMENU_RENDER_NO_FONT_OUTLINE, + VRMENU_RENDER_POLYGON_OFFSET, + VRMENU_RENDER_BILLBOARD, + VRMENU_RENDER_NO_DEPTH_MASK, + VRMENU_RENDER_SUBMIT_TEXT_SURFACE +}; +typedef OVR::BitFlagsT VRMenuRenderFlags_t; + +class VRMenuComponent; +class VRMenuComponent_OnFocusGained; +class VRMenuComponent_OnFocusLost; +class VRMenuComponent_OnDown; +class VRMenuComponent_OnUp; +class VRMenuComponent_OnSubmitForRendering; +class VRMenuComponent_OnRender; +class VRMenuComponent_OnTouchRelative; +class BitmapFont; +struct fontParms_t; + +// border indices +enum eVRMenuBorder { BORDER_LEFT, BORDER_BOTTOM, BORDER_RIGHT, BORDER_TOP }; + +//============================================================== +// VRMenuSurfaceParms +class VRMenuSurfaceParms { + public: + VRMenuSurfaceParms() + : SurfaceName(""), + ImageNames(), + Contents(CONTENT_SOLID), + Color(1.0f), + Anchors(0.5f, 0.5f), + Border(0.0f, 0.0f, 0.0f, 0.0f), + CropUV(0.0f), + OffsetUVs(0.0f), + Dims(0.0f, 0.0f) { + InitSurfaceProperties(); + } + + explicit VRMenuSurfaceParms(char const* surfaceName) + : SurfaceName(surfaceName != nullptr ? surfaceName : ""), + ImageNames(), + Contents(CONTENT_SOLID), + Color(1.0f), + Anchors(0.5f, 0.5f), + Border(0.0f, 0.0f, 0.0f, 0.0f), + CropUV(0.0f), + OffsetUVs(0.0f), + Dims(0.0f, 0.0f) + + { + InitSurfaceProperties(); + } + VRMenuSurfaceParms( + char const* surfaceName, + char const* imageName0, + eSurfaceTextureType textureType0, + char const* imageName1, + eSurfaceTextureType textureType1, + char const* imageName2, + eSurfaceTextureType textureType2) + : SurfaceName(surfaceName != nullptr ? surfaceName : ""), + ImageNames(), + Contents(CONTENT_SOLID), + Color(1.0f), + Anchors(0.5f, 0.5f), + Border(0.0f, 0.0f, 0.0f, 0.0f), + CropUV(0.0f), + OffsetUVs(0.0f), + Dims(0.0f, 0.0f) + + { + InitSurfaceProperties(); + ImageNames[VRMENUSURFACE_IMAGE_0] = imageName0 != nullptr ? imageName0 : ""; + TextureTypes[VRMENUSURFACE_IMAGE_0] = textureType0; + + ImageNames[VRMENUSURFACE_IMAGE_1] = imageName1 != nullptr ? imageName1 : ""; + TextureTypes[VRMENUSURFACE_IMAGE_1] = textureType1; + + ImageNames[VRMENUSURFACE_IMAGE_2] = imageName2 != nullptr ? imageName2 : ""; + TextureTypes[VRMENUSURFACE_IMAGE_2] = textureType2; + } + VRMenuSurfaceParms( + char const* surfaceName, + char const* imageName0, + eSurfaceTextureType textureType0, + char const* imageName1, + eSurfaceTextureType textureType1, + char const* imageName2, + eSurfaceTextureType textureType2, + OVR::Vector2f const& anchors) + : SurfaceName(surfaceName != nullptr ? surfaceName : ""), + ImageNames(), + Contents(CONTENT_SOLID), + Color(1.0f), + Anchors(anchors), + Border(0.0f, 0.0f, 0.0f, 0.0f), + CropUV(0.0f), + OffsetUVs(0.0f), + Dims(0.0f, 0.0f) + + { + InitSurfaceProperties(); + ImageNames[VRMENUSURFACE_IMAGE_0] = imageName0 != nullptr ? imageName0 : ""; + TextureTypes[VRMENUSURFACE_IMAGE_0] = textureType0; + + ImageNames[VRMENUSURFACE_IMAGE_1] = imageName1 != nullptr ? imageName1 : ""; + TextureTypes[VRMENUSURFACE_IMAGE_1] = textureType1; + + ImageNames[VRMENUSURFACE_IMAGE_2] = imageName2 != nullptr ? imageName2 : ""; + TextureTypes[VRMENUSURFACE_IMAGE_2] = textureType2; + } + VRMenuSurfaceParms( + char const* surfaceName, + char const* imageNames[], + eSurfaceTextureType textureTypes[]) + : SurfaceName(surfaceName), + ImageNames(), + TextureTypes(), + Contents(CONTENT_SOLID), + Color(1.0f), + Anchors(0.5f, 0.5f), + Border(0.0f, 0.0f, 0.0f, 0.0f), + CropUV(0.0f), + OffsetUVs(0.0f), + Dims(0.0f, 0.0f) + + { + InitSurfaceProperties(); + for (int i = 0; i < VRMENUSURFACE_IMAGE_MAX && imageNames[i] != NULL; ++i) { + ImageNames[i] = imageNames[i]; + TextureTypes[i] = textureTypes[i]; + } + } + VRMenuSurfaceParms( + char const* surfaceName, + GLuint imageTexId0, + int width0, + int height0, + eSurfaceTextureType textureType0, + GLuint imageTexId1, + int width1, + int height1, + eSurfaceTextureType textureType1, + GLuint imageTexId2, + int width2, + int height2, + eSurfaceTextureType textureType2) + : SurfaceName(surfaceName != nullptr ? surfaceName : ""), + ImageNames(), + Contents(CONTENT_SOLID), + Color(1.0f), + Anchors(0.5f, 0.5f), + Border(0.0f, 0.0f, 0.0f, 0.0f), + CropUV(0.0f), + OffsetUVs(0.0f), + Dims(0.0f, 0.0f) + + { + InitSurfaceProperties(); + ImageTexId[VRMENUSURFACE_IMAGE_0] = imageTexId0; + TextureTypes[VRMENUSURFACE_IMAGE_0] = textureType0; + ImageWidth[VRMENUSURFACE_IMAGE_0] = width0; + ImageHeight[VRMENUSURFACE_IMAGE_0] = height0; + + ImageTexId[VRMENUSURFACE_IMAGE_1] = imageTexId1; + TextureTypes[VRMENUSURFACE_IMAGE_1] = textureType1; + ImageWidth[VRMENUSURFACE_IMAGE_1] = width1; + ImageHeight[VRMENUSURFACE_IMAGE_1] = height1; + + ImageTexId[VRMENUSURFACE_IMAGE_2] = imageTexId2; + TextureTypes[VRMENUSURFACE_IMAGE_2] = textureType2; + ImageWidth[VRMENUSURFACE_IMAGE_2] = width2; + ImageHeight[VRMENUSURFACE_IMAGE_2] = height2; + } + + std::string SurfaceName; // for debugging only + std::string ImageNames[VRMENUSURFACE_IMAGE_MAX]; + GLuint ImageTexId[VRMENUSURFACE_IMAGE_MAX]; + int ImageWidth[VRMENUSURFACE_IMAGE_MAX]; + int ImageHeight[VRMENUSURFACE_IMAGE_MAX]; + eSurfaceTextureType TextureTypes[VRMENUSURFACE_IMAGE_MAX]; + ContentFlags_t Contents; + OVR::Vector4f Color; + OVR::Vector2f Anchors; + OVR::Vector4f Border; // if set to non-zero, sets the border on a sliced sprite + OVR::Vector4f + CropUV; // specifies the crop range of the texture in UV space, defaults to full texture + OVR::Vector2f OffsetUVs; // offset UVs + OVR::Vector2f Dims; // if set to zero, use texture size, non-zero sets dims to absolute size + + private: + void InitSurfaceProperties() { + for (int i = 0; i < VRMENUSURFACE_IMAGE_MAX; ++i) { + TextureTypes[i] = SURFACE_TEXTURE_MAX; + ImageTexId[i] = 0; + ImageWidth[i] = 0; + ImageHeight[i] = 0; + } + + CropUV = OVR::Vector4f(0.0f, 0.0f, 1.0f, 1.0f); + } +}; + +//============================================================== +// VRMenuFontParms +class VRMenuFontParms { + public: + VRMenuFontParms() + : AlignHoriz(HORIZONTAL_CENTER), + AlignVert(VERTICAL_CENTER), + Billboard(false), + TrackRoll(false), + Outline(false), + ColorCenter(0.5f), + AlphaCenter(0.5f), + Scale(1.0f), + WrapWidth(-1.0f), + MaxLines(16), + MultiLine(true) {} + + VRMenuFontParms( + bool const centerHoriz, + bool const centerVert, + bool const billboard, + bool const trackRoll, + bool const outline, + float const colorCenter, + float const alphaCenter, + float const scale) + : AlignHoriz(centerHoriz ? HORIZONTAL_CENTER : HORIZONTAL_LEFT), + AlignVert(centerVert ? VERTICAL_CENTER : VERTICAL_BASELINE), + Billboard(billboard), + TrackRoll(trackRoll), + Outline(outline), + ColorCenter(colorCenter), + AlphaCenter(alphaCenter), + Scale(scale), + WrapWidth(-1.0f), + MaxLines(16), + MultiLine(true) {} + + VRMenuFontParms( + bool const centerHoriz, + bool const centerVert, + bool const billboard, + bool const trackRoll, + bool const outline, + float const colorCenter, + float const alphaCenter, + float const scale, + float const wrapWidth, + int const maxLines, + bool const multiLine) + : AlignHoriz(centerHoriz ? HORIZONTAL_CENTER : HORIZONTAL_LEFT), + AlignVert(centerVert ? VERTICAL_CENTER : VERTICAL_BASELINE), + Billboard(billboard), + TrackRoll(trackRoll), + Outline(outline), + ColorCenter(colorCenter), + AlphaCenter(alphaCenter), + Scale(scale), + WrapWidth(wrapWidth), + MaxLines(maxLines), + MultiLine(multiLine) {} + + VRMenuFontParms( + bool const centerHoriz, + bool const centerVert, + bool const billboard, + bool const trackRoll, + bool const outline, + float const scale) + : AlignHoriz(centerHoriz ? HORIZONTAL_CENTER : HORIZONTAL_LEFT), + AlignVert(centerVert ? VERTICAL_CENTER : VERTICAL_BASELINE), + Billboard(billboard), + TrackRoll(trackRoll), + Outline(outline), + ColorCenter(outline ? 0.5f : 0.0f), + AlphaCenter(outline ? 0.425f : 0.5f), + Scale(scale), + WrapWidth(-1.0f), + MaxLines(16), + MultiLine(true) {} + + VRMenuFontParms( + HorizontalJustification const alignHoriz, + VerticalJustification const alignVert, + bool const billboard, + bool const trackRoll, + bool const outline, + float const colorCenter, + float const alphaCenter, + float const scale) + : AlignHoriz(alignHoriz), + AlignVert(alignVert), + Billboard(billboard), + TrackRoll(trackRoll), + Outline(outline), + ColorCenter(colorCenter), + AlphaCenter(alphaCenter), + Scale(scale), + WrapWidth(-1.0f), + MaxLines(16), + MultiLine(true) {} + + VRMenuFontParms( + HorizontalJustification const alignHoriz, + VerticalJustification const alignVert, + bool const billboard, + bool const trackRoll, + bool const outline, + float const scale) + : AlignHoriz(alignHoriz), + AlignVert(alignVert), + Billboard(billboard), + TrackRoll(trackRoll), + Outline(outline), + ColorCenter(outline ? 0.5f : 0.0f), + AlphaCenter(outline ? 0.425f : 0.5f), + Scale(scale), + WrapWidth(-1.0f), + MaxLines(16), + MultiLine(true) {} + + HorizontalJustification + AlignHoriz; // horizontal justification around the specified x coordinate + VerticalJustification AlignVert; // vertical justification around the specified y coordinate + bool Billboard; // true to always face the camera + bool TrackRoll; // when billboarding, track with camera roll + bool Outline; // true if font should rended with an outline + float ColorCenter; // center value for color -- used for outlining + float AlphaCenter; // center value for alpha -- used for outlined or non-outlined + float Scale; // scale factor for the text + float WrapWidth; // wrap width in meters, < 0 means don't wrap + int MaxLines; // maximum number of lines that this text area can display. Currently clamped + // to 16. ( TODO: hook this up to scrolling ) + bool MultiLine; // used in conjunction with WrapWidth. If set we only scale and don't wrap text + // ( single line ) +}; + +//============================================================== +// VRMenumObjectParms +// +// Parms passed to the VRMenuObject factory to create a VRMenuObject +class VRMenuObjectParms { + public: + static VRMenuObjectParms* Create() { + return new VRMenuObjectParms(); + } + + VRMenuObjectParms( + eVRMenuObjectType const type, + std::vector const& components, + VRMenuSurfaceParms const& surfaceParms, + char const* text, + OVR::Posef const& localPose, + OVR::Vector3f const& localScale, + VRMenuFontParms const& fontParms, + VRMenuId_t const id, + VRMenuObjectFlags_t const flags, + VRMenuObjectInitFlags_t const initFlags) + : Type(type), + Flags(flags), + InitFlags(initFlags), + Components(components), + SurfaceParms(), + Text(text), + LocalPose(localPose), + LocalScale(localScale), + TextLocalPose(OVR::Quatf(), OVR::Vector3f(0.0f)), + TextLocalScale(1.0f), + FontParms(fontParms), + Color(1.0f), + TextColor(1.0f), + Id(id), + Contents(CONTENT_SOLID), + TexelCoords(false), + Selected(false) { + SurfaceParms.push_back(surfaceParms); + } + + // same as above with additional text local parms + VRMenuObjectParms( + eVRMenuObjectType const type, + std::vector const& components, + VRMenuSurfaceParms const& surfaceParms, + char const* text, + OVR::Posef const& localPose, + OVR::Vector3f const& localScale, + OVR::Posef const& textLocalPose, + OVR::Vector3f const& textLocalScale, + VRMenuFontParms const& fontParms, + VRMenuId_t const id, + VRMenuObjectFlags_t const flags, + VRMenuObjectInitFlags_t const initFlags) + : Type(type), + Flags(flags), + InitFlags(initFlags), + Components(components), + SurfaceParms(), + Text(text), + LocalPose(localPose), + LocalScale(localScale), + TextLocalPose(textLocalPose), + TextLocalScale(textLocalScale), + FontParms(fontParms), + Color(1.0f), + TextColor(1.0f), + Id(id), + Contents(CONTENT_SOLID), + TexelCoords(false), + Selected(false) { + SurfaceParms.push_back(surfaceParms); + } + + // takes an array of surface parms + VRMenuObjectParms( + eVRMenuObjectType const type, + std::vector const& components, + std::vector const& surfaceParms, + char const* text, + OVR::Posef const& localPose, + OVR::Vector3f const& localScale, + OVR::Posef const& textLocalPose, + OVR::Vector3f const& textLocalScale, + VRMenuFontParms const& fontParms, + VRMenuId_t const id, + VRMenuObjectFlags_t const flags, + VRMenuObjectInitFlags_t const initFlags) + : Type(type), + Flags(flags), + InitFlags(initFlags), + Components(components), + SurfaceParms(surfaceParms), + Text(text), + LocalPose(localPose), + LocalScale(localScale), + TextLocalPose(textLocalPose), + TextLocalScale(textLocalScale), + FontParms(fontParms), + Color(1.0f), + TextColor(1.0f), + Id(id), + Contents(CONTENT_SOLID), + TexelCoords(false), + Selected(false) {} + + eVRMenuObjectType Type; // type of menu object + VRMenuObjectFlags_t Flags; // bit flags + VRMenuObjectInitFlags_t + InitFlags; // intialization-only flags (not referenced beyond object initialization) + std::vector Components; // list of pointers to components + std::vector + SurfaceParms; // list of surface parameters for the object. Each parm will result in one + // surface, and surfaces will render in the same order as this list. + std::string Text; // text to display on this object (if any) + OVR::Posef LocalPose; // local-space position and orientation + OVR::Vector3f LocalScale; // local-space scale + OVR::Posef TextLocalPose; // offset of text, local to surface + OVR::Vector3f TextLocalScale; // scale of text, local to surface + VRMenuFontParms FontParms; // parameters for rendering the object's text + OVR::Vector4f Color; // color modulation for surfaces + OVR::Vector4f TextColor; // color of text + VRMenuId_t Id; // user identifier, so the client using a menu can find a particular object + VRMenuId_t ParentId; // id of object that should be made this object's parent. + ContentFlags_t Contents; // collision contents for the menu object + std::string Name; // for easier parenting + std::string ParentName; + std::string Tag; + bool TexelCoords; // if true, pose translation units are in texels + bool Selected; // true to start selected + + private: + VRMenuObjectParms() // only VRMenuObjectParms::Create() can create a default object of this type + : Type(VRMENU_MAX), + Flags(VRMENUOBJECT_RENDER_HIERARCHY_ORDER), + InitFlags(VRMENUOBJECT_INIT_FORCE_POSITION), + LocalPose(OVR::Quatf(), OVR::Vector3f(0.0f)), + LocalScale(1.0f), + TextLocalPose(OVR::Quatf(), OVR::Vector3f(0.0f)), + TextLocalScale(1.0f), + FontParms(), + Color(1.0f), + TextColor(1.0f), + Contents(CONTENT_SOLID), + TexelCoords(false), + Selected(false) {} +}; + +//============================================================== +// HitTestResult +// returns the results of a hit test vs menu objects +class HitTestResult : public OvrCollisionResult { + public: + HitTestResult& operator=(OvrCollisionResult& rhs) { + this->HitHandle.Release(); + this->RayStart = OVR::Vector3f::ZERO; + this->RayDir = OVR::Vector3f::ZERO; + this->t = rhs.t; + this->uv = rhs.uv; + return *this; + } + + menuHandle_t HitHandle; + OVR::Vector3f RayStart; + OVR::Vector3f RayDir; +}; + +typedef std::int16_t guiIndex_t; + +struct textMetrics_t { + textMetrics_t() : w(0.0f), h(0.0f), ascent(0.0f), descent(0.0f), fontHeight(0.0f) {} + + float w; + float h; + float ascent; + float descent; + float fontHeight; +}; + +class App; + +//============================================================== +// VRMenuSurfaceTexture +class VRMenuSurfaceTexture { + public: + VRMenuSurfaceTexture(); + + bool LoadTexture( + OvrGuiSys& guiSys, + eSurfaceTextureType const type, + char const* imageName, + bool const allowDefault); + void LoadTexture( + eSurfaceTextureType const type, + const GLuint texId, + const int width, + const int height); + void Free(); + void SetOwnership(const bool isOwner) { + OwnsTexture = isOwner; + } + + GlTexture const& GetTexture() const { + return Texture; + } + int GetWidth() const { + return Texture.Width; + } + int GetHeight() const { + return Texture.Height; + } + eSurfaceTextureType GetType() const { + return Type; + } + + private: + GlTexture Texture; + eSurfaceTextureType Type; // specifies how this image is used for rendering + bool OwnsTexture; // if true, free texture on a reload or deconstruct +}; + +//============================================================== +// SubmittedMenuObject +class SubmittedMenuObject { + public: + SubmittedMenuObject() + : SurfaceIndex(-1), + DistanceIndex(-1), + Scale(1.0f), + Color(1.0f), + ColorTableOffset(0.0f), + SkipAdditivePass(false), + Offsets(0.0f), + ClipUVs(0.0f, 0.0f, 1.0f, 1.0f), + OffsetUVs(0.0f, 0.0f), + FadeDirection(0.0f) {} + + menuHandle_t Handle; // handle of the object + int SurfaceIndex; // surface of the object + int DistanceIndex; // use the position at this index to calc sort distance + OVR::Posef Pose; // pose in model space + OVR::Vector3f Scale; // scale of the object + OVR::Vector4f Color; // color of the object + OVR::Vector2f ColorTableOffset; // color table offset for color ramp fx + bool SkipAdditivePass; // true to skip any additive multi-texture pass + VRMenuRenderFlags_t Flags; // various flags + OVR::Vector2f Offsets; // offsets based on anchors (width / height * anchor.x / .y) + OVR::Vector4f ClipUVs; // x,y are min clip uvs, z,w are max clip uvs + OVR::Vector2f OffsetUVs; // offset for UV + OVR::Vector3f FadeDirection; // Fades vertices based on direction - default is zero vector which + // indicates off + std::string SurfaceName; // for debugging only + OVR::Bounds3f LocalBounds; // local bounds +}; + +//============================================================== +// VRMenuSurface +class VRMenuSurface { + public: + // artificial bounds in Z, which is technically 0 for surfaces that are an image + static const float Z_BOUNDS; + + VRMenuSurface(); + ~VRMenuSurface(); + + void CreateFromSurfaceParms(OvrGuiSys& guiSys, VRMenuSurfaceParms const& parms); + + void Free(); + + void RegenerateSurfaceGeometry(); + + OVR::Bounds3f const& GetLocalBounds() const { + return Tris.GetBounds(); + } + + bool IsRenderable() const { + return (SurfaceDef.geo.vertexCount > 0 && SurfaceDef.geo.indexCount > 0) && Visible; + } + + bool IntersectRay( + OVR::Vector3f const& start, + OVR::Vector3f const& dir, + OVR::Posef const& pose, + OVR::Vector3f const& scale, + ContentFlags_t const testContents, + OvrCollisionResult& result) const; + // the ray should already be in local space + bool IntersectRay( + OVR::Vector3f const& localStart, + OVR::Vector3f const& localDir, + OVR::Vector3f const& scale, + ContentFlags_t const testContents, + OvrCollisionResult& result) const; + void LoadTexture( + OvrGuiSys& guiSys, + int const textureIndex, + eSurfaceTextureType const type, + const char* imageName); + void LoadTexture( + int const textureIndex, + eSurfaceTextureType const type, + const GLuint texId, + const int width, + const int height); + + OVR::Vector4f const& GetColor() const { + return Color; + } + void SetColor(OVR::Vector4f const& color) { + Color = color; + } + + OVR::Vector2f const& GetDims() const { + return Dims; + } + void SetDims(OVR::Vector2f const& dims) { + Dims = dims; + } // requires call to CreateFromSurfaceParms or RegenerateSurfaceGeometry() to take effect + + OVR::Vector2f const& GetAnchors() const { + return Anchors; + } + void SetAnchors(OVR::Vector2f const& a) { + Anchors = a; + } + + OVR::Vector2f GetAnchorOffsets() const; + + OVR::Vector4f const& GetBorder() const { + return Border; + } + void SetBorder(OVR::Vector4f const& a) { + Border = a; + } // requires call to CreateFromSurfaceParms or RegenerateSurfaceGeometry() to take effect + + OVR::Vector4f const& GetClipUVs() const { + return ClipUVs; + } + void SetClipUVs(OVR::Vector4f const& uvs) { + ClipUVs = uvs; + } + + OVR::Vector2f const& GetOffsetUVs() const { + return OffsetUVs; + } + void SetOffsetUVs(OVR::Vector2f const& uvs) { + OffsetUVs = uvs; + } + + VRMenuSurfaceTexture const& GetTexture(int const index) const { + return Textures[index]; + } + void SetOwnership(int const index, bool const isOwner); + + bool GetVisible() const { + return Visible; + } + void SetVisible(bool const v) { + Visible = v; + } + + std::string const& GetName() const { + return SurfaceName; + } + + void BuildDrawSurface( + OvrVRMenuMgr const& menuMgr, + OVR::Matrix4f const& modelMatrix, + const char* surfaceName, + OVR::Vector4f const& color, + OVR::Vector3f const& fadeDirection, + OVR::Vector2f const& colorTableOffset, + OVR::Vector4f const& clipUVs, + OVR::Vector2f const& offsetUVs, + bool const skipAdditivePass, + VRMenuRenderFlags_t const& flags, + OVR::Bounds3f const& worldBounds, + ovrDrawSurface& outSurf); + + OvrTriCollisionPrimitive const& GetTris() const { + return Tris; + } + + private: + VRMenuSurfaceTexture Textures[VRMENUSURFACE_IMAGE_MAX]; + // GlGeometry Geo; // VBO for this surface + OvrTriCollisionPrimitive Tris; // per-poly collision object + OVR::Vector4f Color; // Color, modulated with object color + OVR::Vector2i TextureDims; // texture width and height + OVR::Vector2f Dims; // width and height + OVR::Vector2f Anchors; // anchors + OVR::Vector4f Border; // border size for sliced sprite + OVR::Vector4f ClipUVs; // UV boundaries for fragment clipping + OVR::Vector2f OffsetUVs; // UV offsets + OVR::Vector4f CropUV; // Crop range in UV space + OVR::Vector4f FadeDirection; // fade direction for some shaders + OVR::Vector2f ColorTableOffset; // color table offset + std::string SurfaceName; // name of the surface for debugging + ContentFlags_t Contents; + bool Visible; // must be true to render -- used to animate between different surfaces + + eGUIProgramType ProgramType; + + mutable ovrSurfaceDef SurfaceDef; + + private: + void CreateImageGeometry( + int const textureWidth, + int const textureHeight, + const OVR::Vector2f& dims, + const OVR::Vector4f& border, + const OVR::Vector4f& crop, + ContentFlags_t const content); + // This searches the loaded textures for the specified number of matching types. For instance, + // ( SURFACE_TEXTURE_DIFFUSE, 2 ) would only return true if two of the textures were of type + // SURFACE_TEXTURE_DIFFUSE. This is used to determine, based on surface types, which shaders + // to use to render the surface, independent of how the texture maps are ordered. + bool HasTexturesOfType(eSurfaceTextureType const t, int const requiredCount) const; + // Returns the index in Textures[] of the n-th occurence of type t. + int IndexForTextureType(eSurfaceTextureType const t, int const occurenceCount) const; + void SetTextureSampling(eGUIProgramType const pt); +}; + +//============================================================== +// ovrComponentList +class ovrComponentList { + public: + ovrComponentList() {} // std::vector<> requires a default constructor because ummm... yeah. + + ovrComponentList(menuHandle_t const ownerHandle) : OwnerHandle(ownerHandle) {} + + menuHandle_t GetOwnerHandle() const { + return OwnerHandle; + } + + void AddComponent(VRMenuComponent* component) { + // never add a component twice + for (auto* c : Components) { + if (c == component) { + return; + } + } + Components.push_back(component); + } + + std::vector& GetComponents() { + return Components; + } + + private: + menuHandle_t OwnerHandle; // object that owns the components + std::vector Components; // components to delete +}; + +//============================================================== +// VRMenuObject +// base class for all menu objects +class VRMenuObject { + public: + friend class VRMenuMgr; + friend class VRMenuMgrLocal; + + class ovrRecursionFunctor { + public: + virtual ~ovrRecursionFunctor() {} + + virtual void AtNode(VRMenuObject* obj) = 0; + }; + + static float constexpr TEXELS_PER_METER{500.0f}; + static float constexpr DEFAULT_TEXEL_SCALE{1.0f / TEXELS_PER_METER}; + + // Initialize the object after creation + void Init(OvrGuiSys& guiSys, VRMenuObjectParms const& parms); + + // Frees all of this object's children + void FreeChildren(OvrVRMenuMgr& menuMgr); + + // Adds a child to this menu object + void AddChild(OvrVRMenuMgr& menuMgr, menuHandle_t const handle); + // Adds a child to this menu object + void AddChild(VRMenuObject* child); + // Removes a child from this menu object, but does not free the child object. + void RemoveChild(OvrVRMenuMgr& menuMgr, menuHandle_t const handle); + // Removes a child from tis menu object and frees it, along with any children it may have. + void FreeChild(OvrVRMenuMgr& menuMgr, menuHandle_t const handle); + // Returns true if the handle is in this menu's tree of children + bool IsDescendant(OvrVRMenuMgr& menuMgr, menuHandle_t const handle) const; + + // Update this menu for a frame, including testing the gaze direction against the bounds + // of this menu and all its children + void Frame(OvrVRMenuMgr& menuMgr, OVR::Matrix4f const& viewMatrix); + + // Tests a ray (in object's local space) for intersection. + bool IntersectRay( + OVR::Vector3f const& localStart, + OVR::Vector3f const& localDir, + OVR::Vector3f const& parentScale, + OVR::Bounds3f const& bounds, + float& bounds_t0, + float& bounds_t1, + ContentFlags_t const testContents, + OvrCollisionResult& result) const; + + // Test the ray against this object and all child objects, returning the first object that was + // hit by the ray. The ray should be in parent-local space - for the current root menu this is + // always world space. + menuHandle_t HitTest( + OvrGuiSys const& guiSys, + OVR::Posef const& worldPose, + OVR::Vector3f const& rayStart, + OVR::Vector3f const& rayDir, + ContentFlags_t const testContents, + HitTestResult& result) const; + + //-------------------------------------------------------------- + // components + //-------------------------------------------------------------- + void AddComponent(VRMenuComponent* component); + void DeleteComponent(OvrGuiSys& guiSys, VRMenuComponent* component); + + std::vector const& GetComponentList() const { + return Components; + } + + VRMenuComponent* GetComponentById_Impl(int const typeId, const char* name) const; + VRMenuComponent* GetComponentByTypeName_Impl(const char* typeName) const; + + // TODO We might want to refactor these into a single GetComponent which internally manages + // unique ids (using hashed class names for ex. ) Helper for getting component - returns NULL if + // it fails. Required Component class to overload GetTypeId and define unique TYPE_ID + template + ComponentType* GetComponentById() const { + return static_cast(GetComponentById_Impl(ComponentType::TYPE_ID, NULL)); + } + + template + ComponentType* GetComponentByName(const char* name) const { + return static_cast(GetComponentById_Impl(ComponentType::TYPE_ID, name)); + } + + // Helper for getting component - returns NULL if it fails. Required Component class to overload + // GetTypeName and define unique TYPE_NAME + template + ComponentType* GetComponentByTypeName() const { + return static_cast(GetComponentByTypeName_Impl(ComponentType::TYPE_NAME)); + } + + //-------------------------------------------------------------- + // accessors + //-------------------------------------------------------------- + eVRMenuObjectType GetType() const { + return Type; + } + menuHandle_t GetHandle() const { + return Handle; + } + menuHandle_t GetParentHandle() const { + return ParentHandle; + } + void SetParentHandle(menuHandle_t const h) { + assert(h != Handle); + ParentHandle = h; + } + + VRMenuObjectFlags_t const& GetFlags() const { + return Flags; + } + void SetFlags(VRMenuObjectFlags_t const& flags) { + Flags = flags; + } + void AddFlags(VRMenuObjectFlags_t const& flags) { + Flags |= flags; + } + void RemoveFlags(VRMenuObjectFlags_t const& flags) { + Flags &= ~flags; + } + + void ModifyFlags(bool const add, VRMenuObjectFlags_t const& flags) { + if (add) { + AddFlags(flags); + } else { + RemoveFlags(flags); + } + } + + std::string const& GetText() const { + return Text; + } + void SetText(char const* text); + + template + void SetText(const std::string& text, Args... args) { + int size_s = std::snprintf(nullptr, 0, text.c_str(), args...) + 1; // Extra space for '\0' + if (size_s <= 0) { + ALOGE("VRGUI SetText formatting error!"); + return; + } + auto buf = std::vector(size_s); + std::snprintf(buf.data(), buf.size(), text.c_str(), args...); + + Text = std::string(buf.data()); + TextDirty = true; + } + + void + SetTextWordWrapped(char const* text, class BitmapFont const& font, float const widthInMeters); + + bool IsHilighted() const { + return Hilighted; + } + void SetHilighted(bool const b) { + Hilighted = b; + } + bool IsSelected() const { + return Selected; + } + // NOTE: this will not send the VRMENU_EVENT_SELECTED or VRMENU_EVENT_DESELECTED event. + // For that, use VRMenu::SetSelected() + void SetSelected(bool const b) { + Selected = b; + } + int NumChildren() const { + return static_cast(Children.size()); + } + menuHandle_t GetChildHandleForIndex(int const index) const { + return Children[index]; + } + + OVR::Posef const& GetLocalPose() const { + return LocalPose; + } + void SetLocalPose(OVR::Posef const& pose) { + LocalPose = pose; + } + OVR::Vector3f const& GetLocalPosition() const { + return LocalPose.Translation; + } + void SetLocalPosition(OVR::Vector3f const& pos) { + LocalPose.Translation = pos; + } + OVR::Quatf const& GetLocalRotation() const { + return LocalPose.Rotation; + } + void SetLocalRotation(OVR::Quatf const& rot) { + LocalPose.Rotation = rot; + } + OVR::Vector3f GetLocalScale() const; + void SetLocalScale(OVR::Vector3f const& scale) { + LocalScale = scale; + } + + OVR::Posef const& GetHilightPose() const { + return HilightPose; + } + void SetHilightPose(OVR::Posef const& pose) { + HilightPose = pose; + } + float GetHilightScale() const { + return HilightScale; + } + void SetHilightScale(float const s) { + HilightScale = s; + } + + void SetTextLocalPose(OVR::Posef const& pose) { + TextLocalPose = pose; + } + OVR::Posef const& GetTextLocalPose() const { + return TextLocalPose; + } + void SetTextLocalPosition(OVR::Vector3f const& pos) { + TextLocalPose.Translation = pos; + } + OVR::Vector3f const& GetTextLocalPosition() const { + return TextLocalPose.Translation; + } + void SetTextLocalRotation(OVR::Quatf const& rot) { + TextLocalPose.Rotation = rot; + } + OVR::Quatf const& GetTextLocalRotation() const { + return TextLocalPose.Rotation; + } + OVR::Vector3f GetTextLocalScale() const; + float GetWrapScale() const { + return WrapScale; + } + void SetTextLocalScale(OVR::Vector3f const& scale) { + TextLocalScale = scale; + } + + void SetLocalBoundsExpand(OVR::Vector3f const mins, OVR::Vector3f const& maxs); + + OVR::Bounds3f GetLocalBounds(BitmapFont const& font) const; + OVR::Bounds3f GetTextLocalBounds(BitmapFont const& font) const; + OVR::Bounds3f CalcLocalBoundsForText(BitmapFont const& font, std::string& text) const; + + OVR::Bounds3f const& GetCullBounds() const { + return CullBounds; + } + void SetCullBounds(OVR::Bounds3f const& bounds) const { + CullBounds = bounds; + } + + OVR::Vector2f const& GetColorTableOffset() const; + void SetColorTableOffset(OVR::Vector2f const& ofs); + + OVR::Vector4f const& GetColor() const; + void SetColor(OVR::Vector4f const& c); + + OVR::Vector4f const& GetTextColor() const { + return TextColor; + } + void SetTextColor(OVR::Vector4f const& c) { + TextColor = c; + } + + std::string const& GetName() const { + return Name; + } + std::string const& GetTag() const { + return Tag; + } + VRMenuId_t GetId() const { + return Id; + } + VRMenuObject* ChildForId(OvrVRMenuMgr const& menuMgr, VRMenuId_t const id) const; + menuHandle_t ChildHandleForId(OvrVRMenuMgr const& menuMgr, VRMenuId_t const id) const; + menuHandle_t ChildHandleForName(OvrVRMenuMgr const& menuMgr, char const* name) const; + menuHandle_t ChildHandleForTag(OvrVRMenuMgr const& menuMgr, char const* tag) const; + + void SetFontParms(VRMenuFontParms const& fontParms) { + FontParms = fontParms; + } + VRMenuFontParms const& GetFontParms() const { + return FontParms; + } + + OVR::Vector3f const& GetFadeDirection() const { + return FadeDirection; + } + void SetFadeDirection(OVR::Vector3f const& dir) { + FadeDirection = dir; + } + + void SetVisible(bool visible); + + // returns the index of the first surface with SURFACE_TEXTURE_ADDITIVE. + // If singular is true, then the matching surface must have only one texture map and it must be + // of that type. + int FindSurfaceWithTextureType(eSurfaceTextureType const type, bool const singular) const; + int FindSurfaceWithTextureType( + eSurfaceTextureType const type, + bool const singular, + bool const visibleOnly) const; + void SetSurfaceColor(int const surfaceIndex, OVR::Vector4f const& color); + OVR::Vector4f const& GetSurfaceColor(int const surfaceIndex) const; + void SetSurfaceVisible(int const surfaceIndex, bool const v); + bool GetSurfaceVisible(int const surfaceIndex) const; + int NumSurfaces() const; + int AllocSurface(); + void CreateFromSurfaceParms( + OvrGuiSys& guiSys, + int const surfaceIndex, + VRMenuSurfaceParms const& parms); + + void SetSurfaceTexture( + OvrGuiSys& guiSys, + int const surfaceIndex, + int const textureIndex, + eSurfaceTextureType const type, + char const* imageName); + + void SetSurfaceTexture( + int const surfaceIndex, + int const textureIndex, + eSurfaceTextureType const type, + GLuint const texId, + int const width, + int const height); + void SetSurfaceTexture( + int const surfaceIndex, + int const textureIndex, + eSurfaceTextureType const type, + class GlTexture const& texture); + + void SetSurfaceTextureTakeOwnership( + int const surfaceIndex, + int const textureIndex, + eSurfaceTextureType const type, + GLuint const texId, + int const width, + int const height); + void SetSurfaceTextureTakeOwnership( + int const surfaceIndex, + int const textureIndex, + eSurfaceTextureType const type, + class GlTexture const& texture); + + void RegenerateSurfaceGeometry(int const surfaceIndex, const bool freeSurfaceGeometry); + + OVR::Vector2f const& GetSurfaceDims(int const surfaceIndex) const; + void SetSurfaceDims(int const surfaceIndex, OVR::Vector2f const& dims); + + OVR::Vector4f const& GetSurfaceBorder(int const surfaceIndex); + void SetSurfaceBorder(int const surfaceIndex, OVR::Vector4f const& border); + + //-------------------------------------------------------------- + // collision + //-------------------------------------------------------------- + void SetCollisionPrimitive(OvrCollisionPrimitive* c); + OvrCollisionPrimitive* GetCollisionPrimitive() { + return CollisionPrimitive; + } + OvrCollisionPrimitive const* GetCollisionPrimitive() const { + return CollisionPrimitive; + } + + ContentFlags_t GetContents() const { + return Contents; + } + void SetContents(ContentFlags_t const c) { + Contents = c; + } + + //-------------------------------------------------------------- + // surfaces (non-virtual) + //-------------------------------------------------------------- + VRMenuSurface const& GetSurface(int const s) const { + return Surfaces[s]; + } + VRMenuSurface& GetSurface(int const s) { + return Surfaces[s]; + } + std::vector const& GetSurfaces() const { + return Surfaces; + } + + void BuildDrawSurface( + OvrVRMenuMgr const& menuMgr, + OVR::Matrix4f const& modelMatrix, + const char* surfaceName, + int const surfaceIndex, + OVR::Vector4f const& color, + OVR::Vector3f const& fadeDirection, + OVR::Vector2f const& colorTableOffset, + OVR::Vector4f const& clipUVs, + OVR::Vector2f const& offsetUVs, + bool const skipAdditivePass, + VRMenuRenderFlags_t const& flags, + OVR::Bounds3f const& worldBounds, + std::vector& surfaceList); + + //-------------------------------------------------------------- + // transformation (used by VRMenuMgr) + //-------------------------------------------------------------- + void GetWorldTransform( + OvrVRMenuMgr& menuMgr, + OVR::Posef const& menuPose, + OVR::Posef& outPose, + OVR::Vector3f& outScale, + OVR::Vector4f& outColor) const; + + static void TransformByParent( + OVR::Posef const& parentPose, + OVR::Vector3f const& parentScale, + OVR::Vector4f const& parentColor, + OVR::Posef const& localPose, + OVR::Vector3f const& localScale, + OVR::Vector4f const& localColor, + VRMenuObjectFlags_t const& objectFlags, + OVR::Posef& outPose, + OVR::Vector3f& outScale, + OVR::Vector4f& outColor); + + static void + Recurse(OvrVRMenuMgr const& menuMgr, ovrRecursionFunctor& functor, VRMenuObject* obj); + + //-------------------------------------------------------------- + // reflection + //-------------------------------------------------------------- + static ovrParseResult ParseItemParms( + ovrReflection& refl, + ovrLocale const& locale, + char const* fileName, + std::vector const& buffer, + std::vector& itemParms); + + private: + eVRMenuObjectType Type; // type of this object + menuHandle_t Handle; // handle of this object + menuHandle_t ParentHandle; // handle of this object's parent + VRMenuId_t Id; // opaque id that the creator of the menu can use to identify a menu object + VRMenuObjectFlags_t Flags; // various bit flags + std::string Name; // name of this object (can be empty) + std::string Tag; // tags are like names but are always child-relative. + OVR::Posef LocalPose; // local-space position and orientation + OVR::Vector3f LocalScale; // local-space scale of this item + OVR::Posef HilightPose; // additional pose applied when hilighted + float HilightScale; // additional scale when hilighted + mutable float WrapScale; // scale used when wrapping text fails + OVR::Posef TextLocalPose; // local-space position and orientation of text, local to this node + // (i.e. after LocalPose / LocalScale are applied) + OVR::Vector3f TextLocalScale; // local-space scale of the text at this node + mutable std::string Text; // text to display on this object - this is mutable but only changes + // if word wrapping is required + std::vector Children; // array of direct children of this object + std::vector Components; // array of components on this object + OvrCollisionPrimitive* CollisionPrimitive; // collision surface, if any + ContentFlags_t Contents; // content flags for this object + + // may be cleaner to put texture and geometry in a separate surface structure + std::vector Surfaces; + OVR::Vector4f Color; // color modulation + OVR::Vector4f TextColor; // color modulation for text + OVR::Vector2f ColorTableOffset; // offset for color-ramp shader fx + VRMenuFontParms FontParms; // parameters for text rendering + OVR::Vector3f FadeDirection; // Fades vertices based on direction - default is zero vector which + // indicates off + + bool Hilighted; // true if hilighted + bool Selected; // true if selected + mutable bool TextDirty; // if true, recalculate text bounds + + // cached state + OVR::Vector3f MinsBoundsExpand; // amount to expand local bounds mins + OVR::Vector3f MaxsBoundsExpand; // amount to expand local bounds maxs + mutable OVR::Bounds3f + CullBounds; // bounds of this object and all its children in the local space of its parent + mutable textMetrics_t TextMetrics; // cached metrics for the text + + struct ovrTextSurface { + ovrSurfaceDef SurfaceDef; + OVR::Matrix4f ModelMatrix; + }; + + mutable ovrTextSurface* TextSurface; + + private: + // only VRMenuMgrLocal static methods can construct and destruct a menu object. + VRMenuObject(VRMenuObjectParms const& parms, menuHandle_t const handle); + ~VRMenuObject(); + + bool IntersectRayBounds( + OVR::Vector3f const& start, + OVR::Vector3f const& dir, + OVR::Vector3f const& mins, + OVR::Vector3f const& maxs, + ContentFlags_t const testContents, + float& t0, + float& t1) const; + + // Test the ray against this object and all child objects, returning the first object that was + // hit by the ray. The ray should be in parent-local space - for the current root menu this is + // always world space. + bool HitTest_r( + OvrGuiSys const& guiSys, + OVR::Posef const& parentPose, + OVR::Vector3f const& parentScale, + OVR::Vector3f const& rayStart, + OVR::Vector3f const& rayDir, + ContentFlags_t const testContents, + HitTestResult& result) const; + + int GetComponentIndex(VRMenuComponent* component) const; + + void FreeTextSurface() const; + + // Called by VRMenuMgr to free deleted components. + void FreeComponents(ovrComponentList& componentList); +}; + +} // namespace OVRFW diff --git a/Samples/SampleXrFramework/Src/GUI/ui_default.h b/Samples/SampleXrFramework/Src/GUI/ui_default.h new file mode 100755 index 0000000..1bfd699 --- /dev/null +++ b/Samples/SampleXrFramework/Src/GUI/ui_default.h @@ -0,0 +1,1676 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/************************************************************************************ + +Filename : ui_default.h +Content : Embedded default texture for UI. +Created : +Authors : + +*************************************************************************************/ + +// clang-format off +static const size_t uiDefaultTgaSize = 24910; +static unsigned char uiDefaultTgaData[] = { + 0x38, 0x42, 0x50, 0x53, 0x00, 0x1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4, 0x00, + 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x8, 0x00, 0x3, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x53, 0xc6, 0x38, 0x42, 0x49, 0x4d, 0x4, 0x25, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x38, 0x42, 0x49, 0x4d, 0x4, 0x24, 0x00, 0x00, 0x00, 0x00, 0x36, 0x74, 0x3c, + 0x3f, 0x78, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x74, 0x20, 0x62, 0x65, 0x67, 0x69, 0x6e, 0x3d, + 0x22, 0xef, 0xbb, 0xbf, 0x22, 0x20, 0x69, 0x64, 0x3d, 0x22, 0x57, 0x35, 0x4d, 0x30, 0x4d, + 0x70, 0x43, 0x65, 0x68, 0x69, 0x48, 0x7a, 0x72, 0x65, 0x53, 0x7a, 0x4e, 0x54, 0x63, 0x7a, + 0x6b, 0x63, 0x39, 0x64, 0x22, 0x3f, 0x3e, 0xa, 0x3c, 0x78, 0x3a, 0x78, 0x6d, 0x70, 0x6d, + 0x65, 0x74, 0x61, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x78, 0x3d, 0x22, 0x61, 0x64, + 0x6f, 0x62, 0x65, 0x3a, 0x6e, 0x73, 0x3a, 0x6d, 0x65, 0x74, 0x61, 0x2f, 0x22, 0x20, 0x78, + 0x3a, 0x78, 0x6d, 0x70, 0x74, 0x6b, 0x3d, 0x22, 0x41, 0x64, 0x6f, 0x62, 0x65, 0x20, 0x58, + 0x4d, 0x50, 0x20, 0x43, 0x6f, 0x72, 0x65, 0x20, 0x35, 0x2e, 0x35, 0x2d, 0x63, 0x30, 0x32, + 0x31, 0x20, 0x37, 0x39, 0x2e, 0x31, 0x35, 0x34, 0x39, 0x31, 0x31, 0x2c, 0x20, 0x32, 0x30, + 0x31, 0x33, 0x2f, 0x31, 0x30, 0x2f, 0x32, 0x39, 0x2d, 0x31, 0x31, 0x3a, 0x34, 0x37, 0x3a, + 0x31, 0x36, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x3e, 0xa, 0x20, 0x20, + 0x20, 0x3c, 0x72, 0x64, 0x66, 0x3a, 0x52, 0x44, 0x46, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, + 0x3a, 0x72, 0x64, 0x66, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, + 0x77, 0x2e, 0x77, 0x33, 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x31, 0x39, 0x39, 0x39, 0x2f, 0x30, + 0x32, 0x2f, 0x32, 0x32, 0x2d, 0x72, 0x64, 0x66, 0x2d, 0x73, 0x79, 0x6e, 0x74, 0x61, 0x78, + 0x2d, 0x6e, 0x73, 0x23, 0x22, 0x3e, 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x72, + 0x64, 0x66, 0x3a, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x20, + 0x72, 0x64, 0x66, 0x3a, 0x61, 0x62, 0x6f, 0x75, 0x74, 0x3d, 0x22, 0x22, 0xa, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, + 0x3a, 0x78, 0x6d, 0x70, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6e, 0x73, + 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x61, 0x70, 0x2f, + 0x31, 0x2e, 0x30, 0x2f, 0x22, 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x64, 0x63, 0x3d, 0x22, 0x68, 0x74, + 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x70, 0x75, 0x72, 0x6c, 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x64, + 0x63, 0x2f, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x2f, 0x31, 0x2e, 0x31, 0x2f, + 0x22, 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x78, + 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x78, 0x6d, 0x70, 0x4d, 0x4d, 0x3d, 0x22, 0x68, 0x74, 0x74, + 0x70, 0x3a, 0x2f, 0x2f, 0x6e, 0x73, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x63, 0x6f, + 0x6d, 0x2f, 0x78, 0x61, 0x70, 0x2f, 0x31, 0x2e, 0x30, 0x2f, 0x6d, 0x6d, 0x2f, 0x22, 0xa, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x78, 0x6d, 0x6c, + 0x6e, 0x73, 0x3a, 0x73, 0x74, 0x45, 0x76, 0x74, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, + 0x2f, 0x2f, 0x6e, 0x73, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, + 0x78, 0x61, 0x70, 0x2f, 0x31, 0x2e, 0x30, 0x2f, 0x73, 0x54, 0x79, 0x70, 0x65, 0x2f, 0x52, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x23, 0x22, 0xa, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x78, 0x6d, 0x6c, + 0x6e, 0x73, 0x3a, 0x70, 0x68, 0x6f, 0x74, 0x6f, 0x73, 0x68, 0x6f, 0x70, 0x3d, 0x22, 0x68, + 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6e, 0x73, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, + 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x68, 0x6f, 0x74, 0x6f, 0x73, 0x68, 0x6f, 0x70, 0x2f, 0x31, + 0x2e, 0x30, 0x2f, 0x22, 0x3e, 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x3c, 0x78, 0x6d, 0x70, 0x3a, 0x43, 0x72, 0x65, 0x61, 0x74, 0x6f, 0x72, 0x54, 0x6f, 0x6f, + 0x6c, 0x3e, 0x41, 0x64, 0x6f, 0x62, 0x65, 0x20, 0x50, 0x68, 0x6f, 0x74, 0x6f, 0x73, 0x68, + 0x6f, 0x70, 0x20, 0x43, 0x43, 0x20, 0x28, 0x57, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x73, 0x29, + 0x3c, 0x2f, 0x78, 0x6d, 0x70, 0x3a, 0x43, 0x72, 0x65, 0x61, 0x74, 0x6f, 0x72, 0x54, 0x6f, + 0x6f, 0x6c, 0x3e, 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x78, + 0x6d, 0x70, 0x3a, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x44, 0x61, 0x74, 0x65, 0x3e, 0x32, + 0x30, 0x31, 0x34, 0x2d, 0x30, 0x36, 0x2d, 0x30, 0x35, 0x54, 0x31, 0x33, 0x3a, 0x32, 0x34, + 0x3a, 0x30, 0x34, 0x2d, 0x30, 0x35, 0x3a, 0x30, 0x30, 0x3c, 0x2f, 0x78, 0x6d, 0x70, 0x3a, + 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x44, 0x61, 0x74, 0x65, 0x3e, 0xa, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x78, 0x6d, 0x70, 0x3a, 0x4d, 0x65, 0x74, 0x61, + 0x64, 0x61, 0x74, 0x61, 0x44, 0x61, 0x74, 0x65, 0x3e, 0x32, 0x30, 0x31, 0x34, 0x2d, 0x30, + 0x36, 0x2d, 0x30, 0x35, 0x54, 0x31, 0x33, 0x3a, 0x32, 0x34, 0x3a, 0x30, 0x34, 0x2d, 0x30, + 0x35, 0x3a, 0x30, 0x30, 0x3c, 0x2f, 0x78, 0x6d, 0x70, 0x3a, 0x4d, 0x65, 0x74, 0x61, 0x64, + 0x61, 0x74, 0x61, 0x44, 0x61, 0x74, 0x65, 0x3e, 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x3c, 0x78, 0x6d, 0x70, 0x3a, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x44, + 0x61, 0x74, 0x65, 0x3e, 0x32, 0x30, 0x31, 0x34, 0x2d, 0x30, 0x36, 0x2d, 0x30, 0x35, 0x54, + 0x31, 0x33, 0x3a, 0x32, 0x34, 0x3a, 0x30, 0x34, 0x2d, 0x30, 0x35, 0x3a, 0x30, 0x30, 0x3c, + 0x2f, 0x78, 0x6d, 0x70, 0x3a, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x44, 0x61, 0x74, 0x65, + 0x3e, 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x64, 0x63, 0x3a, + 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x3e, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x2f, 0x76, 0x6e, 0x64, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x70, + 0x68, 0x6f, 0x74, 0x6f, 0x73, 0x68, 0x6f, 0x70, 0x3c, 0x2f, 0x64, 0x63, 0x3a, 0x66, 0x6f, + 0x72, 0x6d, 0x61, 0x74, 0x3e, 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x3c, 0x78, 0x6d, 0x70, 0x4d, 0x4d, 0x3a, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, + 0x49, 0x44, 0x3e, 0x78, 0x6d, 0x70, 0x2e, 0x69, 0x69, 0x64, 0x3a, 0x30, 0x37, 0x35, 0x32, + 0x64, 0x31, 0x62, 0x35, 0x2d, 0x30, 0x34, 0x61, 0x35, 0x2d, 0x33, 0x35, 0x34, 0x36, 0x2d, + 0x39, 0x64, 0x61, 0x35, 0x2d, 0x62, 0x34, 0x63, 0x32, 0x31, 0x66, 0x32, 0x39, 0x36, 0x34, + 0x35, 0x34, 0x3c, 0x2f, 0x78, 0x6d, 0x70, 0x4d, 0x4d, 0x3a, 0x49, 0x6e, 0x73, 0x74, 0x61, + 0x6e, 0x63, 0x65, 0x49, 0x44, 0x3e, 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x3c, 0x78, 0x6d, 0x70, 0x4d, 0x4d, 0x3a, 0x44, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, + 0x74, 0x49, 0x44, 0x3e, 0x78, 0x6d, 0x70, 0x2e, 0x64, 0x69, 0x64, 0x3a, 0x30, 0x37, 0x35, + 0x32, 0x64, 0x31, 0x62, 0x35, 0x2d, 0x30, 0x34, 0x61, 0x35, 0x2d, 0x33, 0x35, 0x34, 0x36, + 0x2d, 0x39, 0x64, 0x61, 0x35, 0x2d, 0x62, 0x34, 0x63, 0x32, 0x31, 0x66, 0x32, 0x39, 0x36, + 0x34, 0x35, 0x34, 0x3c, 0x2f, 0x78, 0x6d, 0x70, 0x4d, 0x4d, 0x3a, 0x44, 0x6f, 0x63, 0x75, + 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x44, 0x3e, 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x3c, 0x78, 0x6d, 0x70, 0x4d, 0x4d, 0x3a, 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, + 0x61, 0x6c, 0x44, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x44, 0x3e, 0x78, 0x6d, + 0x70, 0x2e, 0x64, 0x69, 0x64, 0x3a, 0x30, 0x37, 0x35, 0x32, 0x64, 0x31, 0x62, 0x35, 0x2d, + 0x30, 0x34, 0x61, 0x35, 0x2d, 0x33, 0x35, 0x34, 0x36, 0x2d, 0x39, 0x64, 0x61, 0x35, 0x2d, + 0x62, 0x34, 0x63, 0x32, 0x31, 0x66, 0x32, 0x39, 0x36, 0x34, 0x35, 0x34, 0x3c, 0x2f, 0x78, + 0x6d, 0x70, 0x4d, 0x4d, 0x3a, 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x6c, 0x44, 0x6f, + 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x44, 0x3e, 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x3c, 0x78, 0x6d, 0x70, 0x4d, 0x4d, 0x3a, 0x48, 0x69, 0x73, 0x74, + 0x6f, 0x72, 0x79, 0x3e, 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x3c, 0x72, 0x64, 0x66, 0x3a, 0x53, 0x65, 0x71, 0x3e, 0xa, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x72, 0x64, + 0x66, 0x3a, 0x6c, 0x69, 0x20, 0x72, 0x64, 0x66, 0x3a, 0x70, 0x61, 0x72, 0x73, 0x65, 0x54, + 0x79, 0x70, 0x65, 0x3d, 0x22, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x22, 0x3e, + 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x3c, 0x73, 0x74, 0x45, 0x76, 0x74, 0x3a, 0x61, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x3e, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x3c, 0x2f, 0x73, 0x74, 0x45, + 0x76, 0x74, 0x3a, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0xa, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, + 0x73, 0x74, 0x45, 0x76, 0x74, 0x3a, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x49, + 0x44, 0x3e, 0x78, 0x6d, 0x70, 0x2e, 0x69, 0x69, 0x64, 0x3a, 0x30, 0x37, 0x35, 0x32, 0x64, + 0x31, 0x62, 0x35, 0x2d, 0x30, 0x34, 0x61, 0x35, 0x2d, 0x33, 0x35, 0x34, 0x36, 0x2d, 0x39, + 0x64, 0x61, 0x35, 0x2d, 0x62, 0x34, 0x63, 0x32, 0x31, 0x66, 0x32, 0x39, 0x36, 0x34, 0x35, + 0x34, 0x3c, 0x2f, 0x73, 0x74, 0x45, 0x76, 0x74, 0x3a, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, + 0x63, 0x65, 0x49, 0x44, 0x3e, 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x73, 0x74, 0x45, 0x76, 0x74, + 0x3a, 0x77, 0x68, 0x65, 0x6e, 0x3e, 0x32, 0x30, 0x31, 0x34, 0x2d, 0x30, 0x36, 0x2d, 0x30, + 0x35, 0x54, 0x31, 0x33, 0x3a, 0x32, 0x34, 0x3a, 0x30, 0x34, 0x2d, 0x30, 0x35, 0x3a, 0x30, + 0x30, 0x3c, 0x2f, 0x73, 0x74, 0x45, 0x76, 0x74, 0x3a, 0x77, 0x68, 0x65, 0x6e, 0x3e, 0xa, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x3c, 0x73, 0x74, 0x45, 0x76, 0x74, 0x3a, 0x73, 0x6f, 0x66, 0x74, 0x77, + 0x61, 0x72, 0x65, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x3e, 0x41, 0x64, 0x6f, 0x62, 0x65, 0x20, + 0x50, 0x68, 0x6f, 0x74, 0x6f, 0x73, 0x68, 0x6f, 0x70, 0x20, 0x43, 0x43, 0x20, 0x28, 0x57, + 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x73, 0x29, 0x3c, 0x2f, 0x73, 0x74, 0x45, 0x76, 0x74, 0x3a, + 0x73, 0x6f, 0x66, 0x74, 0x77, 0x61, 0x72, 0x65, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x3e, 0xa, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x3c, 0x2f, 0x72, 0x64, 0x66, 0x3a, 0x6c, 0x69, 0x3e, 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x2f, 0x72, 0x64, 0x66, 0x3a, 0x53, 0x65, + 0x71, 0x3e, 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x2f, 0x78, + 0x6d, 0x70, 0x4d, 0x4d, 0x3a, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x3e, 0xa, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x70, 0x68, 0x6f, 0x74, 0x6f, 0x73, + 0x68, 0x6f, 0x70, 0x3a, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x4d, 0x6f, 0x64, 0x65, 0x3e, 0x33, + 0x3c, 0x2f, 0x70, 0x68, 0x6f, 0x74, 0x6f, 0x73, 0x68, 0x6f, 0x70, 0x3a, 0x43, 0x6f, 0x6c, + 0x6f, 0x72, 0x4d, 0x6f, 0x64, 0x65, 0x3e, 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x3c, 0x70, 0x68, 0x6f, 0x74, 0x6f, 0x73, 0x68, 0x6f, 0x70, 0x3a, 0x49, 0x43, + 0x43, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x3e, 0x73, 0x52, 0x47, 0x42, 0x20, 0x49, + 0x45, 0x43, 0x36, 0x31, 0x39, 0x36, 0x36, 0x2d, 0x32, 0x2e, 0x31, 0x3c, 0x2f, 0x70, 0x68, + 0x6f, 0x74, 0x6f, 0x73, 0x68, 0x6f, 0x70, 0x3a, 0x49, 0x43, 0x43, 0x50, 0x72, 0x6f, 0x66, + 0x69, 0x6c, 0x65, 0x3e, 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x2f, 0x72, 0x64, + 0x66, 0x3a, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0xa, + 0x20, 0x20, 0x20, 0x3c, 0x2f, 0x72, 0x64, 0x66, 0x3a, 0x52, 0x44, 0x46, 0x3e, 0xa, 0x3c, + 0x2f, 0x78, 0x3a, 0x78, 0x6d, 0x70, 0x6d, 0x65, 0x74, 0x61, 0x3e, 0xa, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xa, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xa, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xa, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xa, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xa, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xa, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xa, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xa, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xa, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xa, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xa, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xa, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xa, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xa, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xa, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xa, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xa, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xa, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xa, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xa, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xa, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xa, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xa, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xa, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xa, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xa, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xa, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xa, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xa, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xa, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xa, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xa, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xa, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xa, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xa, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xa, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xa, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xa, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xa, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0xa, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xa, + 0x3c, 0x3f, 0x78, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x74, 0x20, 0x65, 0x6e, 0x64, 0x3d, 0x22, + 0x77, 0x22, 0x3f, 0x3e, 0x38, 0x42, 0x49, 0x4d, 0x4, 0x3a, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xe5, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x1, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb, + 0x70, 0x72, 0x69, 0x6e, 0x74, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x00, 0x00, 0x00, 0x5, + 0x00, 0x00, 0x00, 0x00, 0x50, 0x73, 0x74, 0x53, 0x62, 0x6f, 0x6f, 0x6c, 0x1, 0x00, 0x00, + 0x00, 0x00, 0x49, 0x6e, 0x74, 0x65, 0x65, 0x6e, 0x75, 0x6d, 0x00, 0x00, 0x00, 0x00, 0x49, + 0x6e, 0x74, 0x65, 0x00, 0x00, 0x00, 0x00, 0x43, 0x6c, 0x72, 0x6d, 0x00, 0x00, 0x00, 0xf, + 0x70, 0x72, 0x69, 0x6e, 0x74, 0x53, 0x69, 0x78, 0x74, 0x65, 0x65, 0x6e, 0x42, 0x69, 0x74, + 0x62, 0x6f, 0x6f, 0x6c, 0x00, 0x00, 0x00, 0x00, 0xb, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x65, + 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x54, 0x45, 0x58, 0x54, 0x00, 0x00, 0x00, 0x1, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xf, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x53, + 0x65, 0x74, 0x75, 0x70, 0x4f, 0x62, 0x6a, 0x63, 0x00, 0x00, 0x00, 0xc, 0x00, 0x50, 0x00, + 0x72, 0x00, 0x6f, 0x00, 0x6f, 0x00, 0x66, 0x00, 0x20, 0x00, 0x53, 0x00, 0x65, 0x00, 0x74, + 0x00, 0x75, 0x00, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa, 0x70, 0x72, 0x6f, 0x6f, 0x66, + 0x53, 0x65, 0x74, 0x75, 0x70, 0x00, 0x00, 0x00, 0x1, 0x00, 0x00, 0x00, 0x00, 0x42, 0x6c, + 0x74, 0x6e, 0x65, 0x6e, 0x75, 0x6d, 0x00, 0x00, 0x00, 0xc, 0x62, 0x75, 0x69, 0x6c, 0x74, + 0x69, 0x6e, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x00, 0x00, 0x00, 0x9, 0x70, 0x72, 0x6f, 0x6f, + 0x66, 0x43, 0x4d, 0x59, 0x4b, 0x00, 0x38, 0x42, 0x49, 0x4d, 0x4, 0x3b, 0x00, 0x00, 0x00, + 0x00, 0x2, 0x2d, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x1, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x12, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x4f, 0x70, + 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x00, 0x43, 0x70, + 0x74, 0x6e, 0x62, 0x6f, 0x6f, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x43, 0x6c, 0x62, 0x72, + 0x62, 0x6f, 0x6f, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x52, 0x67, 0x73, 0x4d, 0x62, 0x6f, + 0x6f, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x43, 0x72, 0x6e, 0x43, 0x62, 0x6f, 0x6f, 0x6c, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x43, 0x6e, 0x74, 0x43, 0x62, 0x6f, 0x6f, 0x6c, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x4c, 0x62, 0x6c, 0x73, 0x62, 0x6f, 0x6f, 0x6c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x4e, 0x67, 0x74, 0x76, 0x62, 0x6f, 0x6f, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x45, + 0x6d, 0x6c, 0x44, 0x62, 0x6f, 0x6f, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x49, 0x6e, 0x74, + 0x72, 0x62, 0x6f, 0x6f, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x63, 0x6b, 0x67, 0x4f, + 0x62, 0x6a, 0x63, 0x00, 0x00, 0x00, 0x1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x52, 0x47, + 0x42, 0x43, 0x00, 0x00, 0x00, 0x3, 0x00, 0x00, 0x00, 0x00, 0x52, 0x64, 0x20, 0x20, 0x64, + 0x6f, 0x75, 0x62, 0x40, 0x6f, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x47, 0x72, 0x6e, 0x20, 0x64, 0x6f, 0x75, 0x62, 0x40, 0x6f, 0xe0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x6c, 0x20, 0x20, 0x64, 0x6f, 0x75, 0x62, 0x40, 0x6f, + 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x72, 0x64, 0x54, 0x55, + 0x6e, 0x74, 0x46, 0x23, 0x52, 0x6c, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x42, 0x6c, 0x64, 0x20, 0x55, 0x6e, 0x74, 0x46, 0x23, 0x52, 0x6c, + 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x52, 0x73, + 0x6c, 0x74, 0x55, 0x6e, 0x74, 0x46, 0x23, 0x50, 0x78, 0x6c, 0x40, 0x52, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa, 0x76, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x44, 0x61, + 0x74, 0x61, 0x62, 0x6f, 0x6f, 0x6c, 0x1, 0x00, 0x00, 0x00, 0x00, 0x50, 0x67, 0x50, 0x73, + 0x65, 0x6e, 0x75, 0x6d, 0x00, 0x00, 0x00, 0x00, 0x50, 0x67, 0x50, 0x73, 0x00, 0x00, 0x00, + 0x00, 0x50, 0x67, 0x50, 0x43, 0x00, 0x00, 0x00, 0x00, 0x4c, 0x65, 0x66, 0x74, 0x55, 0x6e, + 0x74, 0x46, 0x23, 0x52, 0x6c, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x54, 0x6f, 0x70, 0x20, 0x55, 0x6e, 0x74, 0x46, 0x23, 0x52, 0x6c, 0x74, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x53, 0x63, 0x6c, + 0x20, 0x55, 0x6e, 0x74, 0x46, 0x23, 0x50, 0x72, 0x63, 0x40, 0x59, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x63, 0x72, 0x6f, 0x70, 0x57, 0x68, 0x65, 0x6e, 0x50, + 0x72, 0x69, 0x6e, 0x74, 0x69, 0x6e, 0x67, 0x62, 0x6f, 0x6f, 0x6c, 0x00, 0x00, 0x00, 0x00, + 0xe, 0x63, 0x72, 0x6f, 0x70, 0x52, 0x65, 0x63, 0x74, 0x42, 0x6f, 0x74, 0x74, 0x6f, 0x6d, + 0x6c, 0x6f, 0x6e, 0x67, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc, 0x63, 0x72, 0x6f, + 0x70, 0x52, 0x65, 0x63, 0x74, 0x4c, 0x65, 0x66, 0x74, 0x6c, 0x6f, 0x6e, 0x67, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xd, 0x63, 0x72, 0x6f, 0x70, 0x52, 0x65, 0x63, 0x74, 0x52, + 0x69, 0x67, 0x68, 0x74, 0x6c, 0x6f, 0x6e, 0x67, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xb, 0x63, 0x72, 0x6f, 0x70, 0x52, 0x65, 0x63, 0x74, 0x54, 0x6f, 0x70, 0x6c, 0x6f, 0x6e, + 0x67, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x42, 0x49, 0x4d, 0x3, 0xed, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x10, 0x00, 0x48, 0x00, 0x00, 0x00, 0x1, 0x00, 0x1, 0x00, 0x48, 0x00, 0x00, + 0x00, 0x1, 0x00, 0x1, 0x38, 0x42, 0x49, 0x4d, 0x4, 0x26, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x80, 0x00, 0x00, + 0x38, 0x42, 0x49, 0x4d, 0x3, 0xee, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8, 0x7, 0x41, 0x6c, + 0x70, 0x68, 0x61, 0x20, 0x31, 0x38, 0x42, 0x49, 0x4d, 0x4, 0x15, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x14, 0x00, 0x00, 0x00, 0x8, 0x00, 0x41, 0x00, 0x6c, 0x00, 0x70, 0x00, 0x68, 0x00, + 0x61, 0x00, 0x20, 0x00, 0x31, 0x00, 0x00, 0x38, 0x42, 0x49, 0x4d, 0x4, 0x35, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x1, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x32, 0x1, 0x00, 0x38, 0x42, 0x49, 0x4d, 0x4, 0x1d, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x4, 0x00, 0x00, 0x00, 0x2, 0x38, 0x42, 0x49, 0x4d, 0x4, 0xd, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x4, 0x00, 0x00, 0x00, 0x78, 0x38, 0x42, 0x49, 0x4d, 0x4, 0x19, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x4, 0x00, 0x00, 0x00, 0x1e, 0x38, 0x42, 0x49, 0x4d, 0x3, + 0xf3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1, 0x00, 0x38, 0x42, 0x49, 0x4d, 0x27, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa, 0x00, + 0x1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1, 0x38, 0x42, 0x49, 0x4d, 0x3, 0xf5, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x2f, 0x66, 0x66, 0x00, 0x1, 0x00, 0x6c, 0x66, + 0x66, 0x00, 0x6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1, 0x00, 0x2f, 0x66, 0x66, 0x00, 0x1, + 0x00, 0xa1, 0x99, 0x9a, 0x00, 0x6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1, 0x00, 0x32, 0x00, + 0x00, 0x00, 0x1, 0x00, 0x5a, 0x00, 0x00, 0x00, 0x6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1, + 0x00, 0x35, 0x00, 0x00, 0x00, 0x1, 0x00, 0x2d, 0x00, 0x00, 0x00, 0x6, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x1, 0x38, 0x42, 0x49, 0x4d, 0x3, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, + 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3, 0xe8, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3, 0xe8, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x3, 0xe8, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0x3, 0xe8, 0x00, 0x00, 0x38, 0x42, 0x49, 0x4d, 0x4, 0x8, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x1, 0x00, 0x00, 0x2, 0x40, 0x00, 0x00, 0x2, + 0x40, 0x00, 0x00, 0x00, 0x00, 0x38, 0x42, 0x49, 0x4d, 0x4, 0x1e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x4, 0x00, 0x00, 0x00, 0x00, 0x38, 0x42, 0x49, 0x4d, 0x4, 0x1a, 0x00, 0x00, 0x00, + 0x00, 0x3, 0x35, 0x00, 0x00, 0x00, 0x6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, + 0x00, 0x00, 0x1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6e, 0x75, 0x6c, 0x6c, 0x00, 0x00, + 0x00, 0x2, 0x00, 0x00, 0x00, 0x6, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x73, 0x4f, 0x62, 0x6a, + 0x63, 0x00, 0x00, 0x00, 0x1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x52, 0x63, 0x74, 0x31, + 0x00, 0x00, 0x00, 0x4, 0x00, 0x00, 0x00, 0x00, 0x54, 0x6f, 0x70, 0x20, 0x6c, 0x6f, 0x6e, + 0x67, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4c, 0x65, 0x66, 0x74, 0x6c, 0x6f, + 0x6e, 0x67, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x74, 0x6f, 0x6d, 0x6c, + 0x6f, 0x6e, 0x67, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x52, 0x67, 0x68, 0x74, + 0x6c, 0x6f, 0x6e, 0x67, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x6, 0x73, 0x6c, 0x69, + 0x63, 0x65, 0x73, 0x56, 0x6c, 0x4c, 0x73, 0x00, 0x00, 0x00, 0x1, 0x4f, 0x62, 0x6a, 0x63, + 0x00, 0x00, 0x00, 0x1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5, 0x73, 0x6c, 0x69, 0x63, 0x65, + 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x7, 0x73, 0x6c, 0x69, 0x63, 0x65, 0x49, 0x44, + 0x6c, 0x6f, 0x6e, 0x67, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7, 0x67, 0x72, 0x6f, + 0x75, 0x70, 0x49, 0x44, 0x6c, 0x6f, 0x6e, 0x67, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x6, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x65, 0x6e, 0x75, 0x6d, 0x00, 0x00, 0x00, 0xc, + 0x45, 0x53, 0x6c, 0x69, 0x63, 0x65, 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x00, 0x00, 0x00, + 0xd, 0x61, 0x75, 0x74, 0x6f, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x00, + 0x00, 0x00, 0x00, 0x54, 0x79, 0x70, 0x65, 0x65, 0x6e, 0x75, 0x6d, 0x00, 0x00, 0x00, 0xa, + 0x45, 0x53, 0x6c, 0x69, 0x63, 0x65, 0x54, 0x79, 0x70, 0x65, 0x00, 0x00, 0x00, 0x00, 0x49, + 0x6d, 0x67, 0x20, 0x00, 0x00, 0x00, 0x6, 0x62, 0x6f, 0x75, 0x6e, 0x64, 0x73, 0x4f, 0x62, + 0x6a, 0x63, 0x00, 0x00, 0x00, 0x1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x52, 0x63, 0x74, + 0x31, 0x00, 0x00, 0x00, 0x4, 0x00, 0x00, 0x00, 0x00, 0x54, 0x6f, 0x70, 0x20, 0x6c, 0x6f, + 0x6e, 0x67, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4c, 0x65, 0x66, 0x74, 0x6c, + 0x6f, 0x6e, 0x67, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x74, 0x6f, 0x6d, + 0x6c, 0x6f, 0x6e, 0x67, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x52, 0x67, 0x68, + 0x74, 0x6c, 0x6f, 0x6e, 0x67, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x3, 0x75, 0x72, + 0x6c, 0x54, 0x45, 0x58, 0x54, 0x00, 0x00, 0x00, 0x1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x6e, 0x75, 0x6c, 0x6c, 0x54, 0x45, 0x58, 0x54, 0x00, 0x00, 0x00, 0x1, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x4d, 0x73, 0x67, 0x65, 0x54, 0x45, 0x58, 0x54, 0x00, 0x00, 0x00, 0x1, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x6, 0x61, 0x6c, 0x74, 0x54, 0x61, 0x67, 0x54, 0x45, 0x58, + 0x54, 0x00, 0x00, 0x00, 0x1, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe, 0x63, 0x65, 0x6c, 0x6c, + 0x54, 0x65, 0x78, 0x74, 0x49, 0x73, 0x48, 0x54, 0x4d, 0x4c, 0x62, 0x6f, 0x6f, 0x6c, 0x1, + 0x00, 0x00, 0x00, 0x8, 0x63, 0x65, 0x6c, 0x6c, 0x54, 0x65, 0x78, 0x74, 0x54, 0x45, 0x58, + 0x54, 0x00, 0x00, 0x00, 0x1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9, 0x68, 0x6f, 0x72, 0x7a, + 0x41, 0x6c, 0x69, 0x67, 0x6e, 0x65, 0x6e, 0x75, 0x6d, 0x00, 0x00, 0x00, 0xf, 0x45, 0x53, + 0x6c, 0x69, 0x63, 0x65, 0x48, 0x6f, 0x72, 0x7a, 0x41, 0x6c, 0x69, 0x67, 0x6e, 0x00, 0x00, + 0x00, 0x7, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x00, 0x00, 0x00, 0x9, 0x76, 0x65, + 0x72, 0x74, 0x41, 0x6c, 0x69, 0x67, 0x6e, 0x65, 0x6e, 0x75, 0x6d, 0x00, 0x00, 0x00, 0xf, + 0x45, 0x53, 0x6c, 0x69, 0x63, 0x65, 0x56, 0x65, 0x72, 0x74, 0x41, 0x6c, 0x69, 0x67, 0x6e, + 0x00, 0x00, 0x00, 0x7, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x00, 0x00, 0x00, 0xb, + 0x62, 0x67, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x54, 0x79, 0x70, 0x65, 0x65, 0x6e, 0x75, 0x6d, + 0x00, 0x00, 0x00, 0x11, 0x45, 0x53, 0x6c, 0x69, 0x63, 0x65, 0x42, 0x47, 0x43, 0x6f, 0x6c, + 0x6f, 0x72, 0x54, 0x79, 0x70, 0x65, 0x00, 0x00, 0x00, 0x00, 0x4e, 0x6f, 0x6e, 0x65, 0x00, + 0x00, 0x00, 0x9, 0x74, 0x6f, 0x70, 0x4f, 0x75, 0x74, 0x73, 0x65, 0x74, 0x6c, 0x6f, 0x6e, + 0x67, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa, 0x6c, 0x65, 0x66, 0x74, 0x4f, 0x75, + 0x74, 0x73, 0x65, 0x74, 0x6c, 0x6f, 0x6e, 0x67, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xc, 0x62, 0x6f, 0x74, 0x74, 0x6f, 0x6d, 0x4f, 0x75, 0x74, 0x73, 0x65, 0x74, 0x6c, 0x6f, + 0x6e, 0x67, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb, 0x72, 0x69, 0x67, 0x68, 0x74, + 0x4f, 0x75, 0x74, 0x73, 0x65, 0x74, 0x6c, 0x6f, 0x6e, 0x67, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x38, 0x42, 0x49, 0x4d, 0x4, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc, 0x00, 0x00, 0x00, + 0x2, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x42, 0x49, 0x4d, 0x4, 0xf, + 0x00, 0x00, 0x00, 0x00, 0xc, 0x48, 0x00, 0x00, 0xc, 0x48, 0x4c, 0x69, 0x6e, 0x6f, 0x2, + 0x10, 0x00, 0x00, 0x6d, 0x6e, 0x74, 0x72, 0x52, 0x47, 0x42, 0x20, 0x58, 0x59, 0x5a, 0x20, + 0x7, 0xce, 0x00, 0x2, 0x00, 0x9, 0x00, 0x6, 0x00, 0x31, 0x00, 0x00, 0x61, 0x63, 0x73, + 0x70, 0x4d, 0x53, 0x46, 0x54, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x43, 0x20, 0x73, 0x52, + 0x47, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1, 0x00, + 0x00, 0xf6, 0xd6, 0x00, 0x1, 0x00, 0x00, 0x00, 0x00, 0xd3, 0x2d, 0x48, 0x50, 0x20, 0x20, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x11, 0x63, 0x70, 0x72, 0x74, 0x00, 0x00, 0x1, 0x50, 0x00, 0x00, 0x00, 0x33, + 0x64, 0x65, 0x73, 0x63, 0x00, 0x00, 0x1, 0x84, 0x00, 0x00, 0x00, 0x6c, 0x77, 0x74, 0x70, + 0x74, 0x00, 0x00, 0x1, 0xf0, 0x00, 0x00, 0x00, 0x14, 0x62, 0x6b, 0x70, 0x74, 0x00, 0x00, + 0x2, 0x4, 0x00, 0x00, 0x00, 0x14, 0x72, 0x58, 0x59, 0x5a, 0x00, 0x00, 0x2, 0x18, 0x00, + 0x00, 0x00, 0x14, 0x67, 0x58, 0x59, 0x5a, 0x00, 0x00, 0x2, 0x2c, 0x00, 0x00, 0x00, 0x14, + 0x62, 0x58, 0x59, 0x5a, 0x00, 0x00, 0x2, 0x40, 0x00, 0x00, 0x00, 0x14, 0x64, 0x6d, 0x6e, + 0x64, 0x00, 0x00, 0x2, 0x54, 0x00, 0x00, 0x00, 0x70, 0x64, 0x6d, 0x64, 0x64, 0x00, 0x00, + 0x2, 0xc4, 0x00, 0x00, 0x00, 0x88, 0x76, 0x75, 0x65, 0x64, 0x00, 0x00, 0x3, 0x4c, 0x00, + 0x00, 0x00, 0x86, 0x76, 0x69, 0x65, 0x77, 0x00, 0x00, 0x3, 0xd4, 0x00, 0x00, 0x00, 0x24, + 0x6c, 0x75, 0x6d, 0x69, 0x00, 0x00, 0x3, 0xf8, 0x00, 0x00, 0x00, 0x14, 0x6d, 0x65, 0x61, + 0x73, 0x00, 0x00, 0x4, 0xc, 0x00, 0x00, 0x00, 0x24, 0x74, 0x65, 0x63, 0x68, 0x00, 0x00, + 0x4, 0x30, 0x00, 0x00, 0x00, 0xc, 0x72, 0x54, 0x52, 0x43, 0x00, 0x00, 0x4, 0x3c, 0x00, + 0x00, 0x8, 0xc, 0x67, 0x54, 0x52, 0x43, 0x00, 0x00, 0x4, 0x3c, 0x00, 0x00, 0x8, 0xc, + 0x62, 0x54, 0x52, 0x43, 0x00, 0x00, 0x4, 0x3c, 0x00, 0x00, 0x8, 0xc, 0x74, 0x65, 0x78, + 0x74, 0x00, 0x00, 0x00, 0x00, 0x43, 0x6f, 0x70, 0x79, 0x72, 0x69, 0x67, 0x68, 0x74, 0x20, + 0x28, 0x63, 0x29, 0x20, 0x31, 0x39, 0x39, 0x38, 0x20, 0x48, 0x65, 0x77, 0x6c, 0x65, 0x74, + 0x74, 0x2d, 0x50, 0x61, 0x63, 0x6b, 0x61, 0x72, 0x64, 0x20, 0x43, 0x6f, 0x6d, 0x70, 0x61, + 0x6e, 0x79, 0x00, 0x00, 0x64, 0x65, 0x73, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x12, 0x73, 0x52, 0x47, 0x42, 0x20, 0x49, 0x45, 0x43, 0x36, 0x31, 0x39, 0x36, 0x36, 0x2d, + 0x32, 0x2e, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, + 0x73, 0x52, 0x47, 0x42, 0x20, 0x49, 0x45, 0x43, 0x36, 0x31, 0x39, 0x36, 0x36, 0x2d, 0x32, + 0x2e, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x58, 0x59, 0x5a, 0x20, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xf3, 0x51, 0x00, 0x1, 0x00, 0x00, 0x00, 0x1, 0x16, 0xcc, 0x58, 0x59, 0x5a, + 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x58, 0x59, 0x5a, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6f, 0xa2, 0x00, + 0x00, 0x38, 0xf5, 0x00, 0x00, 0x3, 0x90, 0x58, 0x59, 0x5a, 0x20, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x62, 0x99, 0x00, 0x00, 0xb7, 0x85, 0x00, 0x00, 0x18, 0xda, 0x58, 0x59, 0x5a, + 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0xa0, 0x00, 0x00, 0xf, 0x84, 0x00, 0x00, + 0xb6, 0xcf, 0x64, 0x65, 0x73, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x49, + 0x45, 0x43, 0x20, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x69, + 0x65, 0x63, 0x2e, 0x63, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x16, 0x49, 0x45, 0x43, 0x20, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, + 0x77, 0x2e, 0x69, 0x65, 0x63, 0x2e, 0x63, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0x65, 0x73, 0x63, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x2e, 0x49, 0x45, 0x43, 0x20, 0x36, 0x31, 0x39, 0x36, 0x36, + 0x2d, 0x32, 0x2e, 0x31, 0x20, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x20, 0x52, 0x47, + 0x42, 0x20, 0x63, 0x6f, 0x6c, 0x6f, 0x75, 0x72, 0x20, 0x73, 0x70, 0x61, 0x63, 0x65, 0x20, + 0x2d, 0x20, 0x73, 0x52, 0x47, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x2e, 0x49, 0x45, 0x43, 0x20, 0x36, 0x31, 0x39, 0x36, 0x36, 0x2d, 0x32, 0x2e, + 0x31, 0x20, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x20, 0x52, 0x47, 0x42, 0x20, 0x63, + 0x6f, 0x6c, 0x6f, 0x75, 0x72, 0x20, 0x73, 0x70, 0x61, 0x63, 0x65, 0x20, 0x2d, 0x20, 0x73, + 0x52, 0x47, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0x65, 0x73, 0x63, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, + 0x65, 0x20, 0x56, 0x69, 0x65, 0x77, 0x69, 0x6e, 0x67, 0x20, 0x43, 0x6f, 0x6e, 0x64, 0x69, + 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x69, 0x6e, 0x20, 0x49, 0x45, 0x43, 0x36, 0x31, 0x39, 0x36, + 0x36, 0x2d, 0x32, 0x2e, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x2c, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x20, 0x56, 0x69, 0x65, + 0x77, 0x69, 0x6e, 0x67, 0x20, 0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x20, + 0x69, 0x6e, 0x20, 0x49, 0x45, 0x43, 0x36, 0x31, 0x39, 0x36, 0x36, 0x2d, 0x32, 0x2e, 0x31, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0x69, 0x65, 0x77, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0xa4, 0xfe, 0x00, 0x14, 0x5f, 0x2e, 0x00, 0x10, 0xcf, + 0x14, 0x00, 0x3, 0xed, 0xcc, 0x00, 0x4, 0x13, 0xb, 0x00, 0x3, 0x5c, 0x9e, 0x00, 0x00, + 0x00, 0x1, 0x58, 0x59, 0x5a, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4c, 0x9, 0x56, 0x00, + 0x50, 0x00, 0x00, 0x00, 0x57, 0x1f, 0xe7, 0x6d, 0x65, 0x61, 0x73, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2, 0x8f, 0x00, 0x00, 0x00, 0x2, 0x73, 0x69, + 0x67, 0x20, 0x00, 0x00, 0x00, 0x00, 0x43, 0x52, 0x54, 0x20, 0x63, 0x75, 0x72, 0x76, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x4, 0x00, 0x00, 0x00, 0x00, 0x5, 0x00, 0xa, 0x00, 0xf, + 0x00, 0x14, 0x00, 0x19, 0x00, 0x1e, 0x00, 0x23, 0x00, 0x28, 0x00, 0x2d, 0x00, 0x32, 0x00, + 0x37, 0x00, 0x3b, 0x00, 0x40, 0x00, 0x45, 0x00, 0x4a, 0x00, 0x4f, 0x00, 0x54, 0x00, 0x59, + 0x00, 0x5e, 0x00, 0x63, 0x00, 0x68, 0x00, 0x6d, 0x00, 0x72, 0x00, 0x77, 0x00, 0x7c, 0x00, + 0x81, 0x00, 0x86, 0x00, 0x8b, 0x00, 0x90, 0x00, 0x95, 0x00, 0x9a, 0x00, 0x9f, 0x00, 0xa4, + 0x00, 0xa9, 0x00, 0xae, 0x00, 0xb2, 0x00, 0xb7, 0x00, 0xbc, 0x00, 0xc1, 0x00, 0xc6, 0x00, + 0xcb, 0x00, 0xd0, 0x00, 0xd5, 0x00, 0xdb, 0x00, 0xe0, 0x00, 0xe5, 0x00, 0xeb, 0x00, 0xf0, + 0x00, 0xf6, 0x00, 0xfb, 0x1, 0x1, 0x1, 0x7, 0x1, 0xd, 0x1, 0x13, 0x1, 0x19, 0x1, + 0x1f, 0x1, 0x25, 0x1, 0x2b, 0x1, 0x32, 0x1, 0x38, 0x1, 0x3e, 0x1, 0x45, 0x1, 0x4c, + 0x1, 0x52, 0x1, 0x59, 0x1, 0x60, 0x1, 0x67, 0x1, 0x6e, 0x1, 0x75, 0x1, 0x7c, 0x1, + 0x83, 0x1, 0x8b, 0x1, 0x92, 0x1, 0x9a, 0x1, 0xa1, 0x1, 0xa9, 0x1, 0xb1, 0x1, 0xb9, + 0x1, 0xc1, 0x1, 0xc9, 0x1, 0xd1, 0x1, 0xd9, 0x1, 0xe1, 0x1, 0xe9, 0x1, 0xf2, 0x1, + 0xfa, 0x2, 0x3, 0x2, 0xc, 0x2, 0x14, 0x2, 0x1d, 0x2, 0x26, 0x2, 0x2f, 0x2, 0x38, + 0x2, 0x41, 0x2, 0x4b, 0x2, 0x54, 0x2, 0x5d, 0x2, 0x67, 0x2, 0x71, 0x2, 0x7a, 0x2, + 0x84, 0x2, 0x8e, 0x2, 0x98, 0x2, 0xa2, 0x2, 0xac, 0x2, 0xb6, 0x2, 0xc1, 0x2, 0xcb, + 0x2, 0xd5, 0x2, 0xe0, 0x2, 0xeb, 0x2, 0xf5, 0x3, 0x00, 0x3, 0xb, 0x3, 0x16, 0x3, + 0x21, 0x3, 0x2d, 0x3, 0x38, 0x3, 0x43, 0x3, 0x4f, 0x3, 0x5a, 0x3, 0x66, 0x3, 0x72, + 0x3, 0x7e, 0x3, 0x8a, 0x3, 0x96, 0x3, 0xa2, 0x3, 0xae, 0x3, 0xba, 0x3, 0xc7, 0x3, + 0xd3, 0x3, 0xe0, 0x3, 0xec, 0x3, 0xf9, 0x4, 0x6, 0x4, 0x13, 0x4, 0x20, 0x4, 0x2d, + 0x4, 0x3b, 0x4, 0x48, 0x4, 0x55, 0x4, 0x63, 0x4, 0x71, 0x4, 0x7e, 0x4, 0x8c, 0x4, + 0x9a, 0x4, 0xa8, 0x4, 0xb6, 0x4, 0xc4, 0x4, 0xd3, 0x4, 0xe1, 0x4, 0xf0, 0x4, 0xfe, + 0x5, 0xd, 0x5, 0x1c, 0x5, 0x2b, 0x5, 0x3a, 0x5, 0x49, 0x5, 0x58, 0x5, 0x67, 0x5, + 0x77, 0x5, 0x86, 0x5, 0x96, 0x5, 0xa6, 0x5, 0xb5, 0x5, 0xc5, 0x5, 0xd5, 0x5, 0xe5, + 0x5, 0xf6, 0x6, 0x6, 0x6, 0x16, 0x6, 0x27, 0x6, 0x37, 0x6, 0x48, 0x6, 0x59, 0x6, + 0x6a, 0x6, 0x7b, 0x6, 0x8c, 0x6, 0x9d, 0x6, 0xaf, 0x6, 0xc0, 0x6, 0xd1, 0x6, 0xe3, + 0x6, 0xf5, 0x7, 0x7, 0x7, 0x19, 0x7, 0x2b, 0x7, 0x3d, 0x7, 0x4f, 0x7, 0x61, 0x7, + 0x74, 0x7, 0x86, 0x7, 0x99, 0x7, 0xac, 0x7, 0xbf, 0x7, 0xd2, 0x7, 0xe5, 0x7, 0xf8, + 0x8, 0xb, 0x8, 0x1f, 0x8, 0x32, 0x8, 0x46, 0x8, 0x5a, 0x8, 0x6e, 0x8, 0x82, 0x8, + 0x96, 0x8, 0xaa, 0x8, 0xbe, 0x8, 0xd2, 0x8, 0xe7, 0x8, 0xfb, 0x9, 0x10, 0x9, 0x25, + 0x9, 0x3a, 0x9, 0x4f, 0x9, 0x64, 0x9, 0x79, 0x9, 0x8f, 0x9, 0xa4, 0x9, 0xba, 0x9, + 0xcf, 0x9, 0xe5, 0x9, 0xfb, 0xa, 0x11, 0xa, 0x27, 0xa, 0x3d, 0xa, 0x54, 0xa, 0x6a, + 0xa, 0x81, 0xa, 0x98, 0xa, 0xae, 0xa, 0xc5, 0xa, 0xdc, 0xa, 0xf3, 0xb, 0xb, 0xb, + 0x22, 0xb, 0x39, 0xb, 0x51, 0xb, 0x69, 0xb, 0x80, 0xb, 0x98, 0xb, 0xb0, 0xb, 0xc8, + 0xb, 0xe1, 0xb, 0xf9, 0xc, 0x12, 0xc, 0x2a, 0xc, 0x43, 0xc, 0x5c, 0xc, 0x75, 0xc, + 0x8e, 0xc, 0xa7, 0xc, 0xc0, 0xc, 0xd9, 0xc, 0xf3, 0xd, 0xd, 0xd, 0x26, 0xd, 0x40, + 0xd, 0x5a, 0xd, 0x74, 0xd, 0x8e, 0xd, 0xa9, 0xd, 0xc3, 0xd, 0xde, 0xd, 0xf8, 0xe, + 0x13, 0xe, 0x2e, 0xe, 0x49, 0xe, 0x64, 0xe, 0x7f, 0xe, 0x9b, 0xe, 0xb6, 0xe, 0xd2, + 0xe, 0xee, 0xf, 0x9, 0xf, 0x25, 0xf, 0x41, 0xf, 0x5e, 0xf, 0x7a, 0xf, 0x96, 0xf, + 0xb3, 0xf, 0xcf, 0xf, 0xec, 0x10, 0x9, 0x10, 0x26, 0x10, 0x43, 0x10, 0x61, 0x10, 0x7e, + 0x10, 0x9b, 0x10, 0xb9, 0x10, 0xd7, 0x10, 0xf5, 0x11, 0x13, 0x11, 0x31, 0x11, 0x4f, 0x11, + 0x6d, 0x11, 0x8c, 0x11, 0xaa, 0x11, 0xc9, 0x11, 0xe8, 0x12, 0x7, 0x12, 0x26, 0x12, 0x45, + 0x12, 0x64, 0x12, 0x84, 0x12, 0xa3, 0x12, 0xc3, 0x12, 0xe3, 0x13, 0x3, 0x13, 0x23, 0x13, + 0x43, 0x13, 0x63, 0x13, 0x83, 0x13, 0xa4, 0x13, 0xc5, 0x13, 0xe5, 0x14, 0x6, 0x14, 0x27, + 0x14, 0x49, 0x14, 0x6a, 0x14, 0x8b, 0x14, 0xad, 0x14, 0xce, 0x14, 0xf0, 0x15, 0x12, 0x15, + 0x34, 0x15, 0x56, 0x15, 0x78, 0x15, 0x9b, 0x15, 0xbd, 0x15, 0xe0, 0x16, 0x3, 0x16, 0x26, + 0x16, 0x49, 0x16, 0x6c, 0x16, 0x8f, 0x16, 0xb2, 0x16, 0xd6, 0x16, 0xfa, 0x17, 0x1d, 0x17, + 0x41, 0x17, 0x65, 0x17, 0x89, 0x17, 0xae, 0x17, 0xd2, 0x17, 0xf7, 0x18, 0x1b, 0x18, 0x40, + 0x18, 0x65, 0x18, 0x8a, 0x18, 0xaf, 0x18, 0xd5, 0x18, 0xfa, 0x19, 0x20, 0x19, 0x45, 0x19, + 0x6b, 0x19, 0x91, 0x19, 0xb7, 0x19, 0xdd, 0x1a, 0x4, 0x1a, 0x2a, 0x1a, 0x51, 0x1a, 0x77, + 0x1a, 0x9e, 0x1a, 0xc5, 0x1a, 0xec, 0x1b, 0x14, 0x1b, 0x3b, 0x1b, 0x63, 0x1b, 0x8a, 0x1b, + 0xb2, 0x1b, 0xda, 0x1c, 0x2, 0x1c, 0x2a, 0x1c, 0x52, 0x1c, 0x7b, 0x1c, 0xa3, 0x1c, 0xcc, + 0x1c, 0xf5, 0x1d, 0x1e, 0x1d, 0x47, 0x1d, 0x70, 0x1d, 0x99, 0x1d, 0xc3, 0x1d, 0xec, 0x1e, + 0x16, 0x1e, 0x40, 0x1e, 0x6a, 0x1e, 0x94, 0x1e, 0xbe, 0x1e, 0xe9, 0x1f, 0x13, 0x1f, 0x3e, + 0x1f, 0x69, 0x1f, 0x94, 0x1f, 0xbf, 0x1f, 0xea, 0x20, 0x15, 0x20, 0x41, 0x20, 0x6c, 0x20, + 0x98, 0x20, 0xc4, 0x20, 0xf0, 0x21, 0x1c, 0x21, 0x48, 0x21, 0x75, 0x21, 0xa1, 0x21, 0xce, + 0x21, 0xfb, 0x22, 0x27, 0x22, 0x55, 0x22, 0x82, 0x22, 0xaf, 0x22, 0xdd, 0x23, 0xa, 0x23, + 0x38, 0x23, 0x66, 0x23, 0x94, 0x23, 0xc2, 0x23, 0xf0, 0x24, 0x1f, 0x24, 0x4d, 0x24, 0x7c, + 0x24, 0xab, 0x24, 0xda, 0x25, 0x9, 0x25, 0x38, 0x25, 0x68, 0x25, 0x97, 0x25, 0xc7, 0x25, + 0xf7, 0x26, 0x27, 0x26, 0x57, 0x26, 0x87, 0x26, 0xb7, 0x26, 0xe8, 0x27, 0x18, 0x27, 0x49, + 0x27, 0x7a, 0x27, 0xab, 0x27, 0xdc, 0x28, 0xd, 0x28, 0x3f, 0x28, 0x71, 0x28, 0xa2, 0x28, + 0xd4, 0x29, 0x6, 0x29, 0x38, 0x29, 0x6b, 0x29, 0x9d, 0x29, 0xd0, 0x2a, 0x2, 0x2a, 0x35, + 0x2a, 0x68, 0x2a, 0x9b, 0x2a, 0xcf, 0x2b, 0x2, 0x2b, 0x36, 0x2b, 0x69, 0x2b, 0x9d, 0x2b, + 0xd1, 0x2c, 0x5, 0x2c, 0x39, 0x2c, 0x6e, 0x2c, 0xa2, 0x2c, 0xd7, 0x2d, 0xc, 0x2d, 0x41, + 0x2d, 0x76, 0x2d, 0xab, 0x2d, 0xe1, 0x2e, 0x16, 0x2e, 0x4c, 0x2e, 0x82, 0x2e, 0xb7, 0x2e, + 0xee, 0x2f, 0x24, 0x2f, 0x5a, 0x2f, 0x91, 0x2f, 0xc7, 0x2f, 0xfe, 0x30, 0x35, 0x30, 0x6c, + 0x30, 0xa4, 0x30, 0xdb, 0x31, 0x12, 0x31, 0x4a, 0x31, 0x82, 0x31, 0xba, 0x31, 0xf2, 0x32, + 0x2a, 0x32, 0x63, 0x32, 0x9b, 0x32, 0xd4, 0x33, 0xd, 0x33, 0x46, 0x33, 0x7f, 0x33, 0xb8, + 0x33, 0xf1, 0x34, 0x2b, 0x34, 0x65, 0x34, 0x9e, 0x34, 0xd8, 0x35, 0x13, 0x35, 0x4d, 0x35, + 0x87, 0x35, 0xc2, 0x35, 0xfd, 0x36, 0x37, 0x36, 0x72, 0x36, 0xae, 0x36, 0xe9, 0x37, 0x24, + 0x37, 0x60, 0x37, 0x9c, 0x37, 0xd7, 0x38, 0x14, 0x38, 0x50, 0x38, 0x8c, 0x38, 0xc8, 0x39, + 0x5, 0x39, 0x42, 0x39, 0x7f, 0x39, 0xbc, 0x39, 0xf9, 0x3a, 0x36, 0x3a, 0x74, 0x3a, 0xb2, + 0x3a, 0xef, 0x3b, 0x2d, 0x3b, 0x6b, 0x3b, 0xaa, 0x3b, 0xe8, 0x3c, 0x27, 0x3c, 0x65, 0x3c, + 0xa4, 0x3c, 0xe3, 0x3d, 0x22, 0x3d, 0x61, 0x3d, 0xa1, 0x3d, 0xe0, 0x3e, 0x20, 0x3e, 0x60, + 0x3e, 0xa0, 0x3e, 0xe0, 0x3f, 0x21, 0x3f, 0x61, 0x3f, 0xa2, 0x3f, 0xe2, 0x40, 0x23, 0x40, + 0x64, 0x40, 0xa6, 0x40, 0xe7, 0x41, 0x29, 0x41, 0x6a, 0x41, 0xac, 0x41, 0xee, 0x42, 0x30, + 0x42, 0x72, 0x42, 0xb5, 0x42, 0xf7, 0x43, 0x3a, 0x43, 0x7d, 0x43, 0xc0, 0x44, 0x3, 0x44, + 0x47, 0x44, 0x8a, 0x44, 0xce, 0x45, 0x12, 0x45, 0x55, 0x45, 0x9a, 0x45, 0xde, 0x46, 0x22, + 0x46, 0x67, 0x46, 0xab, 0x46, 0xf0, 0x47, 0x35, 0x47, 0x7b, 0x47, 0xc0, 0x48, 0x5, 0x48, + 0x4b, 0x48, 0x91, 0x48, 0xd7, 0x49, 0x1d, 0x49, 0x63, 0x49, 0xa9, 0x49, 0xf0, 0x4a, 0x37, + 0x4a, 0x7d, 0x4a, 0xc4, 0x4b, 0xc, 0x4b, 0x53, 0x4b, 0x9a, 0x4b, 0xe2, 0x4c, 0x2a, 0x4c, + 0x72, 0x4c, 0xba, 0x4d, 0x2, 0x4d, 0x4a, 0x4d, 0x93, 0x4d, 0xdc, 0x4e, 0x25, 0x4e, 0x6e, + 0x4e, 0xb7, 0x4f, 0x00, 0x4f, 0x49, 0x4f, 0x93, 0x4f, 0xdd, 0x50, 0x27, 0x50, 0x71, 0x50, + 0xbb, 0x51, 0x6, 0x51, 0x50, 0x51, 0x9b, 0x51, 0xe6, 0x52, 0x31, 0x52, 0x7c, 0x52, 0xc7, + 0x53, 0x13, 0x53, 0x5f, 0x53, 0xaa, 0x53, 0xf6, 0x54, 0x42, 0x54, 0x8f, 0x54, 0xdb, 0x55, + 0x28, 0x55, 0x75, 0x55, 0xc2, 0x56, 0xf, 0x56, 0x5c, 0x56, 0xa9, 0x56, 0xf7, 0x57, 0x44, + 0x57, 0x92, 0x57, 0xe0, 0x58, 0x2f, 0x58, 0x7d, 0x58, 0xcb, 0x59, 0x1a, 0x59, 0x69, 0x59, + 0xb8, 0x5a, 0x7, 0x5a, 0x56, 0x5a, 0xa6, 0x5a, 0xf5, 0x5b, 0x45, 0x5b, 0x95, 0x5b, 0xe5, + 0x5c, 0x35, 0x5c, 0x86, 0x5c, 0xd6, 0x5d, 0x27, 0x5d, 0x78, 0x5d, 0xc9, 0x5e, 0x1a, 0x5e, + 0x6c, 0x5e, 0xbd, 0x5f, 0xf, 0x5f, 0x61, 0x5f, 0xb3, 0x60, 0x5, 0x60, 0x57, 0x60, 0xaa, + 0x60, 0xfc, 0x61, 0x4f, 0x61, 0xa2, 0x61, 0xf5, 0x62, 0x49, 0x62, 0x9c, 0x62, 0xf0, 0x63, + 0x43, 0x63, 0x97, 0x63, 0xeb, 0x64, 0x40, 0x64, 0x94, 0x64, 0xe9, 0x65, 0x3d, 0x65, 0x92, + 0x65, 0xe7, 0x66, 0x3d, 0x66, 0x92, 0x66, 0xe8, 0x67, 0x3d, 0x67, 0x93, 0x67, 0xe9, 0x68, + 0x3f, 0x68, 0x96, 0x68, 0xec, 0x69, 0x43, 0x69, 0x9a, 0x69, 0xf1, 0x6a, 0x48, 0x6a, 0x9f, + 0x6a, 0xf7, 0x6b, 0x4f, 0x6b, 0xa7, 0x6b, 0xff, 0x6c, 0x57, 0x6c, 0xaf, 0x6d, 0x8, 0x6d, + 0x60, 0x6d, 0xb9, 0x6e, 0x12, 0x6e, 0x6b, 0x6e, 0xc4, 0x6f, 0x1e, 0x6f, 0x78, 0x6f, 0xd1, + 0x70, 0x2b, 0x70, 0x86, 0x70, 0xe0, 0x71, 0x3a, 0x71, 0x95, 0x71, 0xf0, 0x72, 0x4b, 0x72, + 0xa6, 0x73, 0x1, 0x73, 0x5d, 0x73, 0xb8, 0x74, 0x14, 0x74, 0x70, 0x74, 0xcc, 0x75, 0x28, + 0x75, 0x85, 0x75, 0xe1, 0x76, 0x3e, 0x76, 0x9b, 0x76, 0xf8, 0x77, 0x56, 0x77, 0xb3, 0x78, + 0x11, 0x78, 0x6e, 0x78, 0xcc, 0x79, 0x2a, 0x79, 0x89, 0x79, 0xe7, 0x7a, 0x46, 0x7a, 0xa5, + 0x7b, 0x4, 0x7b, 0x63, 0x7b, 0xc2, 0x7c, 0x21, 0x7c, 0x81, 0x7c, 0xe1, 0x7d, 0x41, 0x7d, + 0xa1, 0x7e, 0x1, 0x7e, 0x62, 0x7e, 0xc2, 0x7f, 0x23, 0x7f, 0x84, 0x7f, 0xe5, 0x80, 0x47, + 0x80, 0xa8, 0x81, 0xa, 0x81, 0x6b, 0x81, 0xcd, 0x82, 0x30, 0x82, 0x92, 0x82, 0xf4, 0x83, + 0x57, 0x83, 0xba, 0x84, 0x1d, 0x84, 0x80, 0x84, 0xe3, 0x85, 0x47, 0x85, 0xab, 0x86, 0xe, + 0x86, 0x72, 0x86, 0xd7, 0x87, 0x3b, 0x87, 0x9f, 0x88, 0x4, 0x88, 0x69, 0x88, 0xce, 0x89, + 0x33, 0x89, 0x99, 0x89, 0xfe, 0x8a, 0x64, 0x8a, 0xca, 0x8b, 0x30, 0x8b, 0x96, 0x8b, 0xfc, + 0x8c, 0x63, 0x8c, 0xca, 0x8d, 0x31, 0x8d, 0x98, 0x8d, 0xff, 0x8e, 0x66, 0x8e, 0xce, 0x8f, + 0x36, 0x8f, 0x9e, 0x90, 0x6, 0x90, 0x6e, 0x90, 0xd6, 0x91, 0x3f, 0x91, 0xa8, 0x92, 0x11, + 0x92, 0x7a, 0x92, 0xe3, 0x93, 0x4d, 0x93, 0xb6, 0x94, 0x20, 0x94, 0x8a, 0x94, 0xf4, 0x95, + 0x5f, 0x95, 0xc9, 0x96, 0x34, 0x96, 0x9f, 0x97, 0xa, 0x97, 0x75, 0x97, 0xe0, 0x98, 0x4c, + 0x98, 0xb8, 0x99, 0x24, 0x99, 0x90, 0x99, 0xfc, 0x9a, 0x68, 0x9a, 0xd5, 0x9b, 0x42, 0x9b, + 0xaf, 0x9c, 0x1c, 0x9c, 0x89, 0x9c, 0xf7, 0x9d, 0x64, 0x9d, 0xd2, 0x9e, 0x40, 0x9e, 0xae, + 0x9f, 0x1d, 0x9f, 0x8b, 0x9f, 0xfa, 0xa0, 0x69, 0xa0, 0xd8, 0xa1, 0x47, 0xa1, 0xb6, 0xa2, + 0x26, 0xa2, 0x96, 0xa3, 0x6, 0xa3, 0x76, 0xa3, 0xe6, 0xa4, 0x56, 0xa4, 0xc7, 0xa5, 0x38, + 0xa5, 0xa9, 0xa6, 0x1a, 0xa6, 0x8b, 0xa6, 0xfd, 0xa7, 0x6e, 0xa7, 0xe0, 0xa8, 0x52, 0xa8, + 0xc4, 0xa9, 0x37, 0xa9, 0xa9, 0xaa, 0x1c, 0xaa, 0x8f, 0xab, 0x2, 0xab, 0x75, 0xab, 0xe9, + 0xac, 0x5c, 0xac, 0xd0, 0xad, 0x44, 0xad, 0xb8, 0xae, 0x2d, 0xae, 0xa1, 0xaf, 0x16, 0xaf, + 0x8b, 0xb0, 0x00, 0xb0, 0x75, 0xb0, 0xea, 0xb1, 0x60, 0xb1, 0xd6, 0xb2, 0x4b, 0xb2, 0xc2, + 0xb3, 0x38, 0xb3, 0xae, 0xb4, 0x25, 0xb4, 0x9c, 0xb5, 0x13, 0xb5, 0x8a, 0xb6, 0x1, 0xb6, + 0x79, 0xb6, 0xf0, 0xb7, 0x68, 0xb7, 0xe0, 0xb8, 0x59, 0xb8, 0xd1, 0xb9, 0x4a, 0xb9, 0xc2, + 0xba, 0x3b, 0xba, 0xb5, 0xbb, 0x2e, 0xbb, 0xa7, 0xbc, 0x21, 0xbc, 0x9b, 0xbd, 0x15, 0xbd, + 0x8f, 0xbe, 0xa, 0xbe, 0x84, 0xbe, 0xff, 0xbf, 0x7a, 0xbf, 0xf5, 0xc0, 0x70, 0xc0, 0xec, + 0xc1, 0x67, 0xc1, 0xe3, 0xc2, 0x5f, 0xc2, 0xdb, 0xc3, 0x58, 0xc3, 0xd4, 0xc4, 0x51, 0xc4, + 0xce, 0xc5, 0x4b, 0xc5, 0xc8, 0xc6, 0x46, 0xc6, 0xc3, 0xc7, 0x41, 0xc7, 0xbf, 0xc8, 0x3d, + 0xc8, 0xbc, 0xc9, 0x3a, 0xc9, 0xb9, 0xca, 0x38, 0xca, 0xb7, 0xcb, 0x36, 0xcb, 0xb6, 0xcc, + 0x35, 0xcc, 0xb5, 0xcd, 0x35, 0xcd, 0xb5, 0xce, 0x36, 0xce, 0xb6, 0xcf, 0x37, 0xcf, 0xb8, + 0xd0, 0x39, 0xd0, 0xba, 0xd1, 0x3c, 0xd1, 0xbe, 0xd2, 0x3f, 0xd2, 0xc1, 0xd3, 0x44, 0xd3, + 0xc6, 0xd4, 0x49, 0xd4, 0xcb, 0xd5, 0x4e, 0xd5, 0xd1, 0xd6, 0x55, 0xd6, 0xd8, 0xd7, 0x5c, + 0xd7, 0xe0, 0xd8, 0x64, 0xd8, 0xe8, 0xd9, 0x6c, 0xd9, 0xf1, 0xda, 0x76, 0xda, 0xfb, 0xdb, + 0x80, 0xdc, 0x5, 0xdc, 0x8a, 0xdd, 0x10, 0xdd, 0x96, 0xde, 0x1c, 0xde, 0xa2, 0xdf, 0x29, + 0xdf, 0xaf, 0xe0, 0x36, 0xe0, 0xbd, 0xe1, 0x44, 0xe1, 0xcc, 0xe2, 0x53, 0xe2, 0xdb, 0xe3, + 0x63, 0xe3, 0xeb, 0xe4, 0x73, 0xe4, 0xfc, 0xe5, 0x84, 0xe6, 0xd, 0xe6, 0x96, 0xe7, 0x1f, + 0xe7, 0xa9, 0xe8, 0x32, 0xe8, 0xbc, 0xe9, 0x46, 0xe9, 0xd0, 0xea, 0x5b, 0xea, 0xe5, 0xeb, + 0x70, 0xeb, 0xfb, 0xec, 0x86, 0xed, 0x11, 0xed, 0x9c, 0xee, 0x28, 0xee, 0xb4, 0xef, 0x40, + 0xef, 0xcc, 0xf0, 0x58, 0xf0, 0xe5, 0xf1, 0x72, 0xf1, 0xff, 0xf2, 0x8c, 0xf3, 0x19, 0xf3, + 0xa7, 0xf4, 0x34, 0xf4, 0xc2, 0xf5, 0x50, 0xf5, 0xde, 0xf6, 0x6d, 0xf6, 0xfb, 0xf7, 0x8a, + 0xf8, 0x19, 0xf8, 0xa8, 0xf9, 0x38, 0xf9, 0xc7, 0xfa, 0x57, 0xfa, 0xe7, 0xfb, 0x77, 0xfc, + 0x7, 0xfc, 0x98, 0xfd, 0x29, 0xfd, 0xba, 0xfe, 0x4b, 0xfe, 0xdc, 0xff, 0x6d, 0xff, 0xff, + 0x38, 0x42, 0x49, 0x4d, 0x4, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4, 0x00, 0x00, 0x00, + 0x2, 0x38, 0x42, 0x49, 0x4d, 0x4, 0xc, 0x00, 0x00, 0x00, 0x00, 0x6, 0xb9, 0x00, 0x00, + 0x00, 0x1, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x1, 0x80, 0x00, + 0x00, 0xc0, 0x00, 0x00, 0x00, 0x6, 0x9d, 0x00, 0x18, 0x00, 0x1, 0xff, 0xd8, 0xff, 0xed, + 0x00, 0xc, 0x41, 0x64, 0x6f, 0x62, 0x65, 0x5f, 0x43, 0x4d, 0x00, 0x1, 0xff, 0xee, 0x00, + 0xe, 0x41, 0x64, 0x6f, 0x62, 0x65, 0x00, 0x64, 0x80, 0x00, 0x00, 0x00, 0x1, 0xff, 0xdb, + 0x00, 0x84, 0x00, 0xc, 0x8, 0x8, 0x8, 0x9, 0x8, 0xc, 0x9, 0x9, 0xc, 0x11, 0xb, + 0xa, 0xb, 0x11, 0x15, 0xf, 0xc, 0xc, 0xf, 0x15, 0x18, 0x13, 0x13, 0x15, 0x13, 0x13, + 0x18, 0x11, 0xc, 0xc, 0xc, 0xc, 0xc, 0xc, 0x11, 0xc, 0xc, 0xc, 0xc, 0xc, 0xc, + 0xc, 0xc, 0xc, 0xc, 0xc, 0xc, 0xc, 0xc, 0xc, 0xc, 0xc, 0xc, 0xc, 0xc, 0xc, + 0xc, 0xc, 0xc, 0xc, 0xc, 0xc, 0xc, 0x1, 0xd, 0xb, 0xb, 0xd, 0xe, 0xd, 0x10, + 0xe, 0xe, 0x10, 0x14, 0xe, 0xe, 0xe, 0x14, 0x14, 0xe, 0xe, 0xe, 0xe, 0x14, 0x11, + 0xc, 0xc, 0xc, 0xc, 0xc, 0x11, 0x11, 0xc, 0xc, 0xc, 0xc, 0xc, 0xc, 0x11, 0xc, + 0xc, 0xc, 0xc, 0xc, 0xc, 0xc, 0xc, 0xc, 0xc, 0xc, 0xc, 0xc, 0xc, 0xc, 0xc, + 0xc, 0xc, 0xc, 0xc, 0xc, 0xc, 0xc, 0xc, 0xc, 0xc, 0xc, 0xc, 0xff, 0xc0, 0x00, + 0x11, 0x8, 0x00, 0x80, 0x00, 0x80, 0x3, 0x1, 0x22, 0x00, 0x2, 0x11, 0x1, 0x3, 0x11, + 0x1, 0xff, 0xdd, 0x00, 0x4, 0x00, 0x8, 0xff, 0xc4, 0x1, 0x3f, 0x00, 0x00, 0x1, 0x5, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3, 0x00, + 0x1, 0x2, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0x1, 0x00, 0x1, 0x5, 0x1, + 0x1, 0x1, 0x1, 0x1, 0x1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1, 0x00, 0x2, + 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0x10, 0x00, 0x1, 0x4, 0x1, 0x3, + 0x2, 0x4, 0x2, 0x5, 0x7, 0x6, 0x8, 0x5, 0x3, 0xc, 0x33, 0x1, 0x00, 0x2, 0x11, + 0x3, 0x4, 0x21, 0x12, 0x31, 0x5, 0x41, 0x51, 0x61, 0x13, 0x22, 0x71, 0x81, 0x32, 0x6, + 0x14, 0x91, 0xa1, 0xb1, 0x42, 0x23, 0x24, 0x15, 0x52, 0xc1, 0x62, 0x33, 0x34, 0x72, 0x82, + 0xd1, 0x43, 0x7, 0x25, 0x92, 0x53, 0xf0, 0xe1, 0xf1, 0x63, 0x73, 0x35, 0x16, 0xa2, 0xb2, + 0x83, 0x26, 0x44, 0x93, 0x54, 0x64, 0x45, 0xc2, 0xa3, 0x74, 0x36, 0x17, 0xd2, 0x55, 0xe2, + 0x65, 0xf2, 0xb3, 0x84, 0xc3, 0xd3, 0x75, 0xe3, 0xf3, 0x46, 0x27, 0x94, 0xa4, 0x85, 0xb4, + 0x95, 0xc4, 0xd4, 0xe4, 0xf4, 0xa5, 0xb5, 0xc5, 0xd5, 0xe5, 0xf5, 0x56, 0x66, 0x76, 0x86, + 0x96, 0xa6, 0xb6, 0xc6, 0xd6, 0xe6, 0xf6, 0x37, 0x47, 0x57, 0x67, 0x77, 0x87, 0x97, 0xa7, + 0xb7, 0xc7, 0xd7, 0xe7, 0xf7, 0x11, 0x00, 0x2, 0x2, 0x1, 0x2, 0x4, 0x4, 0x3, 0x4, + 0x5, 0x6, 0x7, 0x7, 0x6, 0x5, 0x35, 0x1, 0x00, 0x2, 0x11, 0x3, 0x21, 0x31, 0x12, + 0x4, 0x41, 0x51, 0x61, 0x71, 0x22, 0x13, 0x5, 0x32, 0x81, 0x91, 0x14, 0xa1, 0xb1, 0x42, + 0x23, 0xc1, 0x52, 0xd1, 0xf0, 0x33, 0x24, 0x62, 0xe1, 0x72, 0x82, 0x92, 0x43, 0x53, 0x15, + 0x63, 0x73, 0x34, 0xf1, 0x25, 0x6, 0x16, 0xa2, 0xb2, 0x83, 0x7, 0x26, 0x35, 0xc2, 0xd2, + 0x44, 0x93, 0x54, 0xa3, 0x17, 0x64, 0x45, 0x55, 0x36, 0x74, 0x65, 0xe2, 0xf2, 0xb3, 0x84, + 0xc3, 0xd3, 0x75, 0xe3, 0xf3, 0x46, 0x94, 0xa4, 0x85, 0xb4, 0x95, 0xc4, 0xd4, 0xe4, 0xf4, + 0xa5, 0xb5, 0xc5, 0xd5, 0xe5, 0xf5, 0x56, 0x66, 0x76, 0x86, 0x96, 0xa6, 0xb6, 0xc6, 0xd6, + 0xe6, 0xf6, 0x27, 0x37, 0x47, 0x57, 0x67, 0x77, 0x87, 0x97, 0xa7, 0xb7, 0xc7, 0xff, 0xda, + 0x00, 0xc, 0x3, 0x1, 0x00, 0x2, 0x11, 0x3, 0x11, 0x00, 0x3f, 0x00, 0xd3, 0x49, 0x25, + 0xce, 0xac, 0x3f, 0x81, 0x7c, 0xb, 0xfd, 0x29, 0xef, 0xfe, 0xbf, 0xd8, 0xf6, 0x3d, 0xbf, + 0xf2, 0x7e, 0xf7, 0x1f, 0xbd, 0xee, 0x7f, 0xac, 0xc5, 0xc3, 0xc3, 0xed, 0x38, 0x9f, 0xf1, + 0x7b, 0xfe, 0x2f, 0x7f, 0xa6, 0x3e, 0xf1, 0xfd, 0x23, 0xee, 0xff, 0x00, 0x77, 0xf6, 0xff, + 0x00, 0xc9, 0xfb, 0xdc, 0x7e, 0xf7, 0xb9, 0xfe, 0xb3, 0xf, 0xf, 0xf, 0xb2, 0xf4, 0x49, + 0x2e, 0x75, 0x25, 0xb5, 0xff, 0x00, 0x21, 0xbf, 0xf2, 0xbb, 0xff, 0x00, 0x18, 0xff, 0x00, + 0xd7, 0xee, 0xff, 0x00, 0xfe, 0x47, 0x9f, 0xf9, 0xf0, 0xff, 0x00, 0xdb, 0x6f, 0xfe, 0x19, + 0x7a, 0x24, 0x97, 0x3a, 0x92, 0x5f, 0xf2, 0x1b, 0xff, 0x00, 0x2b, 0xbf, 0xf1, 0x8f, 0xfd, + 0x7e, 0xaf, 0xfc, 0x8f, 0x3f, 0xf3, 0xe1, 0xff, 0x00, 0xb6, 0xdf, 0xfc, 0x32, 0xf4, 0x49, + 0x2e, 0x75, 0x24, 0xbf, 0xe4, 0x37, 0xfe, 0x57, 0x7f, 0xe3, 0x1f, 0xfa, 0xfd, 0x5f, 0xf9, + 0x1e, 0x7f, 0xe7, 0xc3, 0xff, 0x00, 0x6d, 0xbf, 0xf8, 0x65, 0xe8, 0x92, 0x5c, 0xea, 0x49, + 0x7f, 0xc8, 0x6f, 0xfc, 0xae, 0xff, 0x00, 0xc6, 0x3f, 0xf5, 0xfa, 0xbf, 0xf2, 0x3c, 0xff, + 0x00, 0xcf, 0x87, 0xfe, 0xdb, 0x7f, 0xf0, 0xcb, 0xd1, 0x24, 0xb9, 0xd4, 0x92, 0xff, 0x00, + 0x90, 0xdf, 0xf9, 0x5d, 0xff, 0x00, 0x8c, 0x7f, 0xeb, 0xf5, 0x7f, 0xe4, 0x79, 0xff, 0x00, + 0x9f, 0xf, 0xfd, 0xb6, 0xff, 0x00, 0xe1, 0x97, 0xa2, 0x49, 0x73, 0xa9, 0x25, 0xff, 0x00, + 0x21, 0xbf, 0xf2, 0xbb, 0xff, 0x00, 0x18, 0xff, 0x00, 0xd7, 0xea, 0xff, 0x00, 0xc8, 0xf3, + 0xff, 0x00, 0x3e, 0x1f, 0xfb, 0x6d, 0xff, 0x00, 0xc3, 0x2f, 0x44, 0x92, 0xe7, 0x57, 0x44, + 0xb1, 0x7e, 0x3b, 0xf0, 0x2f, 0xf4, 0x5f, 0xb1, 0xfa, 0xff, 0x00, 0x7f, 0xdf, 0xf7, 0x3f, + 0xc9, 0xfb, 0x3c, 0x1e, 0xcf, 0xb7, 0xfe, 0xb3, 0x2f, 0x17, 0x17, 0xba, 0xe0, 0x7f, 0xc6, + 0x1f, 0xf8, 0xbd, 0xfe, 0x87, 0xfb, 0xbf, 0xf4, 0x8f, 0xbc, 0x7d, 0xe3, 0xdc, 0xff, 0x00, + 0x27, 0xec, 0xf0, 0x7b, 0x3e, 0xdf, 0xfa, 0xcc, 0xdc, 0x5c, 0x5e, 0xf3, 0xff, 0xd0, 0xd3, + 0x5c, 0xea, 0xe8, 0x97, 0x3a, 0x9b, 0xff, 0x00, 0x11, 0xbf, 0xf0, 0x77, 0xfd, 0x43, 0xff, + 0x00, 0x53, 0xb2, 0x7f, 0xd6, 0xf3, 0xff, 0x00, 0x2e, 0x1f, 0xfa, 0x6d, 0xff, 0x00, 0xbf, + 0x2a, 0x49, 0x24, 0x97, 0x6c, 0xf7, 0xca, 0x49, 0x24, 0x92, 0x52, 0x92, 0x49, 0x24, 0x94, + 0xa4, 0x92, 0x49, 0x25, 0x29, 0x24, 0x92, 0x49, 0x4a, 0x49, 0x24, 0x92, 0x52, 0x97, 0x44, + 0xb9, 0xd5, 0xd1, 0x2e, 0x27, 0xfe, 0x3c, 0xff, 0x00, 0xe0, 0x1f, 0xfa, 0xbf, 0xfe, 0xa0, + 0x78, 0x1f, 0xfa, 0xe1, 0xff, 0x00, 0xe5, 0xbf, 0xff, 0x00, 0x4e, 0x7f, 0xf7, 0xd9, 0xff, + 0xd1, 0xd3, 0x5c, 0xea, 0xe8, 0x97, 0x3a, 0x9b, 0xff, 0x00, 0x11, 0xbf, 0xf0, 0x77, 0xfd, + 0x43, 0xff, 0x00, 0x53, 0xb2, 0x7f, 0xd6, 0xf3, 0xff, 0x00, 0x2e, 0x1f, 0xfa, 0x6d, 0xff, + 0x00, 0xbf, 0x2a, 0x49, 0x24, 0x97, 0x6c, 0xf7, 0xca, 0x49, 0x24, 0x92, 0x52, 0x92, 0x49, + 0x24, 0x94, 0xa4, 0x92, 0x49, 0x25, 0x29, 0x24, 0x92, 0x49, 0x4a, 0x49, 0x24, 0x92, 0x52, + 0x97, 0x44, 0xb9, 0xd5, 0xd1, 0x2e, 0x27, 0xfe, 0x3c, 0xff, 0x00, 0xe0, 0x1f, 0xfa, 0xbf, + 0xfe, 0xa0, 0x78, 0x1f, 0xfa, 0xe1, 0xff, 0x00, 0xe5, 0xbf, 0xff, 0x00, 0x4e, 0x7f, 0xf7, + 0xd9, 0xff, 0xd2, 0xd3, 0x5c, 0xea, 0xe8, 0x97, 0x3a, 0x9b, 0xff, 0x00, 0x11, 0xbf, 0xf0, + 0x77, 0xfd, 0x43, 0xff, 0x00, 0x53, 0xb2, 0x7f, 0xd6, 0xf3, 0xff, 0x00, 0x2e, 0x1f, 0xfa, + 0x6d, 0xff, 0x00, 0xbf, 0x2a, 0x49, 0x24, 0x97, 0x6c, 0xf7, 0xca, 0x49, 0x24, 0x92, 0x52, + 0x92, 0x49, 0x24, 0x94, 0xa4, 0x92, 0x49, 0x25, 0x29, 0x24, 0x92, 0x49, 0x4a, 0x49, 0x24, + 0x92, 0x52, 0x97, 0x44, 0xb9, 0xd5, 0xd1, 0x2e, 0x27, 0xfe, 0x3c, 0xff, 0x00, 0xe0, 0x1f, + 0xfa, 0xbf, 0xfe, 0xa0, 0x78, 0x1f, 0xfa, 0xe1, 0xff, 0x00, 0xe5, 0xbf, 0xff, 0x00, 0x4e, + 0x7f, 0xf7, 0xd9, 0xff, 0xd3, 0xd3, 0x5c, 0xea, 0xe8, 0x97, 0x3a, 0x9b, 0xff, 0x00, 0x11, + 0xbf, 0xf0, 0x77, 0xfd, 0x43, 0xff, 0x00, 0x53, 0xb2, 0x7f, 0xd6, 0xf3, 0xff, 0x00, 0x2e, + 0x1f, 0xfa, 0x6d, 0xff, 0x00, 0xbf, 0x2a, 0x49, 0x24, 0x97, 0x6c, 0xf7, 0xca, 0x49, 0x24, + 0x92, 0x52, 0x92, 0x49, 0x24, 0x94, 0xa4, 0x92, 0x49, 0x25, 0x29, 0x24, 0x92, 0x49, 0x4a, + 0x49, 0x24, 0x92, 0x52, 0x97, 0x44, 0xb9, 0xd5, 0xd1, 0x2e, 0x27, 0xfe, 0x3c, 0xff, 0x00, + 0xe0, 0x1f, 0xfa, 0xbf, 0xfe, 0xa0, 0x78, 0x1f, 0xfa, 0xe1, 0xff, 0x00, 0xe5, 0xbf, 0xff, + 0x00, 0x4e, 0x7f, 0xf7, 0xd9, 0xff, 0xd4, 0xd3, 0x5c, 0xea, 0xe8, 0x97, 0x3a, 0x9b, 0xff, + 0x00, 0x11, 0xbf, 0xf0, 0x77, 0xfd, 0x43, 0xff, 0x00, 0x53, 0xb2, 0x7f, 0xd6, 0xf3, 0xff, + 0x00, 0x2e, 0x1f, 0xfa, 0x6d, 0xff, 0x00, 0xbf, 0x2a, 0x49, 0x24, 0x97, 0x6c, 0xf7, 0xca, + 0x49, 0x24, 0x92, 0x52, 0x92, 0x49, 0x24, 0x94, 0xa4, 0x92, 0x49, 0x25, 0x29, 0x24, 0x92, + 0x49, 0x4a, 0x49, 0x24, 0x92, 0x52, 0x97, 0x44, 0xb9, 0xd5, 0xd1, 0x2e, 0x27, 0xfe, 0x3c, + 0xff, 0x00, 0xe0, 0x1f, 0xfa, 0xbf, 0xfe, 0xa0, 0x78, 0x1f, 0xfa, 0xe1, 0xff, 0x00, 0xe5, + 0xbf, 0xff, 0x00, 0x4e, 0x7f, 0xf7, 0xd9, 0xff, 0xd5, 0xd3, 0x5c, 0xea, 0xe8, 0x97, 0x3a, + 0x9b, 0xff, 0x00, 0x11, 0xbf, 0xf0, 0x77, 0xfd, 0x43, 0xff, 0x00, 0x53, 0xb2, 0x7f, 0xd6, + 0xf3, 0xff, 0x00, 0x2e, 0x1f, 0xfa, 0x6d, 0xff, 0x00, 0xbf, 0x2a, 0x49, 0x24, 0x97, 0x6c, + 0xf7, 0xca, 0x49, 0x24, 0x92, 0x52, 0x92, 0x49, 0x24, 0x94, 0xa4, 0x92, 0x49, 0x25, 0x29, + 0x24, 0x92, 0x49, 0x4a, 0x49, 0x24, 0x92, 0x52, 0x97, 0x44, 0xb9, 0xd5, 0xd1, 0x2e, 0x27, + 0xfe, 0x3c, 0xff, 0x00, 0xe0, 0x1f, 0xfa, 0xbf, 0xfe, 0xa0, 0x78, 0x1f, 0xfa, 0xe1, 0xff, + 0x00, 0xe5, 0xbf, 0xff, 0x00, 0x4e, 0x7f, 0xf7, 0xd9, 0xff, 0xd6, 0xd3, 0x5c, 0xea, 0xe8, + 0x92, 0x58, 0x7f, 0x2, 0xf8, 0xef, 0xfa, 0x2f, 0xdf, 0xfd, 0x47, 0xbf, 0xef, 0xfb, 0x7f, + 0xe5, 0x3d, 0x9e, 0xf, 0x67, 0xdc, 0xff, 0x00, 0x57, 0x97, 0x8b, 0x8b, 0xdd, 0x71, 0x3f, + 0xe2, 0xf7, 0xfc, 0x61, 0xff, 0x00, 0x43, 0xfd, 0xe3, 0xfa, 0x3f, 0xde, 0x3e, 0xf1, 0xed, + 0xff, 0x00, 0x94, 0xf6, 0x78, 0x3d, 0x9f, 0x73, 0xfd, 0x5e, 0x6e, 0x2e, 0x2f, 0x79, 0xe7, + 0x52, 0x5d, 0x12, 0x4b, 0x6b, 0xfe, 0x5c, 0xff, 0x00, 0xe5, 0xf, 0xfe, 0x3f, 0xff, 0x00, + 0xae, 0x1d, 0xff, 0x00, 0xfc, 0x90, 0xff, 0x00, 0xf3, 0xdf, 0xff, 0x00, 0xb7, 0x3f, 0xfc, + 0x2c, 0xf3, 0xa9, 0x2e, 0x89, 0x24, 0xbf, 0xe5, 0xcf, 0xfe, 0x50, 0xff, 0x00, 0xe3, 0xff, + 0x00, 0xfa, 0xe1, 0x5f, 0xf9, 0x21, 0xff, 0x00, 0xe7, 0xbf, 0xff, 0x00, 0x6e, 0x7f, 0xf8, + 0x59, 0xe7, 0x52, 0x5d, 0x12, 0x49, 0x7f, 0xcb, 0x9f, 0xfc, 0xa1, 0xff, 0x00, 0xc7, 0xff, + 0x00, 0xf5, 0xc2, 0xbf, 0xf2, 0x43, 0xff, 0x00, 0xcf, 0x7f, 0xfe, 0xdc, 0xff, 0x00, 0xf0, + 0xb3, 0xce, 0xa4, 0xba, 0x24, 0x92, 0xff, 0x00, 0x97, 0x3f, 0xf9, 0x43, 0xff, 0x00, 0x8f, + 0xff, 0x00, 0xeb, 0x85, 0x7f, 0xe4, 0x87, 0xff, 0x00, 0x9e, 0xff, 0x00, 0xfd, 0xb9, 0xff, + 0x00, 0xe1, 0x67, 0x9d, 0x49, 0x74, 0x49, 0x25, 0xff, 0x00, 0x2e, 0x7f, 0xf2, 0x87, 0xff, + 0x00, 0x1f, 0xff, 0x00, 0xd7, 0xa, 0xff, 0x00, 0xc9, 0xf, 0xff, 0x00, 0x3d, 0xff, 0x00, + 0xfb, 0x73, 0xff, 0x00, 0xc2, 0xcf, 0x3a, 0x92, 0xe8, 0x92, 0x4b, 0xfe, 0x5c, 0xff, 0x00, + 0xe5, 0xf, 0xfe, 0x3f, 0xff, 0x00, 0xae, 0x15, 0xff, 0x00, 0x92, 0x1f, 0xfe, 0x7b, 0xff, + 0x00, 0xf6, 0xe7, 0xff, 0x00, 0x85, 0x9e, 0x75, 0x74, 0x49, 0x24, 0xb1, 0x7e, 0x3b, 0xf1, + 0xdf, 0xf4, 0xa7, 0xb1, 0xfa, 0x8f, 0x63, 0xd8, 0xf7, 0x3f, 0xca, 0x7b, 0xdc, 0x7e, 0xf7, + 0xb7, 0xfe, 0xaf, 0x17, 0xf, 0xf, 0xb4, 0xe0, 0x7f, 0xc6, 0x1f, 0xf8, 0xc3, 0xfe, 0x98, + 0xfb, 0xbf, 0xf4, 0x7f, 0xbb, 0xfd, 0xdf, 0xdc, 0xff, 0x00, 0x29, 0xef, 0x71, 0xfb, 0xde, + 0xdf, 0xfa, 0xbc, 0x3c, 0x3c, 0x3e, 0xcb, 0xff, 0xd9, 0x00, 0x38, 0x42, 0x49, 0x4d, 0x4, + 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x53, 0x00, 0x00, 0x00, 0x1, 0x1, 0x00, 0x00, 0x00, + 0xf, 0x00, 0x41, 0x00, 0x64, 0x00, 0x6f, 0x00, 0x62, 0x00, 0x65, 0x00, 0x20, 0x00, 0x50, + 0x00, 0x68, 0x00, 0x6f, 0x00, 0x74, 0x00, 0x6f, 0x00, 0x73, 0x00, 0x68, 0x00, 0x6f, 0x00, + 0x70, 0x00, 0x00, 0x00, 0x12, 0x00, 0x41, 0x00, 0x64, 0x00, 0x6f, 0x00, 0x62, 0x00, 0x65, + 0x00, 0x20, 0x00, 0x50, 0x00, 0x68, 0x00, 0x6f, 0x00, 0x74, 0x00, 0x6f, 0x00, 0x73, 0x00, + 0x68, 0x00, 0x6f, 0x00, 0x70, 0x00, 0x20, 0x00, 0x43, 0x00, 0x43, 0x00, 0x00, 0x00, 0x1, + 0x00, 0x38, 0x42, 0x49, 0x4d, 0x4, 0x22, 0x00, 0x00, 0x00, 0x00, 0x1, 0x2e, 0x4d, 0x4d, + 0x00, 0x2a, 0x00, 0x00, 0x00, 0x8, 0x00, 0x7, 0x1, 0x12, 0x00, 0x3, 0x00, 0x00, 0x00, + 0x1, 0x00, 0x1, 0x00, 0x00, 0x1, 0x1a, 0x00, 0x5, 0x00, 0x00, 0x00, 0x1, 0x00, 0x00, + 0x00, 0x62, 0x1, 0x1b, 0x00, 0x5, 0x00, 0x00, 0x00, 0x1, 0x00, 0x00, 0x00, 0x6a, 0x1, + 0x28, 0x00, 0x3, 0x00, 0x00, 0x00, 0x1, 0x00, 0x2, 0x00, 0x00, 0x1, 0x31, 0x00, 0x2, + 0x00, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x00, 0x72, 0x1, 0x32, 0x00, 0x2, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x8f, 0x87, 0x69, 0x00, 0x4, 0x00, 0x00, 0x00, 0x1, 0x00, 0x00, + 0x00, 0xa4, 0x00, 0x00, 0x00, 0xd0, 0x00, 0xa, 0xfc, 0x80, 0x00, 0x00, 0x27, 0x10, 0x00, + 0xa, 0xfc, 0x80, 0x00, 0x00, 0x27, 0x10, 0x41, 0x64, 0x6f, 0x62, 0x65, 0x20, 0x50, 0x68, + 0x6f, 0x74, 0x6f, 0x73, 0x68, 0x6f, 0x70, 0x20, 0x43, 0x43, 0x20, 0x28, 0x57, 0x69, 0x6e, + 0x64, 0x6f, 0x77, 0x73, 0x29, 0x00, 0x32, 0x30, 0x31, 0x34, 0x3a, 0x30, 0x36, 0x3a, 0x30, + 0x35, 0x20, 0x31, 0x33, 0x3a, 0x32, 0x34, 0x3a, 0x30, 0x34, 0x00, 0x00, 0x00, 0x3, 0xa0, + 0x1, 0x00, 0x3, 0x00, 0x00, 0x00, 0x1, 0x00, 0x1, 0x00, 0x00, 0xa0, 0x2, 0x00, 0x4, + 0x00, 0x00, 0x00, 0x1, 0x00, 0x00, 0x00, 0x80, 0xa0, 0x3, 0x00, 0x4, 0x00, 0x00, 0x00, + 0x1, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6, 0x1, 0x3, + 0x00, 0x3, 0x00, 0x00, 0x00, 0x1, 0x00, 0x6, 0x00, 0x00, 0x1, 0x1a, 0x00, 0x5, 0x00, + 0x00, 0x00, 0x1, 0x00, 0x00, 0x1, 0x1e, 0x1, 0x1b, 0x00, 0x5, 0x00, 0x00, 0x00, 0x1, + 0x00, 0x00, 0x1, 0x26, 0x1, 0x28, 0x00, 0x3, 0x00, 0x00, 0x00, 0x1, 0x00, 0x2, 0x00, + 0x00, 0x2, 0x1, 0x00, 0x4, 0x00, 0x00, 0x00, 0x1, 0x00, 0x00, 0x1, 0x2e, 0x2, 0x2, + 0x00, 0x4, 0x00, 0x00, 0x00, 0x1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x1, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x1, + 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x42, 0x49, + 0x4d, 0x46, 0x4d, 0x73, 0x6b, 0x00, 0x00, 0x00, 0xc, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x1, 0x00, 0x2, 0x00, 0x2, 0x00, 0x2, 0x00, + 0x2, 0x00, 0x2, 0x00, 0x2, 0x00, 0x2, 0x00, 0x2, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, + 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, + 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, + 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, + 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, + 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, + 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, + 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, + 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, + 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, + 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, + 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, + 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, + 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, + 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, + 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x2, 0x00, 0x2, 0x00, 0x2, 0x00, + 0x2, 0x00, 0x2, 0x00, 0x2, 0x00, 0x2, 0x00, 0x2, 0x00, 0x2, 0x00, 0x2, 0x00, 0x2, + 0x00, 0x2, 0x00, 0x2, 0x00, 0x2, 0x00, 0x2, 0x00, 0x2, 0x00, 0x6, 0x00, 0x6, 0x00, + 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, + 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, + 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, + 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, + 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, + 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, + 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, + 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, + 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, + 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, + 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, + 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, + 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, + 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, + 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x2, 0x00, 0x2, 0x00, 0x2, + 0x00, 0x2, 0x00, 0x2, 0x00, 0x2, 0x00, 0x2, 0x00, 0x2, 0x00, 0x2, 0x00, 0x2, 0x00, + 0x2, 0x00, 0x2, 0x00, 0x2, 0x00, 0x2, 0x00, 0x2, 0x00, 0x2, 0x00, 0x6, 0x00, 0x6, + 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, + 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, + 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, + 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, + 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, + 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, + 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, + 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, + 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, + 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, + 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, + 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, + 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, + 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, + 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x6, 0x00, 0x2, 0x00, 0x2, 0x00, + 0x2, 0x00, 0x2, 0x00, 0x2, 0x00, 0x2, 0x00, 0x2, 0x00, 0x2, 0x00, 0x2, 0x00, 0x2, + 0x00, 0x2, 0x00, 0x2, 0x00, 0x2, 0x00, 0x2, 0x00, 0x2, 0x00, 0x2, 0x00, 0x2, 0x00, + 0x2, 0x00, 0x2, 0x00, 0x2, 0x00, 0x2, 0x00, 0x2, 0x00, 0x2, 0x00, 0x2, 0x00, 0x2, + 0x00, 0x2, 0x00, 0x2, 0x00, 0x2, 0x00, 0x2, 0x00, 0x2, 0x00, 0x2, 0x00, 0x2, 0x00, + 0x2, 0x00, 0x2, 0x00, 0x2, 0x00, 0x2, 0x00, 0x2, 0x00, 0x2, 0x00, 0x2, 0x00, 0x2, + 0x00, 0x2, 0x00, 0x2, 0x00, 0x2, 0x00, 0x2, 0x00, 0x2, 0x00, 0x2, 0x00, 0x2, 0x00, + 0x2, 0x00, 0x2, 0x00, 0x2, 0x00, 0x2, 0x00, 0x2, 0x00, 0x2, 0x00, 0x2, 0x00, 0x2, + 0x00, 0x2, 0x00, 0x2, 0x00, 0x2, 0x00, 0x2, 0x00, 0x2, 0x00, 0x2, 0x00, 0x2, 0x00, + 0x2, 0x00, 0x2, 0x00, 0x2, 0x00, 0x2, 0x00, 0x2, 0x00, 0x2, 0x00, 0x2, 0x00, 0x2, + 0x00, 0x2, 0x00, 0x2, 0x00, 0x2, 0x00, 0x2, 0x00, 0x2, 0x00, 0x2, 0x00, 0x2, 0x00, + 0x2, 0x00, 0x2, 0x00, 0x2, 0x00, 0x2, 0x00, 0x2, 0x00, 0x2, 0x00, 0x2, 0x00, 0x2, + 0x00, 0x2, 0x00, 0x2, 0x00, 0x2, 0x00, 0x2, 0x00, 0x2, 0x00, 0x2, 0x00, 0x2, 0x00, + 0x2, 0x00, 0x2, 0x00, 0x2, 0x00, 0x2, 0x00, 0x2, 0x00, 0x2, 0x00, 0x2, 0x00, 0x2, + 0x00, 0x2, 0x00, 0x2, 0x00, 0x2, 0x00, 0x2, 0x00, 0x2, 0x00, 0x2, 0x00, 0x2, 0x00, + 0x2, 0x00, 0x2, 0x00, 0x2, 0x00, 0x2, 0x00, 0x2, 0x00, 0x2, 0x00, 0x2, 0x00, 0x2, + 0x00, 0x2, 0x00, 0x2, 0x00, 0x2, 0x00, 0x2, 0x00, 0x2, 0x00, 0x2, 0x00, 0x2, 0x00, + 0x2, 0x00, 0x2, 0x00, 0x2, 0x00, 0x2, 0x00, 0x2, 0x00, 0x2, 0x00, 0x2, 0x00, 0x2, + 0x00, 0x2, 0x00, 0x2, 0x00, 0x2, 0x00, 0x2, 0x00, 0x2, 0x00, 0x2, 0x81, 0x18, 0x81, + 0x18, 0x81, 0x18, 0x81, 0x18, 0x81, 0x18, 0x81, 0x18, 0x81, 0x18, 0x81, 0x18, 0xf9, 0x18, + 0x91, 0xff, 0xf9, 0x18, 0xf9, 0x18, 0x91, 0xff, 0xf9, 0x18, 0xf9, 0x18, 0x91, 0xff, 0xf9, + 0x18, 0xf9, 0x18, 0x91, 0xff, 0xf9, 0x18, 0xf9, 0x18, 0x91, 0xff, 0xf9, 0x18, 0xf9, 0x18, + 0x91, 0xff, 0xf9, 0x18, 0xf9, 0x18, 0x91, 0xff, 0xf9, 0x18, 0xf9, 0x18, 0x91, 0xff, 0xf9, + 0x18, 0xf9, 0x18, 0x91, 0xff, 0xf9, 0x18, 0xf9, 0x18, 0x91, 0xff, 0xf9, 0x18, 0xf9, 0x18, + 0x91, 0xff, 0xf9, 0x18, 0xf9, 0x18, 0x91, 0xff, 0xf9, 0x18, 0xf9, 0x18, 0x91, 0xff, 0xf9, + 0x18, 0xf9, 0x18, 0x91, 0xff, 0xf9, 0x18, 0xf9, 0x18, 0x91, 0xff, 0xf9, 0x18, 0xf9, 0x18, + 0x91, 0xff, 0xf9, 0x18, 0xf9, 0x18, 0x91, 0xff, 0xf9, 0x18, 0xf9, 0x18, 0x91, 0xff, 0xf9, + 0x18, 0xf9, 0x18, 0x91, 0xff, 0xf9, 0x18, 0xf9, 0x18, 0x91, 0xff, 0xf9, 0x18, 0xf9, 0x18, + 0x91, 0xff, 0xf9, 0x18, 0xf9, 0x18, 0x91, 0xff, 0xf9, 0x18, 0xf9, 0x18, 0x91, 0xff, 0xf9, + 0x18, 0xf9, 0x18, 0x91, 0xff, 0xf9, 0x18, 0xf9, 0x18, 0x91, 0xff, 0xf9, 0x18, 0xf9, 0x18, + 0x91, 0xff, 0xf9, 0x18, 0xf9, 0x18, 0x91, 0xff, 0xf9, 0x18, 0xf9, 0x18, 0x91, 0xff, 0xf9, + 0x18, 0xf9, 0x18, 0x91, 0xff, 0xf9, 0x18, 0xf9, 0x18, 0x91, 0xff, 0xf9, 0x18, 0xf9, 0x18, + 0x91, 0xff, 0xf9, 0x18, 0xf9, 0x18, 0x91, 0xff, 0xf9, 0x18, 0xf9, 0x18, 0x91, 0xff, 0xf9, + 0x18, 0xf9, 0x18, 0x91, 0xff, 0xf9, 0x18, 0xf9, 0x18, 0x91, 0xff, 0xf9, 0x18, 0xf9, 0x18, + 0x91, 0xff, 0xf9, 0x18, 0xf9, 0x18, 0x91, 0xff, 0xf9, 0x18, 0xf9, 0x18, 0x91, 0xff, 0xf9, + 0x18, 0xf9, 0x18, 0x91, 0xff, 0xf9, 0x18, 0xf9, 0x18, 0x91, 0xff, 0xf9, 0x18, 0xf9, 0x18, + 0x91, 0xff, 0xf9, 0x18, 0xf9, 0x18, 0x91, 0xff, 0xf9, 0x18, 0xf9, 0x18, 0x91, 0xff, 0xf9, + 0x18, 0xf9, 0x18, 0x91, 0xff, 0xf9, 0x18, 0xf9, 0x18, 0x91, 0xff, 0xf9, 0x18, 0xf9, 0x18, + 0x91, 0xff, 0xf9, 0x18, 0xf9, 0x18, 0x91, 0xff, 0xf9, 0x18, 0xf9, 0x18, 0x91, 0xff, 0xf9, + 0x18, 0xf9, 0x18, 0x91, 0xff, 0xf9, 0x18, 0xf9, 0x18, 0x91, 0xff, 0xf9, 0x18, 0xf9, 0x18, + 0x91, 0xff, 0xf9, 0x18, 0xf9, 0x18, 0x91, 0xff, 0xf9, 0x18, 0xf9, 0x18, 0x91, 0xff, 0xf9, + 0x18, 0xf9, 0x18, 0x91, 0xff, 0xf9, 0x18, 0xf9, 0x18, 0x91, 0xff, 0xf9, 0x18, 0xf9, 0x18, + 0x91, 0xff, 0xf9, 0x18, 0xf9, 0x18, 0x91, 0xff, 0xf9, 0x18, 0xf9, 0x18, 0x91, 0xff, 0xf9, + 0x18, 0xf9, 0x18, 0x91, 0xff, 0xf9, 0x18, 0xf9, 0x18, 0x91, 0xff, 0xf9, 0x18, 0xf9, 0x18, + 0x91, 0xff, 0xf9, 0x18, 0xf9, 0x18, 0x91, 0xff, 0xf9, 0x18, 0xf9, 0x18, 0x91, 0xff, 0xf9, + 0x18, 0xf9, 0x18, 0x91, 0xff, 0xf9, 0x18, 0xf9, 0x18, 0x91, 0xff, 0xf9, 0x18, 0xf9, 0x18, + 0x91, 0xff, 0xf9, 0x18, 0xf9, 0x18, 0x91, 0xff, 0xf9, 0x18, 0xf9, 0x18, 0x91, 0xff, 0xf9, + 0x18, 0xf9, 0x18, 0x91, 0xff, 0xf9, 0x18, 0xf9, 0x18, 0x91, 0xff, 0xf9, 0x18, 0xf9, 0x18, + 0x91, 0xff, 0xf9, 0x18, 0xf9, 0x18, 0x91, 0xff, 0xf9, 0x18, 0xf9, 0x18, 0x91, 0xff, 0xf9, + 0x18, 0xf9, 0x18, 0x91, 0xff, 0xf9, 0x18, 0xf9, 0x18, 0x91, 0xff, 0xf9, 0x18, 0xf9, 0x18, + 0x91, 0xff, 0xf9, 0x18, 0xf9, 0x18, 0x91, 0xff, 0xf9, 0x18, 0xf9, 0x18, 0x91, 0xff, 0xf9, + 0x18, 0xf9, 0x18, 0x91, 0xff, 0xf9, 0x18, 0xf9, 0x18, 0x91, 0xff, 0xf9, 0x18, 0xf9, 0x18, + 0x91, 0xff, 0xf9, 0x18, 0xf9, 0x18, 0x91, 0xff, 0xf9, 0x18, 0xf9, 0x18, 0x91, 0xff, 0xf9, + 0x18, 0xf9, 0x18, 0x91, 0xff, 0xf9, 0x18, 0xf9, 0x18, 0x91, 0xff, 0xf9, 0x18, 0xf9, 0x18, + 0x91, 0xff, 0xf9, 0x18, 0xf9, 0x18, 0x91, 0xff, 0xf9, 0x18, 0xf9, 0x18, 0x91, 0xff, 0xf9, + 0x18, 0xf9, 0x18, 0x91, 0xff, 0xf9, 0x18, 0xf9, 0x18, 0x91, 0xff, 0xf9, 0x18, 0xf9, 0x18, + 0x91, 0xff, 0xf9, 0x18, 0xf9, 0x18, 0x91, 0xff, 0xf9, 0x18, 0xf9, 0x18, 0x91, 0xff, 0xf9, + 0x18, 0xf9, 0x18, 0x91, 0xff, 0xf9, 0x18, 0xf9, 0x18, 0x91, 0xff, 0xf9, 0x18, 0xf9, 0x18, + 0x91, 0xff, 0xf9, 0x18, 0xf9, 0x18, 0x91, 0xff, 0xf9, 0x18, 0xf9, 0x18, 0x91, 0xff, 0xf9, + 0x18, 0xf9, 0x18, 0x91, 0xff, 0xf9, 0x18, 0xf9, 0x18, 0x91, 0xff, 0xf9, 0x18, 0xf9, 0x18, + 0x91, 0xff, 0xf9, 0x18, 0xf9, 0x18, 0x91, 0xff, 0xf9, 0x18, 0xf9, 0x18, 0x91, 0xff, 0xf9, + 0x18, 0xf9, 0x18, 0x91, 0xff, 0xf9, 0x18, 0xf9, 0x18, 0x91, 0xff, 0xf9, 0x18, 0xf9, 0x18, + 0x91, 0xff, 0xf9, 0x18, 0xf9, 0x18, 0x91, 0xff, 0xf9, 0x18, 0xf9, 0x18, 0x91, 0xff, 0xf9, + 0x18, 0xf9, 0x18, 0x91, 0xff, 0xf9, 0x18, 0xf9, 0x18, 0x91, 0xff, 0xf9, 0x18, 0xf9, 0x18, + 0x91, 0xff, 0xf9, 0x18, 0xf9, 0x18, 0x91, 0xff, 0xf9, 0x18, 0x81, 0x18, 0x81, 0x18, 0x81, + 0x18, 0x81, 0x18, 0x81, 0x18, 0x81, 0x18, 0x81, 0x18, 0x81, 0x18, 0x81, 0xff, 0x81, 0xff, + 0x81, 0xff, 0x81, 0xff, 0x81, 0xff, 0x81, 0xff, 0x81, 0xff, 0x81, 0xff, 0xf9, 0xff, 0x91, + 0x00, 0xf9, 0xff, 0xf9, 0xff, 0x91, 0x00, 0xf9, 0xff, 0xf9, 0xff, 0x91, 0x00, 0xf9, 0xff, + 0xf9, 0xff, 0x91, 0x00, 0xf9, 0xff, 0xf9, 0xff, 0x91, 0x00, 0xf9, 0xff, 0xf9, 0xff, 0x91, + 0x00, 0xf9, 0xff, 0xf9, 0xff, 0x91, 0x00, 0xf9, 0xff, 0xf9, 0xff, 0x91, 0x00, 0xf9, 0xff, + 0xf9, 0xff, 0x91, 0x00, 0xf9, 0xff, 0xf9, 0xff, 0x91, 0x00, 0xf9, 0xff, 0xf9, 0xff, 0x91, + 0x00, 0xf9, 0xff, 0xf9, 0xff, 0x91, 0x00, 0xf9, 0xff, 0xf9, 0xff, 0x91, 0x00, 0xf9, 0xff, + 0xf9, 0xff, 0x91, 0x00, 0xf9, 0xff, 0xf9, 0xff, 0x91, 0x00, 0xf9, 0xff, 0xf9, 0xff, 0x91, + 0x00, 0xf9, 0xff, 0xf9, 0xff, 0x91, 0x00, 0xf9, 0xff, 0xf9, 0xff, 0x91, 0x00, 0xf9, 0xff, + 0xf9, 0xff, 0x91, 0x00, 0xf9, 0xff, 0xf9, 0xff, 0x91, 0x00, 0xf9, 0xff, 0xf9, 0xff, 0x91, + 0x00, 0xf9, 0xff, 0xf9, 0xff, 0x91, 0x00, 0xf9, 0xff, 0xf9, 0xff, 0x91, 0x00, 0xf9, 0xff, + 0xf9, 0xff, 0x91, 0x00, 0xf9, 0xff, 0xf9, 0xff, 0x91, 0x00, 0xf9, 0xff, 0xf9, 0xff, 0x91, + 0x00, 0xf9, 0xff, 0xf9, 0xff, 0x91, 0x00, 0xf9, 0xff, 0xf9, 0xff, 0x91, 0x00, 0xf9, 0xff, + 0xf9, 0xff, 0x91, 0x00, 0xf9, 0xff, 0xf9, 0xff, 0x91, 0x00, 0xf9, 0xff, 0xf9, 0xff, 0x91, + 0x00, 0xf9, 0xff, 0xf9, 0xff, 0x91, 0x00, 0xf9, 0xff, 0xf9, 0xff, 0x91, 0x00, 0xf9, 0xff, + 0xf9, 0xff, 0x91, 0x00, 0xf9, 0xff, 0xf9, 0xff, 0x91, 0x00, 0xf9, 0xff, 0xf9, 0xff, 0x91, + 0x00, 0xf9, 0xff, 0xf9, 0xff, 0x91, 0x00, 0xf9, 0xff, 0xf9, 0xff, 0x91, 0x00, 0xf9, 0xff, + 0xf9, 0xff, 0x91, 0x00, 0xf9, 0xff, 0xf9, 0xff, 0x91, 0x00, 0xf9, 0xff, 0xf9, 0xff, 0x91, + 0x00, 0xf9, 0xff, 0xf9, 0xff, 0x91, 0x00, 0xf9, 0xff, 0xf9, 0xff, 0x91, 0x00, 0xf9, 0xff, + 0xf9, 0xff, 0x91, 0x00, 0xf9, 0xff, 0xf9, 0xff, 0x91, 0x00, 0xf9, 0xff, 0xf9, 0xff, 0x91, + 0x00, 0xf9, 0xff, 0xf9, 0xff, 0x91, 0x00, 0xf9, 0xff, 0xf9, 0xff, 0x91, 0x00, 0xf9, 0xff, + 0xf9, 0xff, 0x91, 0x00, 0xf9, 0xff, 0xf9, 0xff, 0x91, 0x00, 0xf9, 0xff, 0xf9, 0xff, 0x91, + 0x00, 0xf9, 0xff, 0xf9, 0xff, 0x91, 0x00, 0xf9, 0xff, 0xf9, 0xff, 0x91, 0x00, 0xf9, 0xff, + 0xf9, 0xff, 0x91, 0x00, 0xf9, 0xff, 0xf9, 0xff, 0x91, 0x00, 0xf9, 0xff, 0xf9, 0xff, 0x91, + 0x00, 0xf9, 0xff, 0xf9, 0xff, 0x91, 0x00, 0xf9, 0xff, 0xf9, 0xff, 0x91, 0x00, 0xf9, 0xff, + 0xf9, 0xff, 0x91, 0x00, 0xf9, 0xff, 0xf9, 0xff, 0x91, 0x00, 0xf9, 0xff, 0xf9, 0xff, 0x91, + 0x00, 0xf9, 0xff, 0xf9, 0xff, 0x91, 0x00, 0xf9, 0xff, 0xf9, 0xff, 0x91, 0x00, 0xf9, 0xff, + 0xf9, 0xff, 0x91, 0x00, 0xf9, 0xff, 0xf9, 0xff, 0x91, 0x00, 0xf9, 0xff, 0xf9, 0xff, 0x91, + 0x00, 0xf9, 0xff, 0xf9, 0xff, 0x91, 0x00, 0xf9, 0xff, 0xf9, 0xff, 0x91, 0x00, 0xf9, 0xff, + 0xf9, 0xff, 0x91, 0x00, 0xf9, 0xff, 0xf9, 0xff, 0x91, 0x00, 0xf9, 0xff, 0xf9, 0xff, 0x91, + 0x00, 0xf9, 0xff, 0xf9, 0xff, 0x91, 0x00, 0xf9, 0xff, 0xf9, 0xff, 0x91, 0x00, 0xf9, 0xff, + 0xf9, 0xff, 0x91, 0x00, 0xf9, 0xff, 0xf9, 0xff, 0x91, 0x00, 0xf9, 0xff, 0xf9, 0xff, 0x91, + 0x00, 0xf9, 0xff, 0xf9, 0xff, 0x91, 0x00, 0xf9, 0xff, 0xf9, 0xff, 0x91, 0x00, 0xf9, 0xff, + 0xf9, 0xff, 0x91, 0x00, 0xf9, 0xff, 0xf9, 0xff, 0x91, 0x00, 0xf9, 0xff, 0xf9, 0xff, 0x91, + 0x00, 0xf9, 0xff, 0xf9, 0xff, 0x91, 0x00, 0xf9, 0xff, 0xf9, 0xff, 0x91, 0x00, 0xf9, 0xff, + 0xf9, 0xff, 0x91, 0x00, 0xf9, 0xff, 0xf9, 0xff, 0x91, 0x00, 0xf9, 0xff, 0xf9, 0xff, 0x91, + 0x00, 0xf9, 0xff, 0xf9, 0xff, 0x91, 0x00, 0xf9, 0xff, 0xf9, 0xff, 0x91, 0x00, 0xf9, 0xff, + 0xf9, 0xff, 0x91, 0x00, 0xf9, 0xff, 0xf9, 0xff, 0x91, 0x00, 0xf9, 0xff, 0xf9, 0xff, 0x91, + 0x00, 0xf9, 0xff, 0xf9, 0xff, 0x91, 0x00, 0xf9, 0xff, 0xf9, 0xff, 0x91, 0x00, 0xf9, 0xff, + 0xf9, 0xff, 0x91, 0x00, 0xf9, 0xff, 0xf9, 0xff, 0x91, 0x00, 0xf9, 0xff, 0xf9, 0xff, 0x91, + 0x00, 0xf9, 0xff, 0xf9, 0xff, 0x91, 0x00, 0xf9, 0xff, 0xf9, 0xff, 0x91, 0x00, 0xf9, 0xff, + 0xf9, 0xff, 0x91, 0x00, 0xf9, 0xff, 0xf9, 0xff, 0x91, 0x00, 0xf9, 0xff, 0xf9, 0xff, 0x91, + 0x00, 0xf9, 0xff, 0xf9, 0xff, 0x91, 0x00, 0xf9, 0xff, 0xf9, 0xff, 0x91, 0x00, 0xf9, 0xff, + 0xf9, 0xff, 0x91, 0x00, 0xf9, 0xff, 0xf9, 0xff, 0x91, 0x00, 0xf9, 0xff, 0xf9, 0xff, 0x91, + 0x00, 0xf9, 0xff, 0xf9, 0xff, 0x91, 0x00, 0xf9, 0xff, 0xf9, 0xff, 0x91, 0x00, 0xf9, 0xff, + 0xf9, 0xff, 0x91, 0x00, 0xf9, 0xff, 0xf9, 0xff, 0x91, 0x00, 0xf9, 0xff, 0xf9, 0xff, 0x91, + 0x00, 0xf9, 0xff, 0xf9, 0xff, 0x91, 0x00, 0xf9, 0xff, 0x81, 0xff, 0x81, 0xff, 0x81, 0xff, + 0x81, 0xff, 0x81, 0xff, 0x81, 0xff, 0x81, 0xff, 0x81, 0xff, 0x81, 0x00, 0x81, 0x00, 0x81, + 0x00, 0x81, 0x00, 0x81, 0x00, 0x81, 0x00, 0x81, 0x00, 0x81, 0x00, 0xf9, 0x00, 0x91, 0xff, + 0xf9, 0x00, 0xf9, 0x00, 0x91, 0xff, 0xf9, 0x00, 0xf9, 0x00, 0x91, 0xff, 0xf9, 0x00, 0xf9, + 0x00, 0x91, 0xff, 0xf9, 0x00, 0xf9, 0x00, 0x91, 0xff, 0xf9, 0x00, 0xf9, 0x00, 0x91, 0xff, + 0xf9, 0x00, 0xf9, 0x00, 0x91, 0xff, 0xf9, 0x00, 0xf9, 0x00, 0x91, 0xff, 0xf9, 0x00, 0xf9, + 0x00, 0x91, 0xff, 0xf9, 0x00, 0xf9, 0x00, 0x91, 0xff, 0xf9, 0x00, 0xf9, 0x00, 0x91, 0xff, + 0xf9, 0x00, 0xf9, 0x00, 0x91, 0xff, 0xf9, 0x00, 0xf9, 0x00, 0x91, 0xff, 0xf9, 0x00, 0xf9, + 0x00, 0x91, 0xff, 0xf9, 0x00, 0xf9, 0x00, 0x91, 0xff, 0xf9, 0x00, 0xf9, 0x00, 0x91, 0xff, + 0xf9, 0x00, 0xf9, 0x00, 0x91, 0xff, 0xf9, 0x00, 0xf9, 0x00, 0x91, 0xff, 0xf9, 0x00, 0xf9, + 0x00, 0x91, 0xff, 0xf9, 0x00, 0xf9, 0x00, 0x91, 0xff, 0xf9, 0x00, 0xf9, 0x00, 0x91, 0xff, + 0xf9, 0x00, 0xf9, 0x00, 0x91, 0xff, 0xf9, 0x00, 0xf9, 0x00, 0x91, 0xff, 0xf9, 0x00, 0xf9, + 0x00, 0x91, 0xff, 0xf9, 0x00, 0xf9, 0x00, 0x91, 0xff, 0xf9, 0x00, 0xf9, 0x00, 0x91, 0xff, + 0xf9, 0x00, 0xf9, 0x00, 0x91, 0xff, 0xf9, 0x00, 0xf9, 0x00, 0x91, 0xff, 0xf9, 0x00, 0xf9, + 0x00, 0x91, 0xff, 0xf9, 0x00, 0xf9, 0x00, 0x91, 0xff, 0xf9, 0x00, 0xf9, 0x00, 0x91, 0xff, + 0xf9, 0x00, 0xf9, 0x00, 0x91, 0xff, 0xf9, 0x00, 0xf9, 0x00, 0x91, 0xff, 0xf9, 0x00, 0xf9, + 0x00, 0x91, 0xff, 0xf9, 0x00, 0xf9, 0x00, 0x91, 0xff, 0xf9, 0x00, 0xf9, 0x00, 0x91, 0xff, + 0xf9, 0x00, 0xf9, 0x00, 0x91, 0xff, 0xf9, 0x00, 0xf9, 0x00, 0x91, 0xff, 0xf9, 0x00, 0xf9, + 0x00, 0x91, 0xff, 0xf9, 0x00, 0xf9, 0x00, 0x91, 0xff, 0xf9, 0x00, 0xf9, 0x00, 0x91, 0xff, + 0xf9, 0x00, 0xf9, 0x00, 0x91, 0xff, 0xf9, 0x00, 0xf9, 0x00, 0x91, 0xff, 0xf9, 0x00, 0xf9, + 0x00, 0x91, 0xff, 0xf9, 0x00, 0xf9, 0x00, 0x91, 0xff, 0xf9, 0x00, 0xf9, 0x00, 0x91, 0xff, + 0xf9, 0x00, 0xf9, 0x00, 0x91, 0xff, 0xf9, 0x00, 0xf9, 0x00, 0x91, 0xff, 0xf9, 0x00, 0xf9, + 0x00, 0x91, 0xff, 0xf9, 0x00, 0xf9, 0x00, 0x91, 0xff, 0xf9, 0x00, 0xf9, 0x00, 0x91, 0xff, + 0xf9, 0x00, 0xf9, 0x00, 0x91, 0xff, 0xf9, 0x00, 0xf9, 0x00, 0x91, 0xff, 0xf9, 0x00, 0xf9, + 0x00, 0x91, 0xff, 0xf9, 0x00, 0xf9, 0x00, 0x91, 0xff, 0xf9, 0x00, 0xf9, 0x00, 0x91, 0xff, + 0xf9, 0x00, 0xf9, 0x00, 0x91, 0xff, 0xf9, 0x00, 0xf9, 0x00, 0x91, 0xff, 0xf9, 0x00, 0xf9, + 0x00, 0x91, 0xff, 0xf9, 0x00, 0xf9, 0x00, 0x91, 0xff, 0xf9, 0x00, 0xf9, 0x00, 0x91, 0xff, + 0xf9, 0x00, 0xf9, 0x00, 0x91, 0xff, 0xf9, 0x00, 0xf9, 0x00, 0x91, 0xff, 0xf9, 0x00, 0xf9, + 0x00, 0x91, 0xff, 0xf9, 0x00, 0xf9, 0x00, 0x91, 0xff, 0xf9, 0x00, 0xf9, 0x00, 0x91, 0xff, + 0xf9, 0x00, 0xf9, 0x00, 0x91, 0xff, 0xf9, 0x00, 0xf9, 0x00, 0x91, 0xff, 0xf9, 0x00, 0xf9, + 0x00, 0x91, 0xff, 0xf9, 0x00, 0xf9, 0x00, 0x91, 0xff, 0xf9, 0x00, 0xf9, 0x00, 0x91, 0xff, + 0xf9, 0x00, 0xf9, 0x00, 0x91, 0xff, 0xf9, 0x00, 0xf9, 0x00, 0x91, 0xff, 0xf9, 0x00, 0xf9, + 0x00, 0x91, 0xff, 0xf9, 0x00, 0xf9, 0x00, 0x91, 0xff, 0xf9, 0x00, 0xf9, 0x00, 0x91, 0xff, + 0xf9, 0x00, 0xf9, 0x00, 0x91, 0xff, 0xf9, 0x00, 0xf9, 0x00, 0x91, 0xff, 0xf9, 0x00, 0xf9, + 0x00, 0x91, 0xff, 0xf9, 0x00, 0xf9, 0x00, 0x91, 0xff, 0xf9, 0x00, 0xf9, 0x00, 0x91, 0xff, + 0xf9, 0x00, 0xf9, 0x00, 0x91, 0xff, 0xf9, 0x00, 0xf9, 0x00, 0x91, 0xff, 0xf9, 0x00, 0xf9, + 0x00, 0x91, 0xff, 0xf9, 0x00, 0xf9, 0x00, 0x91, 0xff, 0xf9, 0x00, 0xf9, 0x00, 0x91, 0xff, + 0xf9, 0x00, 0xf9, 0x00, 0x91, 0xff, 0xf9, 0x00, 0xf9, 0x00, 0x91, 0xff, 0xf9, 0x00, 0xf9, + 0x00, 0x91, 0xff, 0xf9, 0x00, 0xf9, 0x00, 0x91, 0xff, 0xf9, 0x00, 0xf9, 0x00, 0x91, 0xff, + 0xf9, 0x00, 0xf9, 0x00, 0x91, 0xff, 0xf9, 0x00, 0xf9, 0x00, 0x91, 0xff, 0xf9, 0x00, 0xf9, + 0x00, 0x91, 0xff, 0xf9, 0x00, 0xf9, 0x00, 0x91, 0xff, 0xf9, 0x00, 0xf9, 0x00, 0x91, 0xff, + 0xf9, 0x00, 0xf9, 0x00, 0x91, 0xff, 0xf9, 0x00, 0xf9, 0x00, 0x91, 0xff, 0xf9, 0x00, 0xf9, + 0x00, 0x91, 0xff, 0xf9, 0x00, 0xf9, 0x00, 0x91, 0xff, 0xf9, 0x00, 0xf9, 0x00, 0x91, 0xff, + 0xf9, 0x00, 0xf9, 0x00, 0x91, 0xff, 0xf9, 0x00, 0xf9, 0x00, 0x91, 0xff, 0xf9, 0x00, 0xf9, + 0x00, 0x91, 0xff, 0xf9, 0x00, 0xf9, 0x00, 0x91, 0xff, 0xf9, 0x00, 0xf9, 0x00, 0x91, 0xff, + 0xf9, 0x00, 0xf9, 0x00, 0x91, 0xff, 0xf9, 0x00, 0xf9, 0x00, 0x91, 0xff, 0xf9, 0x00, 0xf9, + 0x00, 0x91, 0xff, 0xf9, 0x00, 0xf9, 0x00, 0x91, 0xff, 0xf9, 0x00, 0xf9, 0x00, 0x91, 0xff, + 0xf9, 0x00, 0xf9, 0x00, 0x91, 0xff, 0xf9, 0x00, 0x81, 0x00, 0x81, 0x00, 0x81, 0x00, 0x81, + 0x00, 0x81, 0x00, 0x81, 0x00, 0x81, 0x00, 0x81, 0x00, 0x81, 0xff, 0x81, 0xff, 0x81, 0xff, + 0x81, 0xff, 0x81, 0xff, 0x81, 0xff, 0x81, 0xff, 0x81, 0xff, 0x81, 0xff, 0x81, 0xff, 0x81, + 0xff, 0x81, 0xff, 0x81, 0xff, 0x81, 0xff, 0x81, 0xff, 0x81, 0xff, 0x81, 0xff, 0x81, 0xff, + 0x81, 0xff, 0x81, 0xff, 0x81, 0xff, 0x81, 0xff, 0x81, 0xff, 0x81, 0xff, 0x81, 0xff, 0x81, + 0xff, 0x81, 0xff, 0x81, 0xff, 0x81, 0xff, 0x81, 0xff, 0x81, 0xff, 0x81, 0xff, 0x81, 0xff, + 0x81, 0xff, 0x81, 0xff, 0x81, 0xff, 0x81, 0xff, 0x81, 0xff, 0x81, 0xff, 0x81, 0xff, 0x81, + 0xff, 0x81, 0xff, 0x81, 0xff, 0x81, 0xff, 0x81, 0xff, 0x81, 0xff, 0x81, 0xff, 0x81, 0xff, + 0x81, 0xff, 0x81, 0xff, 0x81, 0xff, 0x81, 0xff, 0x81, 0xff, 0x81, 0xff, 0x81, 0xff, 0x81, + 0xff, 0x81, 0xff, 0x81, 0xff, 0x81, 0xff, 0x81, 0xff, 0x81, 0xff, 0x81, 0xff, 0x81, 0xff, + 0x81, 0xff, 0x81, 0xff, 0x81, 0xff, 0x81, 0xff, 0x81, 0xff, 0x81, 0xff, 0x81, 0xff, 0x81, + 0xff, 0x81, 0xff, 0x81, 0xff, 0x81, 0xff, 0x81, 0xff, 0x81, 0xff, 0x81, 0xff, 0x81, 0xff, + 0x81, 0xff, 0x81, 0xff, 0x81, 0xff, 0x81, 0xff, 0x81, 0xff, 0x81, 0xff, 0x81, 0xff, 0x81, + 0xff, 0x81, 0xff, 0x81, 0xff, 0x81, 0xff, 0x81, 0xff, 0x81, 0xff, 0x81, 0xff, 0x81, 0xff, + 0x81, 0xff, 0x81, 0xff, 0x81, 0xff, 0x81, 0xff, 0x81, 0xff, 0x81, 0xff, 0x81, 0xff, 0x81, + 0xff, 0x81, 0xff, 0x81, 0xff, 0x81, 0xff, 0x81, 0xff, 0x81, 0xff, 0x81, 0xff, 0x81, 0xff, + 0x81, 0xff, 0x81, 0xff, 0x81, 0xff, 0x81, 0xff, 0x81, 0xff, 0x81, 0xff, 0x81, 0xff, 0x81, + 0xff, 0x81, 0xff, 0x81, 0xff, 0x81, 0xff, 0x81, 0xff, 0x81, 0xff, 0x81, 0xff, 0x81, 0xff, + 0x81, 0xff, 0x81, 0xff, 0x81, 0xff, 0x81, 0xff, 0x81, 0xff +}; diff --git a/Samples/SampleXrFramework/Src/Input/ArmModel.cpp b/Samples/SampleXrFramework/Src/Input/ArmModel.cpp new file mode 100755 index 0000000..35125bd --- /dev/null +++ b/Samples/SampleXrFramework/Src/Input/ArmModel.cpp @@ -0,0 +1,185 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/************************************************************************************ + +Filename : ArmModel.cpp +Content : An arm model for the tracked remote +Created : 2/20/2017 +Authors : Jonathan E. Wright + +************************************************************************************/ + +#include "ArmModel.h" + +#include + +using OVR::Matrix4f; +using OVR::Posef; +using OVR::Quatf; +using OVR::Vector3f; +using OVR::Vector4f; + +namespace OVRFW { + +ovrArmModel::ovrArmModel() + : TorsoYaw(0.0f), + TorsoTracksHead(false), + ForceRecenter(false), + ClavicleJointIdx(-1), + ShoulderJointIdx(-1), + ElbowJointIdx(-1), + WristJointIdx(-1) {} + +void ovrArmModel::InitSkeleton(bool isLeft) { + std::vector joints; + + /// + const int noParent = -1; + const int headJointIdx = 0; + + /// Set the joints needed to update the model + ClavicleJointIdx = 1; + ShoulderJointIdx = 2; + ElbowJointIdx = 3; + WristJointIdx = 4; + + joints.push_back(ovrJoint("head", Vector4f(0.0f, 1.0f, 0.0f, 1.0f), Posef(), noParent)); + joints.push_back(ovrJoint( + "neck", + Vector4f(0.0f, 1.0f, 0.0f, 1.0f), + Posef(Quatf(), Vector3f(0.0f, -0.2032f, 0.0f)), + headJointIdx)); + joints.push_back(ovrJoint( + "clavicle", + Vector4f(0.0f, 1.0f, 0.0f, 1.0f), + Posef(Quatf(), Vector3f((isLeft ? -0.2286f : 0.2286f), 0.0f, 0.0f)), + ClavicleJointIdx)); + joints.push_back(ovrJoint( + "shoulder", + Vector4f(1.0f, 0.0f, 1.0f, 1.0f), + Posef(Quatf(), Vector3f(0.0f, -0.2441f, 0.02134f)), + ShoulderJointIdx)); + joints.push_back(ovrJoint( + "elbow", + Vector4f(1.0f, 0.0f, 0.0f, 1.0f), + Posef(Quatf(), Vector3f(0.0f, 0.0f, -0.3048f)), + 3)); + joints.push_back(ovrJoint( + "wrist", + Vector4f(1.0f, 1.0f, 0.0f, 1.0f), + Posef(Quatf(), Vector3f(0.0f, 0.0f, -0.0762f)), + ElbowJointIdx)); + joints.push_back(ovrJoint( + "hand", + Vector4f(1.0f, 1.0f, 0.0f, 1.0f), + Posef(Quatf(), Vector3f(0.0f, 0.0381f, -0.0381f)), + WristJointIdx)); + + Skeleton.SetJoints(joints); + TransformedJoints = Skeleton.GetJoints(); +} + +void ovrArmModel::Update( + const Posef& headPose, + const Posef& remotePose, + const ovrHandedness handedness, + const bool recenteredController, + Posef& outPose) { + Matrix4f eyeMatrix(headPose); + + float eyeYaw; + float eyePitch; + float eyeRoll; // ya... like, seriously??? + eyeMatrix.ToEulerAngles( + &eyeYaw, &eyePitch, &eyeRoll); + + auto ConstrainTorsoYaw = [](const Quatf& headRot, const float torsoYaw) { + const Vector3f worldUp(0.0f, 1.0f, 0.0f); + const Vector3f worldFwd(0.0f, 0.0f, -1.0f); + + const Vector3f projectedHeadFwd = (headRot * worldFwd).ProjectToPlane(worldUp); + if (projectedHeadFwd.LengthSq() < 0.001f) { + return torsoYaw; + } + + // calculate the world rotation of the head on the horizon plane + const Vector3f headFwd = projectedHeadFwd.Normalized(); + + // calculate the world rotation of the torso + const Quatf torsoRot(worldUp, torsoYaw); + const Vector3f torsoFwd = torsoRot * worldFwd; + + // find the angle between the torso and head + const float torsoMaxYawOffset = MATH_FLOAT_DEGREETORADFACTOR * 30.0f; + const float torsoDot = torsoFwd.Dot(headFwd); + if (torsoDot >= cosf(torsoMaxYawOffset)) { + return torsoYaw; + } + + // calculate the rotation of the torso when it's constrained in that direction + const Vector3f headRight(-headFwd.z, 0.0f, headFwd.x); + const Quatf projectedHeadRot = Quatf::FromBasisVectors(headFwd, headRight, worldUp); + + const float offsetDir = headRight.Dot(torsoFwd) < 0.0f ? 1.0f : -1.0f; + const float offsetYaw = torsoMaxYawOffset * offsetDir; + const Quatf constrainedTorsoRot = projectedHeadRot * Quatf(worldUp, offsetYaw); + + // slerp torso towards the constrained rotation + float const slerpFactor = 1.0f / 15.0f; + const Quatf slerped = torsoRot.Slerp(constrainedTorsoRot, slerpFactor); + + float y; + float p; + float r; + slerped.GetYawPitchRoll(&y, &p, &r); + return y; + }; + + TorsoYaw = ConstrainTorsoYaw(headPose.Rotation, TorsoYaw); + + if (ForceRecenter || TorsoTracksHead || recenteredController) { + ForceRecenter = false; + TorsoYaw = eyeYaw; + } + + FootPose = Posef(Quatf(Vector3f(0.0f, 1.0f, 0.0f), TorsoYaw), eyeMatrix.GetTranslation()); + Quatf remoteRot(remotePose.Rotation); + + const float MAX_ROLL = MATH_FLOAT_PIOVER2; + const float MIN_PITCH = MATH_FLOAT_PIOVER2 * 0.825f; + float remoteYaw; + float remotePitch; + float remoteRoll; + remoteRot.GetYawPitchRoll(&remoteYaw, &remotePitch, &remoteRoll); + if ((remoteRoll >= -MAX_ROLL && remoteRoll <= MAX_ROLL) || + (remotePitch <= -MIN_PITCH || remotePitch >= MIN_PITCH)) { + LastUnclampedRoll = remoteRoll; + } else { + remoteRoll = LastUnclampedRoll; + } + + Matrix4f m = Matrix4f::RotationY(remoteYaw) * Matrix4f::RotationX(remotePitch) * + Matrix4f::RotationZ(remoteRoll); + remoteRot = Quatf(m); + + Quatf localRemoteRot(FootPose.Rotation.Inverted() * remoteRot); + + Quatf shoulderRot = Quatf().Slerp(localRemoteRot, 0.0f); + Quatf elbowRot = Quatf().Slerp(localRemoteRot, 0.6f); + Quatf wristRot = Quatf().Slerp(localRemoteRot, 0.4f); + + /// Pose the skeleton + Skeleton.UpdateLocalRotation(shoulderRot, ShoulderJointIdx); + Skeleton.UpdateLocalRotation(elbowRot, ElbowJointIdx); + Skeleton.UpdateLocalRotation(wristRot, WristJointIdx); + /// Move the skeleton root + Skeleton.TransformLocal(FootPose, 0); + + /// Update the joints for beam rendering + for (int i = 0; i < (int)TransformedJoints.size(); ++i) { + TransformedJoints[i].Pose = Skeleton.GetWorldSpacePoses()[i]; + } + outPose = TransformedJoints[static_cast(TransformedJoints.size()) - 1].Pose; +} + +} // namespace OVRFW diff --git a/Samples/SampleXrFramework/Src/Input/ArmModel.h b/Samples/SampleXrFramework/Src/Input/ArmModel.h new file mode 100755 index 0000000..a704f87 --- /dev/null +++ b/Samples/SampleXrFramework/Src/Input/ArmModel.h @@ -0,0 +1,64 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/************************************************************************************ + +Filename : ArmModel.h +Content : An arm model for the tracked remote +Created : 2/20/2017 +Authors : Jonathan E. Wright + +************************************************************************************/ + +#pragma once + +#include + +#include "OVR_Types.h" + +#include "Skeleton.h" + +namespace OVRFW { + +class ovrArmModel { + public: + enum ovrHandedness { HAND_UNKNOWN = -1, HAND_LEFT, HAND_RIGHT, HAND_MAX }; + + ovrArmModel(); + + void InitSkeleton(bool isLeft); + + void Update( + const OVR::Posef& headPose, + const OVR::Posef& remotePose, + const ovrHandedness handedness, + const bool recenteredController, + OVR::Posef& outPose); + + const ovrSkeleton& GetSkeleton() const { + return Skeleton; + } + ovrSkeleton& GetSkeleton() { + return Skeleton; + } + const std::vector& GetTransformedJoints() const { + return TransformedJoints; + } + + private: + ovrSkeleton Skeleton; + OVR::Posef FootPose; + std::vector TransformedJoints; + + float LastUnclampedRoll; + + float TorsoYaw; // current yaw of the torso + bool TorsoTracksHead; // true to make the torso track the head + bool ForceRecenter; // true to force the torso to the head yaw + + int ClavicleJointIdx; + int ShoulderJointIdx; + int ElbowJointIdx; + int WristJointIdx; +}; + +} // namespace OVRFW diff --git a/Samples/SampleXrFramework/Src/Input/AxisRenderer.cpp b/Samples/SampleXrFramework/Src/Input/AxisRenderer.cpp new file mode 100755 index 0000000..d2b20c7 --- /dev/null +++ b/Samples/SampleXrFramework/Src/Input/AxisRenderer.cpp @@ -0,0 +1,122 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/************************************************************************************ + +Filename : AxisRenderer.cpp +Content : A rendering component for axis +Created : September 2020 +Authors : Federico Schliemann + +************************************************************************************/ + +#include "AxisRenderer.h" +#include "Misc/Log.h" + +using OVR::Matrix4f; +using OVR::Posef; +using OVR::Quatf; +using OVR::Vector3f; +using OVR::Vector4f; + +static const char* AxisVertexShaderSrc = R"glsl( + uniform JointMatrices + { + highp mat4 Joints[128]; + } jb; + + attribute highp vec4 Position; + attribute lowp vec4 VertexColor; + varying lowp vec4 oColor; + + void main() + { + highp vec4 localPos = jb.Joints[ gl_InstanceID ] * Position; + gl_Position = TransformVertex( localPos ); + oColor = VertexColor; + } +)glsl"; + +static const char* AxisFragmentShaderSrc = R"glsl( + varying lowp vec4 oColor; + void main() + { + gl_FragColor = oColor; + } +)glsl"; + +namespace OVRFW { + +bool ovrAxisRenderer::Init(size_t count, float size) { + /// Defaults + Count = count; + AxisSize = size; + + /// Create Axis program + static ovrProgramParm AxisUniformParms[] = { + {"JointMatrices", ovrProgramParmType::BUFFER_UNIFORM}, + }; + ProgAxis = GlProgram::Build( + AxisVertexShaderSrc, + AxisFragmentShaderSrc, + AxisUniformParms, + sizeof(AxisUniformParms) / sizeof(ovrProgramParm)); + + TransformMatrices.resize(Count, OVR::Matrix4f::Identity()); + InstancedBoneUniformBuffer.Create( + GLBUFFER_TYPE_UNIFORM, Count * sizeof(Matrix4f), TransformMatrices.data()); + + /// Create Axis surface definition + AxisSurfaceDef.surfaceName = "AxisSurfaces"; + AxisSurfaceDef.geo = OVRFW::BuildAxis(AxisSize); + AxisSurfaceDef.numInstances = 0; + /// Build the graphics command + auto& gc = AxisSurfaceDef.graphicsCommand; + gc.Program = ProgAxis; + gc.UniformData[0].Data = &InstancedBoneUniformBuffer; + gc.GpuState.depthEnable = gc.GpuState.depthMaskEnable = true; + gc.GpuState.blendEnable = ovrGpuState::BLEND_DISABLE; + gc.GpuState.blendSrc = GL_ONE; + /// Add surface + AxisSurface.surface = &(AxisSurfaceDef); + + return true; +} + +void ovrAxisRenderer::Shutdown() { + OVRFW::GlProgram::Free(ProgAxis); + InstancedBoneUniformBuffer.Destroy(); +} + +void ovrAxisRenderer::Update(const std::vector& points) { + Update(points.data(), points.size()); +} + +void ovrAxisRenderer::Update(const OVR::Posef* points, size_t count) { + if (count != Count) { + Count = count; + TransformMatrices.resize(Count, OVR::Matrix4f::Identity()); + InstancedBoneUniformBuffer.Destroy(); + InstancedBoneUniformBuffer.Create( + GLBUFFER_TYPE_UNIFORM, Count * sizeof(Matrix4f), TransformMatrices.data()); + } + for (size_t j = 0; j < count; ++j) { + /// Compute transform + OVR::Matrix4f t(points[j]); + TransformMatrices[j] = t.Transposed(); + } + InstancedBoneUniformBuffer.Update( + TransformMatrices.size() * sizeof(Matrix4f), TransformMatrices.data()); +} + +void ovrAxisRenderer::Render( + const OVR::Matrix4f& worldMatrix, + const OVRFW::ovrApplFrameIn& in, + OVRFW::ovrRendererOutput& out) { + AxisSurfaceDef.numInstances = Count; + AxisSurface.modelMatrix = worldMatrix; + if (Count > 0) { + out.Surfaces.push_back(AxisSurface); + } +} + +} // namespace OVRFW diff --git a/Samples/SampleXrFramework/Src/Input/AxisRenderer.h b/Samples/SampleXrFramework/Src/Input/AxisRenderer.h new file mode 100755 index 0000000..a9c8aaa --- /dev/null +++ b/Samples/SampleXrFramework/Src/Input/AxisRenderer.h @@ -0,0 +1,47 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/************************************************************************************ + +Filename : AxisRenderer.h +Content : A rendering component for axis +Created : September 2020 +Authors : Federico Schliemann + +************************************************************************************/ + +#pragma once + +#include + +#include "OVR_Math.h" +#include "FrameParams.h" +#include "Render/SurfaceRender.h" +#include "Render/GlProgram.h" + +namespace OVRFW { + +class ovrAxisRenderer { + public: + ovrAxisRenderer() = default; + ~ovrAxisRenderer() = default; + + bool Init(size_t count = 64, float size = 0.025f); + void Shutdown(); + void Update(const std::vector& points); + void Update(const OVR::Posef* points, size_t count); + void Render( + const OVR::Matrix4f& worldMatrix, + const OVRFW::ovrApplFrameIn& in, + OVRFW::ovrRendererOutput& out); + + private: + float AxisSize; + GlProgram ProgAxis; + ovrSurfaceDef AxisSurfaceDef; + ovrDrawSurface AxisSurface; + std::vector TransformMatrices; + GlBuffer InstancedBoneUniformBuffer; + size_t Count; +}; + +} // namespace OVRFW diff --git a/Samples/SampleXrFramework/Src/Input/ControllerRenderer.cpp b/Samples/SampleXrFramework/Src/Input/ControllerRenderer.cpp new file mode 100755 index 0000000..d48ab56 --- /dev/null +++ b/Samples/SampleXrFramework/Src/Input/ControllerRenderer.cpp @@ -0,0 +1,271 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/************************************************************************************ + +Filename : ControllerRenderer.cpp +Content : A one stop for rendering controllers +Created : July 2020 +Authors : Federico Schliemann + +************************************************************************************/ + +#include "ControllerRenderer.h" + +#include "Render/GeometryBuilder.h" +#include "Render/GlGeometry.h" + +using OVR::Matrix4f; +using OVR::Posef; +using OVR::Quatf; +using OVR::Vector3f; +using OVR::Vector4f; + +namespace OVRFW { + +namespace Controller { + +/// clang-format off +static const char* VertexShaderSrc = R"glsl( +attribute highp vec4 Position; +attribute highp vec3 Normal; +attribute highp vec3 Tangent; +attribute highp vec3 Binormal; +attribute highp vec2 TexCoord; +attribute lowp vec4 VertexColor; + +varying lowp vec3 oEye; +varying lowp vec3 oNormal; +varying lowp vec2 oTexCoord; +varying lowp vec4 oColor; + +vec3 multiply( mat4 m, vec3 v ) +{ + return vec3( + m[0].x * v.x + m[1].x * v.y + m[2].x * v.z, + m[0].y * v.x + m[1].y * v.y + m[2].y * v.z, + m[0].z * v.x + m[1].z * v.y + m[2].z * v.z ); +} + +vec3 transposeMultiply( mat4 m, vec3 v ) +{ + return vec3( + m[0].x * v.x + m[0].y * v.y + m[0].z * v.z, + m[1].x * v.x + m[1].y * v.y + m[1].z * v.z, + m[2].x * v.x + m[2].y * v.y + m[2].z * v.z ); +} + +void main() +{ + gl_Position = TransformVertex( Position ); + highp vec3 eye = transposeMultiply( sm.ViewMatrix[VIEW_ID], -vec3( sm.ViewMatrix[VIEW_ID][3] ) ); + oEye = eye - vec3( ModelMatrix * Position ); + + oNormal = multiply( ModelMatrix, Normal ); + + oTexCoord = TexCoord; + oColor = VertexColor; +} +)glsl"; + +static const char* FragmentShaderSrc = R"glsl( +uniform sampler2D Texture0; +uniform lowp vec3 SpecularLightDirection; +uniform lowp vec3 SpecularLightColor; +uniform lowp vec3 AmbientLightColor; + +varying lowp vec3 oEye; +varying lowp vec3 oNormal; +varying lowp vec2 oTexCoord; +varying lowp vec4 oColor; + +lowp vec3 multiply( lowp mat3 m, lowp vec3 v ) +{ + return vec3( + m[0].x * v.x + m[1].x * v.y + m[2].x * v.z, + m[0].y * v.x + m[1].y * v.y + m[2].y * v.z, + m[0].z * v.x + m[1].z * v.y + m[2].z * v.z ); +} + +void main() +{ + lowp vec3 eyeDir = normalize( oEye.xyz ); + lowp vec3 Normal = normalize( oNormal ); + + lowp vec3 reflectionDir = dot( eyeDir, Normal ) * 2.0 * Normal - eyeDir; + + lowp vec4 diffuse = oColor; +#ifdef USE_TEXTURE + diffuse = texture2D( Texture0, oTexCoord ); +#endif + + lowp vec3 ambientValue = diffuse.xyz * AmbientLightColor; + + lowp float nDotL = max( dot( Normal , SpecularLightDirection ), 0.0 ); + lowp vec3 diffuseValue = diffuse.xyz * SpecularLightColor * nDotL; + + lowp float specularPower = 1.0f; + lowp vec3 H = normalize( SpecularLightDirection + eyeDir ); + lowp float nDotH = max( dot( Normal, H ), 0.0 ); + lowp float specularIntensity = pow( nDotH, 64.0f * ( specularPower ) ) * specularPower; + lowp vec3 specularValue = specularIntensity * SpecularLightColor; + + lowp vec3 controllerColor = diffuseValue + ambientValue + specularValue; + gl_FragColor.xyz = controllerColor; + gl_FragColor.w = 1.0f; +} +)glsl"; +/// clang-format on + +} // namespace Controller + +void ControllerRenderer::LoadModelFromResource( + OVRFW::ovrFileSys* fileSys, + const char* controllerModelFile) { + if (Model) { + delete Model; + Model = nullptr; + } + if (controllerModelFile && fileSys) { + ModelGlPrograms programs(&ProgControllerTexture); + MaterialParms materials; + Model = LoadModelFile(*fileSys, controllerModelFile, programs, materials); + if (Model != nullptr) { + for (auto& model : Model->Models) { + auto& gc = model.surfaces[0].surfaceDef.graphicsCommand; + gc.UniformData[0].Data = &gc.Textures[0]; + gc.UniformData[1].Data = &SpecularLightDirection; + gc.UniformData[2].Data = &SpecularLightColor; + gc.UniformData[3].Data = &AmbientLightColor; + /// gpu state needs alpha blending + gc.GpuState.depthEnable = gc.GpuState.depthMaskEnable = true; + gc.GpuState.blendEnable = ovrGpuState::BLEND_ENABLE; + gc.GpuState.blendSrc = GL_SRC_ALPHA; + gc.GpuState.blendDst = GL_ONE_MINUS_SRC_ALPHA; + ControllerSurfaceDef = model.surfaces[0].surfaceDef; + } + ControllerSurface.surface = &(ControllerSurfaceDef); + } + } +} + +bool ControllerRenderer::Init( + bool leftController, + OVRFW::ovrFileSys* fileSys, + const char* controllerModelFile, + const OVR::Matrix4f& poseCorrection) { + Model = nullptr; + + PoseCorrection = poseCorrection; + + /// Shader + ovrProgramParm UniformParms[] = { + {"Texture0", ovrProgramParmType::TEXTURE_SAMPLED}, + {"SpecularLightDirection", ovrProgramParmType::FLOAT_VECTOR3}, + {"SpecularLightColor", ovrProgramParmType::FLOAT_VECTOR3}, + {"AmbientLightColor", ovrProgramParmType::FLOAT_VECTOR3}, + }; + ProgControllerTexture = GlProgram::Build( + "#define USE_TEXTURE 1\n", + Controller::VertexShaderSrc, + "#define USE_TEXTURE 1\n", + Controller::FragmentShaderSrc, + UniformParms, + sizeof(UniformParms) / sizeof(ovrProgramParm)); + + ProgControllerColor = GlProgram::Build( + "#define USE_COLOR 1\n", + Controller::VertexShaderSrc, + "#define USE_COLOR 1\n", + Controller::FragmentShaderSrc, + UniformParms, + sizeof(UniformParms) / sizeof(ovrProgramParm)); + + /// Create surface definition + ControllerSurfaceDef.surfaceName = + leftController ? "ControllerSurfaceL" : "ControllerkSurfaceR"; + + /// Atempt to load a resource if passed in + LoadModelFromResource(fileSys, controllerModelFile); + + /// Build geometry from mesh + if (Model == nullptr) { + /// We didn't get a resource, build using gemetry primitives + OVRFW::GeometryBuilder gb; + + /// Loosely hand-calibrated for Quest 2 controllers, based on OpenXR 'hand/x/grip/pose' + /// If you're here to fix controller offset using vrapi you need a conversion + /// long capsure + const Matrix4f capsuleMatrix = Matrix4f::Translation({0.00f, -0.015f, -0.01f}) * + Matrix4f::RotationX(OVR::DegreeToRad(90.0f + 20.0f)); + + gb.Add( + OVRFW::BuildTesselatedCapsuleDescriptor(0.02f, 0.08f, 10, 7), + 0, + {1.0f, 0.9f, 0.25f, 1.0f}, + capsuleMatrix); + + /// ring + const Matrix4f ringMatrix = Matrix4f::Translation({0.0f, 0.02f, 0.04f}); + gb.Add( + OVRFW::BuildTesselatedCylinderDescriptor(0.04f, 0.015f, 24, 2, 1.0f, 1.0f), + 0, + {0.6f, 0.8f, 0.25f, 1.0f}, + ringMatrix); + + ControllerSurfaceDef.geo = gb.ToGeometry(); + + ovrGraphicsCommand& gc = ControllerSurfaceDef.graphicsCommand; + gc.GpuState.cullEnable = false; // Double sided + } + + /// Build the graphics command + ovrGraphicsCommand& gc = ControllerSurfaceDef.graphicsCommand; + /// Program + gc.Program = (Model == nullptr) ? ProgControllerColor : ProgControllerTexture; + /// Uniforms to match UniformParms abovve + gc.UniformData[0].Data = &gc.Textures[0]; + gc.UniformData[1].Data = &SpecularLightDirection; + gc.UniformData[2].Data = &SpecularLightColor; + gc.UniformData[3].Data = &AmbientLightColor; + /// gpu state needs alpha blending + gc.GpuState.depthEnable = gc.GpuState.depthMaskEnable = true; + gc.GpuState.blendEnable = ovrGpuState::BLEND_ENABLE; + gc.GpuState.blendSrc = GL_SRC_ALPHA; + gc.GpuState.blendDst = GL_ONE_MINUS_SRC_ALPHA; + + /// Add surface + ControllerSurface.surface = &(ControllerSurfaceDef); + + /// Set defaults + SpecularLightDirection = Vector3f(1.0f, 1.0f, 0.0f); + SpecularLightColor = Vector3f(1.0f, 0.95f, 0.8f); + AmbientLightColor = Vector3f(1.0f, 1.0f, 1.0f) * 0.15f; + + /// Set hand + isLeftController = leftController; + + /// all good + return true; +} + +void ControllerRenderer::Shutdown() { + OVRFW::GlProgram::Free(ProgControllerTexture); + OVRFW::GlProgram::Free(ProgControllerColor); + ControllerSurfaceDef.geo.Free(); + if (Model != nullptr) { + delete Model; + Model = nullptr; + } +} + +void ControllerRenderer::Update(const OVR::Posef& pose) { + const OVR::Posef controllerPose = pose; + const OVR::Matrix4f matDeviceModel = OVR::Matrix4f(controllerPose) * PoseCorrection; + ControllerSurface.modelMatrix = matDeviceModel; +} + +void ControllerRenderer::Render(std::vector& surfaceList) { + surfaceList.push_back(ControllerSurface); +} + +} // namespace OVRFW diff --git a/Samples/SampleXrFramework/Src/Input/ControllerRenderer.h b/Samples/SampleXrFramework/Src/Input/ControllerRenderer.h new file mode 100755 index 0000000..870ea45 --- /dev/null +++ b/Samples/SampleXrFramework/Src/Input/ControllerRenderer.h @@ -0,0 +1,65 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/************************************************************************************ + +Filename : ControllerRenderer.h +Content : A one stop for rendering controllers +Created : July 2020 +Authors : Federico Schliemann + +************************************************************************************/ + +#pragma once + +#include +#include +#include + +/// Sample Framework +#include "Model/SceneView.h" +#include "Model/ModelFile.h" +#include "Render/GlProgram.h" +#include "Render/SurfaceRender.h" + +#include "OVR_Math.h" + +namespace OVRFW { + +class ControllerRenderer { + public: + ControllerRenderer() = default; + ~ControllerRenderer() = default; + + bool Init( + bool leftController, + OVRFW::ovrFileSys* fileSys = nullptr, + const char* controllerModelFile = nullptr, + const OVR::Matrix4f& poseCorrection = + (OVR::Matrix4f::RotationY(OVR::DegreeToRad(180.0f)) * + OVR::Matrix4f::RotationX(OVR::DegreeToRad(-90.0f)))); + void Shutdown(); + void Update(const OVR::Posef& pose); + void Render(std::vector& surfaceList); + + bool IsLeft() const { + return isLeftController; + } + + void LoadModelFromResource(OVRFW::ovrFileSys* fileSys, const char* controllerModelFile); + + public: + OVR::Vector3f SpecularLightDirection; + OVR::Vector3f SpecularLightColor; + OVR::Vector3f AmbientLightColor; + OVR::Matrix4f PoseCorrection; + + private: + bool isLeftController; + OVRFW::GlProgram ProgControllerTexture; + OVRFW::GlProgram ProgControllerColor; + OVRFW::ovrSurfaceDef ControllerSurfaceDef; + OVRFW::ovrDrawSurface ControllerSurface; + OVRFW::ModelFile* Model; +}; + +} // namespace OVRFW diff --git a/Samples/SampleXrFramework/Src/Input/HandMaskRenderer.cpp b/Samples/SampleXrFramework/Src/Input/HandMaskRenderer.cpp new file mode 100755 index 0000000..e117f2b --- /dev/null +++ b/Samples/SampleXrFramework/Src/Input/HandMaskRenderer.cpp @@ -0,0 +1,404 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/************************************************************************************ + +Filename : HandMaskRenderer.cpp +Content : A one stop for rendering hand masks +Created : May 2021 +Authors : Federico Schliemann + +************************************************************************************/ + +#include "HandMaskRenderer.h" + +using OVR::Matrix4f; +using OVR::Posef; +using OVR::Quatf; +using OVR::Vector3f; +using OVR::Vector4f; + +namespace OVRFW { + +static_assert(MAX_JOINTS == 64, "MAX_JOINTS != 64"); + +const char* VertexShaderSrc = R"glsl( + uniform JointMatrices + { + highp mat4 Joints[64]; + } jb; + + uniform JointColors + { + highp vec3 Colors[64]; + } jc; + + attribute highp vec4 Position; + attribute highp vec2 TexCoord; + varying highp vec2 oTexCoord; + varying highp vec3 oInstanceColor; + + void main() + { + highp vec4 localPos = jb.Joints[ gl_InstanceID ] * Position; + gl_Position = TransformVertex( localPos ); + oTexCoord = TexCoord; + oInstanceColor = jc.Colors[ gl_InstanceID ]; + } +)glsl"; + +static const char* FragmentShaderSrc = R"glsl( + precision highp float; + + varying highp vec2 oTexCoord; + varying highp vec3 oInstanceColor; + + uniform float LayerBlend; + uniform float Falloff; + uniform float Intensity; + uniform float FadeIntensity; + + float BorderFade(vec2 uv, float falloff, float intensity) + { + uv *= 1.0 - uv.yx; + float fade = uv.x * uv.y * intensity; + fade = pow(fade, falloff); + return clamp(fade, 0.0, 1.0) * FadeIntensity; + } + + float AlphaGradient(vec2 uv) + { + vec2 v = (uv - vec2(0.5)) * vec2(2.0); + float r = 1.0 - clamp(length(v), 0.0, 1.0); + r = smoothstep(0.0, 1.0, sqrt(r)); + return r; + } + + void main() + { +#ifdef USE_BORDER_FADE + float r = BorderFade(oTexCoord, 4.0, 15.0); +#else + float r = AlphaGradient(oTexCoord); +#endif /// USE_BORDER_FADE + + float a = r * LayerBlend; + gl_FragColor = vec4(oInstanceColor,a); + } +)glsl"; + +/* clang-format off */ +std::vector cells = { + {0.00500, 0.00000, 0.00000}, + {0.01740, -0.00030, -0.00030}, + {0.00000, 0.00000, 0.00000}, + {0.01370, 0.00000, 0.00000}, + {0.00253, 0.00000, 0.00000}, + {0.01443, 0.00000, 0.00000}, + {0.02646, 0.00000, 0.00000}, + {0.00000, 0.00000, 0.00000}, + {0.01530, -0.00040, -0.00030}, + {0.00000, 0.00000, 0.00000}, + {0.01470, 0.00000, 0.00000}, + {0.00000, 0.00000, 0.00000}, + {0.01570, 0.00000, 0.00000}, + {0.02890, 0.00000, 0.00000}, + {0.00430, 0.00000, 0.00000}, + {0.01680, 0.00000, 0.00000}, + {-0.00170, 0.00000, 0.00000}, + {0.01110, 0.00000, 0.00000}, + {0.00100, 0.00000, 0.00000}, + {0.01460, 0.00000, 0.00000}, + {0.00260, 0.00000, 0.00000}, + {0.01610, 0.00000, -0.00010}, + {0.00000, 0.00000, 0.00000}, + {0.01400, 0.00000, 0.00000}, + {-0.01100, 0.00000, 0.00000}, + {0.00200, 0.00000, 0.00000}, + {0.01690, 0.00000, 0.00000}, + {0.00000, 0.00000, 0.00000}, + {0.01320, 0.00000, 0.00000}, + {0.00000, 0.00000, 0.00000}, + {0.01660, 0.00000, 0.00000}, + {0.00000, 0.00000, 0.00000}, + {0.01620, 0.00000, 0.00000}, + {0.08120, -0.01130, 0.02410}, + {0.03450, -0.01130, 0.01390}, + {0.06310, -0.01130, -0.03170}, + {0.03250, -0.01130, -0.01460}, + {0.08050, -0.01130, 0.00180}, + {0.06470, -0.01130, 0.02340}, + {0.04860, -0.01130, -0.00030}, + {0.04480, -0.01130, -0.02370}, + {0.07980, -0.01130, -0.01600}, + {0.03250, -0.01130, 0.00060}, + {0.06350, -0.01130, 0.01030}, + {0.04820, -0.01130, 0.01390}, + {0.04820, -0.01130, 0.02790}, + {0.06060, -0.01130, 0.03870}, + {0.06350, -0.01130, -0.00600}, + {0.06350, -0.01130, -0.01690}, + {0.04860, -0.01130, -0.01290} +}; + +std::vector cellParents = { + XR_HAND_JOINT_INDEX_DISTAL_EXT, + XR_HAND_JOINT_INDEX_DISTAL_EXT, + XR_HAND_JOINT_INDEX_INTERMEDIATE_EXT, + XR_HAND_JOINT_INDEX_INTERMEDIATE_EXT, + XR_HAND_JOINT_INDEX_PROXIMAL_EXT, + XR_HAND_JOINT_INDEX_PROXIMAL_EXT, + XR_HAND_JOINT_INDEX_PROXIMAL_EXT, + XR_HAND_JOINT_MIDDLE_DISTAL_EXT, + XR_HAND_JOINT_MIDDLE_DISTAL_EXT, + XR_HAND_JOINT_MIDDLE_INTERMEDIATE_EXT, + XR_HAND_JOINT_MIDDLE_INTERMEDIATE_EXT, + XR_HAND_JOINT_MIDDLE_PROXIMAL_EXT, + XR_HAND_JOINT_MIDDLE_PROXIMAL_EXT, + XR_HAND_JOINT_MIDDLE_PROXIMAL_EXT, + XR_HAND_JOINT_LITTLE_DISTAL_EXT, + XR_HAND_JOINT_LITTLE_DISTAL_EXT, + XR_HAND_JOINT_LITTLE_INTERMEDIATE_EXT, + XR_HAND_JOINT_LITTLE_INTERMEDIATE_EXT, + XR_HAND_JOINT_LITTLE_PROXIMAL_EXT, + XR_HAND_JOINT_LITTLE_PROXIMAL_EXT, + XR_HAND_JOINT_RING_DISTAL_EXT, + XR_HAND_JOINT_RING_DISTAL_EXT, + XR_HAND_JOINT_RING_INTERMEDIATE_EXT, + XR_HAND_JOINT_RING_INTERMEDIATE_EXT, + XR_HAND_JOINT_RING_INTERMEDIATE_EXT, + XR_HAND_JOINT_RING_PROXIMAL_EXT, + XR_HAND_JOINT_RING_PROXIMAL_EXT, + XR_HAND_JOINT_THUMB_DISTAL_EXT, + XR_HAND_JOINT_THUMB_DISTAL_EXT, + XR_HAND_JOINT_THUMB_PROXIMAL_EXT, + XR_HAND_JOINT_THUMB_PROXIMAL_EXT, + XR_HAND_JOINT_THUMB_METACARPAL_EXT, + XR_HAND_JOINT_THUMB_METACARPAL_EXT, + XR_HAND_JOINT_WRIST_EXT, + XR_HAND_JOINT_WRIST_EXT, + XR_HAND_JOINT_WRIST_EXT, + XR_HAND_JOINT_WRIST_EXT, + XR_HAND_JOINT_WRIST_EXT, + XR_HAND_JOINT_WRIST_EXT, + XR_HAND_JOINT_WRIST_EXT, + XR_HAND_JOINT_WRIST_EXT, + XR_HAND_JOINT_WRIST_EXT, + XR_HAND_JOINT_WRIST_EXT, + XR_HAND_JOINT_WRIST_EXT, + XR_HAND_JOINT_WRIST_EXT, + XR_HAND_JOINT_WRIST_EXT, + XR_HAND_JOINT_WRIST_EXT, + XR_HAND_JOINT_WRIST_EXT, + XR_HAND_JOINT_WRIST_EXT, + XR_HAND_JOINT_WRIST_EXT +}; + +std::vector cellColors = { + {1.0, 0.0, 0.0}, + {1.0, 0.0, 0.0}, + {1.0, 0.2, 0.0}, + {1.0, 0.2, 0.0}, + {1.0, 0.4, 0.0}, + {1.0, 0.4, 0.0}, + {1.0, 0.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.0, 0.0, 0.0}, + {0.0, 0.0, 0.0}, + {0.0, 0.0, 0.5} +}; + +const OVR::Quatf cellAdjustL = + OVR::Quatf({1.0f, 0.0f, 0.0f}, OVR::DegreeToRad(180.0f)) * + OVR::Quatf({0.0f, 1.0f, 0.0f}, OVR::DegreeToRad(-90.0f)); +const OVR::Quatf cellAdjustR = + OVR::Quatf({0.0f, 1.0f, 0.0f}, OVR::DegreeToRad(90.0f)); + +/* clang-format off */ + + +void HandMaskRenderer::Init(bool leftHand) { + /// Shader + static ovrProgramParm UniformParms[] = { + {"JointMatrices", ovrProgramParmType::BUFFER_UNIFORM}, + {"JointColors", ovrProgramParmType::BUFFER_UNIFORM}, + {"LayerBlend", ovrProgramParmType::FLOAT}, + {"Falloff", ovrProgramParmType::FLOAT}, + {"Intensity", ovrProgramParmType::FLOAT}, + {"FadeIntensity", ovrProgramParmType::FLOAT}, + }; + + ProgHandMaskAlphaGradient = GlProgram::Build( + "", + VertexShaderSrc, + "", + FragmentShaderSrc, + UniformParms, + sizeof(UniformParms) / sizeof(ovrProgramParm)); + ProgHandMaskBorderFade = GlProgram::Build( + "#define USE_BORDER_FADE 1", + VertexShaderSrc, + "#define USE_BORDER_FADE 1", + FragmentShaderSrc, + UniformParms, + sizeof(UniformParms) / sizeof(ovrProgramParm)); + + /// Shader instance buffer + HandMaskMatrices.resize(MAX_JOINTS, OVR::Matrix4f::Identity()); + HandMaskUniformBuffer.Create( + GLBUFFER_TYPE_UNIFORM, MAX_JOINTS * sizeof(Matrix4f), HandMaskMatrices.data()); + + HandMaskColors.resize(MAX_JOINTS, OVR::Vector3f(0.0f,0.0f,0.0f)); + HandColorUniformBuffer.Create( + GLBUFFER_TYPE_UNIFORM, MAX_JOINTS * sizeof(Vector3f), HandMaskColors.data()); + + /// Create surface definition + HandMaskSurfaceDef.surfaceName = leftHand ? "HandMaskSurfaceL" : "HandMaskSurfaceR"; + HandMaskSurfaceDef.geo = BuildTesselatedQuad(1, 1, false); + HandMaskSurfaceDef.numInstances = 0; + /// Build the graphics command + auto& gc = HandMaskSurfaceDef.graphicsCommand; + gc.Program = ProgHandMaskBorderFade; + gc.UniformData[0].Data = &HandMaskUniformBuffer; + gc.UniformData[1].Data = &HandColorUniformBuffer; + gc.UniformData[2].Data = &LayerBlend; + gc.UniformData[3].Data = &Falloff; + gc.UniformData[4].Data = &Intensity; + gc.UniformData[5].Data = &FadeIntensity; + gc.GpuState.blendEnable = ovrGpuState::BLEND_ENABLE; + gc.GpuState.blendMode = GL_FUNC_REVERSE_SUBTRACT; + gc.GpuState.blendSrc = GL_SRC_ALPHA; + gc.GpuState.blendDst = GL_ONE_MINUS_SRC_ALPHA; + gc.GpuState.depthEnable = false; + gc.GpuState.depthMaskEnable = false; + /// Add surface + HandMaskSurface.surface = &(HandMaskSurfaceDef); + + /// Set defaults + LayerBlend = 1.0f; + Falloff = 4.0; + Intensity = 15.0f; + FadeIntensity = 0.75f; + UseBorderFade = false; + BorderFadeSize = 0.01f; + AlphaMaskSize = 0.0175f; + RenderInverseSubtract = false; + + /// Set hand + IsLeftHand = leftHand; +} + +void HandMaskRenderer::Shutdown() {} + +inline Vector3f GetViewMatrixForward(const Matrix4f& m) { + return Vector3f(-m.M[2][0], -m.M[2][1], -m.M[2][2]).Normalized(); +} + +inline Matrix4f GetViewMatrixFromPose(const OVR::Posef& pose) { + const Matrix4f transform = Matrix4f(pose); + return transform.Inverted(); +} + +void HandMaskRenderer::Update( + const OVR::Posef& headPose, + const OVR::Posef& handPose, + const std::vector& jointTransforms, + const float handSize) { + /// get view position + const Matrix4f centerEyeViewMatrix = GetViewMatrixFromPose(headPose); + const Matrix4f invViewMatrix = centerEyeViewMatrix.Inverted(); + const Vector3f viewPos = invViewMatrix.GetTranslation(); + + /// apply hand transform to the bones + const Matrix4f matDeviceModel = Matrix4f(handPose); + const float particleSize = (UseBorderFade ? BorderFadeSize : AlphaMaskSize) * handSize; + + auto& gc = HandMaskSurfaceDef.graphicsCommand; + gc.Program = UseBorderFade ? ProgHandMaskBorderFade : ProgHandMaskAlphaGradient; + if (RenderInverseSubtract) { + gc.GpuState.blendMode = GL_FUNC_REVERSE_SUBTRACT; + gc.GpuState.blendSrc = GL_SRC_ALPHA; + gc.GpuState.blendDst = GL_ONE_MINUS_SRC_ALPHA; + } else { + gc.GpuState.blendMode = GL_FUNC_ADD; + gc.GpuState.blendSrc = GL_SRC_ALPHA; + gc.GpuState.blendDst = GL_ONE_MINUS_SRC_ALPHA; + } + + const OVR::Quatf q = (IsLeftHand ? cellAdjustL : cellAdjustR).Inverted(); + for (uint32_t i = 0; i < cells.size(); ++i) { + const uint32_t parent = cellParents[i]; + + /// convert the cells to the adjusted screen space from the initial + /// space provided by design + Vector3f offset = IsLeftHand ? cells[i] * -1.0f : cells[i]; + offset.x *= -1.0f; + offset = q.Rotate(offset); + const Matrix4f cellOffset = Matrix4f::Translation(offset); + + const Matrix4f m = (matDeviceModel * (jointTransforms[parent] * cellOffset)); + const Vector3f pos = m.GetTranslation(); + Vector3f normal = (viewPos - pos).Normalized(); + if (normal.LengthSq() < 0.999f) { + normal = GetViewMatrixForward(centerEyeViewMatrix); + } + Matrix4f t = Matrix4f::CreateFromBasisVectors(normal, Vector3f(0.0f, 1.0f, 0.0f)); + t.SetTranslation(pos); + t = t * Matrix4f::Scaling(particleSize); + HandMaskMatrices[i] = t.Transposed(); +/// colorize mask for debug purposes +#if 0 + HandMaskColors[i] = cellColors[i]; +#endif + } + HandMaskSurface.modelMatrix = Matrix4f(); + HandMaskSurfaceDef.numInstances = cells.size(); + HandMaskUniformBuffer.Update( + HandMaskMatrices.size() * sizeof(Matrix4f), HandMaskMatrices.data()); + HandColorUniformBuffer.Update( + HandMaskColors.size() * sizeof(Vector3f), HandMaskColors.data()); +} + +void HandMaskRenderer::Render(std::vector& surfaceList) { + surfaceList.push_back(HandMaskSurface); +} + +} // namespace OVRFW diff --git a/Samples/SampleXrFramework/Src/Input/HandMaskRenderer.h b/Samples/SampleXrFramework/Src/Input/HandMaskRenderer.h new file mode 100755 index 0000000..8e20d9c --- /dev/null +++ b/Samples/SampleXrFramework/Src/Input/HandMaskRenderer.h @@ -0,0 +1,77 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/************************************************************************************ + +Filename : HandMaskRenderer.h +Content : A one stop for rendering hand masks +Created : 03/24/2020 +Authors : Federico Schliemann + +************************************************************************************/ + +#pragma once + +#include +#include +#include + +/// Sample Framework +#include "Misc/Log.h" +#include "Model/SceneView.h" +#include "Render/GlProgram.h" +#include "Render/SurfaceRender.h" + +#include "OVR_Math.h" + +#if defined(ANDROID) +#define XR_USE_GRAPHICS_API_OPENGL_ES 1 +#define XR_USE_PLATFORM_ANDROID 1 +#elif defined(WIN32) +#include +#define XR_USE_GRAPHICS_API_OPENGL 1 +#define XR_USE_PLATFORM_WIN32 1 +#endif // defined(ANDROID) + +#include +#include +#include + +namespace OVRFW { + +class HandMaskRenderer { + public: + HandMaskRenderer() = default; + ~HandMaskRenderer() = default; + + void Init(bool leftHand); + void Shutdown(); + void Update( + const OVR::Posef& headPose, + const OVR::Posef& handPose, + const std::vector& jointTransforms, + const float handSize = 1.0f); + void Render(std::vector& surfaceList); + + public: + float LayerBlend; + float Falloff; + float Intensity; + float FadeIntensity; + bool UseBorderFade; + float BorderFadeSize; + float AlphaMaskSize; + bool RenderInverseSubtract; + ovrSurfaceDef HandMaskSurfaceDef; + + private: + GlProgram ProgHandMaskAlphaGradient; + GlProgram ProgHandMaskBorderFade; + ovrDrawSurface HandMaskSurface; + std::vector HandMaskMatrices; + std::vector HandMaskColors; + GlBuffer HandMaskUniformBuffer; + GlBuffer HandColorUniformBuffer; + bool IsLeftHand; +}; + +} // namespace OVRFW diff --git a/Samples/SampleXrFramework/Src/Input/HandRenderer.cpp b/Samples/SampleXrFramework/Src/Input/HandRenderer.cpp new file mode 100755 index 0000000..7a5da90 --- /dev/null +++ b/Samples/SampleXrFramework/Src/Input/HandRenderer.cpp @@ -0,0 +1,297 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/************************************************************************************ + +Filename : HandRenderer.cpp +Content : A one stop for rendering hands +Created : April 2020 +Authors : Federico Schliemann + +************************************************************************************/ + +#include "HandRenderer.h" +#include "XrApp.h" + +using OVR::Matrix4f; +using OVR::Posef; +using OVR::Quatf; +using OVR::Vector3f; +using OVR::Vector4f; + +namespace OVRFW { + +namespace Hand { + +/// clang-format off +static_assert(MAX_JOINTS == 64, "MAX_JOINTS != 64"); +const char* VertexShaderSrc = R"glsl( + uniform JointMatrices + { + highp mat4 Joints[64]; + } jb; + attribute highp vec4 Position; + attribute highp vec3 Normal; + attribute highp vec3 Tangent; + attribute highp vec3 Binormal; + attribute highp vec2 TexCoord; + attribute highp vec4 JointWeights; + attribute highp vec4 JointIndices; + + varying lowp vec3 oEye; + varying lowp vec3 oNormal; + varying lowp vec2 oTexCoord; + + vec3 multiply( mat4 m, vec3 v ) + { + return vec3( + m[0].x * v.x + m[1].x * v.y + m[2].x * v.z, + m[0].y * v.x + m[1].y * v.y + m[2].y * v.z, + m[0].z * v.x + m[1].z * v.y + m[2].z * v.z ); + } + vec3 transposeMultiply( mat4 m, vec3 v ) + { + return vec3( + m[0].x * v.x + m[0].y * v.y + m[0].z * v.z, + m[1].x * v.x + m[1].y * v.y + m[1].z * v.z, + m[2].x * v.x + m[2].y * v.y + m[2].z * v.z ); + } + void main() + { + highp vec4 localPos1 = jb.Joints[int(JointIndices.x)] * Position; + highp vec4 localPos2 = jb.Joints[int(JointIndices.y)] * Position; + highp vec4 localPos3 = jb.Joints[int(JointIndices.z)] * Position; + highp vec4 localPos4 = jb.Joints[int(JointIndices.w)] * Position; + highp vec4 localPos = localPos1 * JointWeights.x + + localPos2 * JointWeights.y + + localPos3 * JointWeights.z + + localPos4 * JointWeights.w; + gl_Position = TransformVertex( localPos ); + + highp vec3 eye = transposeMultiply( sm.ViewMatrix[VIEW_ID], -vec3( sm.ViewMatrix[VIEW_ID][3] ) ); + oEye = eye - vec3( ModelMatrix * Position ); + + highp vec3 localNormal1 = multiply(jb.Joints[int(JointIndices.x)], Normal); + highp vec3 localNormal2 = multiply(jb.Joints[int(JointIndices.y)], Normal); + highp vec3 localNormal3 = multiply(jb.Joints[int(JointIndices.z)], Normal); + highp vec3 localNormal4 = multiply(jb.Joints[int(JointIndices.w)], Normal); + highp vec3 localNormal = localNormal1 * JointWeights.x + + localNormal2 * JointWeights.y + + localNormal3 * JointWeights.z + + localNormal4 * JointWeights.w; + oNormal = normalize(multiply(ModelMatrix, localNormal)); + + oTexCoord = TexCoord; + } +)glsl"; + +static const char* FragmentShaderSrc = R"glsl( + precision lowp float; + + uniform sampler2D Texture0; + uniform lowp vec3 SpecularLightDirection; + uniform lowp vec3 SpecularLightColor; + uniform lowp vec3 AmbientLightColor; + uniform lowp vec3 GlowColor; + uniform float Confidence; + uniform float Solidity; + + varying lowp vec3 oEye; + varying lowp vec3 oNormal; + varying lowp vec2 oTexCoord; + + lowp vec3 multiply( lowp mat3 m, lowp vec3 v ) + { + return vec3( + m[0].x * v.x + m[1].x * v.y + m[2].x * v.z, + m[0].y * v.x + m[1].y * v.y + m[2].y * v.z, + m[0].z * v.x + m[1].z * v.y + m[2].z * v.z ); + } + + lowp float pow5( float x ) + { + float x2 = x * x; + return x2 * x2 * x; + } + + lowp float pow16( float x ) + { + float x2 = x * x; + float x4 = x2 * x2; + float x8 = x4 * x4; + float x16 = x8 * x8; + return x16; + } + + void main() + { + lowp vec3 eyeDir = normalize( oEye.xyz ); + lowp vec3 Normal = normalize( oNormal ); + + lowp vec4 diffuse = texture2D( Texture0, oTexCoord ); + lowp vec3 ambientValue = diffuse.xyz * AmbientLightColor; + + lowp float nDotL = max( dot( Normal, SpecularLightDirection ), 0.0 ); + lowp vec3 diffuseValue = diffuse.xyz * SpecularLightColor * nDotL; + + lowp vec3 H = normalize( SpecularLightDirection + eyeDir ); + lowp float nDotH = max( dot( Normal, H ), 0.0 ); + + lowp float specularPower = 1.0f - diffuse.a; + specularPower = specularPower * specularPower; + lowp float specularIntensity = pow16( nDotH ); + lowp vec3 specularValue = specularIntensity * SpecularLightColor; + + lowp float vDotN = dot( eyeDir, Normal ); + lowp float fresnel = clamp( pow5( 1.0 - vDotN ), 0.0, 1.0 ); + lowp vec3 fresnelValue = GlowColor * fresnel; + lowp vec3 handColor = diffuseValue + + ambientValue + + specularValue + + fresnelValue + ; + gl_FragColor.xyz = handColor; + gl_FragColor.w = (Solidity + (1.0f - Solidity) * ( clamp( fresnel, 0.0, 0.7 ) + 0.3 ) * Confidence); + } +)glsl"; +/// clang-format on + +} // namespace Hand + +bool HandRenderer::Init(const XrHandTrackingMeshFB* mesh, bool leftHand) { + /// Shader + ovrProgramParm UniformParms[] = { + {"Texture0", ovrProgramParmType::TEXTURE_SAMPLED}, + {"SpecularLightDirection", ovrProgramParmType::FLOAT_VECTOR3}, + {"SpecularLightColor", ovrProgramParmType::FLOAT_VECTOR3}, + {"AmbientLightColor", ovrProgramParmType::FLOAT_VECTOR3}, + {"JointMatrices", ovrProgramParmType::BUFFER_UNIFORM}, + {"GlowColor", ovrProgramParmType::FLOAT_VECTOR3}, + {"Confidence", ovrProgramParmType::FLOAT}, + {"Solidity", ovrProgramParmType::FLOAT}, + }; + ProgHand = GlProgram::Build( + "", + Hand::VertexShaderSrc, + "", + Hand::FragmentShaderSrc, + UniformParms, + sizeof(UniformParms) / sizeof(ovrProgramParm)); + + /// Build geometry from mesh + VertexAttribs attribs; + std::vector indices; + attribs.position.resize(mesh->vertexCountOutput); + memcpy( + attribs.position.data(), + &mesh->vertexPositions[0], + mesh->vertexCountOutput * sizeof(XrVector3f)); + attribs.normal.resize(mesh->vertexCountOutput); + memcpy( + attribs.normal.data(), + &mesh->vertexNormals[0], + mesh->vertexCountOutput * sizeof(XrVector3f)); + attribs.uv0.resize(mesh->vertexCountOutput); + memcpy(attribs.uv0.data(), &mesh->vertexUVs[0], mesh->vertexCountOutput * sizeof(XrVector2f)); + attribs.jointIndices.resize(mesh->vertexCountOutput); + /// We can't do a straight copy heere since the sizes don't match + for (std::uint32_t i = 0; i < mesh->vertexCountOutput; ++i) { + const XrVector4sFB& blendIndices = mesh->vertexBlendIndices[i]; + attribs.jointIndices[i].x = blendIndices.x; + attribs.jointIndices[i].y = blendIndices.y; + attribs.jointIndices[i].z = blendIndices.z; + attribs.jointIndices[i].w = blendIndices.w; + } + attribs.jointWeights.resize(mesh->vertexCountOutput); + memcpy( + attribs.jointWeights.data(), + &mesh->vertexBlendWeights[0], + mesh->vertexCountOutput * sizeof(XrVector4f)); + indices.resize(mesh->indexCountOutput); + memcpy(indices.data(), mesh->indices, mesh->indexCountOutput * sizeof(int16_t)); + + /// Model/Render buffers + TransformMatrices.resize(MAX_JOINTS, OVR::Matrix4f::Identity()); + BindMatrices.resize(MAX_JOINTS, OVR::Matrix4f::Identity()); + SkinMatrices.resize(MAX_JOINTS, OVR::Matrix4f::Identity()); + SkinUniformBuffer.Create( + GLBUFFER_TYPE_UNIFORM, MAX_JOINTS * sizeof(Matrix4f), SkinMatrices.data()); + + /// Skeleton/Bind pose + for (int i = 0; i < XR_HAND_JOINT_COUNT_EXT; ++i) { + const OVR::Posef pose = FromXrPosef(mesh->jointBindPoses[i]); + TransformMatrices[i] = Matrix4f(pose); + BindMatrices[i] = TransformMatrices[i].Inverted(); + } + + /// Create surface definition + HandSurfaceDef.surfaceName = leftHand ? "HandSurfaceL" : "HandkSurfaceR"; + HandSurfaceDef.geo.Create(attribs, indices); + HandSurfaceDef.numInstances = 0; + /// Build the graphics command + ovrGraphicsCommand& gc = HandSurfaceDef.graphicsCommand; + /// Program + gc.Program = ProgHand; + /// Uniforms to match UniformParms abovve + gc.UniformData[0].Data = &gc.Textures[0]; + gc.UniformData[1].Data = &SpecularLightDirection; + gc.UniformData[2].Data = &SpecularLightColor; + gc.UniformData[3].Data = &AmbientLightColor; + gc.UniformData[4].Count = MAX_JOINTS; + gc.UniformData[4].Data = &SkinUniformBuffer; + gc.UniformData[5].Data = &GlowColor; + gc.UniformData[6].Data = &Confidence; + gc.UniformData[7].Data = &Solidity; + /// gpu state needs alpha blending + gc.GpuState.depthEnable = gc.GpuState.depthMaskEnable = true; + gc.GpuState.blendEnable = ovrGpuState::BLEND_ENABLE; + gc.GpuState.blendSrc = GL_SRC_ALPHA; + gc.GpuState.blendDst = GL_ONE_MINUS_SRC_ALPHA; + + /// Add surface + HandSurface.surface = &(HandSurfaceDef); + HandSurface.modelMatrix = Matrix4f::Identity(); + + /// Set defaults + SpecularLightDirection = Vector3f(1.0f, 1.0f, 0.0f); + SpecularLightColor = Vector3f(1.0f, 0.95f, 0.8f) * 0.75f; + AmbientLightColor = Vector3f(1.0f, 1.0f, 1.0f) * 0.15f; + GlowColor = OVR::Vector3f(0.75f); + Confidence = 1.0f; + Solidity = 0.0f; + + /// Set hand + isLeftHand = leftHand; + + /// all good + return true; +} + +void HandRenderer::Shutdown() { + OVRFW::GlProgram::Free(ProgHand); + HandSurfaceDef.geo.Free(); +} + +void HandRenderer::Update(const XrHandJointLocationEXT* joints, const float scale) { + /// Compute transform for the root + const OVR::Posef root = FromXrPosef(joints[XR_HAND_JOINT_WRIST_EXT].pose); + const OVR::Matrix4f rootMatrix = OVR::Matrix4f(root); + HandSurface.modelMatrix = rootMatrix * OVR::Matrix4f::Scaling(scale); + const OVR::Matrix4f rootMatrixInv = rootMatrix.Inverted(); + + /// update transforms + for (int i = 0; i < XR_HAND_JOINT_COUNT_EXT; ++i) { + /// Compute transform + const OVR::Posef pose = FromXrPosef(joints[i].pose); + TransformMatrices[i] = rootMatrixInv * Matrix4f(pose); + Matrix4f m = TransformMatrices[i] * BindMatrices[i]; + SkinMatrices[i] = m.Transposed(); + } + /// Update the shader uniform parameters + SkinUniformBuffer.Update(SkinMatrices.size() * sizeof(Matrix4f), SkinMatrices.data()); +} + +void HandRenderer::Render(std::vector& surfaceList) { + surfaceList.push_back(HandSurface); +} + +} // namespace OVRFW diff --git a/Samples/SampleXrFramework/Src/Input/HandRenderer.h b/Samples/SampleXrFramework/Src/Input/HandRenderer.h new file mode 100755 index 0000000..9ef70c3 --- /dev/null +++ b/Samples/SampleXrFramework/Src/Input/HandRenderer.h @@ -0,0 +1,77 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/************************************************************************************ + +Filename : HandRenderer.h +Content : A one stop for rendering hands +Created : April 2020 +Authors : Federico Schliemann + +************************************************************************************/ + +#pragma once + +#include +#include +#include + +/// Sample Framework +#include "Misc/Log.h" +#include "Model/SceneView.h" +#include "Render/GlProgram.h" +#include "Render/SurfaceRender.h" + +#include "OVR_Math.h" + +#if defined(ANDROID) +#define XR_USE_GRAPHICS_API_OPENGL_ES 1 +#define XR_USE_PLATFORM_ANDROID 1 +#elif defined(WIN32) +#include +#define XR_USE_GRAPHICS_API_OPENGL 1 +#define XR_USE_PLATFORM_WIN32 1 +#endif // defined(ANDROID) + +#include +#include +#include + +namespace OVRFW { + +class HandRenderer { + public: + HandRenderer() = default; + ~HandRenderer() = default; + + bool Init(const XrHandTrackingMeshFB* mesh, bool leftHand); + void Shutdown(); + void Update(const XrHandJointLocationEXT* joints, const float scale = 1.0f); + void Render(std::vector& surfaceList); + + bool IsLeftHand() const { + return isLeftHand; + } + const std::vector& Transforms() const { + return TransformMatrices; + } + + public: + OVR::Vector3f SpecularLightDirection; + OVR::Vector3f SpecularLightColor; + OVR::Vector3f AmbientLightColor; + OVR::Vector3f GlowColor; + float Confidence; + float Solidity; + + private: + bool isLeftHand; + GlProgram ProgHand; + ovrSurfaceDef HandSurfaceDef; + ovrDrawSurface HandSurface; + std::vector TransformMatrices; + std::vector BindMatrices; + std::vector SkinMatrices; + GlBuffer SkinUniformBuffer; +}; + +} // namespace OVRFW diff --git a/Samples/SampleXrFramework/Src/Input/Skeleton.cpp b/Samples/SampleXrFramework/Src/Input/Skeleton.cpp new file mode 100755 index 0000000..cc39fcd --- /dev/null +++ b/Samples/SampleXrFramework/Src/Input/Skeleton.cpp @@ -0,0 +1,100 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/************************************************************************************ + +Filename : Skeleton.cpp +Content : skeleton for arm model implementation +Created : 2/20/2017 +Authors : Jonathan E. Wright + +************************************************************************************/ + +#include "Skeleton.h" +#include + +using OVR::Posef; + +namespace OVRFW { + +ovrSkeleton::ovrSkeleton() : WorldSpaceDirty(true) {} + +const ovrJoint& ovrSkeleton::GetJoint(int const idx) const { + assert(idx >= 0 && idx < static_cast(Joints.size())); + return Joints[idx]; +} + +int ovrSkeleton::GetParentIndex(int const idx) const { + if (idx < 0 || idx >= static_cast(Joints.size())) { + return -1; + } + return Joints[idx].ParentIndex; +} + +void ovrSkeleton::SetJoints(const std::vector& newJoints) { + Joints = newJoints; + LocalSpacePoses.resize(Joints.size()); + WorldSpacePoses.resize(Joints.size()); + + /// Set local + for (int i = 0; i < (int)Joints.size(); ++i) { + LocalSpacePoses[i] = Joints[i].Pose; + } + + /// Set World + WorldSpaceDirty = true; + UpdateWorldFromLocal(); +} + +void ovrSkeleton::UpdateWorldFromLocal() const { + if (false == WorldSpaceDirty) + return; + + assert(Joints.size() == LocalSpacePoses.size()); + assert(Joints.size() == WorldSpacePoses.size()); + for (int i = 0; i < (int)Joints.size(); ++i) { + WorldSpacePoses[i] = (Joints[i].ParentIndex < 0) + ? LocalSpacePoses[i] + : (WorldSpacePoses[Joints[i].ParentIndex] * LocalSpacePoses[i]); + } + WorldSpaceDirty = false; +} + +void ovrSkeleton::TransformLocal(const OVR::Posef& t, int idx) { + assert(idx >= 0 && idx < static_cast(Joints.size())); + if (idx >= 0 && idx < static_cast(Joints.size())) { + LocalSpacePoses[idx] = Joints[idx].Pose * t; + WorldSpaceDirty = true; + } +} + +void ovrSkeleton::UpdateLocalRotation(const OVR::Quatf& q, int idx) { + assert(idx >= 0 && idx < static_cast(Joints.size())); + if (idx >= 0 && idx < static_cast(Joints.size())) { + LocalSpacePoses[idx].Rotation = q; + WorldSpaceDirty = true; + } +} + +void ovrSkeleton::UpdateLocalTranslation(const OVR::Vector3f& t, int idx) { + assert(idx >= 0 && idx < static_cast(Joints.size())); + LocalSpacePoses[idx].Translation = t; + WorldSpaceDirty = true; +} + +void ovrSkeleton::TransformWorld(const OVR::Posef& t, int idx) { + assert(idx >= 0 && idx < static_cast(Joints.size())); + if (idx >= 0 && idx < static_cast(Joints.size())) { + WorldSpacePoses[idx] = t; + const int paretnIndex = Joints[idx].ParentIndex; + if (paretnIndex < 0) { + // the root remains the same + LocalSpacePoses[idx] = t; + } else { + const Posef& parentPose = WorldSpacePoses[paretnIndex]; + LocalSpacePoses[idx] = parentPose.Inverted() * WorldSpacePoses[idx]; + } + WorldSpaceDirty = true; + } +} + +} // namespace OVRFW diff --git a/Samples/SampleXrFramework/Src/Input/Skeleton.h b/Samples/SampleXrFramework/Src/Input/Skeleton.h new file mode 100755 index 0000000..352f464 --- /dev/null +++ b/Samples/SampleXrFramework/Src/Input/Skeleton.h @@ -0,0 +1,79 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/************************************************************************************ + +Filename : Skeleton.h +Content : Skeleton for arm and hand model implementation +Created : 2/20/2017 +Authors : Jonathan E. Wright, Federico Schliemann + +************************************************************************************/ + +#pragma once + +#include + +#include "OVR_Math.h" + +namespace OVRFW { + +class ovrJoint { + public: + ovrJoint() : Name(nullptr), Color(1.0f), ParentIndex(-1) {} + + ovrJoint( + const char* name, + const OVR::Vector4f& color, + const OVR::Posef& pose, + const int parentIndex) + : Name(name), Color(color), Pose(pose), ParentIndex(parentIndex) {} + + char const* Name; // name of the joint + OVR::Vector4f Color; + OVR::Posef Pose; // pose of this joint + int ParentIndex; // index of this joint's parent +}; + +class ovrSkeleton { + public: + ovrSkeleton(); + + const ovrJoint& GetJoint(int const idx) const; + int GetParentIndex(int const idx) const; + const std::vector& GetJoints() const { + return Joints; + } + const std::vector& GetLocalSpacePoses() const { + return LocalSpacePoses; + } + const std::vector& GetWorldSpacePoses(bool updateFromLocal = true) const { + if (updateFromLocal) { + UpdateWorldFromLocal(); + } + return WorldSpacePoses; + } + void SetJoints(const std::vector& newJoints); + void TransformLocal(const OVR::Posef& t, int idx); + void TransformWorld(const OVR::Posef& t, int idx); + void UpdateLocalRotation(const OVR::Quatf& q, int idx); + void UpdateLocalTranslation(const OVR::Vector3f& t, int idx); + + private: + void UpdateWorldFromLocal() const; + + /// Essentially a BIND pose for the skeleton + std::vector Joints; + + /// Current skeleton state for each joint + std::vector LocalSpacePoses; + + /// These two are semantically `const` but in practice lazily updated whenever + /// UpdateWorldFromLocal is called on the const GetWorldSpacePoses method. + /// The expectation is that calling any of the Transform methods will update LocalSpacePoses + /// instantly and invalidate WorldSpaceDirty so that they can be lazily updated on demand. + /// Declaring them mutable lets them change inside a `const` method deliberately. + mutable std::vector WorldSpacePoses; + mutable bool WorldSpaceDirty; +}; + +} // namespace OVRFW diff --git a/Samples/SampleXrFramework/Src/Input/SkeletonRenderer.cpp b/Samples/SampleXrFramework/Src/Input/SkeletonRenderer.cpp new file mode 100755 index 0000000..cada94e --- /dev/null +++ b/Samples/SampleXrFramework/Src/Input/SkeletonRenderer.cpp @@ -0,0 +1,198 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/************************************************************************************ + +Filename : SkeletonRenderer.cpp +Content : A rendering component for sample skeletons +Created : April 2020 +Authors : Federico Schliemann + +************************************************************************************/ + +#include "SkeletonRenderer.h" +#include "Misc/Log.h" + +using OVR::Matrix4f; +using OVR::Posef; +using OVR::Quatf; +using OVR::Vector3f; +using OVR::Vector4f; + +namespace OVRFW { + +bool ovrSkeletonRenderer::Init(OVRFW::ovrFileSys* fs) { + if (!fs) { + ALOG("ovrSkeletonRenderer::Init FAILED -> NULL ovrFileSys."); + return false; + } + OVRFW::ovrFileSys& FileSys = *fs; + + /// Defaults + BoneColor = OVR::Vector4f(0.0f, 0.0f, 1.0f, 1.0f); + JointRadius = 0.008f; + BoneWidth = 0.005f; + DrawAxis = false; + + /// Create Beam & Particle Renderers + SpriteAtlas = std::make_unique(); + if (SpriteAtlas) { + SpriteAtlas->Init(FileSys, "apk:///assets/particles2.ktx"); + SpriteAtlas->BuildSpritesFromGrid(4, 2, 8); + ParticleSystem = std::make_unique(); + ParticleSystem->Init( + 2048, SpriteAtlas.get(), ovrParticleSystem::GetDefaultGpuState(), false); + } else { + ALOG("ovrSkeletonRenderer::Init FAILED -> could not create SpriteAtlas."); + return false; + } + BeamAtlas = std::make_unique(); + if (BeamAtlas) { + BeamAtlas->Init(FileSys, "apk:///assets/beams.ktx"); + BeamAtlas->BuildSpritesFromGrid(2, 1, 2); + BeamRenderer = std::make_unique(); + BeamRenderer->Init(256, true); + } else { + ALOG("ovrSkeletonRenderer::Init FAILED -> could not create BeamAtlas."); + return false; + } + + /// Create Axis program + if (AxisRenderer.Init() == false) { + ALOG("ovrSkeletonRenderer::Init FAILED -> could not create AxisRenderer."); + return false; + } + + return true; +} + +void ovrSkeletonRenderer::Shutdown() { + ParticleSystem->Shutdown(); + BeamRenderer->Shutdown(); + SpriteAtlas->Shutdown(); + BeamAtlas->Shutdown(); + AxisRenderer.Shutdown(); +} + +void ovrSkeletonRenderer::Render( + const OVR::Matrix4f& worldMatrix, + const std::vector& joints, + const ovrApplFrameIn& in, + OVRFW::ovrRendererOutput& out) { + if (joints.size() != JointHandles.size()) { + ResetBones(JointHandles); + JointHandles.resize(joints.size()); + } + + RenderBones(in, worldMatrix, joints, JointHandles, BoneColor, JointRadius, BoneWidth); + + const OVR::Matrix4f projectionMatrix = OVR::Matrix4f(); /// currently Unused + BeamRenderer->Frame(in, out.FrameMatrices.CenterView, *BeamAtlas); + ParticleSystem->Frame(in, SpriteAtlas.get(), out.FrameMatrices.CenterView); + ParticleSystem->RenderEyeView(out.FrameMatrices.CenterView, projectionMatrix, out.Surfaces); + BeamRenderer->RenderEyeView(out.FrameMatrices.CenterView, projectionMatrix, out.Surfaces); + + /// Instance the Axes ... + if (DrawAxis) { + std::vector poses(joints.size()); + for (size_t j = 0; j < joints.size(); ++j) { + poses[j] = joints[j].Pose; + } + AxisRenderer.Update(poses); + AxisRenderer.Render(worldMatrix, in, out); + } +} + +void ovrSkeletonRenderer::RenderBones( + const OVRFW::ovrApplFrameIn& frame, + const OVR::Matrix4f& worldMatrix, + const std::vector& joints, + jointHandles_t& handles, + const OVR::Vector4f& boneColor, + const float jointRadius, + const float boneWidth) { + const uint16_t particleAtlasIndex = 0; + const uint16_t beamAtlasIndex = 0; + OVRFW::ovrParticleSystem* ps = ParticleSystem.get(); + OVRFW::ovrBeamRenderer* br = BeamRenderer.get(); + OVRFW::ovrTextureAtlas& beamAtlas = *BeamAtlas.get(); + + for (int i = 0; i < static_cast(joints.size()); ++i) { + const ovrJoint& joint = joints[i]; + OVR::Vector3f jwPosition = worldMatrix.Transform(joint.Pose.Translation); + + OVR::Vector4f jointColor = joint.Color; + if (!handles[i].first.IsValid()) { + handles[i].first = ps->AddParticle( + frame, + jwPosition, + 0.0f, + Vector3f(0.0f), + Vector3f(0.0f), + jointColor, + ovrEaseFunc::NONE, + 0.0f, + jointRadius, + FLT_MAX, + particleAtlasIndex); + } else { + ps->UpdateParticle( + frame, + handles[i].first, + jwPosition, + 0.0f, + Vector3f(0.0f), + Vector3f(0.0f), + jointColor, + ovrEaseFunc::NONE, + 0.0f, + jointRadius, + FLT_MAX, + particleAtlasIndex); + } + + if (i > 0) { + const ovrJoint& parentJoint = joints[joint.ParentIndex]; + OVR::Vector3f pwPosition = worldMatrix.Transform(parentJoint.Pose.Translation); + + if (!handles[i].second.IsValid()) { + handles[i].second = br->AddBeam( + frame, + beamAtlas, + beamAtlasIndex, + boneWidth, + pwPosition, + jwPosition, + boneColor, + ovrBeamRenderer::LIFETIME_INFINITE); + } else { + br->UpdateBeam( + frame, + handles[i].second, + beamAtlas, + beamAtlasIndex, + boneWidth, + pwPosition, + jwPosition, + boneColor); + } + } + } +} + +void ovrSkeletonRenderer::ResetBones(jointHandles_t& handles) { + OVRFW::ovrParticleSystem* ps = ParticleSystem.get(); + OVRFW::ovrBeamRenderer* br = BeamRenderer.get(); + + for (int i = 0; i < static_cast(handles.size()); ++i) { + if (handles[i].first.IsValid()) { + ps->RemoveParticle(handles[i].first); + handles[i].first.Release(); + } + if (handles[i].second.IsValid()) { + br->RemoveBeam(handles[i].second); + handles[i].second.Release(); + } + } +} + +} // namespace OVRFW diff --git a/Samples/SampleXrFramework/Src/Input/SkeletonRenderer.h b/Samples/SampleXrFramework/Src/Input/SkeletonRenderer.h new file mode 100755 index 0000000..89b9644 --- /dev/null +++ b/Samples/SampleXrFramework/Src/Input/SkeletonRenderer.h @@ -0,0 +1,70 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/************************************************************************************ + +Filename : SkeletonRenderer.h +Content : A rendering component for sample skeletons +Created : April 2020 +Authors : Federico Schliemann + +************************************************************************************/ + +#pragma once + +#include +#include + +#include "Input/Skeleton.h" +#include "OVR_FileSys.h" +#include "Render/TextureAtlas.h" +#include "Render/BeamRenderer.h" +#include "Render/ParticleSystem.h" +#include "Render/GlProgram.h" + +#include "AxisRenderer.h" + +namespace OVRFW { + +typedef std::vector> + jointHandles_t; + +class ovrSkeletonRenderer { + public: + ovrSkeletonRenderer() = default; + ~ovrSkeletonRenderer() = default; + + bool Init(OVRFW::ovrFileSys* FileSys); + void Shutdown(); + void Render( + const OVR::Matrix4f& worldMatrix, + const std::vector& joints, + const ovrApplFrameIn& in, + OVRFW::ovrRendererOutput& out); + + public: + OVR::Vector4f BoneColor; + float JointRadius; + float BoneWidth; + bool DrawAxis; + + protected: + void ResetBones(jointHandles_t& handles); + void RenderBones( + const ovrApplFrameIn& frame, + const OVR::Matrix4f& worldMatrix, + const std::vector& joints, + jointHandles_t& handles, + const OVR::Vector4f& boneColor, + const float jointRadius, + const float boneWidth); + + private: + std::unique_ptr ParticleSystem; + std::unique_ptr BeamRenderer; + std::unique_ptr SpriteAtlas; + std::unique_ptr BeamAtlas; + jointHandles_t JointHandles; + OVRFW::ovrAxisRenderer AxisRenderer; +}; + +} // namespace OVRFW diff --git a/Samples/SampleXrFramework/Src/Input/TinyUI.cpp b/Samples/SampleXrFramework/Src/Input/TinyUI.cpp new file mode 100755 index 0000000..7a8dfc5 --- /dev/null +++ b/Samples/SampleXrFramework/Src/Input/TinyUI.cpp @@ -0,0 +1,433 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/************************************************************************************ + +Filename : TinyUI.cpp +Content : Componentized wrappers for GuiSys +Created : July 2020 +Authors : Federico Schliemann + +************************************************************************************/ + +#include "TinyUI.h" + +#include +#include +#include +#include +#include + +#include "GUI/GuiSys.h" +#include "GUI/DefaultComponent.h" +#include "GUI/ActionComponents.h" +#include "GUI/VRMenu.h" +#include "GUI/VRMenuObject.h" +#include "GUI/VRMenuMgr.h" +#include "GUI/Reflection.h" +#include "Render/DebugLines.h" + +using OVR::Matrix4f; +using OVR::Posef; +using OVR::Quatf; +using OVR::Vector2f; +using OVR::Vector3f; +using OVR::Vector4f; + +namespace OVRFW { + +static const char* MenuDefinitionFile = R"menu_definition( +itemParms { + // panel + VRMenuObjectParms { + Type = VRMENU_STATIC; + Flags = VRMENUOBJECT_RENDER_HIERARCHY_ORDER; + TexelCoords = true; + SurfaceParms { + VRMenuSurfaceParms { + SurfaceName = "panel"; + ImageNames { + string[0] = "apk:///assets/panel.ktx"; + } + TextureTypes { + eSurfaceTextureType[0] = SURFACE_TEXTURE_DIFFUSE; + } + Color = ( 0.0f, 0.0f, 0.1f, 1.0f ); // MENU_DEFAULT_COLOR + Border = ( 16.0f, 16.0f, 16.0f, 16.0f ); + Dims = ( 100.0f, 100.0f ); + } + } + Text = "Panel"; + LocalPose { + Position = ( 0.0f, 00.0f, 0.0f ); + Orientation = ( 0.0f, 0.0f, 0.0f, 1.0f ); + } + LocalScale = ( 100.0f, 100.0f, 1.0f ); + TextLocalPose { + Position = ( 0.0f, 0.0f, 0.0f ); + Orientation = ( 0.0f, 0.0f, 0.0f, 1.0f ); + } + TextLocalScale = ( 1.0f, 1.0f, 1.0f ); + FontParms { + AlignHoriz = HORIZONTAL_CENTER; + AlignVert = VERTICAL_CENTER; + Scale = 0.5f; + } + ParentId = -1; + Id = 0; + Name = "panel"; + } +} +)menu_definition"; + +class SimpleTargetMenu : public OVRFW::VRMenu { + public: + static SimpleTargetMenu* Create( + OVRFW::OvrGuiSys& guiSys, + OVRFW::ovrLocale& locale, + const std::string& menuName, + const std::string& text) { + return new SimpleTargetMenu(guiSys, locale, menuName, text); + } + + private: + SimpleTargetMenu( + OVRFW::OvrGuiSys& guiSys, + OVRFW::ovrLocale& locale, + const std::string& menuName, + const std::string& text) + : OVRFW::VRMenu(menuName.c_str()) { + std::vector buffer; + std::vector itemParms; + + size_t bufferLen = OVR::OVR_strlen(MenuDefinitionFile); + buffer.resize(bufferLen + 1); + memcpy(buffer.data(), MenuDefinitionFile, bufferLen); + buffer[bufferLen] = '\0'; + + OVRFW::ovrParseResult parseResult = OVRFW::VRMenuObject::ParseItemParms( + guiSys.GetReflection(), locale, menuName.c_str(), buffer, itemParms); + if (!parseResult) { + DeletePointerArray(itemParms); + ALOG("SimpleTargetMenu FAILED -> %s", parseResult.GetErrorText()); + return; + } + + /// Hijack params + for (auto* ip : itemParms) { + // Find the one panel + if ((int)ip->Id.Get() == 0) { + const_cast(ip)->Text = text; + } + } + + InitWithItems( + guiSys, + 2.0f, + OVRFW::VRMenuFlags_t(OVRFW::VRMENU_FLAG_SHORT_PRESS_HANDLED_BY_APP), + itemParms); + + DeletePointerArray(itemParms); + } + + virtual ~SimpleTargetMenu(){}; +}; + +bool TinyUI::Init( + const xrJava* context, + OVRFW::ovrFileSys* FileSys, + bool updateColors /* = true */, + int fontVertexBufferSize /* = 0 */) { + const xrJava* java = context; + UpdateColors = updateColors; + + /// Leftovers that aren't used + auto SoundEffectPlayer = new OvrGuiSys::ovrDummySoundEffectPlayer(); + if (nullptr == SoundEffectPlayer) { + ALOGE("Couldn't create SoundEffectPlayer"); + return false; + } + auto DebugLines = OvrDebugLines::Create(); + if (nullptr == DebugLines) { + ALOGE("Couldn't create DebugLines"); + return false; + } + DebugLines->Init(); + + /// Needed for FONTS + Locale = ovrLocale::Create(*java->Env, java->ActivityObject, "default"); + if (nullptr == Locale) { + ALOGE("Couldn't create Locale"); + return false; + } + std::string fontName; + GetLocale().GetLocalizedString("@string/font_name", "efigs.fnt", fontName); + + GuiSys = OvrGuiSys::Create(context); + if (nullptr == GuiSys) { + ALOGE("Couldn't create GUI"); + return false; + } + + if (fontVertexBufferSize > 0) { + GuiSys->Init( + FileSys, *SoundEffectPlayer, fontName.c_str(), DebugLines, fontVertexBufferSize); + } else { // Rely on default value for fontVertexBufferSize + GuiSys->Init(FileSys, *SoundEffectPlayer, fontName.c_str(), DebugLines); + } + + return true; +} + +void TinyUI::Shutdown() { + OvrGuiSys::Destroy(GuiSys); +} + +void TinyUI::AddHitTestRay(const OVR::Posef& ray, bool isClicking, int deviceNum) { + OVRFW::TinyUI::HitTestDevice device; + device.deviceNum = deviceNum; + device.pointerStart = ray.Transform({0.0f, 0.0f, 0.0f}); + device.pointerEnd = ray.Transform({0.0f, 0.0f, -1.0f}); + device.clicked = isClicking; + Devices.push_back(device); +} + +void TinyUI::Update(const OVRFW::ovrApplFrameIn& in) { + /// Hit test + + /// clear previous frame + if (UpdateColors) { + for (auto& device : PreviousFrameDevices) { + if (device.hitObject) { + // Don't change colors for normal labels + auto it = ButtonHandlers.find(device.hitObject); + if (it != ButtonHandlers.end()) { + device.hitObject->SetSurfaceColor(0, BackgroundColor); + } + } + } + } + + /// hit test + bool hitHandled = false; + for (auto& device : Devices) { + Vector3f pointerStart = device.pointerStart; + Vector3f pointerEnd = device.pointerEnd; + Vector3f pointerDir = (pointerEnd - pointerStart).Normalized(); + Vector3f targetEnd = pointerStart + pointerDir * 10.0f; + + HitTestResult hit = GuiSys->TestRayIntersection(pointerStart, pointerDir); + if (hit.HitHandle.IsValid()) { + device.pointerEnd = pointerStart + hit.RayDir * hit.t - pointerDir * 0.025f; + device.hitObject = GuiSys->GetVRMenuMgr().ToObject(hit.HitHandle); + if (device.hitObject != nullptr) { + /// we hit a menu, make sure it is a button with a registered handler + auto it = ButtonHandlers.find(device.hitObject); + if (it != ButtonHandlers.end()) { + /// hover highlight + if (UpdateColors) { + device.hitObject->SetSurfaceColor(0, HoverColor); + } + pointerEnd = targetEnd; + if (device.clicked) { + // debounce + for (auto& prevFrameDevice : PreviousFrameDevices) { + if (device.deviceNum == prevFrameDevice.deviceNum && + device.hitObject == prevFrameDevice.hitObject) { + if (prevFrameDevice.clicked == false) { + // click highlight + if (UpdateColors) { + device.hitObject->SetSurfaceColor(0, HighlightColor); + } + // run event handler + it->second(); + hitHandled = true; + } + break; + } + } + } + } + } + } + } + + if (!hitHandled && UnhandledClickHandler) { + for (auto& device : Devices) { + if (device.clicked) { + for (auto& prevFrameDevice : PreviousFrameDevices) { + if (device.deviceNum == prevFrameDevice.deviceNum && + prevFrameDevice.clicked == false) { + UnhandledClickHandler(); + } + } + } + } + } + + /// Save these for later + PreviousFrameDevices = Devices; +} + +void TinyUI::Render(const OVRFW::ovrApplFrameIn& in, OVRFW::ovrRendererOutput& out) { + const Matrix4f& traceMat = out.FrameMatrices.CenterView.Inverted(); + GuiSys->Frame(in, out.FrameMatrices.CenterView, traceMat); + GuiSys->AppendSurfaceList(out.FrameMatrices.CenterView, &out.Surfaces); +} + +OVRFW::VRMenuObject* TinyUI::CreateMenu( + const std::string& labelText, + const OVR::Vector3f& position, + const OVR::Vector2f& size) { + /// common naming + static uint32_t menuIndex = 3000; + menuIndex++; + std::stringstream ss; + ss << std::setprecision(4) << std::fixed; + ss << "tinyui_menu_" << menuIndex << "_"; + std::string menuName = ss.str(); + VRMenu* m = SimpleTargetMenu::Create(*GuiSys, *Locale, menuName, labelText); + if (m != nullptr) { + GuiSys->AddMenu(m); + GuiSys->OpenMenu(m->GetName()); + Posef pose = m->GetMenuPose(); + pose.Translation = position; + m->SetMenuPose(pose); + OvrVRMenuMgr& menuMgr = GuiSys->GetVRMenuMgr(); + VRMenuObject* mo = menuMgr.ToObject(m->GetRootHandle()); + if (mo != nullptr) { + mo = menuMgr.ToObject(mo->GetChildHandleForIndex(0)); + mo->SetSurfaceDims(0, size); + mo->RegenerateSurfaceGeometry(0, false); + /// remember everything + AllElements.push_back(mo); + Menus[mo] = m; + return mo; + } + } + return nullptr; +} + +OVRFW::VRMenuObject* TinyUI::AddLabel( + const std::string& labelText, + const OVR::Vector3f& position, + const OVR::Vector2f& size) { + return CreateMenu(labelText, position, size); +} + +OVRFW::VRMenuObject* TinyUI::AddButton( + const std::string& label, + const OVR::Vector3f& position, + const OVR::Vector2f& size, + const std::function& handler) { + auto b = CreateMenu(label, position, size); + if (b && handler) { + ButtonHandlers[b] = handler; + } + return b; +} + +OVRFW::VRMenuObject* TinyUI::AddToggleButton( + const std::string& labelTextOn, + const std::string& labelTextOff, + bool* value, + const OVR::Vector3f& position, + const OVR::Vector2f& size, + const std::function& postHandler) { + auto b = CreateMenu("", position, size); + const std::function& handler = [=]() { + if (b) { + *value = !(*value); + b->SetText(*value ? labelTextOn.c_str() : labelTextOff.c_str()); + if (postHandler) { + postHandler(); + } + } + }; + if (b && handler && value) { + b->SetText(*value ? labelTextOn.c_str() : labelTextOff.c_str()); + ButtonHandlers[b] = handler; + } + return b; +} + +void TinyUI::SetUnhandledClickHandler(const std::function& postHandler) { + UnhandledClickHandler = postHandler; +} + +void TinyUI::RemoveParentMenu(OVRFW::VRMenuObject* menuObject) { + auto menuObjectIter = std::find(AllElements.begin(), AllElements.end(), menuObject); + AllElements.erase(menuObjectIter); + + // Destroying the menu will destroy the corresponding VRMenuObjects as well + auto menuIter = Menus.find(menuObject); + GuiSys->DestroyMenu(menuIter->second); + Menus.erase(menuIter); +} + +OVRFW::VRMenuObject* TinyUI::AddSlider( + const std::string& label, + const OVR::Vector3f& position, + float* value, + const float defaultValue, + const float delta, + const float minLimit, + const float maxLimit) { + VRMenuObject* lb = CreateMenu(label, position, {150.0f, 50.0f}); + VRMenuObject* lt = CreateMenu("-", position + Vector3f{0.20f, 0.0f, 0.0f}, {50.0f, 50.0f}); + VRMenuObject* val = CreateMenu("0.0", position + Vector3f{0.35f, 0.0f, 0.0f}, {100.0f, 50.0f}); + VRMenuObject* gt = CreateMenu("+", position + Vector3f{0.50f, 0.0f, 0.0f}, {50.0f, 50.0f}); + + if (defaultValue < minLimit || defaultValue > maxLimit) { + ALOGE("TinyUI Slider: defaultValue cannot be out of limit"); + return lb; + } + + auto updateText = [=]() { + std::stringstream ss; + ss << std::setprecision(4) << std::fixed; + ss << *value; + val->SetText(ss.str().c_str()); + }; + ButtonHandlers[lt] = [=]() { + *value -= delta; + *value = std::max(minLimit, *value); + updateText(); + }; + ButtonHandlers[gt] = [=]() { + *value += delta; + *value = std::min(maxLimit, *value); + updateText(); + }; + ButtonHandlers[val] = [=]() { + *value = defaultValue; + updateText(); + }; + ButtonHandlers[lb] = [=]() { updateText(); }; + updateText(); + return lb; +} + +void TinyUI::ShowAll() { + ForAll([](VRMenuObject* menu) { menu->SetVisible(true); }); +} +void TinyUI::HideAll(const std::vector& exceptions) { + ForAll([=](VRMenuObject* menu) { + for (const VRMenuObject* e : exceptions) { + if (e == menu) + return; + } + menu->SetVisible(false); + }); +} + +void TinyUI::ForAll(const std::function& handler) { + if (handler) { + for (VRMenuObject* menu : AllElements) { + if (menu) { + handler(menu); + } + } + } +} + +} // namespace OVRFW diff --git a/Samples/SampleXrFramework/Src/Input/TinyUI.h b/Samples/SampleXrFramework/Src/Input/TinyUI.h new file mode 100755 index 0000000..ab13f7d --- /dev/null +++ b/Samples/SampleXrFramework/Src/Input/TinyUI.h @@ -0,0 +1,122 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/************************************************************************************ + +Filename : TinyUI.h +Content : Componentized wrappers for GuiSys +Created : July 2020 +Authors : Federico Schliemann + +************************************************************************************/ + +#pragma once + +#include "OVR_Math.h" +#include "OVR_FileSys.h" + +#include "GUI/GuiSys.h" +#include "Locale/OVR_Locale.h" + +#include +#include +#include + +namespace OVRFW { + +/// NOTE: this requires the app to have panel.ktx as a resource +class TinyUI { + public: + TinyUI() : GuiSys(nullptr), Locale(nullptr) {} + ~TinyUI() {} + + struct HitTestDevice { + int deviceNum = 0; + OVR::Vector3f pointerStart = {0.0f, 0.0f, 0.0f}; + OVR::Vector3f pointerEnd = {0.0f, 0.0f, 1.0f}; + OVRFW::VRMenuObject* hitObject = nullptr; + bool clicked = false; + }; + + bool Init( + const xrJava* context, + OVRFW::ovrFileSys* FileSys, + bool updateColors = true, + // 0 means use default size (8KB at the time of writing this comment) + int fontVertexBufferSize = 0); + void Shutdown(); + void Update(const OVRFW::ovrApplFrameIn& in); + void Render(const OVRFW::ovrApplFrameIn& in, OVRFW::ovrRendererOutput& out); + + OVRFW::VRMenuObject* AddLabel( + const std::string& labelText, + const OVR::Vector3f& position, + const OVR::Vector2f& size = {100.0f, 50.0f}); + OVRFW::VRMenuObject* AddButton( + const std::string& label, + const OVR::Vector3f& position, + const OVR::Vector2f& size = {100.0f, 50.0f}, + const std::function& handler = {}); + OVRFW::VRMenuObject* AddSlider( + const std::string& label, + const OVR::Vector3f& position, + float* value, + const float defaultValue, + const float delta = 0.02f, + const float minLimit = -FLT_MAX, + const float maxLimit = FLT_MAX); + OVRFW::VRMenuObject* AddToggleButton( + const std::string& labelTextOn, + const std::string& labelTextOff, + bool* value, + const OVR::Vector3f& position, + const OVR::Vector2f& size = {100.0f, 50.0f}, + const std::function& postHandler = {}); + void SetUnhandledClickHandler(const std::function& postHandler = {}); + + void RemoveParentMenu(OVRFW::VRMenuObject* menuObject); + + OVRFW::OvrGuiSys& GetGuiSys() { + return *GuiSys; + } + OVRFW::ovrLocale& GetLocale() { + return *Locale; + } + + /// hit-testing + std::vector& HitTestDevices() { + return Devices; + } + void AddHitTestRay(const OVR::Posef& ray, bool isClicking, int deviceNum = 0); + + /// ui toggle + void ShowAll(); + void HideAll(const std::vector& exceptions = std::vector()); + + /// general manipulation + void ForAll(const std::function& handler = {}); + + public: + /// style + OVR::Vector4f BackgroundColor = {0.0f, 0.0f, 0.1f, 1.0f}; + OVR::Vector4f HoverColor = {0.1f, 0.1f, 0.2f, 1.0f}; + OVR::Vector4f HighlightColor = {0.8f, 1.0f, 0.8f, 1.0f}; + + protected: + OVRFW::VRMenuObject* CreateMenu( + const std::string& labelText, + const OVR::Vector3f& position, + const OVR::Vector2f& size); + + private: + OVRFW::OvrGuiSys* GuiSys; + OVRFW::ovrLocale* Locale; + std::vector AllElements; + std::unordered_map Menus; + std::unordered_map> ButtonHandlers; + std::vector Devices; + std::vector PreviousFrameDevices; + bool UpdateColors; + std::function UnhandledClickHandler; +}; + +} // namespace OVRFW diff --git a/Samples/SampleXrFramework/Src/Locale/OVR_Locale.cpp b/Samples/SampleXrFramework/Src/Locale/OVR_Locale.cpp new file mode 100755 index 0000000..275d89b --- /dev/null +++ b/Samples/SampleXrFramework/Src/Locale/OVR_Locale.cpp @@ -0,0 +1,774 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +/************************************************************************************ + +Filename : OVR_Locale.cpp +Content : Implementation of string localization for strings loaded at run-time. +Created : April 6, 2015 +Authors : Jonathan E. Wright + +************************************************************************************/ + +#include "OVR_Locale.h" + +#include + +#include +#include + +#include "Misc/Log.h" + +#include "tinyxml2.h" +#include "OVR_JSON.h" +#include "OVR_FileSys.h" +#include "OVR_UTF8Util.h" + +namespace OVRFW { + +char const* ovrLocale::LOCALIZED_KEY_PREFIX = "@string/"; +size_t const ovrLocale::LOCALIZED_KEY_PREFIX_LEN = OVR::OVR_strlen(LOCALIZED_KEY_PREFIX); + +//============================================================== +// ovrLocaleInternal +class ovrLocaleInternal : public ovrLocale { + public: + static char const* LOCALIZED_KEY_PREFIX; + static OVR::UPInt LOCALIZED_KEY_PREFIX_LEN; + +#if defined(OVR_OS_ANDROID) + ovrLocaleInternal(JNIEnv& jni_, jobject activity_, char const* name, char const* languageCode); +#else + ovrLocaleInternal(char const* name, char const* languageCode); +#endif + virtual ~ovrLocaleInternal(); + + // returns the language code for this locale object + virtual char const* GetName() const { + return Name.c_str(); + } + + virtual char const* GetLanguageCode() const { + return LanguageCode.c_str(); + } + + virtual bool IsSystemDefaultLocale() const; + + virtual bool LoadStringsFromAndroidFormatXMLFile(ovrFileSys& fileSys, char const* fileName); + + virtual bool + AddStringsFromAndroidFormatXMLBuffer(char const* name, char const* buffer, size_t const size); + + virtual bool GetLocalizedString(char const* key, char const* defaultStr, std::string& out) + const; + + virtual void ReplaceLocalizedText(char const* inText, char* out, size_t const outSize) const; + + private: +#if defined(OVR_OS_ANDROID) + JNIEnv& jni; + jobject activityObject; +#endif + + std::string Name; // user-specified locale name + std::string LanguageCode; // system-specific locale name + std::vector Strings; + std::unordered_map StringHash; + + private: + bool GetStringJNI(char const* key, char const* defaultOut, std::string& out) const; +}; + +char const* ovrLocaleInternal::LOCALIZED_KEY_PREFIX = "@string/"; +OVR::UPInt ovrLocaleInternal::LOCALIZED_KEY_PREFIX_LEN = OVR::OVR_strlen(LOCALIZED_KEY_PREFIX); + +//============================== +// ovrLocaleInternal::ovrLocaleInternal +#if defined(OVR_OS_ANDROID) +ovrLocaleInternal::ovrLocaleInternal( + JNIEnv& jni_, + jobject activity_, + char const* name, + char const* languageCode) + : jni(jni_), activityObject(activity_), Name(name), LanguageCode(languageCode) {} +#else +ovrLocaleInternal::ovrLocaleInternal(char const* name, char const* languageCode) + : Name(name), LanguageCode(languageCode) {} +#endif + +//============================== +// ovrLocaleInternal::~ovrLocaleInternal +ovrLocaleInternal::~ovrLocaleInternal() {} + +//============================== +// ovrLocaleInternal::IsSystemDefaultLocale +bool ovrLocaleInternal::IsSystemDefaultLocale() const { + return OVR::OVR_stricmp(LanguageCode.c_str(), "en") == 0; +} + +static void GetValueFromNode(std::string& v, tinyxml2::XMLNode const* node) { + tinyxml2::XMLNode const* child = node->FirstChild(); + if (child != nullptr) { + GetValueFromNode(v, child); + } else { + v += node->Value(); + } + tinyxml2::XMLNode const* sib = node->NextSibling(); + if (sib != nullptr) { + GetValueFromNode(v, sib); + } +} + +//============================== +// ovrLocaleInternal::AddStringsFromAndroidFormatXMLBuffer +bool ovrLocaleInternal::AddStringsFromAndroidFormatXMLBuffer( + char const* name, + char const* buffer, + size_t const size) { + tinyxml2::XMLDocument doc; + tinyxml2::XMLError error = doc.Parse(buffer, size); + if (error != tinyxml2::XML_NO_ERROR) { + ALOG("ERROR: XML parse error %i parsing '%s'!", error, name); + return false; + } + + tinyxml2::XMLElement* root = doc.RootElement(); + if (OVR::OVR_stricmp(root->Value(), "resources") != 0) { + ALOG("ERROR: Expected root value of 'resources', found '%s'!\n", root->Value()); + return false; + } + + tinyxml2::XMLElement const* curElement = root->FirstChildElement(); + for (; curElement != NULL; curElement = curElement->NextSiblingElement()) { + if (OVR::OVR_stricmp(curElement->Value(), "string") != 0) { + ALOG("WARNING: Expected element value 'string', found '%s'!\n", curElement->Value()); + continue; + } + + tinyxml2::XMLAttribute const* nameAttr = curElement->FindAttribute("name"); + + std::string key = nameAttr->Value(); + + std::string value; + std::string decodedValue; + + tinyxml2::XMLNode const* childNode = curElement->FirstChild(); + if (childNode != nullptr) { + GetValueFromNode(value, childNode); + } else { + value = curElement->GetText(); + } + + // fix special encodings. Use GetFirstCharAt() and GetNextChar() to handle UTF-8. + const char* in = value.c_str(); + uint32_t curChar = UTF8Util::DecodeNextChar(&in); + while (curChar != 0) { + if (curChar == '\\') { + uint32_t nextChar = UTF8Util::DecodeNextChar(&in); + if (nextChar == 0) { + break; + } else if (nextChar == 'n') { + curChar = '\n'; + } else if (nextChar == '\r') { + curChar = '\r'; + } else { + if (nextChar != '<' && nextChar != '>' && nextChar != '"' && nextChar != '\'' && + nextChar != '&') { + ALOG("Unknown escape sequence '\\%x'", nextChar); + decodedValue += std::uint8_t(curChar); + } + curChar = nextChar; + } + } else if (curChar == '%') { + // if we find "%%", skip over the second '%' char because the localization pipeline + // bot is erroneously outputting doubled % format specifiers. + const char* prev = in; + uint32_t nextChar = UTF8Util::DecodeNextChar(&in); + if (nextChar != '%') { + // if it wasn't a double '%', then don't skip the next character + in = prev; + } + } + + decodedValue += std::uint8_t(curChar); + curChar = UTF8Util::DecodeNextChar(&in); + } + // ALOG( "Name: '%s' = '%s'\n", key.c_str(), value.c_str() ); + + if (StringHash.find(key) == StringHash.end()) { + StringHash[key] = static_cast(Strings.size()); + Strings.push_back(decodedValue); + } + } + + ALOG("Added %i strings from '%s'", static_cast(Strings.size()), name); + + return true; +} + +//============================== +// ovrLocaleInternal::LoadStringsFromAndroidFormatXMLFile +bool ovrLocaleInternal::LoadStringsFromAndroidFormatXMLFile( + ovrFileSys& fileSys, + char const* fileName) { + std::vector buffer; + if (!fileSys.ReadFile(fileName, buffer)) { + return false; + } + return AddStringsFromAndroidFormatXMLBuffer( + fileName, + reinterpret_cast(static_cast(buffer.data())), + buffer.size()); +} + +//============================== +// ovrLocale::GetStringJNI +// Get's a localized UTF-8-encoded string from the Android application's string table. +bool ovrLocaleInternal::GetStringJNI(char const* key, char const* defaultOut, std::string& out) + const { +#if defined(OVR_OS_ANDROID) + // ALOG( "Localizing key '%s'", key ); + // if the key doesn't start with KEY_PREFIX then it's not a valid key, just return + // the key itself as the output text. + if (strstr(key, LOCALIZED_KEY_PREFIX) != key) { + out = defaultOut; + ALOG("no prefix, localized to '%s'", out.c_str()); + return true; + } + + char const* realKey = key + LOCALIZED_KEY_PREFIX_LEN; + // ALOG( "realKey = %s", realKey ); + + /// Original JAVA version +#if 0 + private static String getLocalizedString( Context context, String name ) { + //Log.v("VrLocale", "getLocalizedString for " + name ); + String outString = ""; + final String packageName = context.getPackageName(); + final Resources resources = context.getResources(); + int id = resources.getIdentifier( name, "string", packageName ); + if ( id == 0 ) + { + // 0 is not a valid resource id + Log.v("VrLocale", name + " is not a valid resource id!!" ); + return outString; + } + if ( id != 0 ) + { + outString = resources.getText( id ).toString(); + //Log.v("VrLocale", "getLocalizedString resolved " + name + " to " + outString); + } + return outString; + } +#endif + + /// JNI version + JavaClass activityClass(&jni, jni.GetObjectClass(activityObject)); + if (activityClass.GetJClass() == 0) { + ALOG("GetStringJNI activityClass == 0"); + out = "JAVAERROR"; + return false; + } + const jmethodID getPackageNameMethod = + jni.GetMethodID(activityClass.GetJClass(), "getPackageName", "()Ljava/lang/String;"); + if (getPackageNameMethod == 0) { + ALOG("GetStringJNI getPackageNameMethod == 0"); + out = "JAVAERROR"; + return false; + } + const jmethodID getResourcesMethod = jni.GetMethodID( + activityClass.GetJClass(), "getResources", "()Landroid/content/res/Resources;"); + if (getResourcesMethod == 0) { + ALOG("GetStringJNI getResourcesMethod == 0"); + out = "JAVAERROR"; + return false; + } + + JavaObject packageName(&jni, jni.CallObjectMethod(activityObject, getPackageNameMethod)); + if (packageName.GetJObject() == 0) { + ALOG("GetStringJNI packageName == 0"); + out = "JAVAERROR"; + return false; + } + + JavaObject resources(&jni, jni.CallObjectMethod(activityObject, getResourcesMethod)); + if (resources.GetJObject() == 0) { + ALOG("GetStringJNI resources == 0"); + out = "JAVAERROR"; + return false; + } + + JavaClass resourcesClass(&jni, jni.GetObjectClass(resources.GetJObject())); + if (resourcesClass.GetJClass() == 0) { + ALOG("GetStringJNI resourcesClass == 0"); + out = "JAVAERROR"; + return false; + } + + const jmethodID getIdentifierMethod = jni.GetMethodID( + resourcesClass.GetJClass(), + "getIdentifier", + "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)I"); + if (getIdentifierMethod == 0) { + ALOG("GetStringJNI getIdentifierMethod == 0"); + out = "JAVAERROR"; + return false; + } + + JavaString keyObj(&jni, realKey); + if (keyObj.GetJObject() == 0) { + ALOG("GetStringJNI keyObj == 0"); + out = "JAVAERROR"; + return false; + } + + JavaString stringObj(&jni, "string"); + if (stringObj.GetJObject() == 0) { + ALOG("GetStringJNI stringObj == 0"); + out = "JAVAERROR"; + return false; + } + + const int id = jni.CallIntMethod( + resources.GetJObject(), + getIdentifierMethod, + keyObj.GetJObject(), + stringObj.GetJObject(), + packageName.GetJObject()); + if (id == 0) { + ALOG("GetStringJNI id == 0"); + out = "JAVAERROR"; + return false; + } + + const jmethodID getTextMethod = jni.GetMethodID( + resourcesClass.GetJClass(), "getText", "(Ljava/lang/Integer;)Ljava/lang/String;"); + if (getTextMethod == 0) { + ALOG("GetStringJNI getTextMethod == 0"); + out = "JAVAERROR"; + return false; + } + + JavaObject textObject(&jni, jni.CallObjectMethod(resources.GetJObject(), getTextMethod, id)); + if (textObject.GetJObject() == 0) { + ALOG("GetStringJNI textObject == 0"); + out = "JAVAERROR"; + return false; + } + + JavaClass textObjectClass(&jni, jni.GetObjectClass(textObject.GetJObject())); + if (textObjectClass.GetJClass() == 0) { + ALOG("GetStringJNI textObjectClass == 0"); + out = "JAVAERROR"; + return false; + } + + const jmethodID toStringMethd = + jni.GetMethodID(textObjectClass.GetJClass(), "toString", "()Ljava/lang/String;"); + if (toStringMethd == 0) { + ALOG("GetStringJNI toStringMethd == 0"); + out = "JAVAERROR"; + return false; + } + + JavaString textObjectString( + &jni, (jstring)jni.CallObjectMethod(textObject.GetJObject(), toStringMethd)); + if (textObjectString.GetJObject() == 0) { + ALOG("GetStringJNI textObjectString == NULL"); + out = "JAVAERROR"; + return false; + } + + const char* textObjectString_ch = jni.GetStringUTFChars(textObjectString.GetJString(), 0); + out = textObjectString_ch; + jni.ReleaseStringUTFChars(textObjectString.GetJString(), textObjectString_ch); +#else + out = defaultOut; +#endif // defined(OVR_OS_ANDROID) + + return true; +} + +//============================== +// ovrLocaleInternal::GetLocalizedString +bool ovrLocaleInternal::GetLocalizedString( + char const* key, + char const* defaultStr, + std::string& out) const { + if (key == NULL) { + return false; + } + + if (strstr(key, LOCALIZED_KEY_PREFIX) == key) { + if (Strings.size() > 0) { + std::string realKey(key + LOCALIZED_KEY_PREFIX_LEN); + auto it = StringHash.find(realKey); + if (it != StringHash.end()) { + out = Strings[it->second]; + return true; + } + } + } + // try instead to find the string via Android's resources. Ideally, we'd have combined these all + // into our own hash, but enumerating application resources from library code on is problematic + // on android + if (GetStringJNI(key, defaultStr, out)) { + return true; + } + out = defaultStr != NULL ? defaultStr : ""; + return false; +} + +//============================== +// ovrLocaleInternal::ReplaceLocalizedText +void ovrLocaleInternal::ReplaceLocalizedText(char const* inText, char* out, size_t const outSize) + const { + char const* cur = strstr(inText, LOCALIZED_KEY_PREFIX); + if (cur == nullptr) { + OVR::OVR_strcpy(out, outSize, inText); + return; + } + + const size_t MAX_AT_STRING_LEN = 256; + size_t outOfs = 0; + char const* last = inText; + + auto CopyChars = + [](char* out, size_t outSize, size_t& outOfs, char const* src, size_t const count) { + size_t remaining = outSize - outOfs - 1; + size_t copyCount = count > remaining ? remaining : count; + memcpy(&out[outOfs], src, copyCount); + outOfs += copyCount; + if (count > remaining) { + out[outOfs] = '\0'; + return false; + } + return true; + }; + + while (cur != nullptr) { + // copy from the last to the current + if (!CopyChars(out, outSize, outOfs, last, cur - last)) { + return; + } + + // scan ahead to find white space terminating the "@string/" + size_t ofs = 0; + char atString[MAX_AT_STRING_LEN]; + for (; cur[ofs] != '\0' && ofs < MAX_AT_STRING_LEN - 1; ++ofs) { + if (cur[ofs] == '\n' || cur[ofs] == '\r' || cur[ofs] == '\t' || cur[ofs] == ' ') { + break; + } + atString[ofs] = cur[ofs]; + } + + // terminate the @string + atString[ofs] = '\0'; + // advance past the string + cur += ofs; + last = cur; + + // get the localized text + std::string localized; + GetLocalizedString(atString, atString, localized); + + // copy localized text into the output buffer + if (!CopyChars( + out, outSize, outOfs, localized.c_str(), OVR::OVR_strlen(localized.c_str()))) { + return; + } + + cur = strstr(cur, LOCALIZED_KEY_PREFIX); + if (cur == nullptr) { + // copy any remainder + for (size_t i = 0; last[i] != '\0' && outOfs < outSize - 1; ++i) { + out[outOfs++] = last[i]; + } + out[outOfs] = '\0'; + break; + } + } +} + +//============================================================================================== +// ovrLocale +// static functions for managing the global instance to a ovrLocaleInternal object +//============================================================================================== + +//============================== +// ovrLocale::Create +ovrLocale* +ovrLocale::Create(JNIEnv& jni_, jobject activity_, char const* name, ovrFileSys* fileSys) { + ALOG("ovrLocale::Create - entered"); + + ovrLocale* localePtr = NULL; + + /// Find default language, default to EN + std::string languageCode = "en"; + + /// reference of JAVA version of the code below +#if 0 + public static String getCurrentLanguage() { + return Locale.getDefault().getLanguage(); + } +#endif + +#if defined(OVR_OS_ANDROID) + JavaClass localeClass(&jni_, jni_.FindClass("java/util/Locale")); // class pointer of Locale + if (localeClass.GetJClass() == NULL) { + ALOG("ovrLocale::Create - localeClass == NULL"); + } else { + jmethodID getDefaultMethod = + jni_.GetStaticMethodID(localeClass.GetJClass(), "getDefault", "()Ljava/util/Locale;"); + if (getDefaultMethod == NULL) { + ALOG("ovrLocale::Create - getDefaultMethod == NULL"); + } else { + jobject defaultLocale = + jni_.CallStaticObjectMethod(localeClass.GetJClass(), getDefaultMethod); + if (defaultLocale == NULL) { + ALOG("ovrLocale::Create - defaultLocale == NULL"); + } else { + jmethodID getLanguageMethod = jni_.GetMethodID( + localeClass.GetJClass(), "getLanguage", "()Ljava/lang/String;"); + if (getLanguageMethod == NULL) { + ALOG("ovrLocale::Create - getLanguageMethod == NULL"); + } else { + jstring language = + (jstring)jni_.CallObjectMethod(defaultLocale, getLanguageMethod); + if (language) { + const char* language_ch = jni_.GetStringUTFChars(language, 0); + ALOG("ovrLocale::Create - languageCode = `%s`", language_ch); + languageCode = language_ch; + jni_.ReleaseStringUTFChars(language, language_ch); + } + } + } + } + } + + localePtr = new ovrLocaleInternal(jni_, activity_, name, languageCode.c_str()); +#else + localePtr = new ovrLocaleInternal(name, languageCode.c_str()); +#endif // defined(OVR_OS_ANDROID) + + ALOG("ovrLocale::Create - exited"); + return localePtr; +} + +//============================== +// ovrLocale::Destroy +void ovrLocale::Destroy(ovrLocale*& localePtr) { + delete localePtr; + localePtr = NULL; +} + +//============================== +// ovrLocale::MakeStringIdFromUTF8 +// Turns an arbitray ansi string into a string id. +// - Deletes any character that is not a space, letter or number. +// - Turn spaces into underscores. +// - Ignore contiguous spaces. +std::string ovrLocale::MakeStringIdFromUTF8(char const* str) { + enum eLastOutputType { LO_LETTER, LO_DIGIT, LO_SPACE, LO_MAX }; + eLastOutputType lastOutputType = LO_MAX; + std::string out = LOCALIZED_KEY_PREFIX; + char const* ptr = str; + if (strstr(str, LOCALIZED_KEY_PREFIX) == str) { + // skip UTF-8 chars... technically could just += LOCALIZED_KEY_PREFIX_LEN if the key prefix + // is only ANSI chars... + for (size_t i = 0; i < LOCALIZED_KEY_PREFIX_LEN; ++i) { + UTF8Util::DecodeNextChar(&ptr); + } + } + int n = static_cast(UTF8Util::GetLength(ptr)); + for (int i = 0; i < n; ++i) { + uint32_t c = UTF8Util::DecodeNextChar(&ptr); + if ((c >= '0' && c <= '9')) { + if (i == 0) { + // string identifiers in Android cannot start with a number because they + // are also encoded as Java identifiers, so output an underscore first. + out += '_'; + } + out += std::uint8_t(c); + lastOutputType = LO_DIGIT; + } else if ((c >= 'a' && c <= 'z')) { + // just output the character + out += std::uint8_t(c); + lastOutputType = LO_LETTER; + } else if ((c >= 'A' && c <= 'Z')) { + // just output the character as lowercase + out += (std::uint8_t(c) + 32); + lastOutputType = LO_LETTER; + } else if (c == 0x20) { + if (lastOutputType != LO_SPACE) { + out += '_'; + lastOutputType = LO_SPACE; + } + continue; + } + // ignore everything else + } + return out; +} + +//============================== +// ovrLocale::MakeStringIdFromANSI +// Turns an arbitray ansi string into a string id. +// - Deletes any character that is not a space, letter or number. +// - Turn spaces into underscores. +// - Ignore contiguous spaces. +std::string ovrLocale::MakeStringIdFromANSI(char const* str) { + enum eLastOutputType { LO_LETTER, LO_DIGIT, LO_SPACE, LO_PUNCTUATION, LO_MAX }; + eLastOutputType lastOutputType = LO_MAX; + std::string out = LOCALIZED_KEY_PREFIX; + char const* ptr = + strstr(str, LOCALIZED_KEY_PREFIX) == str ? str + LOCALIZED_KEY_PREFIX_LEN : str; + OVR::UPInt n = OVR::OVR_strlen(ptr); + for (unsigned int i = 0; i < n; ++i) { + unsigned char c = ptr[i]; + if ((c >= '0' && c <= '9')) { + if (i == 0) { + // string identifiers in Android cannot start with a number because they + // are also encoded as Java identifiers, so output an underscore first. + out += '_'; + } + out += std::uint8_t(c); + lastOutputType = LO_DIGIT; + } else if ((c >= 'a' && c <= 'z')) { + // just output the character + out += std::uint8_t(c); + lastOutputType = LO_LETTER; + } else if ((c >= 'A' && c <= 'Z')) { + // just output the character as lowercase + out += std::uint8_t(c) + 32; + lastOutputType = LO_LETTER; + } else if (c == 0x20) { + if (lastOutputType != LO_SPACE) { + out += '_'; + lastOutputType = LO_SPACE; + } + continue; + } + // ignore everything else + } + return out; +} + +//============================== +// private_GetXliffFormattedString +// Supports up to 9 arguments and %s format only +// inXliffStr is intentionally not passed by reference because "va_start has undefined behavior with +// reference types" +static std::string private_GetXliffFormattedString(const char* inXliffStr, ...) { + // format spec looks like: %1$s - we expect at least 3 chars after % + const int MIN_NUM_EXPECTED_FORMAT_CHARS = 3; + + // If the passed in string is shorter than minimum expected xliff formatting, just return it + if (OVR::OVR_strlen(inXliffStr) <= MIN_NUM_EXPECTED_FORMAT_CHARS) { + return std::string(inXliffStr); + } + + // Buffer that holds formatted return string + std::string retStrBuffer; + + char const* p = inXliffStr; + for (;;) { + uint32_t charCode = UTF8Util::DecodeNextChar(&p); + if (charCode == '\0') { + break; + } else if (charCode == '%') { + // We found the start of the format specifier + // Now check that there are at least three more characters which contain the format + // specification + std::vector formatSpec; + for (int count = 0; count < MIN_NUM_EXPECTED_FORMAT_CHARS; ++count) { + uint32_t formatCharCode = UTF8Util::DecodeNextChar(&p); + formatSpec.push_back(formatCharCode); + } + + assert(static_cast(formatSpec.size()) >= MIN_NUM_EXPECTED_FORMAT_CHARS); + + uint32_t desiredArgIdxChar = formatSpec[0]; + uint32_t dollarThing = formatSpec[1]; + uint32_t specifier = formatSpec[2]; + + // Checking if it has supported xliff format specifier + if ((desiredArgIdxChar >= '1' && desiredArgIdxChar <= '9') && (dollarThing == '$') && + (specifier == 's')) { + // Found format valid specifier, so processing entire format specifier. + int desiredArgIdxint = desiredArgIdxChar - '0'; + + va_list args; + va_start(args, inXliffStr); + + // Loop till desired argument is found. + for (int j = 0;; ++j) { + const char* tempArg = va_arg(args, const char*); + if (j == (desiredArgIdxint - 1)) // found desired argument + { + retStrBuffer += tempArg; + break; + } + } + + va_end(args); + } else { + ALOG("%s has invalid xliff format - has unsupported format specifier.", inXliffStr); + return std::string(inXliffStr); + } + } else { + retStrBuffer += std::uint8_t(charCode); + } + } + + return retStrBuffer; +} + +//============================== +// ovrLocale::GetXliffFormattedString +std::string ovrLocale::GetXliffFormattedString(const std::string& inXliffStr, const char* arg1) { + return private_GetXliffFormattedString(inXliffStr.c_str(), arg1); +} + +//============================== +// ovrLocale::GetXliffFormattedString +std::string ovrLocale::GetXliffFormattedString( + const std::string& inXliffStr, + const char* arg1, + const char* arg2) { + return private_GetXliffFormattedString(inXliffStr.c_str(), arg1, arg2); +} + +//============================== +// ovrLocale::GetXliffFormattedString +std::string ovrLocale::GetXliffFormattedString( + const std::string& inXliffStr, + const char* arg1, + const char* arg2, + const char* arg3) { + return private_GetXliffFormattedString(inXliffStr.c_str(), arg1, arg2, arg3); +} + +//============================== +// ovrLocale::ToString +std::string ovrLocale::ToString(char const* fmt, float const f) { + char buffer[128]; + OVR::OVR_sprintf(buffer, 128, fmt, f); + return std::string(buffer); +} + +//============================== +// ovrLocale::ToString +std::string ovrLocale::ToString(char const* fmt, int const i) { + char buffer[128]; + OVR::OVR_sprintf(buffer, 128, fmt, i); + return std::string(buffer); +} + +} // namespace OVRFW diff --git a/Samples/SampleXrFramework/Src/Locale/OVR_Locale.h b/Samples/SampleXrFramework/Src/Locale/OVR_Locale.h new file mode 100755 index 0000000..8111abd --- /dev/null +++ b/Samples/SampleXrFramework/Src/Locale/OVR_Locale.h @@ -0,0 +1,99 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +/************************************************************************************ + +Filename : OVR_Locale.h +Content : Header file for string localization interface. +Created : April 6, 2015 +Authors : Jonathan E. Wright + +************************************************************************************/ + +#pragma once + +#include +#include + +#include "OVR_FileSys.h" +#include "JniUtils.h" + +namespace OVRFW { + +class ovrFileSys; + +class ovrLocale { + public: + static char const* LOCALIZED_KEY_PREFIX; + static size_t const LOCALIZED_KEY_PREFIX_LEN; + + //---------------------------------------------------------- + // static methods + //---------------------------------------------------------- + // creates a locale object for the system's current locale. + static ovrLocale* + Create(JNIEnv& jni, jobject activity, char const* name, ovrFileSys* fileSys = nullptr); + + // frees the local object + static void Destroy(ovrLocale*& localePtr); + + // Takes a UTF8 string and returns an identifier that can be used as an Android string id. + static std::string MakeStringIdFromUTF8(char const* str); + + // Takes an ANSI string and returns an identifier that can be used as an Android string id. + static std::string MakeStringIdFromANSI(char const* str); + + // Localization : Returns xliff formatted string + // These are set to const char * to make sure that's all that's passed in - we support up to 9, + // add more functions as needed + static std::string GetXliffFormattedString(const std::string& inXliffStr, const char* arg1); + static std::string + GetXliffFormattedString(const std::string& inXliffStr, const char* arg1, const char* arg2); + static std::string GetXliffFormattedString( + const std::string& inXliffStr, + const char* arg1, + const char* arg2, + const char* arg3); + + static std::string ToString(char const* fmt, float const f); + static std::string ToString(char const* fmt, int const i); + + //---------------------------------------------------------- + // public virtual interface methods + //---------------------------------------------------------- + virtual ~ovrLocale() {} + + virtual char const* GetName() const = 0; + virtual char const* GetLanguageCode() const = 0; + + // returns true if this locale is the system's default locale (on Android this + // means the string resources are loaded from res/values/ vs. res/values-*/ + virtual bool IsSystemDefaultLocale() const = 0; + + virtual bool LoadStringsFromAndroidFormatXMLFile(ovrFileSys& fileSys, char const* fileName) = 0; + + // takes a file name, a buffer and a size in bytes. The buffer must already have + // been loaded. The name is only an identifier used for error reporting. + virtual bool AddStringsFromAndroidFormatXMLBuffer( + char const* name, + char const* buffer, + size_t const size) = 0; + + // returns the localized string associated with the passed key. Returns false if the + // key was not found. If the key was not found, out will be set to the defaultStr. + virtual bool GetLocalizedString(char const* key, char const* defaultStr, std::string& out) + const = 0; + + // Takes a string with potentially multiple "@string/*" keys and outputs the string to the out + // buffer with the keys replaced by the localized text. + virtual void ReplaceLocalizedText(char const* inText, char* out, size_t const outSize) + const = 0; +}; + +} // namespace OVRFW diff --git a/Samples/SampleXrFramework/Src/Locale/tinyxml2.cpp b/Samples/SampleXrFramework/Src/Locale/tinyxml2.cpp new file mode 100755 index 0000000..2d67cfa --- /dev/null +++ b/Samples/SampleXrFramework/Src/Locale/tinyxml2.cpp @@ -0,0 +1,2110 @@ +// clang-format off +/* +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 + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wimplicit-fallthrough" +#endif // __clang__ + +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 + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif // __clang__ diff --git a/Samples/SampleXrFramework/Src/Locale/tinyxml2.h b/Samples/SampleXrFramework/Src/Locale/tinyxml2.h new file mode 100755 index 0000000..15784b1 --- /dev/null +++ b/Samples/SampleXrFramework/Src/Locale/tinyxml2.h @@ -0,0 +1,1912 @@ +// clang-format off +/* +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/Samples/SampleXrFramework/Src/Misc/Log.c b/Samples/SampleXrFramework/Src/Misc/Log.c new file mode 100755 index 0000000..b312da2 --- /dev/null +++ b/Samples/SampleXrFramework/Src/Misc/Log.c @@ -0,0 +1,90 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/******************************************************************************* + +Filename : Log.c +Content : Functions for debug logging. +Created : February 21, 2018 +Authors : J.M.P. van Waveren, Jonathan Wright +Language : C++ + +*******************************************************************************/ + +#include "Log.h" + +#include +#include + +#if defined(WIN32) +#include "windows.h" +#endif + +#if defined(__cplusplus) +extern "C" { +#endif + +static void StripPath(const char* filePath, char* strippedTag, size_t const strippedTagSize) { + // scan backwards from the end to the first slash + const int len = (int)(strlen(filePath)); + int slash; + for (slash = len - 1; slash > 0 && filePath[slash] != '/' && filePath[slash] != '\\'; slash--) { + } + if (filePath[slash] == '/' || filePath[slash] == '\\') { + slash++; + } + // copy forward until a dot or 0 + size_t i; + for (i = 0; i < strippedTagSize - 1; i++) { + const char c = filePath[slash + i]; + if (c == '.' || c == 0) { + break; + } + strippedTag[i] = c; + } + strippedTag[i] = 0; +} + +void LogWithFilenameTag(const int priority, const char* filename, const char* fmt, ...) { + // we keep stack allocations to a minimum to keep logging side-effects to a minimum + char tag[32]; + char msg[512 - sizeof(tag)]; + StripPath(filename, tag, sizeof(tag)); + + // we use vsnprintf here rather than using __android_log_vprintf so we can control the size + // and method of allocations as much as possible. + va_list argPtr; + va_start(argPtr, fmt); + vsnprintf(msg, sizeof(msg), fmt, argPtr); + va_end(argPtr); + +#if defined(ANDROID) + __android_log_write(priority, tag, msg); +#elif defined(WIN32) + OutputDebugStringA("["); + OutputDebugStringA(tag); + switch (priority) { + case SAMPLES_LOG_ERROR: + OutputDebugStringA("] {ERROR} "); + break; + case SAMPLES_LOG_WARN: + OutputDebugStringA("] {WARN} "); + break; + case SAMPLES_LOG_INFO: + OutputDebugStringA("] {INFO} "); + break; + case SAMPLES_LOG_VERBOSE: + default: + OutputDebugStringA("] {VERBOSE} "); + break; + } + OutputDebugStringA(msg); + OutputDebugStringA("\n"); +#else + (void)priority; + printf("[%s] %s\n", tag, msg); +#endif // defined(ANDROID) +} + +#if defined(__cplusplus) +} // extern "C" +#endif diff --git a/Samples/SampleXrFramework/Src/Misc/Log.h b/Samples/SampleXrFramework/Src/Misc/Log.h new file mode 100755 index 0000000..cf3b0b9 --- /dev/null +++ b/Samples/SampleXrFramework/Src/Misc/Log.h @@ -0,0 +1,71 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/******************************************************************************* + +Filename : Log.h +Content : Macros for debug logging. +Created : February 21, 2018 +Authors : Jonathan Wright +Language : C++ + +*******************************************************************************/ + +#pragma once + +#if defined(ANDROID) +#include +#else +#include +#endif // defined(ANDROID) + +#include // abort + +#if defined(__cplusplus) +extern "C" { +#endif + +#if defined(ANDROID) +typedef enum SamplesLogPriority { + SAMPLES_LOG_ERROR = ANDROID_LOG_ERROR, + SAMPLES_LOG_WARN = ANDROID_LOG_WARN, + SAMPLES_LOG_INFO = ANDROID_LOG_INFO, + SAMPLES_LOG_VERBOSE = ANDROID_LOG_VERBOSE, +} SamplesLogPriority; +#else +typedef enum SamplesLogPriority { + SAMPLES_LOG_ERROR = 5, + SAMPLES_LOG_WARN = 4, + SAMPLES_LOG_INFO = 3, + SAMPLES_LOG_VERBOSE = 1, +} SamplesLogPriority; +#endif + +void LogWithFilenameTag(const int priority, const char* filename, const char* fmt, ...); + +#define ALOGE(...) \ + { LogWithFilenameTag(SAMPLES_LOG_ERROR, __FILE__, __VA_ARGS__); } + +#define ALOGE_FAIL(...) \ + { \ + LogWithFilenameTag(SAMPLES_LOG_ERROR, __FILE__, __VA_ARGS__); \ + abort(); \ + } + +#if 1 // DEBUG + +#define ALOG(...) \ + { LogWithFilenameTag(SAMPLES_LOG_INFO, __FILE__, __VA_ARGS__); } + +#define ALOGV(...) \ + { LogWithFilenameTag(SAMPLES_LOG_VERBOSE, __FILE__, __VA_ARGS__); } + +#define ALOGW(...) \ + { LogWithFilenameTag(SAMPLES_LOG_WARN, __FILE__, __VA_ARGS__); } + +#else +#define ALOGV(...) +#endif + +#if defined(__cplusplus) +} // extern "C" +#endif diff --git a/Samples/SampleXrFramework/Src/Model/ModelAnimationUtils.cpp b/Samples/SampleXrFramework/Src/Model/ModelAnimationUtils.cpp new file mode 100755 index 0000000..b8b47b4 --- /dev/null +++ b/Samples/SampleXrFramework/Src/Model/ModelAnimationUtils.cpp @@ -0,0 +1,205 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/************************************************************************************ + +Filename : ModelAnimationUtils.cpp +Content : Utility helpers for model animations. +Created : November 2, 2022 +Authors : Peter Chan + +*************************************************************************************/ + +#include "ModelAnimationUtils.h" +#include "ModelFile.h" + +#include "Misc/Log.h" + +using OVR::OVRMath_Lerp; +using OVR::Quatf; +using OVR::Vector3f; + +namespace OVRFW { + +static Vector3f AnimationInterpolateVector3f( + float* buffer, + int frame, + float fraction, + ModelAnimationInterpolation interpolationType) { + Vector3f firstElement; + firstElement.x = buffer[frame * 3 + 0]; + firstElement.y = buffer[frame * 3 + 1]; + firstElement.z = buffer[frame * 3 + 2]; + Vector3f secondElement; + secondElement.x = buffer[frame * 3 + 3]; + secondElement.y = buffer[frame * 3 + 4]; + secondElement.z = buffer[frame * 3 + 5]; + + if (interpolationType == MODEL_ANIMATION_INTERPOLATION_LINEAR) { + firstElement = firstElement.Lerp(secondElement, fraction); + return firstElement; + } else if (interpolationType == MODEL_ANIMATION_INTERPOLATION_STEP) { + if (fraction >= 1.0f) { + return secondElement; + } else { + return firstElement; + } + } else if (interpolationType == MODEL_ANIMATION_INTERPOLATION_CATMULLROMSPLINE) { + // #TODO implement MODEL_ANIMATION_INTERPOLATION_CATMULLROMSPLINE + ALOGW("MODEL_ANIMATION_INTERPOLATION_CATMULLROMSPLINE not implemented"); + firstElement = firstElement.Lerp(secondElement, fraction); + return firstElement; + } else if (interpolationType == MODEL_ANIMATION_INTERPOLATION_CUBICSPLINE) { + // #TODO implement MODEL_ANIMATION_INTERPOLATION_CUBICSPLINE + ALOGW("MODEL_ANIMATION_INTERPOLATION_CUBICSPLINE not implemented"); + firstElement = firstElement.Lerp(secondElement, fraction); + return firstElement; + } else { + ALOGW("invalid interpolation type on animation"); + return firstElement; + } +} + +static Quatf AnimationInterpolateQuatf( + float* buffer, + int frame, + float fraction, + ModelAnimationInterpolation interpolationType) { + Quatf firstElement; + firstElement.x = buffer[frame * 4 + 0]; + firstElement.y = buffer[frame * 4 + 1]; + firstElement.z = buffer[frame * 4 + 2]; + firstElement.w = buffer[frame * 4 + 3]; + Quatf secondElement; + secondElement.x = buffer[frame * 4 + 4]; + secondElement.y = buffer[frame * 4 + 5]; + secondElement.z = buffer[frame * 4 + 6]; + secondElement.w = buffer[frame * 4 + 7]; + + if (interpolationType == MODEL_ANIMATION_INTERPOLATION_LINEAR) { + firstElement = firstElement.Lerp(secondElement, fraction); + return firstElement; + } else if (interpolationType == MODEL_ANIMATION_INTERPOLATION_STEP) { + if (fraction >= 1.0f) { + return secondElement; + } else { + return firstElement; + } + } else if (interpolationType == MODEL_ANIMATION_INTERPOLATION_CATMULLROMSPLINE) { + ALOGW( + "MODEL_ANIMATION_INTERPOLATION_CATMULLROMSPLINE does not make sense for quaternions."); + firstElement = firstElement.Lerp(secondElement, fraction); + return firstElement; + } else if (interpolationType == MODEL_ANIMATION_INTERPOLATION_CUBICSPLINE) { + ALOGW("MODEL_ANIMATION_INTERPOLATION_CUBICSPLINE does not make sense for quaternions."); + firstElement = firstElement.Lerp(secondElement, fraction); + return firstElement; + } else { + ALOGW("invalid interpolation type on animation"); + return firstElement; + } +} + +static std::vector AnimationInterpolateWeights( + const float* buffer, + int numWeightsPerFrame, + int frame, + float fraction, + ModelAnimationInterpolation interpolationType) { + const int firstElementIndex = frame * numWeightsPerFrame; + const int secondElementIndex = firstElementIndex + numWeightsPerFrame; + const float* firstElement = buffer + firstElementIndex; + const float* secondElement = buffer + secondElementIndex; + + std::vector result(numWeightsPerFrame, 0.0f); + if (interpolationType == MODEL_ANIMATION_INTERPOLATION_LINEAR) { + for (int i = 0; i < numWeightsPerFrame; ++i) { + result[i] = OVRMath_Lerp(firstElement[i], secondElement[i], fraction); + } + } else if (interpolationType == MODEL_ANIMATION_INTERPOLATION_STEP) { + if (fraction >= 1.0f) { + for (int i = 0; i < numWeightsPerFrame; ++i) { + result[i] = secondElement[i]; + } + } else { + for (int i = 0; i < numWeightsPerFrame; ++i) { + result[i] = firstElement[i]; + } + } + } else if (interpolationType == MODEL_ANIMATION_INTERPOLATION_CATMULLROMSPLINE) { + ALOGW("MODEL_ANIMATION_INTERPOLATION_CATMULLROMSPLINE not implemented, treating as linear"); + for (int i = 0; i < numWeightsPerFrame; ++i) { + result[i] = OVRMath_Lerp(firstElement[i], secondElement[i], fraction); + } + } else if (interpolationType == MODEL_ANIMATION_INTERPOLATION_CUBICSPLINE) { + ALOGW("MODEL_ANIMATION_INTERPOLATION_CUBICSPLINE not implemented, treating as linear"); + for (int i = 0; i < numWeightsPerFrame; ++i) { + result[i] = OVRMath_Lerp(firstElement[i], secondElement[i], fraction); + } + } else { + ALOGW("invalid interpolation type on animation"); + } + return result; +} + +void ApplyAnimation(ModelState& modelState, int animationIndex) { + const ModelAnimation& animation = modelState.mf->Animations[animationIndex]; + for (const ModelAnimationChannel& channel : animation.channels) { + ModelNodeState& nodeState = modelState.nodeStates[channel.nodeIndex]; + const ModelAnimationTimeLineState& timeLineState = + modelState.animationTimelineStates[channel.sampler->timeLineIndex]; + + float* bufferData = (float*)(channel.sampler->output->BufferData()); + if (channel.path == MODEL_ANIMATION_PATH_TRANSLATION) { + Vector3f translation = AnimationInterpolateVector3f( + bufferData, + timeLineState.frame, + timeLineState.fraction, + channel.sampler->interpolation); + nodeState.translation = translation; + } else if (channel.path == MODEL_ANIMATION_PATH_SCALE) { + Vector3f scale = AnimationInterpolateVector3f( + bufferData, + timeLineState.frame, + timeLineState.fraction, + channel.sampler->interpolation); + nodeState.scale = scale; + } else if (channel.path == MODEL_ANIMATION_PATH_ROTATION) { + Quatf rotation = AnimationInterpolateQuatf( + bufferData, + timeLineState.frame, + timeLineState.fraction, + channel.sampler->interpolation); + nodeState.rotation = rotation; + } else if (channel.path == MODEL_ANIMATION_PATH_WEIGHTS) { + const int numWeightsPerFrame = + channel.sampler->output->count / channel.sampler->input->count; + std::vector weights = AnimationInterpolateWeights( + bufferData, + numWeightsPerFrame, + timeLineState.frame, + timeLineState.fraction, + channel.sampler->interpolation); + if (nodeState.weights.size() != weights.size()) { + ALOGE( + "Mismatch animation weights count, node:%zu, animation:%zu, channel:%d, '%s'", + nodeState.weights.size(), + weights.size(), + channel.nodeIndex, + animation.name.c_str()); + continue; + } + if (channel.additiveWeightIndex >= 0) { + nodeState.weights[channel.additiveWeightIndex] += + weights[channel.additiveWeightIndex]; + } else { + nodeState.weights = weights; + } + } else { + ALOGW("Bad animation path on channel '%s'", animation.name.c_str()); + } + + nodeState.CalculateLocalTransform(); + } +} + +} // namespace OVRFW diff --git a/Samples/SampleXrFramework/Src/Model/ModelAnimationUtils.h b/Samples/SampleXrFramework/Src/Model/ModelAnimationUtils.h new file mode 100755 index 0000000..2a9a12a --- /dev/null +++ b/Samples/SampleXrFramework/Src/Model/ModelAnimationUtils.h @@ -0,0 +1,20 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/************************************************************************************ + +Filename : ModelAnimationUtils.h +Content : Utility helpers for model animations. +Created : November 2, 2022 +Authors : Peter Chan + +************************************************************************************/ + +#pragma once + +#include "ModelDef.h" + +namespace OVRFW { + +void ApplyAnimation(ModelState& modelState, int animationIndex); + +} // namespace OVRFW diff --git a/Samples/SampleXrFramework/Src/Model/ModelCollision.cpp b/Samples/SampleXrFramework/Src/Model/ModelCollision.cpp new file mode 100755 index 0000000..ce889a8 --- /dev/null +++ b/Samples/SampleXrFramework/Src/Model/ModelCollision.cpp @@ -0,0 +1,205 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/************************************************************************************ + +Filename : ModelCollision.cpp +Content : Basic collision detection for scene walkthroughs. +Created : May 2014 +Authors : J.M.P. van Waveren + +*************************************************************************************/ + +#include "ModelCollision.h" + +#include + +namespace OVRFW { + +//----------------------------------------------------------------------------- +// CollisionPolytope +//----------------------------------------------------------------------------- + +const float COLLISION_EPSILON = 0.01f; + +bool CollisionPolytope::TestPoint(const OVR::Vector3f& p) const { + for (int i = 0; i < static_cast(Planes.size()); i++) { + if (Planes[i].TestSide(p) > 0.0f) { + return false; + } + } + return true; +} + +bool CollisionPolytope::TestRay( + const OVR::Vector3f& start, + const OVR::Vector3f& dir, + float& length, + OVR::Planef* plane) const { + const OVR::Vector3f end = start + dir * length; + + int crossing = -1; + float cdot1 = 0.0f; + float cdot2 = 0.0f; + + for (int i = 0; i < static_cast(Planes.size()); i++) { + const float dot1 = Planes[i].TestSide(start); + if (dot1 > 0.0f) { + const float dot2 = Planes[i].TestSide(end); + if (dot2 > 0.0f) { + return false; + } + if (dot2 <= 0.0f) { + if (crossing == -1) { + crossing = i; + cdot1 = dot1; + cdot2 = dot2; + } else { + if (dot2 > cdot2) { + crossing = i; + cdot1 = dot1; + cdot2 = dot2; + } + } + } + } + } + + if (crossing < 0) { + return false; + } + + length = length * (cdot1 - COLLISION_EPSILON) / (cdot1 - cdot2); + if (length < 0.0f) { + length = 0.0f; + } + + if (plane != NULL) { + *plane = Planes[crossing]; + } + return true; +} + +bool CollisionPolytope::PopOut(OVR::Vector3f& p) const { + float minDist = FLT_MAX; + int crossing = -1; + for (int i = 0; i < static_cast(Planes.size()); i++) { + float dist = Planes[i].TestSide(p); + if (dist > 0.0f) { + return false; + } + dist = fabsf(dist); + if (dist < minDist) { + minDist = dist; + crossing = i; + } + } + p += Planes[crossing].N * COLLISION_EPSILON; + return true; +} + +//----------------------------------------------------------------------------- +// ModelCollision +//----------------------------------------------------------------------------- + +bool ModelCollision::TestPoint(const OVR::Vector3f& p) const { + for (int i = 0; i < static_cast(Polytopes.size()); i++) { + if (Polytopes[i].TestPoint(p)) { + return true; + } + } + return false; +} + +bool ModelCollision::TestRay( + const OVR::Vector3f& start, + const OVR::Vector3f& dir, + float& length, + OVR::Planef* plane) const { + bool clipped = false; + for (int i = 0; i < static_cast(Polytopes.size()); i++) { + OVR::Planef clipPlane; + float clipLength = length; + if (Polytopes[i].TestRay(start, dir, clipLength, &clipPlane)) { + if (clipLength < length) { + length = clipLength; + if (plane != NULL) { + *plane = clipPlane; + } + clipped = true; + } + } + } + return clipped; +} + +bool ModelCollision::PopOut(OVR::Vector3f& p) const { + for (int i = 0; i < static_cast(Polytopes.size()); i++) { + if (Polytopes[i].PopOut(p)) { + return true; + } + } + return false; +} + +//----------------------------------------------------------------------------- +// SlideMove +//----------------------------------------------------------------------------- + +const OVR::Vector3f UpVector(0.0f, 1.0f, 0.0f); +const float RailHeight = 0.8f; + +OVR::Vector3f SlideMove( + const OVR::Vector3f& footPos, + const float eyeHeight, + const OVR::Vector3f& moveDirection, + const float moveDistance, + const ModelCollision& collisionModel, + const ModelCollision& groundCollisionModel) { + // Check for collisions at eye level to prevent slipping under walls. + OVR::Vector3f eyePos = footPos + UpVector * eyeHeight; + + // Pop out of any collision models. + collisionModel.PopOut(eyePos); + + { + OVR::Planef fowardCollisionPlane; + float forwardDistance = moveDistance; + if (!collisionModel.TestRay( + eyePos, moveDirection, forwardDistance, &fowardCollisionPlane)) { + // No collision, move the full distance. + eyePos += moveDirection * moveDistance; + } else { + // Move up to the point of collision. + eyePos += moveDirection * forwardDistance; + + // Project the remaining movement onto the collision plane. + const float COLLISION_BOUNCE = + 0.001f; // don't creep into the plane due to floating-point rounding + const float intoPlane = moveDirection.Dot(fowardCollisionPlane.N) - COLLISION_BOUNCE; + const OVR::Vector3f slideDirection = + (moveDirection - fowardCollisionPlane.N * intoPlane); + + // Try to finish the move by sliding along the collision plane. + float slideDistance = moveDistance; + collisionModel.TestRay( + eyePos - UpVector * RailHeight, slideDirection, slideDistance, NULL); + + eyePos += slideDirection * slideDistance; + } + } + + if (static_cast(groundCollisionModel.Polytopes.size()) != 0) { + // Check for collisions at foot level, which allows following terrain. + float downDistance = 10.0f; + groundCollisionModel.TestRay(eyePos, -UpVector, downDistance, NULL); + + // Maintain the minimum camera height. + if (eyeHeight - downDistance < 1.0f) { + eyePos += UpVector * (eyeHeight - downDistance); + } + } + + return eyePos - UpVector * eyeHeight; +} + +} // namespace OVRFW diff --git a/Samples/SampleXrFramework/Src/Model/ModelCollision.h b/Samples/SampleXrFramework/Src/Model/ModelCollision.h new file mode 100755 index 0000000..1eade87 --- /dev/null +++ b/Samples/SampleXrFramework/Src/Model/ModelCollision.h @@ -0,0 +1,76 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/************************************************************************************ + +Filename : ModelCollision.h +Content : Basic collision detection for scene walkthroughs. +Created : May 2014 +Authors : J.M.P. van Waveren + +*************************************************************************************/ + +#pragma once + +#include "OVR_Math.h" + +#include +#include + +namespace OVRFW { + +class CollisionPolytope { + public: + void Add(const OVR::Planef& p) { + Planes.push_back(p); + } + + // Returns true if the given point is inside this polytope. + bool TestPoint(const OVR::Vector3f& p) const; + + // Returns true if the ray hits the polytope. + // The length of the ray is clipped to the point where the ray enters the polytope. + // Optionally the polytope boundary plane that is hit is returned. + bool TestRay( + const OVR::Vector3f& start, + const OVR::Vector3f& dir, + float& length, + OVR::Planef* plane) const; + + // Pops the given point out of the polytope if inside. + bool PopOut(OVR::Vector3f& p) const; + + public: + std::string Name; + std::vector Planes; +}; + +class ModelCollision { + public: + // Returns true if the given point is inside solid. + bool TestPoint(const OVR::Vector3f& p) const; + + // Returns true if the ray hits solid. + // The length of the ray is clipped to the point where the ray enters solid. + // Optionally the solid boundary plane that is hit is returned. + bool TestRay( + const OVR::Vector3f& start, + const OVR::Vector3f& dir, + float& length, + OVR::Planef* plane) const; + + // Pops the given point out of any collision geometry the point may be inside of. + bool PopOut(OVR::Vector3f& p) const; + + public: + std::vector Polytopes; +}; + +OVR::Vector3f SlideMove( + const OVR::Vector3f& footPos, + const float eyeHeight, + const OVR::Vector3f& moveDirection, + const float moveDistance, + const ModelCollision& collisionModel, + const ModelCollision& groundCollisionModel); + +} // namespace OVRFW diff --git a/Samples/SampleXrFramework/Src/Model/ModelDef.h b/Samples/SampleXrFramework/Src/Model/ModelDef.h new file mode 100755 index 0000000..4f73e05 --- /dev/null +++ b/Samples/SampleXrFramework/Src/Model/ModelDef.h @@ -0,0 +1,556 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/************************************************************************************ + +Filename : ModelDef.h +Content : Model file definitions. +Created : December 2013 +Authors : John Carmack, J.M.P. van Waveren + +*************************************************************************************/ + +#pragma once + +#include +#include +#include + +#include "Render/GlProgram.h" // GlProgram +#include "Render/GlTexture.h" +#include "Render/SurfaceRender.h" +#include "ModelCollision.h" +#include "ModelTrace.h" + +namespace OVRFW { + +class ModelFile; +class ModelState; + +struct MaterialParms { + MaterialParms() + : UseSrgbTextureFormats(false), + EnableDiffuseAniso(false), + EnableEmissiveLodClamp(true), + Transparent(false), + PolygonOffset(false) {} + + bool UseSrgbTextureFormats; // use sRGB textures + bool EnableDiffuseAniso; // enable anisotropic filtering on the diffuse texture + bool EnableEmissiveLodClamp; // enable LOD clamp on the emissive texture to avoid light bleeding + bool Transparent; // surfaces with this material flag need to render in a transparent pass + bool PolygonOffset; // render with polygon offset enabled + std::function ImageUriHandler; // custom image URI handler +}; + +enum ModelJointAnimation { + MODEL_JOINT_ANIMATION_NONE, + MODEL_JOINT_ANIMATION_ROTATE, + MODEL_JOINT_ANIMATION_SWAY, + MODEL_JOINT_ANIMATION_BOB +}; + +struct ModelJoint { + int index; + std::string name; + OVR::Matrix4f transform; + ModelJointAnimation animation; + OVR::Vector3f parameters; + float timeOffset; + float timeScale; +}; + +struct ModelTag { + std::string name; + OVR::Matrix4f matrix; + OVR::Vector4i jointIndices; + OVR::Vector4f jointWeights; +}; + +enum ModelComponentType { + MODEL_COMPONENT_TYPE_BYTE = 0X1400, // GL_BYTE + MODEL_COMPONENT_TYPE_UNSIGNED_BYTE = 0X1401, // GL_UNSIGNED_BYTE + MODEL_COMPONENT_TYPE_SHORT = 0x1402, // GL_SHORT + MODEL_COMPONENT_TYPE_UNSIGNED_SHORT = 0x1403, // GL_UNSIGNED_SHORT + MODEL_COMPONENT_TYPE_UNSIGNED_INT = 0x1405, // GL_UNSIGNED_INT + MODEL_COMPONENT_TYPE_FLOAT = 0x1406, // GL_FLOAT +}; + +struct ModelBuffer { + ModelBuffer() : byteLength(0) {} + + std::string name; + std::vector bufferData; + size_t byteLength; + ModelComponentType componentType = MODEL_COMPONENT_TYPE_UNSIGNED_BYTE; + int componentCount; +}; + +struct ModelBufferView { + ModelBufferView() : buffer(nullptr), byteOffset(0), byteLength(0), byteStride(0), target(0) {} + + std::string name; + const ModelBuffer* buffer; + size_t byteOffset; + size_t byteLength; + int byteStride; + int target; +}; + +enum ModelAccessorType { + ACCESSOR_UNKNOWN, + ACCESSOR_SCALAR, + ACCESSOR_VEC2, + ACCESSOR_VEC3, + ACCESSOR_VEC4, + ACCESSOR_MAT2, + ACCESSOR_MAT3, + ACCESSOR_MAT4 +}; + +#define MAX_MODEL_ACCESSOR_COMPONENT_SIZE 16 +class ModelAccessor { + public: + ModelAccessor() + : bufferView(nullptr), + byteOffset(0), + componentType(0), + count(0), + type(ACCESSOR_UNKNOWN), + minMaxSet(false), + normalized(false) { + memset(intMin, 0, sizeof(int) * MAX_MODEL_ACCESSOR_COMPONENT_SIZE); + memset(intMax, 0, sizeof(int) * MAX_MODEL_ACCESSOR_COMPONENT_SIZE); + memset(floatMin, 0, sizeof(float) * MAX_MODEL_ACCESSOR_COMPONENT_SIZE); + memset(floatMax, 0, sizeof(float) * MAX_MODEL_ACCESSOR_COMPONENT_SIZE); + } + + uint8_t* BufferData() const; + + std::string name; + const ModelBufferView* bufferView; + size_t byteOffset; + int componentType; + int count; + ModelAccessorType type; + bool minMaxSet; + // Minimum and Maximum values for each element in accessor data if the component type is a byte + // or int. + int intMin[MAX_MODEL_ACCESSOR_COMPONENT_SIZE]; + int intMax[MAX_MODEL_ACCESSOR_COMPONENT_SIZE]; + // Minimum and Maximum values for each element in accessor data if the component type is a + // float. + float floatMin[MAX_MODEL_ACCESSOR_COMPONENT_SIZE]; + float floatMax[MAX_MODEL_ACCESSOR_COMPONENT_SIZE]; + bool normalized; + // #TODO: implement Sparse Accessors +}; + +struct ModelTexture { + std::string name; + GlTexture texid; // texture id. will need to be freed when the object destroys itself. +}; + +// #TODO: currently we don't set any sampling parms on textures. +struct ModelSampler { + ModelSampler() + : magFilter(GL_LINEAR), + minFilter(GL_NEAREST_MIPMAP_LINEAR), + wrapS(GL_REPEAT), + wrapT(GL_REPEAT) {} + + std::string name; + int magFilter; + int minFilter; + int wrapS; + int wrapT; +}; + +struct ModelTextureWrapper { + ModelTextureWrapper() : image(nullptr), sampler(nullptr) {} + + std::string name; + const ModelTexture* image; + const ModelSampler* sampler; +}; + +enum ModelAlphaMode { ALPHA_MODE_OPAQUE, ALPHA_MODE_MASK, ALPHA_MODE_BLEND }; + +struct ModelMaterial { + ModelMaterial() + : baseColorTextureWrapper(nullptr), + metallicRoughnessTextureWrapper(nullptr), + normalTextureWrapper(nullptr), + occlusionTextureWrapper(nullptr), + emissiveTextureWrapper(nullptr), + detailTextureWrapper(nullptr), + baseColorFactor(1.0f, 1.0f, 1.0f, 1.0f), + emmisiveFactor(0.0f, 0.0f, 0.0f), + metallicFactor(1.0f), + roughnessFactor(1.0f), + alphaCutoff(0.5f), + alphaMode(ALPHA_MODE_OPAQUE), + normalTexCoord(0), + normalScale(1.0f), + occlusionTexCoord(0), + occlusionStrength(1.0f), + doubleSided(false) {} + + std::string name; + const ModelTextureWrapper* baseColorTextureWrapper; + const ModelTextureWrapper* metallicRoughnessTextureWrapper; + const ModelTextureWrapper* normalTextureWrapper; + const ModelTextureWrapper* occlusionTextureWrapper; + const ModelTextureWrapper* emissiveTextureWrapper; + const ModelTextureWrapper* detailTextureWrapper; + OVR::Vector4f baseColorFactor; + OVR::Vector3f emmisiveFactor; + float metallicFactor; + float roughnessFactor; + float alphaCutoff; + ModelAlphaMode alphaMode; + int normalTexCoord; + float normalScale; + int occlusionTexCoord; + float occlusionStrength; + bool doubleSided; +}; + +struct ModelSurface { + ModelSurface() : material(nullptr) {} + + const ModelMaterial* material; // material used to render this surface + ovrSurfaceDef surfaceDef; + VertexAttribs attribs; // Only populated if morph targets are used + std::vector targets; +}; + +struct Model { + std::string name; + std::vector surfaces; + std::vector weights; +}; + +typedef enum { MODEL_CAMERA_TYPE_PERSPECTIVE, MODEL_CAMERA_TYPE_ORTHOGRAPHIC } ModelCameraType; + +struct ModelPerspectiveCameraData { + ModelPerspectiveCameraData() + : aspectRatio(0.0f), fovDegreesX(0.0f), fovDegreesY(0.0f), nearZ(0.0f), farZ(0.0f) {} + + float aspectRatio; + float fovDegreesX; + float fovDegreesY; + float nearZ; + float farZ; +}; + +struct ModelOthographicCameraData { + ModelOthographicCameraData() : magX(0.0f), magY(0.0f), nearZ(0.0f), farZ(0.0f) {} + + float magX; + float magY; + float nearZ; + float farZ; +}; + +struct ModelCamera { + ModelCamera() : type(MODEL_CAMERA_TYPE_PERSPECTIVE) {} + + std::string name; + ModelCameraType type; + ModelPerspectiveCameraData perspective; + ModelOthographicCameraData orthographic; +}; + +class ModelNode { + public: + ModelNode() + : rotation(0.0f, 0.0f, 0.0f, 1.0f), + translation(0.0f, 0.0f, 0.0f), + scale(1.0f, 1.0f, 1.0f), + parentIndex(-1), + skinIndex(-1), + camera(nullptr), + model(nullptr), + localTransform(OVR::Matrix4f::Identity()), + globalTransform(OVR::Matrix4f::Identity()) {} + + OVR::Matrix4f GetLocalTransform() const { + return localTransform; + } + OVR::Matrix4f GetGlobalTransform() const { + return globalTransform; + } + void SetLocalTransform(const OVR::Matrix4f matrix); + void RecalculateGlobalTransform(ModelFile& modelFile); + + std::string name; + std::string jointName; + OVR::Quatf rotation; + OVR::Vector3f translation; + OVR::Vector3f scale; + std::vector weights; + + std::vector children; + int parentIndex; + int skinIndex; + const ModelCamera* camera; + Model* model; + + // old ovrscene animation system + std::vector JointsOvrScene; + + private: + OVR::Matrix4f localTransform; + OVR::Matrix4f globalTransform; +}; + +typedef enum { + MODEL_ANIMATION_INTERPOLATION_LINEAR, + MODEL_ANIMATION_INTERPOLATION_STEP, + MODEL_ANIMATION_INTERPOLATION_CATMULLROMSPLINE, + MODEL_ANIMATION_INTERPOLATION_CUBICSPLINE +} ModelAnimationInterpolation; + +struct ModelAnimationSampler { + ModelAnimationSampler() + : input(nullptr), + output(nullptr), + timeLineIndex(-1), + interpolation(MODEL_ANIMATION_INTERPOLATION_LINEAR) {} + + const ModelAccessor* input; + const ModelAccessor* output; + int timeLineIndex; + ModelAnimationInterpolation interpolation; +}; + +typedef enum { + MODEL_ANIMATION_PATH_UNKNOWN, + MODEL_ANIMATION_PATH_TRANSLATION, + MODEL_ANIMATION_PATH_ROTATION, + MODEL_ANIMATION_PATH_SCALE, + MODEL_ANIMATION_PATH_WEIGHTS +} ModelAnimationPath; + +struct ModelAnimationChannel { + ModelAnimationChannel() + : nodeIndex(-1), + additiveWeightIndex(-1), + sampler(nullptr), + path(MODEL_ANIMATION_PATH_UNKNOWN) {} + + int nodeIndex; + int additiveWeightIndex; + const ModelAnimationSampler* sampler; + ModelAnimationPath path; +}; + +class ModelAnimationTimeLine { + public: + ModelAnimationTimeLine() + : accessor(nullptr), + startTime(0.0f), + endTime(0.0f), + rcpStep(0.0f), + sampleTimes(nullptr), + sampleCount(0) {} + + void Initialize(const ModelAccessor* _accessor); + + const ModelAccessor* accessor; + float startTime; // in seconds; + float endTime; // in seconds + float rcpStep; // in seconds + const float* sampleTimes; // in seconds + int sampleCount; +}; + +struct ModelAnimation { + ModelAnimation() {} + + std::string name; + std::vector samplers; + std::vector channels; +}; + +struct ModelSkin { + ModelSkin() : inverseBindMatricesAccessor(nullptr) {} + + std::string name; + int skeletonRootIndex; + std::vector jointIndexes; + const ModelAccessor* inverseBindMatricesAccessor; + std::vector inverseBindMatrices; +}; + +struct ModelSubScene { + ModelSubScene() : visible(false) {} + + std::string name; + std::vector nodes; + bool visible; +}; + +class ModelNodeState { + public: + ModelNodeState() + : node(nullptr), + state(nullptr), + rotation(0.0f, 0.0f, 0.0f, 1.0f), + translation(0.0f, 0.0f, 0.0f), + scale(1.0f, 1.0f, 1.0f), + localTransform(OVR::Matrix4f::Identity()), + globalTransform(OVR::Matrix4f::Identity()) {} + + void GenerateStateFromNode(const ModelNode* _node, ModelState* _modelState); + void CalculateLocalTransform(); + void SetLocalTransform(const OVR::Matrix4f matrix); + OVR::Matrix4f GetLocalTransform() const { + return localTransform; + } + OVR::Matrix4f GetGlobalTransform() const { + return globalTransform; + } + void RecalculateMatrix(); + const ModelNode* GetNode() const { + return node; + } + + void AddNodesToEmitList(std::vector& emitList); + + const ModelNode* node; + ModelState* state; + OVR::Quatf rotation; + OVR::Vector3f translation; + OVR::Vector3f scale; + std::vector weights; + + private: + OVR::Matrix4f localTransform; + OVR::Matrix4f globalTransform; +}; + +enum ModelAnimationTimeType { + MODEL_ANIMATION_TIME_TYPE_ONCE_FORWARD, + MODEL_ANIMATION_TIME_TYPE_LOOP_FORWARD, + MODEL_ANIMATION_TIME_TYPE_LOOP_FORWARD_AND_BACK, +}; + +class ModelAnimationTimeLineState { + public: + ModelAnimationTimeLineState() : frame(0), fraction(0.0f), timeline(nullptr) {} + + void CalculateFrameAndFraction(float timeInSeconds); + + int frame; + float fraction; + const ModelAnimationTimeLine* timeline; +}; + +class ModelSubSceneState { + public: + ModelSubSceneState() : visible(false), subScene(nullptr) {} + + void GenerateStateFromSubScene(const ModelSubScene* _subScene); + bool visible; + std::vector nodeStates; + + private: + const ModelSubScene* subScene; +}; + +class ModelState { + public: + ModelState() : DontRenderForClientUid(0), mf(nullptr) { + modelMatrix.Identity(); + } + + void GenerateStateFromModelFile(const ModelFile* _mf); + void SetMatrix(const OVR::Matrix4f matrix); + OVR::Matrix4f GetMatrix() const { + return modelMatrix; + } + + void CalculateAnimationFrameAndFraction(const ModelAnimationTimeType type, float timeInSeconds); + + long long DontRenderForClientUid; // skip rendering the model if the current scene's client uid + // matches this + std::vector nodeStates; + std::vector animationTimelineStates; + std::vector subSceneStates; + + const ModelFile* mf; + + private: + OVR::Matrix4f modelMatrix; +}; + +struct ModelGlPrograms { + ModelGlPrograms() + : ProgVertexColor(nullptr), + ProgSingleTexture(nullptr), + ProgLightMapped(nullptr), + ProgReflectionMapped(nullptr), + ProgSimplePBR(nullptr), + ProgBaseColorPBR(nullptr), + ProgBaseColorEmissivePBR(nullptr), + ProgSkinnedVertexColor(nullptr), + ProgSkinnedSingleTexture(nullptr), + ProgSkinnedLightMapped(nullptr), + ProgSkinnedReflectionMapped(nullptr), + ProgSkinnedSimplePBR(nullptr), + ProgSkinnedBaseColorPBR(nullptr), + ProgSkinnedBaseColorEmissivePBR(nullptr) {} + ModelGlPrograms(const GlProgram* singleTexture) + : ProgVertexColor(singleTexture), + ProgSingleTexture(singleTexture), + ProgLightMapped(singleTexture), + ProgReflectionMapped(singleTexture), + ProgSimplePBR(singleTexture), + ProgBaseColorPBR(singleTexture), + ProgBaseColorEmissivePBR(singleTexture), + ProgSkinnedVertexColor(singleTexture), + ProgSkinnedSingleTexture(singleTexture), + ProgSkinnedLightMapped(singleTexture), + ProgSkinnedReflectionMapped(singleTexture), + ProgSkinnedSimplePBR(singleTexture), + ProgSkinnedBaseColorPBR(singleTexture), + ProgSkinnedBaseColorEmissivePBR(singleTexture) {} + ModelGlPrograms(const GlProgram* singleTexture, const GlProgram* dualTexture) + : ProgVertexColor(singleTexture), + ProgSingleTexture(singleTexture), + ProgLightMapped(dualTexture), + ProgReflectionMapped(dualTexture), + ProgSimplePBR(singleTexture), + ProgBaseColorPBR(singleTexture), + ProgBaseColorEmissivePBR(dualTexture), + ProgSkinnedVertexColor(singleTexture), + ProgSkinnedSingleTexture(singleTexture), + ProgSkinnedLightMapped(dualTexture), + ProgSkinnedReflectionMapped(dualTexture), + ProgSkinnedSimplePBR(singleTexture), + ProgSkinnedBaseColorPBR(singleTexture), + ProgSkinnedBaseColorEmissivePBR(dualTexture) {} + + const GlProgram* ProgVertexColor; + const GlProgram* ProgSingleTexture; + const GlProgram* ProgLightMapped; + const GlProgram* ProgReflectionMapped; + const GlProgram* ProgSimplePBR; + const GlProgram* ProgBaseColorPBR; + const GlProgram* ProgBaseColorEmissivePBR; + const GlProgram* ProgSkinnedVertexColor; + const GlProgram* ProgSkinnedSingleTexture; + const GlProgram* ProgSkinnedLightMapped; + const GlProgram* ProgSkinnedReflectionMapped; + const GlProgram* ProgSkinnedSimplePBR; + const GlProgram* ProgSkinnedBaseColorPBR; + const GlProgram* ProgSkinnedBaseColorEmissivePBR; +}; + +struct ModelGeo { + std::vector positions; + std::vector indices; +}; + +} // namespace OVRFW diff --git a/Samples/SampleXrFramework/Src/Model/ModelFile.cpp b/Samples/SampleXrFramework/Src/Model/ModelFile.cpp new file mode 100755 index 0000000..bd61e29 --- /dev/null +++ b/Samples/SampleXrFramework/Src/Model/ModelFile.cpp @@ -0,0 +1,679 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/************************************************************************************ + +Filename : ModelFile.cpp +Content : Model file loading common elements. +Created : December 2013 +Authors : John Carmack, J.M.P. van Waveren + +*************************************************************************************/ + +#include "ModelFileLoading.h" + +#include "PackageFiles.h" +#include "OVR_FileSys.h" +#include "OVR_MappedFile.h" + +#include "OVR_Std.h" + +#include "Misc/Log.h" + +using OVR::Bounds3f; +using OVR::Matrix4f; +using OVR::Quatf; +using OVR::Vector2f; +using OVR::Vector3f; +using OVR::Vector4f; + +namespace OVRFW { + +//----------------------------------------------------------------------------- +// ModelFile +//----------------------------------------------------------------------------- + +ModelFile::ModelFile() + : UsingSrgbTextures(false), animationStartTime(0.0f), animationEndTime(0.0f) {} + +ModelFile::~ModelFile() { + ALOG("Destroying ModelFileModel %s", FileName.c_str()); + + for (int i = 0; i < static_cast(Textures.size()); i++) { + FreeTexture(Textures[i].texid); + } + + for (int i = 0; i < static_cast(Models.size()); i++) { + for (int j = 0; j < static_cast(Models[i].surfaces.size()); j++) { + const_cast(&(Models[i].surfaces[j].surfaceDef.geo))->Free(); + } + } + + for (int i = 0; i < static_cast(Buffers.size()); i++) { + Buffers[i].bufferData.clear(); + } +} + +ovrSurfaceDef* ModelFile::FindNamedSurface(const char* name) const { + for (int i = 0; i < static_cast(Models.size()); i++) { + for (int j = 0; j < static_cast(Models[i].surfaces.size()); j++) { + const ovrSurfaceDef& sd = Models[i].surfaces[j].surfaceDef; + if (OVR::OVR_stricmp(sd.surfaceName.c_str(), name) == 0) { + return const_cast(&sd); + } + } + } + return nullptr; +} + +const ModelTexture* ModelFile::FindNamedTexture(const char* name) const { + for (int i = 0; i < static_cast(Textures.size()); i++) { + const ModelTexture& st = Textures[i]; + if (OVR::OVR_stricmp(st.name.c_str(), name) == 0) { + return &st; + } + } + return nullptr; +} + +const ModelJoint* ModelFile::FindNamedJoint(const char* name) const { + for (int i = 0; i < static_cast(Nodes.size()); i++) { + for (int j = 0; j < static_cast(Nodes[i].JointsOvrScene.size()); j++) { + const ModelJoint& joint = Nodes[i].JointsOvrScene[j]; + if (OVR::OVR_stricmp(joint.name.c_str(), name) == 0) { + return &joint; + } + } + } + return nullptr; +} + +const ModelTag* ModelFile::FindNamedTag(const char* name) const { + for (int i = 0; i < static_cast(Tags.size()); i++) { + const ModelTag& tag = Tags[i]; + if (OVR::OVR_stricmp(tag.name.c_str(), name) == 0) { + ALOG("Found named tag %s", name); + return &tag; + } + } + ALOG("Did not find named tag %s", name); + return nullptr; +} + +Bounds3f ModelFile::GetBounds() const { + Bounds3f modelBounds; + modelBounds.Clear(); + for (int i = 0; i < static_cast(Models.size()); i++) { + for (int j = 0; j < static_cast(Models[i].surfaces.size()); j++) { + const ovrSurfaceDef& sd = Models[i].surfaces[j].surfaceDef; + modelBounds.AddPoint(sd.geo.localBounds.b[0]); + modelBounds.AddPoint(sd.geo.localBounds.b[1]); + } + } + return modelBounds; +} + +void CalculateTransformFromRTS( + Matrix4f* localTransform, + const Quatf rotation, + const Vector3f translation, + const Vector3f scale) { + Matrix4f::Multiply(localTransform, Matrix4f(rotation), Matrix4f::Scaling(scale)); + Matrix4f::Multiply( + localTransform, Matrix4f::Translation(translation), Matrix4f(*localTransform)); +} + +//----------------------------------------------------------------------------- +// Model Loading +//----------------------------------------------------------------------------- + +void LoadModelFileTexture( + ModelFile& model, + const char* textureName, + const char* buffer, + const int size, + const MaterialParms& materialParms) { + ModelTexture tex; + tex.name = textureName; + tex.name = tex.name.substr(0, tex.name.rfind(".")); + int width; + int height; + tex.texid = LoadTextureFromBuffer( + textureName, + (const uint8_t*)buffer, + size, + (materialParms.UseSrgbTextureFormats ? TextureFlags_t(TEXTUREFLAG_USE_SRGB) + : TextureFlags_t()), + width, + height); + + // ALOG( ( tex.texid.target == GL_TEXTURE_CUBE_MAP ) ? "GL_TEXTURE_CUBE_MAP: %s" : + // "GL_TEXTURE_2D: %s", textureName ); + + // file name metadata for enabling clamp mode + // Used for sky sides in Tuscany. + if (strstr(textureName, "_c.")) { + MakeTextureClamped(tex.texid); + } + + model.Textures.push_back(tex); +} + +static ModelFile* LoadZippedModelFile( + unzFile zfp, + const char* fileName, + const char* fileData, + const int fileDataLength, + const ModelGlPrograms& programs, + const MaterialParms& materialParms, + ModelGeo* outModelGeo = nullptr) { + // LOGCPUTIME( "LoadZippedModelFile" ); + + ModelFile* modelFilePtr = new ModelFile; + + modelFilePtr->FileName = fileName; + modelFilePtr->UsingSrgbTextures = materialParms.UseSrgbTextureFormats; + + bool loaded = false; + + if (!zfp) { + ALOGW("Error: can't load %s", fileName); + } else if (strstr(fileName, ".gltf.ovrscene") != nullptr) { + loaded = LoadModelFile_glTF_OvrScene( + modelFilePtr, + zfp, + fileName, + fileData, + fileDataLength, + programs, + materialParms, + outModelGeo); + } else { + loaded = LoadModelFile_OvrScene( + modelFilePtr, + zfp, + fileName, + fileData, + fileDataLength, + programs, + materialParms, + outModelGeo); + } + + if (!loaded) { + ALOGW("Error: failed to load %s", fileName); + delete modelFilePtr; + modelFilePtr = nullptr; + } + + if (modelFilePtr) { + /// Bind the uniform data slots to the texture objects + for (int i = 0; i < static_cast(modelFilePtr->Models.size()); i++) { + auto& m = modelFilePtr->Models[i]; + for (int j = 0; j < static_cast(m.surfaces.size()); j++) { + ovrGraphicsCommand& gc = + *const_cast(&m.surfaces[j].surfaceDef.graphicsCommand); + gc.BindUniformTextures(); + } + } + } + + return modelFilePtr; +} + +#if !defined(OVR_OS_WIN32) + +struct zlib_mmap_opaque { + MappedFile file; + MappedView view; + const std::uint8_t* data; + const std::uint8_t* ptr; + int len; + int left; +}; + +static voidpf ZCALLBACK mmap_fopen_file_func(voidpf opaque, const char*, int) { + return opaque; +} + +static uLong ZCALLBACK mmap_fread_file_func(voidpf opaque, voidpf, void* buf, uLong size) { + zlib_mmap_opaque* state = (zlib_mmap_opaque*)opaque; + + if ((int)size <= 0 || state->left < (int)size) { + return 0; + } + + memcpy(buf, state->ptr, size); + state->ptr += size; + state->left -= size; + + return size; +} + +static uLong ZCALLBACK mmap_fwrite_file_func(voidpf, voidpf, const void*, uLong) { + return 0; +} + +static long ZCALLBACK mmap_ftell_file_func(voidpf opaque, voidpf) { + zlib_mmap_opaque* state = (zlib_mmap_opaque*)opaque; + + return state->len - state->left; +} + +static long ZCALLBACK mmap_fseek_file_func(voidpf opaque, voidpf, uLong offset, int origin) { + zlib_mmap_opaque* state = (zlib_mmap_opaque*)opaque; + + switch (origin) { + case SEEK_SET: + if ((int)offset < 0 || (int)offset > state->len) { + return 0; + } + state->ptr = state->data + offset; + state->left = state->len - offset; + break; + case SEEK_CUR: + if ((int)offset < 0 || (int)offset > state->left) { + return 0; + } + state->ptr += offset; + state->left -= offset; + break; + case SEEK_END: + state->ptr = state->data + state->len; + state->left = 0; + break; + } + + return 0; +} + +static int ZCALLBACK mmap_fclose_file_func(voidpf, voidpf) { + return 0; +} + +static int ZCALLBACK mmap_ferror_file_func(voidpf, voidpf) { + return 0; +} + +static void mem_set_opaque(zlib_mmap_opaque& opaque, const unsigned char* data, int len) { + opaque.data = data; + opaque.len = len; + opaque.ptr = data; + opaque.left = len; +} + +static bool mmap_open_opaque(const char* fileName, zlib_mmap_opaque& opaque) { + // If unable to open the ZIP file, + if (!opaque.file.OpenRead(fileName, true, true)) { + ALOGW("Couldn't open %s", fileName); + return false; + } + + int len = (int)opaque.file.GetLength(); + if (len <= 0) { + ALOGW("len = %i", len); + return false; + } + if (!opaque.view.Open(&opaque.file)) { + ALOGW("View open failed"); + return false; + } + if (!opaque.view.MapView(0, len)) { + ALOGW("MapView failed"); + return false; + } + + opaque.data = opaque.view.GetFront(); + opaque.len = len; + opaque.ptr = opaque.data; + opaque.left = len; + + return true; +} + +static unzFile open_opaque(zlib_mmap_opaque& zlib_opaque, const char* fileName) { + zlib_filefunc_def zlib_file_funcs = { + mmap_fopen_file_func, + mmap_fread_file_func, + mmap_fwrite_file_func, + mmap_ftell_file_func, + mmap_fseek_file_func, + mmap_fclose_file_func, + mmap_ferror_file_func, + &zlib_opaque}; + + return unzOpen2(fileName, &zlib_file_funcs); +} + +ModelFile* LoadModelFileFromMemory( + const char* fileName, + const void* buffer, + int bufferLength, + const ModelGlPrograms& programs, + const MaterialParms& materialParms, + ModelGeo* outModelGeo) { + // Open the .ModelFile file as a zip. + ALOG("LoadModelFileFromMemory %s %i", fileName, bufferLength); + + // Determine wether it's a glb binary file, or if it is a zipped up ovrscene. + if (strstr(fileName, ".glb") != nullptr) { + return LoadModelFile_glB( + fileName, (char*)buffer, bufferLength, programs, materialParms, outModelGeo); + } + + zlib_mmap_opaque zlib_opaque; + + mem_set_opaque(zlib_opaque, (const unsigned char*)buffer, bufferLength); + + unzFile zfp = open_opaque(zlib_opaque, fileName); + if (!zfp) { + ALOGW("could not open file %s", fileName); + return nullptr; + } + + ALOG("LoadModelFileFromMemory zfp = %p", zfp); + + return LoadZippedModelFile( + zfp, fileName, (char*)buffer, bufferLength, programs, materialParms, outModelGeo); +} + +ModelFile* LoadModelFile( + const char* fileName, + const ModelGlPrograms& programs, + const MaterialParms& materialParms) { + ALOG("LoadModelFile %s", fileName); + + zlib_mmap_opaque zlib_opaque; + + // Map and open the zip file + if (!mmap_open_opaque(fileName, zlib_opaque)) { + ALOGW("could not map file %s", fileName); + return nullptr; + } + + // Determine wether it's a glb binary file, or if it is a zipped up ovrscene. + if (strstr(fileName, ".glb") != nullptr) { + return LoadModelFile_glB( + fileName, (char*)zlib_opaque.data, zlib_opaque.len, programs, materialParms); + } + + unzFile zfp = open_opaque(zlib_opaque, fileName); + if (!zfp) { + ALOGW("could not open file %s", fileName); + return nullptr; + } + + return LoadZippedModelFile( + zfp, fileName, (char*)zlib_opaque.data, zlib_opaque.len, programs, materialParms); +} +#else +ModelFile* LoadModelFileFromMemory( + const char* fileName, + const void* buffer, + int bufferLength, + const ModelGlPrograms& programs, + const MaterialParms& materialParms, + ModelGeo* outModelGeo) { + // Open the .ModelFile file as a zip. + ALOG("LoadModelFileFromMemory %s %i", fileName, bufferLength); + + // Determine wether it's a glb binary file, or if it is a zipped up ovrscene. + if (strstr(fileName, ".glb") != nullptr) { + return LoadModelFile_glB( + fileName, (char*)buffer, bufferLength, programs, materialParms, outModelGeo); + } + + // Mobile code defaults to zipped ovrscene files - read those here + if (strstr(fileName, ".ovrscene") != nullptr) { + // crude URI -> file unpacking + std::string exeDirUri = exeDirAsUri(); + std::string fileUri = std::string(fileName).substr(6); // remove "apk://" + std::string rawFileName = exeDirUri.substr(8) + fileUri; // remove "file:///" + + unzFile zfp = unzOpen(rawFileName.c_str()); + ModelFile* modelFile = LoadZippedModelFile( + zfp, fileName, (char*)buffer, bufferLength, programs, materialParms, outModelGeo); + + return modelFile; + } + + return nullptr; +} + +ModelFile* LoadModelFile( + const char* fileName, + const ModelGlPrograms& programs, + const MaterialParms& materialParms) { + ALOG("LoadModelFile %s", fileName); + return nullptr; +} +#endif // !defined(OVR_OS_WIN32) + +ModelFile* LoadModelFileFromOtherApplicationPackage( + void* zipFile, + const char* nameInZip, + const ModelGlPrograms& programs, + const MaterialParms& materialParms) { + void* buffer; + int bufferLength; + + ovr_ReadFileFromOtherApplicationPackage(zipFile, nameInZip, bufferLength, buffer); + if (buffer == nullptr) { + ALOGW("Failed to load model file '%s' from apk", nameInZip); + return nullptr; + } + + ModelFile* scene = + LoadModelFileFromMemory(nameInZip, buffer, bufferLength, programs, materialParms); + + free(buffer); + return scene; +} + +ModelFile* LoadModelFileFromApplicationPackage( + const char* nameInZip, + const ModelGlPrograms& programs, + const MaterialParms& materialParms) { + return LoadModelFileFromOtherApplicationPackage( + ovr_GetApplicationPackageFile(), nameInZip, programs, materialParms); +} + +ModelFile* LoadModelFile( + ovrFileSys& fileSys, + const char* uri, + const ModelGlPrograms& programs, + const MaterialParms& materialParms) { + std::vector buffer; + if (!fileSys.ReadFile(uri, buffer)) { + ALOGW("Failed to load model uri '%s'", uri); + return nullptr; + } + ModelFile* scene = LoadModelFileFromMemory( + uri, buffer.data(), static_cast(buffer.size()), programs, materialParms); + return scene; +} + +uint8_t* ModelAccessor::BufferData() const { + if (bufferView == nullptr || bufferView->buffer == nullptr || + bufferView->buffer->bufferData.empty()) { + return nullptr; + } + return (uint8_t*)bufferView->buffer->bufferData.data() + bufferView->byteOffset + byteOffset; +} + +void ModelNode::SetLocalTransform(const Matrix4f matrix) { + localTransform = matrix; +} + +void ModelNode::RecalculateGlobalTransform(ModelFile& modelFile) { + if (parentIndex < 0) { + globalTransform = localTransform; + } else { + globalTransform = modelFile.Nodes[parentIndex].GetGlobalTransform() * localTransform; + } + + for (int i = 0; i < static_cast(children.size()); i++) { + modelFile.Nodes[children[i]].RecalculateGlobalTransform(modelFile); + } +} + +void ModelAnimationTimeLine::Initialize(const ModelAccessor* _accessor) { + accessor = _accessor; + sampleCount = accessor->count; + sampleTimes = (float*)(accessor->BufferData()); + startTime = sampleTimes[0]; + endTime = sampleTimes[sampleCount - 1]; + float duration = endTime - startTime; + const float step = duration / sampleCount; + rcpStep = 1.0f / step; + for (int keyFrameIndex = 0; keyFrameIndex < sampleCount; keyFrameIndex++) { + const float delta = + sampleTimes[keyFrameIndex] - (((float)keyFrameIndex) * step + startTime); + // Check if the time is more than 0.1 milliseconds from a fixed-rate time-line. + if (fabs(delta) > 1e-4f) { + rcpStep = 0.0f; + break; + } + } +} + +void ModelAnimationTimeLineState::CalculateFrameAndFraction(float timeInSeconds) { + if (timeInSeconds <= timeline->startTime) { + frame = 0; + fraction = 0.0f; + } else if (timeInSeconds >= timeline->endTime) { + frame = timeline->sampleCount - 2; + fraction = 1.0f; + } else { + if (timeline->rcpStep != 0.0f) { + // Use direct lookup if this is a fixed rate animation. + frame = (int)((timeInSeconds - timeline->startTime) * timeline->rcpStep); + } else { + // Use a binary search to find the key frame. + frame = 0; + // Use a binary search to find the key frame. + for (int sampleCount = timeline->sampleCount; sampleCount > 1; sampleCount >>= 1) { + const int mid = sampleCount >> 1; + if (timeInSeconds >= timeline->sampleTimes[frame + mid]) { + frame += mid; + sampleCount = (sampleCount - mid) * 2; + } + } + } + + fraction = (timeInSeconds - timeline->sampleTimes[frame]) / + (timeline->sampleTimes[frame + 1] - timeline->sampleTimes[frame]); + } +} + +void ModelState::CalculateAnimationFrameAndFraction( + const ModelAnimationTimeType type, + float timeInSeconds) { + // float prevTime = timeInSeconds; + switch (type) { + case MODEL_ANIMATION_TIME_TYPE_ONCE_FORWARD: { + if (timeInSeconds > mf->animationEndTime) { + timeInSeconds = mf->animationEndTime; + } + } break; + case MODEL_ANIMATION_TIME_TYPE_LOOP_FORWARD: { + timeInSeconds = fmodf(timeInSeconds, mf->animationEndTime); + } break; + case MODEL_ANIMATION_TIME_TYPE_LOOP_FORWARD_AND_BACK: { + const float tempDur = mf->animationEndTime * 2.0f; + timeInSeconds = fmodf(timeInSeconds, tempDur); + + if (timeInSeconds > mf->animationEndTime) { + timeInSeconds = timeInSeconds - mf->animationEndTime; + timeInSeconds = mf->animationEndTime - timeInSeconds; + } + } break; + } + + for (int i = 0; i < static_cast(animationTimelineStates.size()); i++) { + animationTimelineStates[i].CalculateFrameAndFraction(timeInSeconds); + } +} + +void ModelNodeState::GenerateStateFromNode(const ModelNode* _node, ModelState* _modelState) { + node = _node; + state = _modelState; + rotation = node->rotation; + translation = node->translation; + scale = node->scale; + weights = node->weights; + + // These values should be calculated already. + localTransform = node->GetLocalTransform(); + globalTransform = node->GetGlobalTransform(); +} + +void ModelNodeState::CalculateLocalTransform() { + CalculateTransformFromRTS(&localTransform, rotation, translation, scale); +} + +void ModelNodeState::SetLocalTransform(const Matrix4f matrix) { + localTransform = matrix; +} + +void ModelNodeState::RecalculateMatrix() { + if (node->parentIndex < 0) { + globalTransform = state->GetMatrix() * localTransform; + } else { + globalTransform = state->nodeStates[node->parentIndex].globalTransform * localTransform; + } + + for (int i = 0; i < static_cast(node->children.size()); i++) { + state->nodeStates[node->children[i]].RecalculateMatrix(); + } +} + +void ModelNodeState::AddNodesToEmitList(std::vector& emitList) { + emitList.push_back(this); + for (int i = 0; i < static_cast(node->children.size()); i++) { + state->nodeStates[node->children[i]].AddNodesToEmitList(emitList); + } +} + +void ModelSubSceneState::GenerateStateFromSubScene(const ModelSubScene* _subScene) { + subScene = _subScene; + visible = subScene->visible; + + nodeStates.resize(subScene->nodes.size()); + for (int i = 0; i < static_cast(subScene->nodes.size()); i++) { + nodeStates[i] = subScene->nodes[i]; + } +} + +void ModelState::GenerateStateFromModelFile(const ModelFile* _mf) { + subSceneStates.clear(); + modelMatrix = Matrix4f::Identity(); + + mf = _mf; + DontRenderForClientUid = 0; + + nodeStates.resize(mf->Nodes.size()); + for (int i = 0; i < static_cast(mf->Nodes.size()); i++) { + nodeStates[i].GenerateStateFromNode(&mf->Nodes[i], this); + } + + animationTimelineStates.resize(mf->AnimationTimeLines.size()); + for (int i = 0; i < static_cast(mf->AnimationTimeLines.size()); i++) { + animationTimelineStates[i].timeline = &mf->AnimationTimeLines[i]; + } + + subSceneStates.resize(mf->SubScenes.size()); + for (int i = 0; i < static_cast(mf->SubScenes.size()); i++) { + subSceneStates[i].GenerateStateFromSubScene(&mf->SubScenes[i]); + } +} + +void ModelState::SetMatrix(const Matrix4f matrix) { + modelMatrix = matrix; + for (int i = 0; i < static_cast(subSceneStates.size()); i++) { + for (int j = 0; j < static_cast(subSceneStates[i].nodeStates.size()); j++) { + nodeStates[subSceneStates[i].nodeStates[j]].RecalculateMatrix(); + } + } +} + +} // namespace OVRFW diff --git a/Samples/SampleXrFramework/Src/Model/ModelFile.h b/Samples/SampleXrFramework/Src/Model/ModelFile.h new file mode 100755 index 0000000..3cb4c68 --- /dev/null +++ b/Samples/SampleXrFramework/Src/Model/ModelFile.h @@ -0,0 +1,106 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/************************************************************************************ + +Filename : ModelFile.h +Content : Model file loading. +Created : December 2013 +Authors : John Carmack, J.M.P. van Waveren + +*************************************************************************************/ + +#pragma once + +#include "ModelDef.h" +#include "OVR_FileSys.h" + +namespace OVRFW { + +// A ModelFile is the in-memory representation of a digested model file. +// It should be imutable in normal circumstances, but it is ok to load +// and modify a model for a particular task, such as changing materials. +class ModelFile { + public: + ModelFile(); + ModelFile(const char* name) : FileName(name) {} + ~ModelFile(); // Frees all textures and geometry + + ovrSurfaceDef* FindNamedSurface(const char* name) const; + const ModelTexture* FindNamedTexture(const char* name) const; + const ModelJoint* FindNamedJoint(const char* name) const; + + // #TODO: deprecate, we should be doing things off Nodes instead of Tags now. + const ModelTag* FindNamedTag(const char* name) const; + + OVR::Bounds3f GetBounds() const; + + public: + std::string FileName; + bool UsingSrgbTextures; + + float animationStartTime; + float animationEndTime; + + std::vector Tags; + + // This is used by the movement code + ModelCollision Collisions; + ModelCollision GroundCollisions; + + // This is typically used for gaze selection. + ModelTrace TraceModel; + + std::vector Buffers; + std::vector BufferViews; + std::vector Accessors; + std::vector Textures; + std::vector Samplers; + std::vector TextureWrappers; + std::vector Materials; + std::vector Models; + std::vector Cameras; + std::vector Nodes; + std::vector Animations; + std::vector AnimationTimeLines; + std::vector Skins; + std::vector SubScenes; +}; + +// Pass in the programs that will be used for the model materials. +// Obviously not very general purpose. +// Returns nullptr if there is an error loading the file +ModelFile* LoadModelFileFromMemory( + const char* fileName, + const void* buffer, + int bufferLength, + const ModelGlPrograms& programs, + const MaterialParms& materialParms, + ModelGeo* outModelGeo = nullptr); + +// Returns nullptr if there is an error loading the file +ModelFile* LoadModelFile( + const char* fileName, + const ModelGlPrograms& programs, + const MaterialParms& materialParms); + +// Returns nullptr if the file is not found. +ModelFile* LoadModelFileFromOtherApplicationPackage( + void* zipFile, + const char* nameInZip, + const ModelGlPrograms& programs, + const MaterialParms& materialParms); + +// Returns nullptr if there is an error loading the file +ModelFile* LoadModelFileFromApplicationPackage( + const char* nameInZip, + const ModelGlPrograms& programs, + const MaterialParms& materialParms); + +// Returns nullptr if there is an error loading the file +ModelFile* LoadModelFile( + class ovrFileSys& fileSys, + const char* uri, + const ModelGlPrograms& programs, + const MaterialParms& materialParms); + +} // namespace OVRFW diff --git a/Samples/SampleXrFramework/Src/Model/ModelFileLoading.h b/Samples/SampleXrFramework/Src/Model/ModelFileLoading.h new file mode 100755 index 0000000..5d16f55 --- /dev/null +++ b/Samples/SampleXrFramework/Src/Model/ModelFileLoading.h @@ -0,0 +1,68 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/************************************************************************************ + +Filename : ModelFileLoading.h +Content : Model file loading. +Created : December 2013 +Authors : John Carmack, J.M.P. van Waveren + +*************************************************************************************/ + +#pragma once +#include "ModelFile.h" + +#include +#include + +#include "OVR_Math.h" + +#include + +// Verbose log, redefine this as LOG() to get lots more info dumped +// #define LOGV ALOG +#define LOGV(...) + +namespace OVRFW { + +void CalculateTransformFromRTS( + OVR::Matrix4f* localTransform, + const OVR::Quatf rotation, + const OVR::Vector3f translation, + const OVR::Vector3f scale); + +void LoadModelFileTexture( + ModelFile& model, + const char* textureName, + const char* buffer, + const int size, + const MaterialParms& materialParms); + +bool LoadModelFile_OvrScene( + ModelFile* modelPtr, + unzFile zfp, + const char* fileName, + const char* fileData, + const int fileDataLength, + const ModelGlPrograms& programs, + const MaterialParms& materialParms, + ModelGeo* outModelGeo = NULL); + +bool LoadModelFile_glTF_OvrScene( + ModelFile* modelFilePtr, + unzFile zfp, + const char* fileName, + const char* fileData, + const int fileDataLength, + const ModelGlPrograms& programs, + const MaterialParms& materialParms, + ModelGeo* outModelGeo = NULL); + +ModelFile* LoadModelFile_glB( + const char* fileName, + const char* fileData, + const int fileDataLength, + const ModelGlPrograms& programs, + const MaterialParms& materialParms, + ModelGeo* outModelGeo = NULL); +} // namespace OVRFW diff --git a/Samples/SampleXrFramework/Src/Model/ModelFile_OvrScene.cpp b/Samples/SampleXrFramework/Src/Model/ModelFile_OvrScene.cpp new file mode 100755 index 0000000..c628e57 --- /dev/null +++ b/Samples/SampleXrFramework/Src/Model/ModelFile_OvrScene.cpp @@ -0,0 +1,846 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/************************************************************************************ + +Filename : ModelFile_OvrScene.cpp +Content : Model file loading ovrscene elements. +Created : December 2013 +Authors : John Carmack, J.M.P. van Waveren + +*************************************************************************************/ + +#include "ModelFileLoading.h" + +#include "Render/GlGeometry.h" + +#include "OVR_Std.h" +#include "OVR_JSON.h" +#include "StringUtils.h" + +#include "Misc/Log.h" +#include "OVR_BinaryFile2.h" + +using OVR::Bounds3f; +using OVR::Matrix4f; +using OVR::Quatf; +using OVR::Vector2f; +using OVR::Vector3f; +using OVR::Vector4f; + +namespace OVRFW { + +template +void ReadModelArray( + std::vector<_type_>& out, + const char* string, + const BinaryReader& bin, + const int numElements) { + if (string != nullptr && string[0] != '\0' && numElements > 0) { + if (!bin.ReadArray(out, numElements)) { + OVR::StringUtils::StringTo(out, string); + } + } +} + +bool LoadModelFile_OvrScene_Json( + ModelFile& modelFile, + const char* modelsJson, + const int modelsJsonLength, + const char* modelsBin, + const int modelsBinLength, + const ModelGlPrograms& programs, + const MaterialParms& materialParms, + ModelGeo* outModelGeo) { + ALOG("parsing %s", modelFile.FileName.c_str()); + OVR_UNUSED(modelsJsonLength); + + const BinaryReader bin((const std::uint8_t*)modelsBin, modelsBinLength); + + if (modelsBin != nullptr && bin.ReadUInt32() != 0x6272766F) { + ALOGW("LoadModelFile_OvrScene_Json: bad binary file for %s", modelFile.FileName.c_str()); + return false; + } + + const char* error = nullptr; + auto json = OVR::JSON::Parse(modelsJson, &error); + if (json == nullptr) { + ALOGW( + "LoadModelFile_OvrScene_Json: Error loading %s : %s", + modelFile.FileName.c_str(), + error); + return false; + } + + if (modelFile.SubScenes.size() > 0 || modelFile.Nodes.size() > 0 || + modelFile.Models.size() > 0) { + ALOGW( + "LoadModelFile_OvrScene_Json: model already has data, replacing with %s", + modelFile.FileName.c_str()); + modelFile.SubScenes.clear(); + modelFile.Nodes.clear(); + modelFile.Models.clear(); + } + + const OVR::JsonReader models(json); + if (models.IsObject()) { + // For OvrScene we will generate just one subScene, node, and model to place the surfaces + // on. + const size_t modelIndex = static_cast(modelFile.Models.size()); + modelFile.Models.emplace_back(Model()); + modelFile.Models[modelIndex].name = "DefaultModelName"; + const size_t nodeIndex = static_cast(modelFile.Nodes.size()); + modelFile.Nodes.emplace_back(ModelNode()); + modelFile.Nodes[nodeIndex].name = "DefaultNodeName"; + modelFile.Nodes[nodeIndex].model = &modelFile.Models[modelIndex]; + const size_t sceneIndex = static_cast(modelFile.SubScenes.size()); + modelFile.SubScenes.emplace_back(ModelSubScene()); + modelFile.SubScenes[sceneIndex].name = "DefaultSceneName"; + modelFile.SubScenes[sceneIndex].visible = true; + modelFile.SubScenes[sceneIndex].nodes.push_back((int)nodeIndex); + + // + // Render Model + // + + const OVR::JsonReader render_model(models.GetChildByName("render_model")); + if (render_model.IsObject()) { + LOGV("loading render model.."); + + // + // Render Model Textures + // + + enum TextureOcclusion { + TEXTURE_OCCLUSION_OPAQUE, + TEXTURE_OCCLUSION_PERFORATED, + TEXTURE_OCCLUSION_TRANSPARENT + }; + + std::vector glTextures; + + const OVR::JsonReader texture_array(render_model.GetChildByName("textures")); + if (texture_array.IsArray()) { + while (!texture_array.IsEndOfArray()) { + const OVR::JsonReader texture(texture_array.GetNextArrayElement()); + if (texture.IsObject()) { + const std::string name = texture.GetChildStringByName("name"); + + // Try to match the texture names with the already loaded texture + // and create a default texture if the texture file is missing. + int i = 0; + for (; i < static_cast(modelFile.Textures.size()); i++) { + if (OVR::OVR_stricmp( + modelFile.Textures[i].name.c_str(), name.c_str()) == 0) { + break; + } + } + if (i == static_cast(modelFile.Textures.size())) { + ALOG("texture %s defaulted", name.c_str()); + // Create a default texture. + LoadModelFileTexture( + modelFile, name.c_str(), nullptr, 0, materialParms); + } + glTextures.push_back(modelFile.Textures[i].texid); + + const std::string usage = texture.GetChildStringByName("usage"); + if (usage == "diffuse") { + if (materialParms.EnableDiffuseAniso == true) { + MakeTextureAniso(modelFile.Textures[i].texid, 2.0f); + } + } else if (usage == "emissive") { + if (materialParms.EnableEmissiveLodClamp == true) { + // LOD clamp lightmap textures to avoid light bleeding + MakeTextureLodClamped(modelFile.Textures[i].texid, 1); + } + } + /* + const std::string occlusion = texture.GetChildStringByName( "occlusion" ); + + TextureOcclusion textureOcclusion = TEXTURE_OCCLUSION_OPAQUE; + if ( occlusion == "opaque" ) { textureOcclusion = + TEXTURE_OCCLUSION_OPAQUE; } else if ( occlusion == "perforated" ) { + textureOcclusion = TEXTURE_OCCLUSION_PERFORATED; } else if ( occlusion == + "transparent" ) { textureOcclusion = TEXTURE_OCCLUSION_TRANSPARENT; } + */ + } + } + } + + // + // Render Model Joints + // + + const OVR::JsonReader joint_array(render_model.GetChildByName("joints")); + if (joint_array.IsArray()) { + while (!joint_array.IsEndOfArray()) { + const OVR::JsonReader joint(joint_array.GetNextArrayElement()); + if (joint.IsObject()) { + const size_t index = + static_cast(modelFile.Nodes[nodeIndex].JointsOvrScene.size()); + modelFile.Nodes[nodeIndex].JointsOvrScene.emplace_back(ModelJoint()); + modelFile.Nodes[nodeIndex].JointsOvrScene[index].index = + static_cast(index); + modelFile.Nodes[nodeIndex].JointsOvrScene[index].name = + joint.GetChildStringByName("name"); + OVR::StringUtils::StringTo( + modelFile.Nodes[nodeIndex].JointsOvrScene[index].transform, + joint.GetChildStringByName("transform").c_str()); + modelFile.Nodes[nodeIndex].JointsOvrScene[index].animation = + MODEL_JOINT_ANIMATION_NONE; + const std::string animation = joint.GetChildStringByName("animation"); + if (animation == "none") { + modelFile.Nodes[nodeIndex].JointsOvrScene[index].animation = + MODEL_JOINT_ANIMATION_NONE; + } else if (animation == "rotate") { + modelFile.Nodes[nodeIndex].JointsOvrScene[index].animation = + MODEL_JOINT_ANIMATION_ROTATE; + } else if (animation == "sway") { + modelFile.Nodes[nodeIndex].JointsOvrScene[index].animation = + MODEL_JOINT_ANIMATION_SWAY; + } else if (animation == "bob") { + modelFile.Nodes[nodeIndex].JointsOvrScene[index].animation = + MODEL_JOINT_ANIMATION_BOB; + } + modelFile.Nodes[nodeIndex].JointsOvrScene[index].parameters.x = + joint.GetChildFloatByName("parmX"); + modelFile.Nodes[nodeIndex].JointsOvrScene[index].parameters.y = + joint.GetChildFloatByName("parmY"); + modelFile.Nodes[nodeIndex].JointsOvrScene[index].parameters.z = + joint.GetChildFloatByName("parmZ"); + modelFile.Nodes[nodeIndex].JointsOvrScene[index].timeOffset = + joint.GetChildFloatByName("timeOffset"); + modelFile.Nodes[nodeIndex].JointsOvrScene[index].timeScale = + joint.GetChildFloatByName("timeScale"); + } + } + } + + // + // Render Model Tags + // + + const OVR::JsonReader tag_array(render_model.GetChildByName("tags")); + if (tag_array.IsArray()) { + modelFile.Tags.clear(); + + while (!tag_array.IsEndOfArray()) { + const OVR::JsonReader tag(tag_array.GetNextArrayElement()); + if (tag.IsObject()) { + const size_t index = static_cast(modelFile.Tags.size()); + modelFile.Tags.emplace_back(ModelTag()); + modelFile.Tags[index].name = tag.GetChildStringByName("name"); + OVR::StringUtils::StringTo( + modelFile.Tags[index].matrix, + tag.GetChildStringByName("matrix").c_str()); + OVR::StringUtils::StringTo( + modelFile.Tags[index].jointIndices, + tag.GetChildStringByName("jointIndices").c_str()); + OVR::StringUtils::StringTo( + modelFile.Tags[index].jointWeights, + tag.GetChildStringByName("jointWeights").c_str()); + } + } + } + + // + // Render Model Surfaces + // + + const OVR::JsonReader surface_array(render_model.GetChildByName("surfaces")); + if (surface_array.IsArray()) { + while (!surface_array.IsEndOfArray()) { + const OVR::JsonReader surface(surface_array.GetNextArrayElement()); + if (surface.IsObject()) { + ModelSurface modelSurface; + + // + // Source Meshes + // + + const OVR::JsonReader source(surface.GetChildByName("source")); + if (source.IsArray()) { + while (!source.IsEndOfArray()) { + if (modelSurface.surfaceDef.surfaceName.length()) { + modelSurface.surfaceDef.surfaceName += ";"; + } + modelSurface.surfaceDef.surfaceName += source.GetNextArrayString(); + } + } + + LOGV("surface %s", modelSurface.surfaceDef.surfaceName.c_str()); + + // + // Surface Material + // + + enum { + MATERIAL_TYPE_OPAQUE, + MATERIAL_TYPE_PERFORATED, + MATERIAL_TYPE_TRANSPARENT, + MATERIAL_TYPE_ADDITIVE + } materialType = MATERIAL_TYPE_OPAQUE; + + int diffuseTextureIndex = -1; + int normalTextureIndex = -1; + int specularTextureIndex = -1; + int emissiveTextureIndex = -1; + int reflectionTextureIndex = -1; + + const OVR::JsonReader material(surface.GetChildByName("material")); + if (material.IsObject()) { + const std::string type = material.GetChildStringByName("type"); + + if (type == "opaque") { + materialType = MATERIAL_TYPE_OPAQUE; + } else if (type == "perforated") { + materialType = MATERIAL_TYPE_PERFORATED; + } else if (type == "transparent") { + materialType = MATERIAL_TYPE_TRANSPARENT; + } else if (type == "additive") { + materialType = MATERIAL_TYPE_ADDITIVE; + } + + diffuseTextureIndex = material.GetChildInt32ByName("diffuse", -1); + normalTextureIndex = material.GetChildInt32ByName("normal", -1); + specularTextureIndex = material.GetChildInt32ByName("specular", -1); + emissiveTextureIndex = material.GetChildInt32ByName("emissive", -1); + reflectionTextureIndex = material.GetChildInt32ByName("reflection", -1); + } + + // + // Surface Bounds + // + + OVR::StringUtils::StringTo( + modelSurface.surfaceDef.geo.localBounds, + surface.GetChildStringByName("bounds").c_str()); + + TriangleIndex indexOffset = 0; + if (outModelGeo != nullptr) { + indexOffset = + static_cast((*outModelGeo).positions.size()); + } + // + // Vertices + // + + VertexAttribs attribs; + + const OVR::JsonReader vertices(surface.GetChildByName("vertices")); + if (vertices.IsObject()) { + const int vertexCount = std::min( + vertices.GetChildInt32ByName("vertexCount"), + GlGeometry::GetMaxGeometryVertices()); + // ALOG( "%5d vertices", vertexCount ); + + ReadModelArray( + attribs.position, + vertices.GetChildStringByName("position").c_str(), + bin, + vertexCount); + ReadModelArray( + attribs.normal, + vertices.GetChildStringByName("normal").c_str(), + bin, + vertexCount); + ReadModelArray( + attribs.tangent, + vertices.GetChildStringByName("tangent").c_str(), + bin, + vertexCount); + ReadModelArray( + attribs.binormal, + vertices.GetChildStringByName("binormal").c_str(), + bin, + vertexCount); + ReadModelArray( + attribs.color, + vertices.GetChildStringByName("color").c_str(), + bin, + vertexCount); + ReadModelArray( + attribs.uv0, + vertices.GetChildStringByName("uv0").c_str(), + bin, + vertexCount); + ReadModelArray( + attribs.uv1, + vertices.GetChildStringByName("uv1").c_str(), + bin, + vertexCount); + ReadModelArray( + attribs.jointIndices, + vertices.GetChildStringByName("jointIndices").c_str(), + bin, + vertexCount); + ReadModelArray( + attribs.jointWeights, + vertices.GetChildStringByName("jointWeights").c_str(), + bin, + vertexCount); + + if (outModelGeo != nullptr) { + for (int i = 0; i < static_cast(attribs.position.size()); + ++i) { + (*outModelGeo).positions.push_back(attribs.position[i]); + } + } + } + + // + // Triangles + // + + std::vector indices; + + const OVR::JsonReader triangles(surface.GetChildByName("triangles")); + if (triangles.IsObject()) { + const int indexCount = std::min( + triangles.GetChildInt32ByName("indexCount"), + GlGeometry::GetMaxGeometryIndices()); + // ALOG( "%5d indices", indexCount ); + + ReadModelArray( + indices, + triangles.GetChildStringByName("indices").c_str(), + bin, + indexCount); + } + + if (outModelGeo != nullptr) { + for (int i = 0; i < static_cast(indices.size()); ++i) { + (*outModelGeo).indices.push_back(indices[i] + indexOffset); + } + } + + // + // Setup geometry, textures and render programs now that the vertex + // attributes are known. + // + + modelSurface.surfaceDef.geo.Create(attribs, indices); + + const char* materialTypeString = "opaque"; + OVR_UNUSED( + materialTypeString); // we'll get warnings if the LOGV's compile out + + // set up additional material flags for the surface + if (materialType == MATERIAL_TYPE_PERFORATED) { + // Just blend because alpha testing is rather expensive. + modelSurface.surfaceDef.graphicsCommand.GpuState.blendEnable = + ovrGpuState::BLEND_ENABLE; + modelSurface.surfaceDef.graphicsCommand.GpuState.depthMaskEnable = + false; + modelSurface.surfaceDef.graphicsCommand.GpuState.blendSrc = + GL_SRC_ALPHA; + modelSurface.surfaceDef.graphicsCommand.GpuState.blendDst = + GL_ONE_MINUS_SRC_ALPHA; + materialTypeString = "perforated"; + } else if ( + materialType == MATERIAL_TYPE_TRANSPARENT || + materialParms.Transparent) { + modelSurface.surfaceDef.graphicsCommand.GpuState.blendEnable = + ovrGpuState::BLEND_ENABLE; + modelSurface.surfaceDef.graphicsCommand.GpuState.depthMaskEnable = + false; + modelSurface.surfaceDef.graphicsCommand.GpuState.blendSrc = + GL_SRC_ALPHA; + modelSurface.surfaceDef.graphicsCommand.GpuState.blendDst = + GL_ONE_MINUS_SRC_ALPHA; + materialTypeString = "transparent"; + } else if (materialType == MATERIAL_TYPE_ADDITIVE) { + modelSurface.surfaceDef.graphicsCommand.GpuState.blendEnable = + ovrGpuState::BLEND_ENABLE; + modelSurface.surfaceDef.graphicsCommand.GpuState.depthMaskEnable = + false; + modelSurface.surfaceDef.graphicsCommand.GpuState.blendSrc = GL_ONE; + modelSurface.surfaceDef.graphicsCommand.GpuState.blendDst = GL_ONE; + materialTypeString = "additive"; + } + + const bool skinned = + (attribs.jointIndices.size() == attribs.position.size() && + attribs.jointWeights.size() == attribs.position.size()); + + if (diffuseTextureIndex >= 0 && + diffuseTextureIndex < static_cast(glTextures.size())) { + modelSurface.surfaceDef.graphicsCommand.Textures[0] = + glTextures[diffuseTextureIndex]; + + if (emissiveTextureIndex >= 0 && + emissiveTextureIndex < static_cast(glTextures.size())) { + modelSurface.surfaceDef.graphicsCommand.Textures[1] = + glTextures[emissiveTextureIndex]; + + if (normalTextureIndex >= 0 && + normalTextureIndex < static_cast(glTextures.size()) && + specularTextureIndex >= 0 && + specularTextureIndex < static_cast(glTextures.size()) && + reflectionTextureIndex >= 0 && + reflectionTextureIndex < static_cast(glTextures.size())) { + // reflection mapped material; + modelSurface.surfaceDef.graphicsCommand.Textures[2] = + glTextures[normalTextureIndex]; + modelSurface.surfaceDef.graphicsCommand.Textures[3] = + glTextures[specularTextureIndex]; + modelSurface.surfaceDef.graphicsCommand.Textures[4] = + glTextures[reflectionTextureIndex]; + + if (skinned) { + if (programs.ProgSkinnedReflectionMapped == nullptr) { + ALOGE_FAIL("No ProgSkinnedReflectionMapped set"); + } + modelSurface.surfaceDef.graphicsCommand.Program = + *programs.ProgSkinnedReflectionMapped; + LOGV( + "%s skinned reflection mapped material", + materialTypeString); + } else { + if (programs.ProgReflectionMapped == nullptr) { + ALOGE_FAIL("No ProgReflectionMapped set"); + } + modelSurface.surfaceDef.graphicsCommand.Program = + *programs.ProgReflectionMapped; + LOGV("%s reflection mapped material", materialTypeString); + } + } else { + // light mapped material + if (skinned) { + if (programs.ProgSkinnedLightMapped == nullptr) { + ALOGE_FAIL("No ProgSkinnedLightMapped set"); + } + modelSurface.surfaceDef.graphicsCommand.Program = + *programs.ProgSkinnedLightMapped; + LOGV( + "%s skinned light mapped material", materialTypeString); + } else { + if (programs.ProgLightMapped == nullptr) { + ALOGE_FAIL("No ProgLightMapped set"); + } + modelSurface.surfaceDef.graphicsCommand.Program = + *programs.ProgLightMapped; + LOGV("%s light mapped material", materialTypeString); + } + } + } else { + // diffuse only material + if (skinned) { + if (programs.ProgSkinnedSingleTexture == nullptr) { + ALOGE_FAIL("No ProgSkinnedSingleTexture set"); + } + modelSurface.surfaceDef.graphicsCommand.Program = + *programs.ProgSkinnedSingleTexture; + LOGV("%s skinned diffuse only material", materialTypeString); + } else { + if (programs.ProgSingleTexture == nullptr) { + ALOGE_FAIL("No ProgSingleTexture set"); + } + modelSurface.surfaceDef.graphicsCommand.Program = + *programs.ProgSingleTexture; + LOGV("%s diffuse only material", materialTypeString); + } + } + } else if (attribs.color.size() > 0) { + // vertex color material + if (skinned) { + if (programs.ProgSkinnedVertexColor == nullptr) { + ALOGE_FAIL("No ProgSkinnedVertexColor set"); + } + modelSurface.surfaceDef.graphicsCommand.Program = + *programs.ProgSkinnedVertexColor; + LOGV("%s skinned vertex color material", materialTypeString); + } else { + if (programs.ProgVertexColor == nullptr) { + ALOGE_FAIL("No ProgVertexColor set"); + } + modelSurface.surfaceDef.graphicsCommand.Program = + *programs.ProgVertexColor; + LOGV("%s vertex color material", materialTypeString); + } + } else { + // surface without texture or vertex colors + modelSurface.surfaceDef.graphicsCommand.Textures[0] = GlTexture(); + if (skinned) { + if (programs.ProgSkinnedSingleTexture == nullptr) { + ALOGE_FAIL("No ProgSkinnedSingleTexture set"); + } + modelSurface.surfaceDef.graphicsCommand.Program = + *programs.ProgSkinnedSingleTexture; + LOGV("%s skinned default texture material", materialTypeString); + } else { + if (programs.ProgSingleTexture == nullptr) { + ALOGE_FAIL("No ProgSingleTexture set"); + } + modelSurface.surfaceDef.graphicsCommand.Program = + *programs.ProgSingleTexture; + LOGV("%s default texture material", materialTypeString); + } + } + + if (materialParms.PolygonOffset) { + modelSurface.surfaceDef.graphicsCommand.GpuState.polygonOffsetEnable = + true; + LOGV("polygon offset material"); + } + + modelFile.Models[modelIndex].surfaces.push_back(modelSurface); + } + } + } + } + + // + // Collision Model + // + + const OVR::JsonReader collision_model(models.GetChildByName("collision_model")); + if (collision_model.IsArray()) { + LOGV("loading collision model.."); + + while (!collision_model.IsEndOfArray()) { + const size_t index = static_cast(modelFile.Collisions.Polytopes.size()); + modelFile.Collisions.Polytopes.emplace_back(CollisionPolytope()); + + const OVR::JsonReader polytope(collision_model.GetNextArrayElement()); + if (polytope.IsObject()) { + modelFile.Collisions.Polytopes[index].Name = + polytope.GetChildStringByName("name"); + OVR::StringUtils::StringTo( + modelFile.Collisions.Polytopes[index].Planes, + polytope.GetChildStringByName("planes").c_str()); + } + } + } + + // + // Ground Collision Model + // + + const OVR::JsonReader ground_collision_model( + models.GetChildByName("ground_collision_model")); + if (ground_collision_model.IsArray()) { + LOGV("loading ground collision model.."); + + while (!ground_collision_model.IsEndOfArray()) { + const size_t index = + static_cast(modelFile.GroundCollisions.Polytopes.size()); + modelFile.GroundCollisions.Polytopes.emplace_back(CollisionPolytope()); + + const OVR::JsonReader polytope(ground_collision_model.GetNextArrayElement()); + if (polytope.IsObject()) { + modelFile.GroundCollisions.Polytopes[index].Name = + polytope.GetChildStringByName("name"); + OVR::StringUtils::StringTo( + modelFile.GroundCollisions.Polytopes[index].Planes, + polytope.GetChildStringByName("planes").c_str()); + } + } + } + + // + // Ray-Trace Model + // + + const OVR::JsonReader raytrace_model(models.GetChildByName("raytrace_model")); + if (raytrace_model.IsObject()) { + LOGV("loading ray-trace model.."); + + ModelTrace& traceModel = modelFile.TraceModel; + + traceModel.header.numVertices = raytrace_model.GetChildInt32ByName("numVertices"); + traceModel.header.numUvs = raytrace_model.GetChildInt32ByName("numUvs"); + traceModel.header.numIndices = raytrace_model.GetChildInt32ByName("numIndices"); + traceModel.header.numNodes = raytrace_model.GetChildInt32ByName("numNodes"); + traceModel.header.numLeafs = raytrace_model.GetChildInt32ByName("numLeafs"); + traceModel.header.numOverflow = raytrace_model.GetChildInt32ByName("numOverflow"); + if (!traceModel.Validate(true)) { + // this is a fatal error so that a model file from an untrusted source is never able + // to cause out-of-bounds reads. + ALOGE_FAIL("Invalid model data"); + } + + OVR::StringUtils::StringTo( + traceModel.header.bounds, raytrace_model.GetChildStringByName("bounds").c_str()); + + ReadModelArray( + traceModel.vertices, + raytrace_model.GetChildStringByName("vertices").c_str(), + bin, + traceModel.header.numVertices); + ReadModelArray( + traceModel.uvs, + raytrace_model.GetChildStringByName("uvs").c_str(), + bin, + traceModel.header.numUvs); + ReadModelArray( + traceModel.indices, + raytrace_model.GetChildStringByName("indices").c_str(), + bin, + traceModel.header.numIndices); + + if (!bin.ReadArray(traceModel.nodes, traceModel.header.numNodes)) { + const OVR::JsonReader nodes_array(raytrace_model.GetChildByName("nodes")); + if (nodes_array.IsArray()) { + while (!nodes_array.IsEndOfArray()) { + const size_t index = static_cast(traceModel.nodes.size()); + traceModel.nodes.emplace_back(kdtree_node_t()); + + const OVR::JsonReader node(nodes_array.GetNextArrayElement()); + if (node.IsObject()) { + traceModel.nodes[index].data = + (std::uint32_t)node.GetChildInt64ByName("data"); + traceModel.nodes[index].dist = node.GetChildFloatByName("dist"); + } + } + } + } + + if (!bin.ReadArray(traceModel.leafs, traceModel.header.numLeafs)) { + const OVR::JsonReader leafs_array(raytrace_model.GetChildByName("leafs")); + if (leafs_array.IsArray()) { + while (!leafs_array.IsEndOfArray()) { + const size_t index = static_cast(traceModel.leafs.size()); + traceModel.leafs.emplace_back(kdtree_leaf_t()); + + const OVR::JsonReader leaf(leafs_array.GetNextArrayElement()); + if (leaf.IsObject()) { + OVR::StringUtils::StringTo( + traceModel.leafs[index].triangles, + RT_KDTREE_MAX_LEAF_TRIANGLES, + leaf.GetChildStringByName("triangles").c_str()); + OVR::StringUtils::StringTo( + traceModel.leafs[index].ropes, + 6, + leaf.GetChildStringByName("ropes").c_str()); + OVR::StringUtils::StringTo( + traceModel.leafs[index].bounds, + leaf.GetChildStringByName("bounds").c_str()); + } + } + } + } + + ReadModelArray( + traceModel.overflow, + raytrace_model.GetChildStringByName("overflow").c_str(), + bin, + traceModel.header.numOverflow); + } + } + + if (!bin.IsAtEnd()) { + ALOGW("failed to properly read binary file"); + } + + return true; +} + +bool LoadModelFile_OvrScene( + ModelFile* modelPtr, + unzFile zfp, + const char* fileName, + const char* fileData, + const int fileDataLength, + const ModelGlPrograms& programs, + const MaterialParms& materialParms, + ModelGeo* outModelGeo) { + // LOGCPUTIME( "LoadModelFile_OvrScene" ); + + ModelFile& model = *modelPtr; + + if (!zfp) { + ALOGW("Error: can't load %s", fileName); + return false; + } + + // load all texture files and locate the model files + + const char* modelsJson = nullptr; + int modelsJsonLength = 0; + + const char* modelsBin = nullptr; + int modelsBinLength = 0; + + for (int ret = unzGoToFirstFile(zfp); ret == UNZ_OK; ret = unzGoToNextFile(zfp)) { + unz_file_info finfo; + char entryName[256]; + unzGetCurrentFileInfo(zfp, &finfo, entryName, sizeof(entryName), nullptr, 0, nullptr, 0); + LOGV("zip level: %ld, file: %s", finfo.compression_method, entryName); + + if (unzOpenCurrentFile(zfp) != UNZ_OK) { + ALOGW("Failed to open %s from %s", entryName, fileName); + continue; + } + + const int size = finfo.uncompressed_size; + char* buffer = nullptr; + + if (finfo.compression_method == 0 && fileData != nullptr) { + buffer = (char*)fileData + unzGetCurrentFileZStreamPos64(zfp); + } else { + buffer = new char[size + 1]; + buffer[size] = '\0'; // always zero terminate text files + + if (unzReadCurrentFile(zfp, buffer, size) != size) { + ALOGW("Failed to read %s from %s", entryName, fileName); + delete[] buffer; + continue; + } + } + + // assume a 3 character extension + const size_t entryLength = strlen(entryName); + const char* extension = (entryLength >= 4) ? &entryName[entryLength - 4] : entryName; + + if (OVR::OVR_stricmp(entryName, "models.json") == 0) { + // save this for parsing + modelsJson = (const char*)buffer; + modelsJsonLength = size; + buffer = nullptr; // don't free it now + } else if (OVR::OVR_stricmp(entryName, "models.bin") == 0) { + // save this for parsing + modelsBin = (const char*)buffer; + modelsBinLength = size; + buffer = nullptr; // don't free it now + } else if ( + OVR::OVR_stricmp(extension, ".pvr") == 0 || OVR::OVR_stricmp(extension, ".ktx") == 0) { + // only support .pvr and .ktx containers for now + LoadModelFileTexture(model, entryName, buffer, size, materialParms); + } else { + // ignore other files + LOGV("Ignoring %s", entryName); + } + + if (buffer < fileData || buffer > fileData + fileDataLength) { + delete[] buffer; + } + + unzCloseCurrentFile(zfp); + } + unzClose(zfp); + + bool loaded = false; + + if (modelsJson != nullptr) { + loaded = LoadModelFile_OvrScene_Json( + model, + modelsJson, + modelsJsonLength, + modelsBin, + modelsBinLength, + programs, + materialParms, + outModelGeo); + } + + if (modelsJson < fileData || modelsJson > fileData + fileDataLength) { + delete modelsJson; + } + if (modelsBin < fileData || modelsBin > fileData + fileDataLength) { + delete modelsBin; + } + + return loaded; +} + +} // namespace OVRFW diff --git a/Samples/SampleXrFramework/Src/Model/ModelFile_glTF.cpp b/Samples/SampleXrFramework/Src/Model/ModelFile_glTF.cpp new file mode 100755 index 0000000..b251b3f --- /dev/null +++ b/Samples/SampleXrFramework/Src/Model/ModelFile_glTF.cpp @@ -0,0 +1,2606 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/************************************************************************************ + +Filename : ModelFile_OvrScene.cpp +Content : Model file loading glTF elements. +Created : December 2013 +Authors : John Carmack, J.M.P. van Waveren + +*************************************************************************************/ + +#include "Model/ModelDef.h" +#include "ModelFileLoading.h" + +#include "OVR_Std.h" +#include "OVR_JSON.h" +#include "StringUtils.h" + +#include "Misc/Log.h" +#include "OVR_BinaryFile2.h" + +#include + +using OVR::Bounds3f; +using OVR::Matrix4f; +using OVR::Quatf; +using OVR::Vector2f; +using OVR::Vector3f; +using OVR::Vector4f; + +namespace OVRFW { + +#define GLTF_BINARY_MAGIC (('g' << 0) | ('l' << 8) | ('T' << 16) | ('F' << 24)) +#define GLTF_BINARY_VERSION 2 +#define GLTF_BINARY_CHUNKTYPE_JSON 0x4E4F534A +#define GLTF_BINARY_CHUNKTYPE_BINARY 0x004E4942 + +typedef struct glTFBinaryHeader { + uint32_t magic; + uint32_t version; + uint32_t length; +} glTFBinaryHeader; + +static uint8_t* +ReadBufferFromZipFile(unzFile zfp, const uint8_t* fileData, const unz_file_info& finfo) { + const int size = finfo.uncompressed_size; + uint8_t* buffer = nullptr; + + if (unzOpenCurrentFile(zfp) != UNZ_OK) { + return buffer; + } + + if (finfo.compression_method == 0 && fileData != nullptr) { + buffer = (uint8_t*)fileData + unzGetCurrentFileZStreamPos64(zfp); + } else { + buffer = new uint8_t[size + 1]; + buffer[size] = '\0'; // always zero terminate text files + + if (unzReadCurrentFile(zfp, buffer, size) != size) { + delete[] buffer; + buffer = nullptr; + } + } + + return buffer; +} + +static uint8_t* ReadFileBufferFromZipFile( + unzFile zfp, + const char* fileName, + int& bufferLength, + const uint8_t* fileData) { + for (int ret = unzGoToFirstFile(zfp); ret == UNZ_OK; ret = unzGoToNextFile(zfp)) { + unz_file_info finfo; + char entryName[256]; + unzGetCurrentFileInfo(zfp, &finfo, entryName, sizeof(entryName), nullptr, 0, nullptr, 0); + + if (OVR::OVR_stricmp(entryName, fileName) == 0) { + bufferLength = finfo.uncompressed_size; + uint8_t* buffer = ReadBufferFromZipFile(zfp, fileData, finfo); + return buffer; + } + } + + bufferLength = 0; + return nullptr; +} + +static void ParseIntArray(int* elements, const int count, const OVR::JsonReader arrayNode) { + int i = 0; + if (arrayNode.IsArray()) { + while (!arrayNode.IsEndOfArray() && i < count) { + auto node = arrayNode.GetNextArrayElement(); + elements[i] = node->GetInt32Value(); + i++; + } + } + + for (; i < count; i++) { + elements[i] = 0; + } +} + +static void ParseFloatArray(float* elements, const int count, OVR::JsonReader arrayNode) { + int i = 0; + if (arrayNode.IsArray()) { + while (!arrayNode.IsEndOfArray() && i < count) { + auto node = arrayNode.GetNextArrayElement(); + elements[i] = node->GetFloatValue(); + i++; + } + } + + for (; i < count; i++) { + elements[i] = 0.0f; + } +} + +static size_t getComponentCount(ModelAccessorType type) { + switch (type) { + case ACCESSOR_SCALAR: + return 1; + case ACCESSOR_VEC2: + return 2; + case ACCESSOR_VEC3: + return 3; + case ACCESSOR_VEC4: + return 4; + case ACCESSOR_MAT2: + return 4; + case ACCESSOR_MAT3: + return 9; + case ACCESSOR_MAT4: + return 16; + case ACCESSOR_UNKNOWN: + default: + return 0; + } +} + +static size_t getComponentSize(int componentType) { + switch (componentType) { + case MODEL_COMPONENT_TYPE_UNSIGNED_BYTE: + case MODEL_COMPONENT_TYPE_BYTE: + return 1; + case MODEL_COMPONENT_TYPE_SHORT: + case MODEL_COMPONENT_TYPE_UNSIGNED_SHORT: + return 2; + case MODEL_COMPONENT_TYPE_UNSIGNED_INT: + case MODEL_COMPONENT_TYPE_FLOAT: + return 4; + default: + return 0; + } +} + +template +bool ReadSurfaceDataFromAccessor( + std::vector<_type_>& out, + ModelFile& modelFile, + const int index, + const ModelAccessorType type, + const int componentType, + const int count, + const bool append) { + bool loaded = true; + if (index >= 0) { + if (index >= int(modelFile.Accessors.size())) { + ALOGW( + "Error: Invalid index on gltfPrimitive accessor %d %d", + index, + static_cast(modelFile.Accessors.size())); + loaded = false; + } + + const ModelAccessor* accessor = &(modelFile.Accessors[index]); + const ModelBufferView* bufferView = accessor->bufferView; + const ModelBuffer* buffer = bufferView->buffer; + + if (count >= 0 && accessor->count != count) { + ALOGW( + "Error: Invalid count on gltfPrimitive accessor %d %d %d", + index, + count, + accessor->count); + loaded = false; + } + if (accessor->type != type) { + ALOGW( + "Error: Invalid type on gltfPrimitive accessor %d %d %d", + index, + type, + accessor->type); + loaded = false; + } + + size_t srcComponentSize = getComponentSize(accessor->componentType); + size_t srcComponentCount = getComponentCount(accessor->type); + size_t srcValueSize = srcComponentSize * srcComponentCount; + + int readStride = srcValueSize; + if (bufferView->byteStride > 0) { + readStride = bufferView->byteStride; + } + + size_t dstComponentSize = getComponentSize(componentType); + size_t dstComponentCount = getComponentCount(type); + size_t dstValueSize = dstComponentSize * dstComponentCount; + + const size_t offset = accessor->byteOffset + bufferView->byteOffset; + + const size_t srcRequiredSize = accessor->count * readStride; + + if ((offset + srcRequiredSize) > buffer->byteLength) { + ALOGW( + "Error: accessor requesting too much data in gltfPrimitive %d %d %d", + index, + (int)accessor->bufferView->byteLength, + (int)(offset + srcRequiredSize)); + loaded = false; + } + + if (loaded) { + const size_t startIndex = append ? out.size() : 0; + out.resize(startIndex + accessor->count); + int valueCount = (int)(accessor->count); + const char* src = (char*)buffer->bufferData.data() + offset; + if (accessor->componentType != componentType) { + if (componentType == MODEL_COMPONENT_TYPE_FLOAT) { + float* dst = (float*)&out[0]; + // for normalized signed integers, we need them to map to whole [-1.0f, 1.0f] + // while having the 0 exactly at 0.0f for byte: + //-128 -> - 1.0f + // 0 -> 0.0f + // 127 -> 1.0f + // to achieve that we do std::max((float)value / MaxValue, -1.0f); + switch (accessor->componentType) { + case MODEL_COMPONENT_TYPE_BYTE: + for (int i = 0; i < valueCount; i++) { + const int8_t* valueSrc = (int8_t*)(src + i * readStride); + float* valueDst = (float*)(dst + i * srcComponentCount); + for (int j = 0; j < (int)srcComponentCount; j++) { + valueDst[j] = std::max(((float)valueSrc[j]) / 127.0f, -1.0f); + } + } + break; + case MODEL_COMPONENT_TYPE_UNSIGNED_BYTE: + default: + for (int i = 0; i < valueCount; i++) { + const uint8_t* valueSrc = (uint8_t*)(src + i * readStride); + float* valueDst = (float*)(dst + i * srcComponentCount); + for (int j = 0; j < (int)srcComponentCount; j++) { + valueDst[j] = ((float)valueSrc[j]) / 255.0f; + } + } + break; + case MODEL_COMPONENT_TYPE_SHORT: + for (int i = 0; i < valueCount; i++) { + const int16_t* valueSrc = (int16_t*)(src + i * readStride); + float* valueDst = (float*)(dst + i * srcComponentCount); + for (int j = 0; j < (int)srcComponentCount; j++) { + valueDst[j] = std::max(((float)valueSrc[j]) / 32767.0f, -1.0f); + } + } + break; + case MODEL_COMPONENT_TYPE_UNSIGNED_SHORT: + for (int i = 0; i < valueCount; i++) { + const uint16_t* valueSrc = (uint16_t*)(src + i * readStride); + float* valueDst = (float*)(dst + i * srcComponentCount); + for (int j = 0; j < (int)srcComponentCount; j++) { + valueDst[j] = ((float)valueSrc[j]) / 65535.0f; + } + } + break; + case MODEL_COMPONENT_TYPE_UNSIGNED_INT: + for (int i = 0; i < valueCount; i++) { + const uint32_t* valueSrc = (uint32_t*)(src + i * readStride); + float* valueDst = (float*)(dst + i * srcComponentCount); + for (int j = 0; j < (int)srcComponentCount; j++) { + valueDst[j] = (float)(((double)valueSrc[j]) / 4294967295.0); + } + } + break; + } + } else { + // slow path for rare cases + if (accessor->componentType == MODEL_COMPONENT_TYPE_FLOAT || + componentType == MODEL_COMPONENT_TYPE_FLOAT) { + char* dst = (char*)&out[0]; + for (int i = 0; i < valueCount; i++) { + for (int j = 0; j < (int)srcComponentCount; j++) { + float value; + const char* valueSrc = src + i * readStride + j * srcComponentSize; + switch (accessor->componentType) { + case MODEL_COMPONENT_TYPE_BYTE: + value = + std::max(((float)(*(int8_t*)valueSrc)) / 127.0f, -1.0f); + break; + case MODEL_COMPONENT_TYPE_UNSIGNED_BYTE: + default: + value = ((float)(*(uint8_t*)valueSrc)) / 255.0f; + break; + case MODEL_COMPONENT_TYPE_SHORT: + value = std::max( + ((float)(*(int16_t*)valueSrc)) / 32767.0f, -1.0f); + break; + case MODEL_COMPONENT_TYPE_UNSIGNED_SHORT: + value = ((float)(*(uint16_t*)valueSrc)) / 65535.0f; + break; + case MODEL_COMPONENT_TYPE_UNSIGNED_INT: + value = (float)(((double)(*(uint32_t*)valueSrc)) / + 4294967295.0); + break; + case MODEL_COMPONENT_TYPE_FLOAT: + value = (*(float*)valueSrc); + break; + } + char* valueDst = dst + i * dstValueSize + j * dstComponentSize; + switch (componentType) { + case MODEL_COMPONENT_TYPE_BYTE: + // -1.0f -> -128, 1.0f -> +127, 0.0f -> 0 + *(int8_t*)valueDst = (int8_t)(value * 128.0f); + break; + case MODEL_COMPONENT_TYPE_UNSIGNED_BYTE: + default: + *(uint8_t*)valueDst = (uint8_t)(value * 255.0f); + break; + // SPECIAL CASES, we don't know if the float is normalized + // or not normalized we assume that the float was not a + // normalized value when someone asks for an uint16 or + // uint32 + case MODEL_COMPONENT_TYPE_SHORT: + *(int16_t*)valueDst = (int16_t)(value); + break; + case MODEL_COMPONENT_TYPE_UNSIGNED_SHORT: + *(uint16_t*)valueDst = (uint16_t)(value); + break; + case MODEL_COMPONENT_TYPE_UNSIGNED_INT: + *(uint32_t*)valueDst = (uint32_t)(value); + break; + case MODEL_COMPONENT_TYPE_FLOAT: + *(float*)valueDst = value; + break; + } + } + } + } else { + // integer to integer, no "proportional" conversion, just change of storage + char* dst = (char*)&out[0]; + for (int i = 0; i < valueCount; i++) { + for (int j = 0; j < (int)srcComponentCount; j++) { + int64_t value; + const char* valueSrc = src + i * readStride + j * srcComponentSize; + switch (accessor->componentType) { + case MODEL_COMPONENT_TYPE_BYTE: + value = (int64_t)(*(int8_t*)valueSrc); + break; + case MODEL_COMPONENT_TYPE_UNSIGNED_BYTE: + default: + value = (int64_t)(*(uint8_t*)valueSrc); + break; + case MODEL_COMPONENT_TYPE_SHORT: + value = (int64_t)(*(int16_t*)valueSrc); + break; + case MODEL_COMPONENT_TYPE_UNSIGNED_SHORT: + value = (int64_t)(*(uint16_t*)valueSrc); + break; + case MODEL_COMPONENT_TYPE_UNSIGNED_INT: + value = (int64_t)(*(uint32_t*)valueSrc); + break; + } + char* valueDst = dst + i * dstValueSize + j * dstComponentSize; + switch (componentType) { + case MODEL_COMPONENT_TYPE_BYTE: + *(int8_t*)valueDst = (int8_t)value; + break; + case MODEL_COMPONENT_TYPE_UNSIGNED_BYTE: + default: + *(uint8_t*)valueDst = (uint8_t)value; + break; + case MODEL_COMPONENT_TYPE_SHORT: + *(int16_t*)valueDst = (int16_t)value; + break; + case MODEL_COMPONENT_TYPE_UNSIGNED_SHORT: + *(uint8_t*)valueDst = (uint16_t)value; + break; + case MODEL_COMPONENT_TYPE_UNSIGNED_INT: + *(uint32_t*)valueDst = (uint32_t)value; + break; + } + } + } + } + } + } else { + if (readStride == (int)srcValueSize) { + memcpy(&out[startIndex], buffer->bufferData.data() + offset, srcRequiredSize); + } else { + char* dst = (char*)&out[0]; + for (int i = 0; i < valueCount; i++) { + const char* valueSrc = src + i * readStride; + char* valueDst = dst + i * srcValueSize; + memcpy(valueDst, valueSrc, srcValueSize); + } + } + } + } + } + + return loaded; +} + +bool ReadVertexAttributes( + const OVR::JsonReader& attributes, + ModelFile& modelFile, + VertexAttribs& attribs, + bool isMorphTarget) { + bool loaded = true; + + { // POSITION and BOUNDS + const int positionIndex = attributes.GetChildInt32ByName("POSITION", -1); + // must have positions unless this is a morph target + if (!isMorphTarget) { + if (positionIndex < 0 || + positionIndex >= static_cast(modelFile.Accessors.size())) { + ALOGW( + "Error: Invalid position accessor index %i, accessor count = %zu", + positionIndex, + modelFile.Accessors.size()); + loaded = false; + return loaded; + } + } + + loaded = ReadSurfaceDataFromAccessor( + attribs.position, modelFile, positionIndex, ACCESSOR_VEC3, GL_FLOAT, -1, false); + } + + // attribute count must match positions unless this is a morph target + const int numVertices = isMorphTarget ? -1 : static_cast(attribs.position.size()); + + if (loaded) { + loaded = ReadSurfaceDataFromAccessor( + attribs.normal, + modelFile, + attributes.GetChildInt32ByName("NORMAL", -1), + ACCESSOR_VEC3, + GL_FLOAT, + numVertices, + false); + } + // #TODO: we have tangent as a vec3, the spec has it as a vec4. + // so we will have to one off the loading of it. + // if (loaded) { + // // loaded = ReadSurfaceDataFromAccessor( + // // attribs.tangent, + // // modelFile, + // // attributes.GetChildInt32ByName("TANGENT", -1), + // // ACCESSOR_VEC4, + // // GL_FLOAT, + // // numVertices, + // // false); + // } + if (loaded) { + loaded = ReadSurfaceDataFromAccessor( + attribs.binormal, + modelFile, + attributes.GetChildInt32ByName("BINORMAL", -1), + ACCESSOR_VEC3, + GL_FLOAT, + numVertices, + false); + } + if (loaded) { + loaded = ReadSurfaceDataFromAccessor( + attribs.color, + modelFile, + attributes.GetChildInt32ByName("COLOR", -1), + ACCESSOR_VEC4, + GL_FLOAT, + numVertices, + false); + } + if (loaded) { + loaded = ReadSurfaceDataFromAccessor( + attribs.uv0, + modelFile, + attributes.GetChildInt32ByName("TEXCOORD_0", -1), + ACCESSOR_VEC2, + GL_FLOAT, + numVertices, + false); + } + if (loaded) { + loaded = ReadSurfaceDataFromAccessor( + attribs.uv1, + modelFile, + attributes.GetChildInt32ByName("TEXCOORD_1", -1), + ACCESSOR_VEC2, + GL_FLOAT, + numVertices, + false); + } + // #TODO: TEXCOORD_2 is in the gltf spec, but we only support 2 + // uv sets. support more uv coordinates, skipping for now. + // if ( loaded ) { loaded = ReadSurfaceDataFromAccessor( + // attribs.uv2, modelFile, + // attributes.GetChildInt32ByName( "TEXCOORD_2", -1 ), + // ACCESSOR_VEC2, GL_FLOAT, static_cast< int + // >( newPrimitive.attribs.position.size() ) ); } + // #TODO: get weights of type unsigned_byte and unsigned_short + // working. + if (loaded) { + loaded = ReadSurfaceDataFromAccessor( + attribs.jointWeights, + modelFile, + attributes.GetChildInt32ByName("WEIGHTS_0", -1), + ACCESSOR_VEC4, + GL_FLOAT, + numVertices, + false); + } + if (loaded) { + int jointIndex = attributes.GetChildInt32ByName("JOINTS_0", -1); + if (jointIndex >= 0 && jointIndex < static_cast(modelFile.Accessors.size())) { + ModelAccessor& acc = modelFile.Accessors[jointIndex]; + loaded = ReadSurfaceDataFromAccessor( + attribs.jointIndices, + modelFile, + attributes.GetChildInt32ByName("JOINTS_0", -1), + ACCESSOR_VEC4, + acc.componentType, + numVertices, + false); + /// List unique joints + std::unordered_map uniqueJoints; + for (const auto& index : attribs.jointIndices) { + for (int i = 0; i < 4; ++i) { + int jointID = index[i]; + auto it = uniqueJoints.find(jointID); + if (it == uniqueJoints.end()) { + uniqueJoints[jointID] = 1u; + } else { + uniqueJoints[jointID] = uniqueJoints[jointID] + 1; + } + } + } + /// print them + ALOGW("Enumerating skinning joints:"); + for (const auto& u : uniqueJoints) { + ALOGW(" - joint: %02d count: %llu", u.first, u.second); + } + } + } + return loaded; +} + +// Requires the buffers and images to already be loaded in the model +bool LoadModelFile_glTF_Json( + ModelFile& modelFile, + const char* modelsJson, + const ModelGlPrograms& programs, + const MaterialParms& materialParms, + ModelGeo* outModelGeo) { + ALOG("LoadModelFile_glTF_Json parsing %s", modelFile.FileName.c_str()); + // LOGCPUTIME( "LoadModelFile_glTF_Json" ); + + bool loaded = true; + + const char* error = nullptr; + auto json = OVR::JSON::Parse(modelsJson, &error); + if (json == nullptr) { + ALOG("LoadModelFile_glTF_Json: Error loading %s : %s", modelFile.FileName.c_str(), error); + loaded = false; + } else { + const OVR::JsonReader models(json); + if (models.IsObject()) { + if (loaded) { // ASSET + const OVR::JsonReader asset(models.GetChildByName("asset")); + if (!asset.IsObject()) { + ALOGW("Error: No asset on gltfSceneFile"); + loaded = false; + } + std::string versionString = asset.GetChildStringByName("version"); + std::string minVersion = asset.GetChildStringByName("minVersion"); + if (OVR::OVR_stricmp(versionString.c_str(), "2.0") != 0 && + OVR::OVR_stricmp(minVersion.c_str(), "2.0") != 0) { + ALOGW( + "Error: Invalid version number '%s' on gltfFile, currently only version 2.0 supported", + versionString.c_str()); + loaded = false; + } + } // END ASSET + + if (loaded) { // ACCESSORS + LOGV("Loading accessors"); + const OVR::JsonReader accessors(models.GetChildByName("accessors")); + if (accessors.IsArray()) { + while (!accessors.IsEndOfArray() && loaded) { + const OVR::JsonReader accessor(accessors.GetNextArrayElement()); + if (accessor.IsObject()) { + ModelAccessor newGltfAccessor; + + newGltfAccessor.name = accessor.GetChildStringByName("name"); + const int bufferView = accessor.GetChildInt32ByName("bufferView"); + newGltfAccessor.byteOffset = accessor.GetChildInt32ByName("byteOffset"); + newGltfAccessor.componentType = + accessor.GetChildInt32ByName("componentType"); + newGltfAccessor.count = accessor.GetChildInt32ByName("count"); + const std::string type = accessor.GetChildStringByName("type"); + newGltfAccessor.normalized = accessor.GetChildBoolByName("normalized"); + + if (bufferView < 0 || + bufferView >= (const int)modelFile.BufferViews.size()) { + ALOGW("Error: Invalid bufferView Index in gltfAccessor"); + loaded = false; + } + + int componentCount = 0; + if (OVR::OVR_stricmp(type.c_str(), "SCALAR") == 0) { + newGltfAccessor.type = ACCESSOR_SCALAR; + componentCount = 1; + } else if (OVR::OVR_stricmp(type.c_str(), "VEC2") == 0) { + newGltfAccessor.type = ACCESSOR_VEC2; + componentCount = 2; + } else if (OVR::OVR_stricmp(type.c_str(), "VEC3") == 0) { + newGltfAccessor.type = ACCESSOR_VEC3; + componentCount = 3; + } else if (OVR::OVR_stricmp(type.c_str(), "VEC4") == 0) { + newGltfAccessor.type = ACCESSOR_VEC4; + componentCount = 4; + } else if (OVR::OVR_stricmp(type.c_str(), "MAT2") == 0) { + newGltfAccessor.type = ACCESSOR_MAT2; + componentCount = 4; + } else if (OVR::OVR_stricmp(type.c_str(), "MAT3") == 0) { + newGltfAccessor.type = ACCESSOR_MAT3; + componentCount = 9; + } else if (OVR::OVR_stricmp(type.c_str(), "MAT4") == 0) { + newGltfAccessor.type = ACCESSOR_MAT4; + componentCount = 16; + } else { + ALOGW("Error: Invalid type in gltfAccessor"); + loaded = false; + } + + auto min = accessor.GetChildByName("min"); + auto max = accessor.GetChildByName("max"); + if (min != nullptr && max != nullptr) { + switch (newGltfAccessor.componentType) { + case GL_BYTE: + case GL_UNSIGNED_BYTE: + case GL_SHORT: + case GL_UNSIGNED_SHORT: + case GL_UNSIGNED_INT: + ParseIntArray(newGltfAccessor.intMin, componentCount, min); + ParseIntArray(newGltfAccessor.intMax, componentCount, max); + break; + case GL_FLOAT: + ParseFloatArray( + newGltfAccessor.floatMin, componentCount, min); + ParseFloatArray( + newGltfAccessor.floatMax, componentCount, max); + break; + default: + ALOGW("Error: Invalid componentType in gltfAccessor"); + loaded = false; + } + newGltfAccessor.minMaxSet = true; + } + + newGltfAccessor.bufferView = &modelFile.BufferViews[bufferView]; + modelFile.Accessors.push_back(newGltfAccessor); + } + } + } + } // END ACCESSORS + + if (loaded) { // SAMPLERS + LOGV("Loading samplers"); + const OVR::JsonReader samplers(models.GetChildByName("samplers")); + if (samplers.IsArray()) { + while (!samplers.IsEndOfArray() && loaded) { + const OVR::JsonReader sampler(samplers.GetNextArrayElement()); + if (sampler.IsObject()) { + ModelSampler newGltfSampler; + + newGltfSampler.name = sampler.GetChildStringByName("name"); + newGltfSampler.magFilter = + sampler.GetChildInt32ByName("magFilter", GL_LINEAR); + newGltfSampler.minFilter = + sampler.GetChildInt32ByName("minFilter", GL_NEAREST_MIPMAP_LINEAR); + newGltfSampler.wrapS = sampler.GetChildInt32ByName("wrapS", GL_REPEAT); + newGltfSampler.wrapT = sampler.GetChildInt32ByName("wrapT", GL_REPEAT); + + if (newGltfSampler.magFilter != GL_NEAREST && + newGltfSampler.magFilter != GL_LINEAR) { + ALOGW("Error: Invalid magFilter in gltfSampler"); + loaded = false; + } + if (newGltfSampler.minFilter != GL_NEAREST && + newGltfSampler.minFilter != GL_LINEAR && + newGltfSampler.minFilter != GL_LINEAR_MIPMAP_NEAREST && + newGltfSampler.minFilter != GL_NEAREST_MIPMAP_LINEAR && + newGltfSampler.minFilter != GL_LINEAR_MIPMAP_LINEAR) { + ALOGW("Error: Invalid minFilter in gltfSampler"); + loaded = false; + } + if (newGltfSampler.wrapS != GL_CLAMP_TO_EDGE && + newGltfSampler.wrapS != GL_MIRRORED_REPEAT && + newGltfSampler.wrapS != GL_REPEAT) { + ALOGW("Error: Invalid wrapS in gltfSampler"); + loaded = false; + } + if (newGltfSampler.wrapT != GL_CLAMP_TO_EDGE && + newGltfSampler.wrapT != GL_MIRRORED_REPEAT && + newGltfSampler.wrapT != GL_REPEAT) { + ALOGW("Error: Invalid wrapT in gltfSampler"); + loaded = false; + } + + modelFile.Samplers.push_back(newGltfSampler); + } + } + } + + // default sampler + ModelSampler defaultGltfSampler; + defaultGltfSampler.name = "Default_Sampler"; + modelFile.Samplers.push_back(defaultGltfSampler); + } // END SAMPLERS + + if (loaded) { // TEXTURES + LOGV("Loading textures"); + const OVR::JsonReader textures(models.GetChildByName("textures")); + if (textures.IsArray() && loaded) { + while (!textures.IsEndOfArray()) { + const OVR::JsonReader texture(textures.GetNextArrayElement()); + if (texture.IsObject()) { + ModelTextureWrapper newGltfTexture; + + newGltfTexture.name = texture.GetChildStringByName("name"); + const int sampler = texture.GetChildInt32ByName("sampler", -1); + int image = texture.GetChildInt32ByName("source", -1); + const OVR::JsonReader textureExtensions = + texture.GetChildByName("extensions"); + if (textureExtensions.IsObject()) { + const OVR::JsonReader basisuExtension = + textureExtensions.GetChildByName("KHR_texture_basisu"); + if (basisuExtension.IsObject()) { + image = basisuExtension.GetChildInt32ByName("source", image); + } + } + + if (sampler < -1 || + sampler >= static_cast(modelFile.Samplers.size())) { + ALOGW("Error: Invalid sampler Index in gltfTexture"); + loaded = false; + } + + if (image < -1 || + image >= static_cast(modelFile.Textures.size())) { + ALOGW("Error: Invalid source Index in gltfTexture"); + loaded = false; + } + + if (sampler < 0) { + newGltfTexture.sampler = + &modelFile + .Samplers[static_cast(modelFile.Samplers.size()) - 1]; + } else { + newGltfTexture.sampler = &modelFile.Samplers[sampler]; + } + if (image < 0) { + newGltfTexture.image = nullptr; + } else { + newGltfTexture.image = &modelFile.Textures[image]; + } + modelFile.TextureWrappers.push_back(newGltfTexture); + } + } + } + } // END TEXTURES + + if (loaded) { // MATERIALS + LOGV("Loading materials"); + const OVR::JsonReader materials(models.GetChildByName("materials")); + if (materials.IsArray() && loaded) { + while (!materials.IsEndOfArray()) { + const OVR::JsonReader material(materials.GetNextArrayElement()); + if (material.IsObject()) { + ModelMaterial newGltfMaterial; + + // material + newGltfMaterial.name = material.GetChildStringByName("name"); + + auto emissiveFactor = material.GetChildByName("emissiveFactor"); + if (emissiveFactor != nullptr) { + if (emissiveFactor->GetItemCount() != 3) { + ALOGW( + "Error: Invalid Itemcount on emissiveFactor for gltfMaterial"); + loaded = false; + } + newGltfMaterial.emmisiveFactor.x = + emissiveFactor->GetItemByIndex(0)->GetFloatValue(); + newGltfMaterial.emmisiveFactor.y = + emissiveFactor->GetItemByIndex(1)->GetFloatValue(); + newGltfMaterial.emmisiveFactor.z = + emissiveFactor->GetItemByIndex(2)->GetFloatValue(); + } + + const std::string alphaModeString = + material.GetChildStringByName("alphaMode", "OPAQUE"); + if (OVR::OVR_stricmp(alphaModeString.c_str(), "OPAQUE") == 0) { + newGltfMaterial.alphaMode = ALPHA_MODE_OPAQUE; + } else if (OVR::OVR_stricmp(alphaModeString.c_str(), "MASK") == 0) { + newGltfMaterial.alphaMode = ALPHA_MODE_MASK; + } else if (OVR::OVR_stricmp(alphaModeString.c_str(), "BLEND") == 0) { + newGltfMaterial.alphaMode = ALPHA_MODE_BLEND; + } else { + ALOGW("Error: Invalid alphaMode in gltfMaterial"); + loaded = false; + } + + newGltfMaterial.alphaCutoff = + material.GetChildFloatByName("alphaCutoff", 0.5f); + newGltfMaterial.doubleSided = + material.GetChildBoolByName("doubleSided", false); + + // pbrMetallicRoughness + const OVR::JsonReader pbrMetallicRoughness = + material.GetChildByName("pbrMetallicRoughness"); + if (pbrMetallicRoughness.IsObject()) { + auto baseColorFactor = + pbrMetallicRoughness.GetChildByName("baseColorFactor"); + if (baseColorFactor != nullptr) { + if (baseColorFactor->GetItemCount() != 4) { + ALOGW( + "Error: Invalid Itemcount on baseColorFactor for gltfMaterial"); + loaded = false; + } + newGltfMaterial.baseColorFactor.x = + baseColorFactor->GetItemByIndex(0)->GetFloatValue(); + newGltfMaterial.baseColorFactor.y = + baseColorFactor->GetItemByIndex(1)->GetFloatValue(); + newGltfMaterial.baseColorFactor.z = + baseColorFactor->GetItemByIndex(2)->GetFloatValue(); + newGltfMaterial.baseColorFactor.w = + baseColorFactor->GetItemByIndex(3)->GetFloatValue(); + } + + const OVR::JsonReader baseColorTexture = + pbrMetallicRoughness.GetChildByName("baseColorTexture"); + if (baseColorTexture.IsObject()) { + int index = baseColorTexture.GetChildInt32ByName("index", -1); + if (index < 0 || + index >= + static_cast(modelFile.TextureWrappers.size())) { + ALOGW( + "Error: Invalid baseColorTexture index in gltfMaterial"); + loaded = false; + } + newGltfMaterial.baseColorTextureWrapper = + &modelFile.TextureWrappers[index]; + } + + newGltfMaterial.metallicFactor = + pbrMetallicRoughness.GetChildFloatByName( + "metallicFactor", 1.0f); + newGltfMaterial.roughnessFactor = + pbrMetallicRoughness.GetChildFloatByName( + "roughnessFactor", 1.0f); + + const OVR::JsonReader metallicRoughnessTexture = + pbrMetallicRoughness.GetChildByName("metallicRoughnessTexture"); + if (metallicRoughnessTexture.IsObject()) { + int index = + metallicRoughnessTexture.GetChildInt32ByName("index", -1); + if (index < 0 || + index >= + static_cast(modelFile.TextureWrappers.size())) { + ALOGW( + "Error: Invalid metallicRoughnessTexture index in gltfMaterial"); + loaded = false; + } + newGltfMaterial.metallicRoughnessTextureWrapper = + &modelFile.TextureWrappers[index]; + } + } + + // normalTexture + const OVR::JsonReader normalTexture = + material.GetChildByName("normalTexture"); + if (normalTexture.IsObject()) { + int index = normalTexture.GetChildInt32ByName("index", -1); + if (index < 0 || + index >= static_cast(modelFile.TextureWrappers.size())) { + ALOGW("Error: Invalid normalTexture index in gltfMaterial"); + loaded = false; + } + newGltfMaterial.normalTextureWrapper = + &modelFile.TextureWrappers[index]; + newGltfMaterial.normalTexCoord = + normalTexture.GetChildInt32ByName("texCoord", 0); + newGltfMaterial.normalScale = + normalTexture.GetChildFloatByName("scale", 1.0f); + } + + // occlusionTexture + const OVR::JsonReader occlusionTexture = + material.GetChildByName("occlusionTexture"); + if (occlusionTexture.IsObject()) { + int index = occlusionTexture.GetChildInt32ByName("index", -1); + if (index < 0 || + index >= static_cast(modelFile.TextureWrappers.size())) { + ALOGW("Error: Invalid occlusionTexture index in gltfMaterial"); + loaded = false; + } + newGltfMaterial.occlusionTextureWrapper = + &modelFile.TextureWrappers[index]; + newGltfMaterial.occlusionTexCoord = + occlusionTexture.GetChildInt32ByName("texCoord", 0); + newGltfMaterial.occlusionStrength = + occlusionTexture.GetChildFloatByName("strength", 1.0f); + } + + // emissiveTexture + const OVR::JsonReader emissiveTexture = + material.GetChildByName("emissiveTexture"); + if (emissiveTexture.IsObject()) { + int index = emissiveTexture.GetChildInt32ByName("index", -1); + if (index < 0 || + index >= static_cast(modelFile.TextureWrappers.size())) { + ALOGW("Error: Invalid emissiveTexture index in gltfMaterial"); + loaded = false; + } + newGltfMaterial.emissiveTextureWrapper = + &modelFile.TextureWrappers[index]; + } + + // detailTexture + const OVR::JsonReader detailTexture = + material.GetChildByName("detailTexture"); + if (detailTexture.IsObject()) { + int index = detailTexture.GetChildInt32ByName("index", -1); + if (index < 0 || + index >= static_cast(modelFile.TextureWrappers.size())) { + ALOGW( + "Error: Invalid texture index in gltfMaterial '%s'", + newGltfMaterial.name.c_str()); + loaded = false; + } + newGltfMaterial.detailTextureWrapper = + &modelFile.TextureWrappers[index]; + } + + modelFile.Materials.push_back(newGltfMaterial); + } + } + // Add a default material at the end of the list for primitives with an + // unspecified material. + ModelMaterial defaultmaterial; + modelFile.Materials.push_back(defaultmaterial); + } + } // END MATERIALS + + if (loaded) { // MODELS (gltf mesh) + LOGV("Loading meshes"); + const OVR::JsonReader meshes(models.GetChildByName("meshes")); + if (meshes.IsArray()) { + while (!meshes.IsEndOfArray() && loaded) { + const OVR::JsonReader mesh(meshes.GetNextArrayElement()); + if (mesh.IsObject()) { + Model newGltfModel; + + newGltfModel.name = mesh.GetChildStringByName("name"); + + { // SURFACES (gltf primitive) + const OVR::JsonReader primitives(mesh.GetChildByName("primitives")); + if (!primitives.IsArray()) { + ALOGW("Error: no primitives on gltfMesh"); + loaded = false; + } + + while (!primitives.IsEndOfArray() && loaded) { + const OVR::JsonReader primitive( + primitives.GetNextArrayElement()); + + ModelSurface newGltfSurface; + + const int materialIndex = + primitive.GetChildInt32ByName("material", -1); + if (materialIndex < 0) { + LOGV( + "Using default for material on %s", + newGltfModel.name.c_str()); + newGltfSurface.material = + &modelFile.Materials + [static_cast(modelFile.Materials.size()) - 1]; + } else if ( + materialIndex >= + static_cast(modelFile.Materials.size())) { + ALOGW("Error: Invalid materialIndex on gltfPrimitive"); + loaded = false; + } else { + newGltfSurface.material = + &modelFile.Materials[materialIndex]; + } + + const int mode = primitive.GetChildInt32ByName("mode", 4); + if (mode < GL_POINTS || mode > GL_TRIANGLE_FAN) { + ALOGW("Error: Invalid mode on gltfPrimitive"); + loaded = false; + } + if (mode != GL_TRIANGLES) { + // #TODO: support modes other than triangle? + ALOGW( + "Error: Mode other then TRIANGLE (4) not currently supported on gltfPrimitive"); + loaded = false; + } + + const OVR::JsonReader attributes( + primitive.GetChildByName("attributes")); + if (!attributes.IsObject()) { + ALOGW("Error: no attributes on gltfPrimitive"); + loaded = false; + } + + TriangleIndex outGeoIndexOffset = 0; + if (outModelGeo != nullptr) { + outGeoIndexOffset = static_cast( + (*outModelGeo).positions.size()); + } + + // VERTICES + VertexAttribs attribs; + loaded = ReadVertexAttributes( + attributes, modelFile, attribs, false /*isMorphTarget*/); + + // MORPH TARGETS + const OVR::JsonReader targets( + primitive.GetChildByName("targets")); + if (targets.IsValid()) { + if (!targets.IsArray()) { + ALOGW("Error: Invalid targets on primitive"); + loaded = false; + } + + while (!targets.IsEndOfArray() && loaded) { + const OVR::JsonReader target( + targets.GetNextArrayElement()); + VertexAttribs targetAttribs; + loaded = ReadVertexAttributes( + target, + modelFile, + targetAttribs, + true /*isMorphTarget*/); + if (loaded) { + // for each morph target attribute, an original + // attribute MUST be present in the mesh primitive +#define CHECK_ATTRIB_COUNT(ATTRIB) \ + if (!targetAttribs.ATTRIB.empty() && targetAttribs.ATTRIB.size() != attribs.ATTRIB.size()) { \ + ALOGW("Error: target " #ATTRIB " count mismatch on gltfPrimitive"); \ + loaded = false; \ + } + CHECK_ATTRIB_COUNT(position); + CHECK_ATTRIB_COUNT(normal); + CHECK_ATTRIB_COUNT(tangent); + CHECK_ATTRIB_COUNT(color); + CHECK_ATTRIB_COUNT(uv0); + CHECK_ATTRIB_COUNT(uv1); +#undef CHECK_ATTRIB_COUNT + newGltfSurface.targets.emplace_back( + std::move(targetAttribs)); + } + } + } + + // TRIANGLES + std::vector indices; + const int indicesIndex = + primitive.GetChildInt32ByName("indices", -1); + if (indicesIndex < 0 || + indicesIndex >= + static_cast(modelFile.Accessors.size())) { + ALOGW("Error: Invalid indices index on gltfPrimitive"); + loaded = false; + } + + // Reduced severity to warning: this doesn't break most data + // types, but can cause unexpected results. + if (modelFile.Accessors[indicesIndex].componentType != + GL_UNSIGNED_SHORT) { + ALOGW( + "Warning: Currently, only componentType of %d supported for indices, %d requested", + GL_UNSIGNED_SHORT, + modelFile.Accessors[indicesIndex].componentType); + } + + if (loaded) { + ReadSurfaceDataFromAccessor( + indices, + modelFile, + primitive.GetChildInt32ByName("indices", -1), + ACCESSOR_SCALAR, + GL_UNSIGNED_SHORT, + -1, + false); + } + + newGltfSurface.surfaceDef.geo.Create(attribs, indices); + bool skinned = + (attribs.jointIndices.size() == attribs.position.size() && + attribs.jointWeights.size() == attribs.position.size()); + + if (outModelGeo != nullptr) { + for (int i = 0; i < static_cast(indices.size()); ++i) { + (*outModelGeo) + .indices.push_back(indices[i] + outGeoIndexOffset); + } + } + + // CREATE COMMAND BUFFERS. + if (newGltfSurface.material->alphaMode == ALPHA_MODE_MASK) { + // #TODO: implement ALPHA_MODE_MASK if we need it. + // Just blend because alpha testing is rather expensive. + ALOGW( + "gltfAlphaMode ALPHA_MODE_MASK requested, doing ALPHA_MODE_BLEND instead"); + newGltfSurface.surfaceDef.graphicsCommand.GpuState + .blendEnable = ovrGpuState::BLEND_ENABLE; + newGltfSurface.surfaceDef.graphicsCommand.GpuState + .depthMaskEnable = false; + newGltfSurface.surfaceDef.graphicsCommand.GpuState + .blendSrc = GL_SRC_ALPHA; + newGltfSurface.surfaceDef.graphicsCommand.GpuState + .blendDst = GL_ONE_MINUS_SRC_ALPHA; + } else if ( + newGltfSurface.material->alphaMode == ALPHA_MODE_BLEND || + materialParms.Transparent) { + if (materialParms.Transparent && + newGltfSurface.material->alphaMode != + ALPHA_MODE_BLEND) { + ALOGW( + "gltfAlphaMode is %d but treating at ALPHA_MODE_BLEND due to materialParms.Transparent", + newGltfSurface.material->alphaMode); + } + newGltfSurface.surfaceDef.graphicsCommand.GpuState + .blendEnable = ovrGpuState::BLEND_ENABLE; + newGltfSurface.surfaceDef.graphicsCommand.GpuState + .depthMaskEnable = false; + newGltfSurface.surfaceDef.graphicsCommand.GpuState + .blendSrc = GL_SRC_ALPHA; + newGltfSurface.surfaceDef.graphicsCommand.GpuState + .blendDst = GL_ONE_MINUS_SRC_ALPHA; + } + // #TODO: GLTF doesn't have a concept of an ADDITIVE mode. maybe + // it should? + // else if ( newGltfSurface.material->alphaMode == + // MATERIAL_TYPE_ADDITIVE ) + //{ + // newGltfSurface.surfaceDef.graphicsCommand.GpuState.blendEnable + //= ovrGpuState::BLEND_ENABLE; + // newGltfSurface.surfaceDef.graphicsCommand.GpuState.depthMaskEnable + //= false; + // newGltfSurface.surfaceDef.graphicsCommand.GpuState.blendSrc + //= GL_ONE; + // newGltfSurface.surfaceDef.graphicsCommand.GpuState.blendDst + //= GL_ONE; + //} + else if ( + newGltfSurface.material->alphaMode == ALPHA_MODE_OPAQUE) { + // default GpuState; + } + + if (newGltfSurface.material->baseColorTextureWrapper != + nullptr) { + newGltfSurface.surfaceDef.graphicsCommand.Textures[0] = + newGltfSurface.material->baseColorTextureWrapper->image + ->texid; + if (newGltfSurface.material->emissiveTextureWrapper != + nullptr) { + if (programs.ProgBaseColorEmissivePBR == nullptr) { + ALOGE_FAIL("No ProgBaseColorEmissivePBR set"); + } + newGltfSurface.surfaceDef.graphicsCommand.Textures[1] = + newGltfSurface.material->emissiveTextureWrapper + ->image->texid; + if (skinned) { + if (programs.ProgSkinnedBaseColorEmissivePBR == + nullptr) { + ALOGE_FAIL( + "No ProgSkinnedBaseColorEmissivePBR set"); + } + + newGltfSurface.surfaceDef.graphicsCommand.Program = + *programs.ProgSkinnedBaseColorEmissivePBR; + newGltfSurface.surfaceDef.surfaceName = + "ProgSkinnedBaseColorEmissivePBR"; + } else { + newGltfSurface.surfaceDef.graphicsCommand.Program = + *programs.ProgBaseColorEmissivePBR; + newGltfSurface.surfaceDef.surfaceName = + "ProgBaseColorEmissivePBR"; + } + } else { + if (newGltfSurface.material->detailTextureWrapper != + nullptr) { + newGltfSurface.surfaceDef.graphicsCommand + .Textures[1] = + newGltfSurface.material->detailTextureWrapper + ->image->texid; + } + + if (skinned) { + if (programs.ProgSkinnedBaseColorPBR == nullptr) { + ALOGE_FAIL("No ProgSkinnedBaseColorPBR set"); + } + newGltfSurface.surfaceDef.graphicsCommand.Program = + *programs.ProgSkinnedBaseColorPBR; + newGltfSurface.surfaceDef.surfaceName = + "ProgSkinnedBaseColorPBR"; + } else { + if (programs.ProgBaseColorPBR == nullptr) { + ALOGE_FAIL("No ProgBaseColorPBR set"); + } + newGltfSurface.surfaceDef.graphicsCommand.Program = + *programs.ProgBaseColorPBR; + newGltfSurface.surfaceDef.surfaceName = + "ProgBaseColorPBR"; + } + } + } else { + if (skinned) { + if (programs.ProgSkinnedSimplePBR == nullptr) { + ALOGE_FAIL("No ProgSkinnedSimplePBR set"); + } + newGltfSurface.surfaceDef.graphicsCommand.Program = + *programs.ProgSkinnedSimplePBR; + newGltfSurface.surfaceDef.surfaceName = + "ProgSkinnedSimplePBR"; + } else { + if (programs.ProgSimplePBR == nullptr) { + ALOGE_FAIL("No ProgSimplePBR set"); + } + newGltfSurface.surfaceDef.graphicsCommand.Program = + *programs.ProgSimplePBR; + newGltfSurface.surfaceDef.surfaceName = "ProgSimplePBR"; + } + } + + if (materialParms.PolygonOffset) { + newGltfSurface.surfaceDef.graphicsCommand.GpuState + .polygonOffsetEnable = true; + } + + if (newGltfSurface.material->doubleSided) { + newGltfSurface.surfaceDef.graphicsCommand.GpuState + .cullEnable = false; + } + + // Retain original vertex data if we use morph targets + if (!newGltfSurface.targets.empty()) { + newGltfSurface.attribs = std::move(attribs); + } + newGltfModel.surfaces.emplace_back(std::move(newGltfSurface)); + } + } // END SURFACES + + // all primitives MUST have the same number of morph targets in the same + // order + for (const auto& surface : newGltfModel.surfaces) { + if (newGltfModel.surfaces[0].targets.size() != + surface.targets.size()) { + ALOGW( + "Error: not all primitives have the same number of morph targets"); + loaded = false; + } + } + + { // WEIGHTS (optional) + if (loaded) { + const OVR::JsonReader weights(mesh.GetChildByName("weights")); + if (weights.IsArray()) { + while (!weights.IsEndOfArray()) { + newGltfModel.weights.push_back( + weights.GetNextArrayFloat(0.0f)); + } + if (newGltfModel.weights.size() != + newGltfModel.surfaces[0].targets.size()) { + ALOGW( + "Error: mesh weights and morph target count mismatch"); + loaded = false; + } + } else if (!newGltfModel.surfaces.empty()) { + // when weights is undefined, the default targets' weights + // are zeros + newGltfModel.weights.resize( + newGltfModel.surfaces[0].targets.size(), 0.0f); + } + } + } // END WEIGHTS + + modelFile.Models.emplace_back(std::move(newGltfModel)); + } + } + } + } // END MODELS + + if (loaded) { // CAMERAS + // #TODO: best way to expose cameras to apps? + LOGV("Loading cameras"); + const OVR::JsonReader cameras(models.GetChildByName("cameras")); + if (cameras.IsArray() && loaded) { + while (!cameras.IsEndOfArray()) { + const OVR::JsonReader camera(cameras.GetNextArrayElement()); + if (camera.IsObject()) { + ModelCamera newGltfCamera; + + newGltfCamera.name = camera.GetChildStringByName("name"); + + const std::string cameraTypeString = + camera.GetChildStringByName("type"); + if (OVR::OVR_stricmp(cameraTypeString.c_str(), "perspective") == 0) { + newGltfCamera.type = MODEL_CAMERA_TYPE_PERSPECTIVE; + } else if ( + OVR::OVR_stricmp(cameraTypeString.c_str(), "orthographic") == 0) { + newGltfCamera.type = MODEL_CAMERA_TYPE_ORTHOGRAPHIC; + } else { + ALOGW( + "Error: Invalid camera type on gltfCamera %s", + cameraTypeString.c_str()); + loaded = false; + } + + if (newGltfCamera.type == MODEL_CAMERA_TYPE_ORTHOGRAPHIC) { + const OVR::JsonReader orthographic( + camera.GetChildByName("orthographic")); + if (!orthographic.IsObject()) { + ALOGW( + "Error: No orthographic object on orthographic gltfCamera"); + loaded = false; + } + newGltfCamera.orthographic.magX = + orthographic.GetChildFloatByName("xmag"); + newGltfCamera.orthographic.magY = + orthographic.GetChildFloatByName("ymag"); + newGltfCamera.orthographic.nearZ = + orthographic.GetChildFloatByName("znear"); + newGltfCamera.orthographic.farZ = + orthographic.GetChildFloatByName("zfar"); + if (newGltfCamera.orthographic.magX <= 0.0f || + newGltfCamera.orthographic.magY <= 0.0f || + newGltfCamera.orthographic.nearZ <= 0.0f || + newGltfCamera.orthographic.farZ <= + newGltfCamera.orthographic.nearZ) { + ALOGW("Error: Invalid data in orthographic gltfCamera"); + loaded = false; + } + } else // MODEL_CAMERA_TYPE_PERSPECTIVE + { + const OVR::JsonReader perspective( + camera.GetChildByName("perspective")); + if (!perspective.IsObject()) { + ALOGW("Error: No perspective object on perspective gltfCamera"); + loaded = false; + } + newGltfCamera.perspective.aspectRatio = + perspective.GetChildFloatByName("aspectRatio"); + const float yfov = perspective.GetChildFloatByName("yfov"); + newGltfCamera.perspective.fovDegreesX = + (180.0f / 3.14159265358979323846f) * 2.0f * + atanf( + tanf(yfov * 0.5f) * newGltfCamera.perspective.aspectRatio); + newGltfCamera.perspective.fovDegreesY = + (180.0f / 3.14159265358979323846f) * yfov; + newGltfCamera.perspective.nearZ = + perspective.GetChildFloatByName("znear"); + newGltfCamera.perspective.farZ = + perspective.GetChildFloatByName("zfar", 10000.0f); + if (newGltfCamera.perspective.fovDegreesX <= 0.0f || + newGltfCamera.perspective.fovDegreesY <= 0.0f || + newGltfCamera.perspective.nearZ <= 0.0f || + newGltfCamera.perspective.farZ <= 0.0f) { + ALOGW("Error: Invalid data in perspective gltfCamera"); + loaded = false; + } + } + modelFile.Cameras.push_back(newGltfCamera); + } + } + } + } // END CAMERAS + + if (loaded) { // NODES + LOGV("Loading nodes"); + auto pNodes = models.GetChildByName("nodes"); + const OVR::JsonReader nodes(pNodes); + if (nodes.IsArray() && loaded) { + modelFile.Nodes.resize(pNodes->GetItemCount()); + + int nodeIndex = 0; + while (!nodes.IsEndOfArray()) { + const OVR::JsonReader node(nodes.GetNextArrayElement()); + if (node.IsObject()) { + ModelNode* pGltfNode = &modelFile.Nodes[nodeIndex]; + + pGltfNode->name = node.GetChildStringByName("name"); + const OVR::JsonReader matrixReader = node.GetChildByName("matrix"); + if (matrixReader.IsArray()) { + Matrix4f matrix; + ParseFloatArray(matrix.M[0], 16, matrixReader); + matrix.Transpose(); + // TRANSLATION + pGltfNode->translation = matrix.GetTranslation(); + // SCALE + pGltfNode->scale.x = sqrtf( + matrix.M[0][0] * matrix.M[0][0] + + matrix.M[0][1] * matrix.M[0][1] + + matrix.M[0][2] * matrix.M[0][2]); + pGltfNode->scale.y = sqrtf( + matrix.M[1][0] * matrix.M[1][0] + + matrix.M[1][1] * matrix.M[1][1] + + matrix.M[1][2] * matrix.M[1][2]); + pGltfNode->scale.z = sqrtf( + matrix.M[2][0] * matrix.M[2][0] + + matrix.M[2][1] * matrix.M[2][1] + + matrix.M[2][2] * matrix.M[2][2]); + // ROTATION + const float rcpScaleX = OVR::RcpSqrt( + matrix.M[0][0] * matrix.M[0][0] + + matrix.M[0][1] * matrix.M[0][1] + + matrix.M[0][2] * matrix.M[0][2]); + const float rcpScaleY = OVR::RcpSqrt( + matrix.M[1][0] * matrix.M[1][0] + + matrix.M[1][1] * matrix.M[1][1] + + matrix.M[1][2] * matrix.M[1][2]); + const float rcpScaleZ = OVR::RcpSqrt( + matrix.M[2][0] * matrix.M[2][0] + + matrix.M[2][1] * matrix.M[2][1] + + matrix.M[2][2] * matrix.M[2][2]); + const float m[9] = { + matrix.M[0][0] * rcpScaleX, + matrix.M[0][1] * rcpScaleX, + matrix.M[0][2] * rcpScaleX, + matrix.M[1][0] * rcpScaleY, + matrix.M[1][1] * rcpScaleY, + matrix.M[1][2] * rcpScaleY, + matrix.M[2][0] * rcpScaleZ, + matrix.M[2][1] * rcpScaleZ, + matrix.M[2][2] * rcpScaleZ}; + if (m[0 * 3 + 0] + m[1 * 3 + 1] + m[2 * 3 + 2] > 0.0f) { + float t = +m[0 * 3 + 0] + m[1 * 3 + 1] + m[2 * 3 + 2] + 1.0f; + float s = OVR::RcpSqrt(t) * 0.5f; + pGltfNode->rotation.w = s * t; + pGltfNode->rotation.z = (m[0 * 3 + 1] - m[1 * 3 + 0]) * s; + pGltfNode->rotation.y = (m[2 * 3 + 0] - m[0 * 3 + 2]) * s; + pGltfNode->rotation.x = (m[1 * 3 + 2] - m[2 * 3 + 1]) * s; + } else if ( + m[0 * 3 + 0] > m[1 * 3 + 1] && m[0 * 3 + 0] > m[2 * 3 + 2]) { + float t = +m[0 * 3 + 0] - m[1 * 3 + 1] - m[2 * 3 + 2] + 1.0f; + float s = OVR::RcpSqrt(t) * 0.5f; + pGltfNode->rotation.x = s * t; + pGltfNode->rotation.y = (m[0 * 3 + 1] + m[1 * 3 + 0]) * s; + pGltfNode->rotation.z = (m[2 * 3 + 0] + m[0 * 3 + 2]) * s; + pGltfNode->rotation.w = (m[1 * 3 + 2] - m[2 * 3 + 1]) * s; + } else if (m[1 * 3 + 1] > m[2 * 3 + 2]) { + float t = -m[0 * 3 + 0] + m[1 * 3 + 1] - m[2 * 3 + 2] + 1.0f; + float s = OVR::RcpSqrt(t) * 0.5f; + pGltfNode->rotation.y = s * t; + pGltfNode->rotation.x = (m[0 * 3 + 1] + m[1 * 3 + 0]) * s; + pGltfNode->rotation.w = (m[2 * 3 + 0] - m[0 * 3 + 2]) * s; + pGltfNode->rotation.z = (m[1 * 3 + 2] + m[2 * 3 + 1]) * s; + } else { + float t = -m[0 * 3 + 0] - m[1 * 3 + 1] + m[2 * 3 + 2] + 1.0f; + float s = OVR::RcpSqrt(t) * 0.5f; + pGltfNode->rotation.z = s * t; + pGltfNode->rotation.w = (m[0 * 3 + 1] - m[1 * 3 + 0]) * s; + pGltfNode->rotation.x = (m[2 * 3 + 0] + m[0 * 3 + 2]) * s; + pGltfNode->rotation.y = (m[1 * 3 + 2] + m[2 * 3 + 1]) * s; + } + } + + auto rotation = node.GetChildByName("rotation"); + if (rotation != nullptr) { + pGltfNode->rotation.x = + rotation->GetItemByIndex(0)->GetFloatValue(); + pGltfNode->rotation.y = + rotation->GetItemByIndex(1)->GetFloatValue(); + pGltfNode->rotation.z = + rotation->GetItemByIndex(2)->GetFloatValue(); + pGltfNode->rotation.w = + rotation->GetItemByIndex(3)->GetFloatValue(); + } + + auto scale = node.GetChildByName("scale"); + if (scale != nullptr) { + pGltfNode->scale.x = scale->GetItemByIndex(0)->GetFloatValue(); + pGltfNode->scale.y = scale->GetItemByIndex(1)->GetFloatValue(); + pGltfNode->scale.z = scale->GetItemByIndex(2)->GetFloatValue(); + } + + auto translation = node.GetChildByName("translation"); + if (translation != nullptr) { + pGltfNode->translation.x = + translation->GetItemByIndex(0)->GetFloatValue(); + pGltfNode->translation.y = + translation->GetItemByIndex(1)->GetFloatValue(); + pGltfNode->translation.z = + translation->GetItemByIndex(2)->GetFloatValue(); + } + + pGltfNode->skinIndex = node.GetChildInt32ByName("skin", -1); + + int cameraIndex = node.GetChildInt32ByName("camera", -1); + if (cameraIndex >= 0) { + if (cameraIndex >= static_cast(modelFile.Cameras.size())) { + ALOGW( + "Error: Invalid camera index %d on gltfNode", cameraIndex); + loaded = false; + } + pGltfNode->camera = &modelFile.Cameras[cameraIndex]; + } + + int meshIndex = node.GetChildInt32ByName("mesh", -1); + if (meshIndex >= 0) { + if (meshIndex >= static_cast(modelFile.Models.size())) { + ALOGW("Error: Invalid Mesh index %d on gltfNode", meshIndex); + loaded = false; + } + pGltfNode->model = &modelFile.Models[meshIndex]; + + // initialize morph target weights + if (!pGltfNode->model->weights.empty()) { + const OVR::JsonReader weightsReader( + node.GetChildByName("weights")); + if (weightsReader.IsArray()) { + // use node weights if it is defined + while (!weightsReader.IsEndOfArray() && loaded) { + pGltfNode->weights.push_back( + weightsReader.GetNextArrayFloat(0.0f)); + } + if (pGltfNode->weights.size() != + pGltfNode->model->weights.size()) { + ALOGW("Error: weights count mismatch on gltfNode"); + loaded = false; + } + } else { + // when node.weights is undefined, mesh.weights property + // MUST be used instead + pGltfNode->weights = pGltfNode->model->weights; + } + } + } + + Matrix4f localTransform; + CalculateTransformFromRTS( + &localTransform, + pGltfNode->rotation, + pGltfNode->translation, + pGltfNode->scale); + pGltfNode->SetLocalTransform(localTransform); + + const OVR::JsonReader children = node.GetChildByName("children"); + if (children.IsArray()) { + while (!children.IsEndOfArray()) { + auto child = children.GetNextArrayElement(); + int childIndex = child->GetInt32Value(); + + if (childIndex < 0 || + childIndex >= static_cast(modelFile.Nodes.size())) { + ALOGW( + "Error: Invalid child node index %d for %d in gltfNode", + childIndex, + nodeIndex); + loaded = false; + } + + pGltfNode->children.push_back(childIndex); + modelFile.Nodes[childIndex].parentIndex = nodeIndex; + } + } + + nodeIndex++; + } + } + } + } // END NODES + + if (loaded) { // ANIMATIONS + LOGV("loading Animations"); + auto animationsJSON = models.GetChildByName("animations"); + const OVR::JsonReader animations = animationsJSON; + if (animations.IsArray()) { + int animationCount = 0; + while (!animations.IsEndOfArray() && loaded) { + modelFile.Animations.resize(animationsJSON->GetArraySize()); + const OVR::JsonReader animation(animations.GetNextArrayElement()); + if (animation.IsObject()) { + ModelAnimation& modelAnimation = modelFile.Animations[animationCount]; + + modelAnimation.name = animation.GetChildStringByName("name"); + + // ANIMATION SAMPLERS + const OVR::JsonReader samplers = animation.GetChildByName("samplers"); + if (samplers.IsArray()) { + while (!samplers.IsEndOfArray() && loaded) { + ModelAnimationSampler modelAnimationSampler; + const OVR::JsonReader sampler = samplers.GetNextArrayElement(); + if (sampler.IsObject()) { + int inputIndex = sampler.GetChildInt32ByName("input", -1); + if (inputIndex < 0 || + inputIndex >= + static_cast(modelFile.Accessors.size())) { + ALOGW( + "bad input index %d on sample on %s", + inputIndex, + modelAnimation.name.c_str()); + loaded = false; + } else { + modelAnimationSampler.input = + &modelFile.Accessors[inputIndex]; + if (modelAnimationSampler.input->componentType != + GL_FLOAT) { + ALOGW( + "animation sampler input not of type GL_FLOAT on '%s'", + modelAnimation.name.c_str()); + loaded = false; + } + } + + int outputIndex = sampler.GetChildInt32ByName("output", -1); + if (outputIndex < 0 || + outputIndex >= + static_cast(modelFile.Accessors.size())) { + ALOGW( + "bad input outputIndex %d on sample on %s", + outputIndex, + modelAnimation.name.c_str()); + loaded = false; + } else { + modelAnimationSampler.output = + &modelFile.Accessors[outputIndex]; + } + + std::string interpolation = + sampler.GetChildStringByName("interpolation", "LINEAR"); + if (OVR::OVR_stricmp(interpolation.c_str(), "LINEAR") == + 0) { + modelAnimationSampler.interpolation = + MODEL_ANIMATION_INTERPOLATION_LINEAR; + } else if ( + OVR::OVR_stricmp(interpolation.c_str(), "STEP") == 0) { + modelAnimationSampler.interpolation = + MODEL_ANIMATION_INTERPOLATION_STEP; + } else if ( + OVR::OVR_stricmp( + interpolation.c_str(), "CATMULLROMSPLINE") == 0) { + modelAnimationSampler.interpolation = + MODEL_ANIMATION_INTERPOLATION_CATMULLROMSPLINE; + } else if ( + OVR::OVR_stricmp( + interpolation.c_str(), "CUBICSPLINE") == 0) { + modelAnimationSampler.interpolation = + MODEL_ANIMATION_INTERPOLATION_CUBICSPLINE; + } else { + ALOGW( + "Error: Invalid interpolation type '%s' on sampler on animation '%s'", + interpolation.c_str(), + modelAnimation.name.c_str()); + loaded = false; + } + + modelAnimation.samplers.push_back(modelAnimationSampler); + } else { + ALOGW("bad sampler on '%s'", modelAnimation.name.c_str()); + loaded = false; + } + } + } else { + ALOGW("bad samplers on '%s'", modelAnimation.name.c_str()); + loaded = false; + } // END ANIMATION SAMPLERS + + // ANIMATION CHANNELS + const OVR::JsonReader channels = animation.GetChildByName("channels"); + if (channels.IsArray()) { + while (!channels.IsEndOfArray() && loaded) { + const OVR::JsonReader channel = channels.GetNextArrayElement(); + if (channel.IsObject()) { + ModelAnimationChannel modelAnimationChannel; + + int samplerIndex = + channel.GetChildInt32ByName("sampler", -1); + if (samplerIndex < 0 || + samplerIndex >= + static_cast(modelAnimation.samplers.size())) { + ALOGW( + "bad samplerIndex %d on channel on %s", + samplerIndex, + modelAnimation.name.c_str()); + loaded = false; + } else { + modelAnimationChannel.sampler = + &modelAnimation.samplers[samplerIndex]; + } + + const OVR::JsonReader target = + channel.GetChildByName("target"); + if (target.IsObject()) { + // not required so -1 means do not do animation. + int nodeIndex = target.GetChildInt32ByName("node", -1); + if (nodeIndex >= + static_cast(modelFile.Nodes.size())) { + ALOGW( + "bad nodeIndex %d on target on '%s'", + nodeIndex, + modelAnimation.name.c_str()); + loaded = false; + } else { + modelAnimationChannel.nodeIndex = nodeIndex; + } + + std::string path = target.GetChildStringByName("path"); + + if (OVR::OVR_stricmp(path.c_str(), "translation") == + 0) { + modelAnimationChannel.path = + MODEL_ANIMATION_PATH_TRANSLATION; + } else if ( + OVR::OVR_stricmp(path.c_str(), "rotation") == 0) { + modelAnimationChannel.path = + MODEL_ANIMATION_PATH_ROTATION; + } else if ( + OVR::OVR_stricmp(path.c_str(), "scale") == 0) { + modelAnimationChannel.path = + MODEL_ANIMATION_PATH_SCALE; + } else if ( + OVR::OVR_stricmp(path.c_str(), "weights") == 0) { + modelAnimationChannel.path = + MODEL_ANIMATION_PATH_WEIGHTS; + } else { + ALOGW( + " bad path '%s' on target on '%s'", + path.c_str(), + modelAnimation.name.c_str()); + loaded = false; + } + + if (loaded) { + // validation sampler now that we have the path + auto sampler = modelAnimationChannel.sampler; + int inputCount = sampler->input->count; + int outputCount = sampler->output->count; + if (modelAnimationChannel.path == + MODEL_ANIMATION_PATH_WEIGHTS) { + auto node = + modelFile + .Nodes[modelAnimationChannel.nodeIndex]; + outputCount /= + node.model->surfaces[0].targets.size(); + } + + if (sampler->interpolation == + MODEL_ANIMATION_INTERPOLATION_LINEAR || + sampler->interpolation == + MODEL_ANIMATION_INTERPOLATION_STEP) { + if (inputCount != outputCount) { + ALOGW( + "input (%d) and output (%d) have different counts on sampler on animation '%s'", + inputCount, + outputCount, + modelAnimation.name.c_str()); + loaded = false; + + auto node = + modelFile.Nodes[modelAnimationChannel + .nodeIndex]; + } + if (inputCount < 2) { + ALOGW( + "invalid number of samples on animation sampler input %d '%s'", + inputCount, + modelAnimation.name.c_str()); + loaded = false; + } + } else if ( + sampler->interpolation == + MODEL_ANIMATION_INTERPOLATION_CATMULLROMSPLINE) { + if ((inputCount + 2) != outputCount) { + ALOGW( + "input and output have invalid counts on sampler on animation '%s'", + modelAnimation.name.c_str()); + loaded = false; + } + if (inputCount < 4) { + ALOGW( + "invalid number of samples on animation sampler input %d '%s'", + inputCount, + modelAnimation.name.c_str()); + loaded = false; + } + } else if ( + sampler->interpolation == + MODEL_ANIMATION_INTERPOLATION_CUBICSPLINE) { + if (inputCount != (outputCount * 3)) { + ALOGW( + "input and output have invalid counts on sampler on animation '%s'", + modelAnimation.name.c_str()); + loaded = false; + } + if (inputCount < 2) { + ALOGW( + "invalid number of samples on animation sampler input %d '%s'", + inputCount, + modelAnimation.name.c_str()); + loaded = false; + } + } else { + ALOGW( + "unkown animaiton interpolation on '%s'", + modelAnimation.name.c_str()); + loaded = false; + } + } + } else { + ALOGW( + "bad target object on '%s'", + modelAnimation.name.c_str()); + loaded = false; + } + + const OVR::JsonReader extras = + channel.GetChildByName("extras"); + if (extras.IsObject()) { + // additive index only make sense for weights + if (modelAnimationChannel.path == + MODEL_ANIMATION_PATH_WEIGHTS) { + modelAnimationChannel.additiveWeightIndex = + extras.GetChildInt32ByName( + "additiveWeightIndex", -1); + } + } + + modelAnimation.channels.push_back(modelAnimationChannel); + } else { + ALOGW("bad channel on '%s'", modelAnimation.name.c_str()); + loaded = false; + } + } + } else { + ALOGW("bad channels on '%s'", modelAnimation.name.c_str()); + loaded = false; + } // END ANIMATION CHANNELS + + animationCount++; + } else { + ALOGW("bad animation object in animations"); + loaded = false; + } + } + } + } // END ANIMATIONS + + if (loaded) { // ANIMATION TIMELINES + // create the timelines + for (int i = 0; i < static_cast(modelFile.Animations.size()); i++) { + for (int j = 0; j < static_cast(modelFile.Animations[i].samplers.size()); + j++) { + // if there isn't already a timeline with this accessor, + // create a new one. + ModelAnimationSampler& sampler = modelFile.Animations[i].samplers[j]; + bool foundTimeLine = false; + for (int timeLineIndex = 0; + timeLineIndex < static_cast(modelFile.AnimationTimeLines.size()); + timeLineIndex++) { + if (modelFile.AnimationTimeLines[timeLineIndex].accessor == + sampler.input) { + foundTimeLine = true; + sampler.timeLineIndex = timeLineIndex; + break; + } + } + + if (!foundTimeLine) { + ModelAnimationTimeLine timeline; + timeline.Initialize(sampler.input); + if (static_cast(modelFile.AnimationTimeLines.size()) == 0) { + modelFile.animationStartTime = timeline.startTime; + modelFile.animationEndTime = timeline.endTime; + } else { + modelFile.animationStartTime = std::min( + modelFile.animationStartTime, timeline.startTime); + modelFile.animationEndTime = + std::max(modelFile.animationEndTime, timeline.endTime); + } + + modelFile.AnimationTimeLines.push_back(timeline); + sampler.timeLineIndex = + static_cast(modelFile.AnimationTimeLines.size()) - 1; + } + } + } + } // END ANIMATION TIMELINES + + if (loaded) { // SKINS + LOGV("Loading skins"); + const OVR::JsonReader skins(models.GetChildByName("skins")); + if (skins.IsArray()) { + while (!skins.IsEndOfArray() && loaded) { + const OVR::JsonReader skin(skins.GetNextArrayElement()); + if (skin.IsObject()) { + ModelSkin newSkin; + + newSkin.name = skin.GetChildStringByName("name"); + newSkin.skeletonRootIndex = skin.GetChildInt32ByName("skeleton", -1); + int bindMatricesAccessorIndex = + skin.GetChildInt32ByName("inverseBindMatrices", -1); + if (bindMatricesAccessorIndex >= + static_cast(modelFile.Accessors.size())) { + ALOGW( + "inverseBindMatrices %d higher then number of accessors on model: %s", + bindMatricesAccessorIndex, + modelFile.FileName.c_str()); + loaded = false; + } else if (bindMatricesAccessorIndex >= 0) { + ModelAccessor& acc = modelFile.Accessors[bindMatricesAccessorIndex]; + newSkin.inverseBindMatricesAccessor = + &modelFile.Accessors[bindMatricesAccessorIndex]; + for (int i = 0; i < acc.count; i++) { + Matrix4f matrix; + memcpy( + matrix.M[0], + ((float*)(acc.BufferData())) + i * 16, + sizeof(float) * 16); + matrix.Transpose(); + newSkin.inverseBindMatrices.push_back(matrix); + } + } + + const OVR::JsonReader joints = skin.GetChildByName("joints"); + if (joints.IsArray()) { + while (!joints.IsEndOfArray() && loaded) { + int jointIndex = joints.GetNextArrayInt32(-1); + if (jointIndex < 0 || + jointIndex >= static_cast(modelFile.Nodes.size())) { + ALOGW( + "bad jointindex %d on skin on model: %s", + jointIndex, + modelFile.FileName.c_str()); + loaded = false; + } else { + ALOGW( + " SKIN - jointIndex: %02d name: %s", + jointIndex, + modelFile.Nodes[jointIndex].name.c_str()); + } + newSkin.jointIndexes.push_back(jointIndex); + } + } else { + ALOGW("no joints on skin on model: %s", modelFile.FileName.c_str()); + loaded = false; + } + + /// Up the number here + const int maxJointsAllowed = 96; /// MAX_JOINTS + + if (static_cast(newSkin.jointIndexes.size()) > maxJointsAllowed) { + ALOGW( + "%d joints on skin on model: %s, currently only %d allowed ", + static_cast(newSkin.jointIndexes.size()), + modelFile.FileName.c_str(), + maxJointsAllowed); + loaded = false; + } + + modelFile.Skins.push_back(newSkin); + } else { + ALOGW("bad skin on model: %s", modelFile.FileName.c_str()); + loaded = false; + } + } + } + + } // END SKINS + + if (loaded) { // verify skin indexes on nodes + for (int i = 0; i < static_cast(modelFile.Nodes.size()); i++) { + if (modelFile.Nodes[i].skinIndex > static_cast(modelFile.Skins.size())) { + ALOGW( + "bad skin index %d on node %d on model: %s", + modelFile.Nodes[i].skinIndex, + i, + modelFile.FileName.c_str()); + loaded = false; + } + } + } + + if (loaded) { // SCENES + LOGV("Loading scenes"); + const OVR::JsonReader scenes(models.GetChildByName("scenes")); + if (scenes.IsArray()) { + while (!scenes.IsEndOfArray() && loaded) { + const OVR::JsonReader scene(scenes.GetNextArrayElement()); + if (scene.IsObject()) { + ModelSubScene newGltfScene; + + newGltfScene.name = scene.GetChildStringByName("name"); + + const OVR::JsonReader nodes = scene.GetChildByName("nodes"); + if (nodes.IsArray()) { + while (!nodes.IsEndOfArray()) { + const int nodeIndex = nodes.GetNextArrayInt32(); + if (nodeIndex < 0 || + nodeIndex >= static_cast(modelFile.Nodes.size())) { + ALOGW("Error: Invalid nodeIndex %d in Model", nodeIndex); + loaded = false; + } + newGltfScene.nodes.push_back(nodeIndex); + } + } + modelFile.SubScenes.push_back(newGltfScene); + } + } + } + + // Calculate the nodes global transforms; + for (int i = 0; i < static_cast(modelFile.SubScenes.size()); i++) { + for (int j = 0; j < static_cast(modelFile.SubScenes[i].nodes.size()); + j++) { + modelFile.Nodes[modelFile.SubScenes[i].nodes[j]].RecalculateGlobalTransform( + modelFile); + } + } + } // END SCENES + + if (loaded) { + const int sceneIndex = models.GetChildInt32ByName("scene", -1); + if (sceneIndex >= 0) { + if (sceneIndex >= static_cast(modelFile.SubScenes.size())) { + ALOGW("Error: Invalid initial scene index %d on gltfFile", sceneIndex); + loaded = false; + } + modelFile.SubScenes[sceneIndex].visible = true; + } + } + + // print out the scene info + if (loaded) { + LOGV("Model Loaded: '%s'", modelFile.FileName.c_str()); + LOGV("\tBuffers : %d", static_cast(modelFile.Buffers.size())); + LOGV("\tBufferViews : %d", static_cast(modelFile.BufferViews.size())); + LOGV("\tAccessors : %d", static_cast(modelFile.Accessors.size())); + LOGV("\tTextures : %d", static_cast(modelFile.Textures.size())); + LOGV("\tTextureWrappers: %d", static_cast(modelFile.TextureWrappers.size())); + LOGV("\tMaterials : %d", static_cast(modelFile.Materials.size())); + LOGV("\tModels : %d", static_cast(modelFile.Models.size())); + LOGV("\tCameras : %d", static_cast(modelFile.Cameras.size())); + LOGV("\tNodes : %d", static_cast(modelFile.Nodes.size())); + LOGV("\tAnimations : %d", static_cast(modelFile.Animations.size())); + LOGV( + "\tAnimationTimeLines: %d", + static_cast(modelFile.AnimationTimeLines.size())); + LOGV("\tSkins : %d", static_cast(modelFile.Skins.size())); + LOGV("\tSubScenes : %d", static_cast(modelFile.SubScenes.size())); + } else { + ALOGW("Could not load model '%s'", modelFile.FileName.c_str()); + } + + // #TODO: what to do with our collision? One possible answer is extras on the data + // tagging certain models as collision. Collision Model Ground Collision Model Ray-Trace + // Model + } else { + loaded = false; + } + } + return loaded; +} + +// A gltf directory zipped up into an ovrscene file. +bool LoadModelFile_glTF_OvrScene( + ModelFile* modelFilePtr, + unzFile zfp, + const char* fileName, + const char* fileData, + const int fileDataLength, + const ModelGlPrograms& programs, + const MaterialParms& materialParms, + ModelGeo* outModelGeo) { + ModelFile& modelFile = *modelFilePtr; + + // Since we are doing a zip file, we are going to parse through the zip file many times to find + // the different data points. + const char* gltfJson = nullptr; + { + // LOGCPUTIME( "Loading GLTF file" ); + for (int ret = unzGoToFirstFile(zfp); ret == UNZ_OK; ret = unzGoToNextFile(zfp)) { + unz_file_info finfo; + char entryName[256]; + unzGetCurrentFileInfo( + zfp, &finfo, entryName, sizeof(entryName), nullptr, 0, nullptr, 0); + const size_t entryLength = strlen(entryName); + const char* extension = (entryLength >= 5) ? &entryName[entryLength - 5] : entryName; + + if (OVR::OVR_stricmp(extension, ".gltf") == 0) { + LOGV("found %s", entryName); + uint8_t* buffer = ReadBufferFromZipFile(zfp, (const uint8_t*)fileData, finfo); + + if (buffer == nullptr) { + ALOGW( + "LoadModelFile_glTF_OvrScene:Failed to read %s from %s", + entryName, + fileName); + continue; + } + + if (gltfJson == nullptr) { + gltfJson = (const char*)buffer; + } else { + ALOGW("LoadModelFile_glTF_OvrScene: multiple .gltf files found %s", fileName); + delete[] buffer; + continue; + } + } + } + } + + bool loaded = true; + + const char* error = nullptr; + auto json = OVR::JSON::Parse(gltfJson, &error); + if (json == nullptr) { + ALOGW( + "LoadModelFile_glTF_OvrScene: Error loading %s : %s", + modelFilePtr->FileName.c_str(), + error); + loaded = false; + } else { + const OVR::JsonReader models(json); + if (models.IsObject()) { + // Buffers BufferViews and Images need access to the data location, in this case the zip + // file. + // after they are loaded it should be identical wether the input is a zip file, a + // folder structure or a bgltf file. + if (loaded) { // BUFFERS + // LOGCPUTIME( "Loading buffers" ); + // gather all the buffers, and try to load them from the zip file. + const OVR::JsonReader buffers(models.GetChildByName("buffers")); + if (buffers.IsArray()) { + while (!buffers.IsEndOfArray() && loaded) { + const OVR::JsonReader bufferReader(buffers.GetNextArrayElement()); + if (bufferReader.IsObject()) { + ModelBuffer newGltfBuffer; + + const std::string name = bufferReader.GetChildStringByName("name"); + const std::string uri = bufferReader.GetChildStringByName("uri"); + newGltfBuffer.byteLength = + bufferReader.GetChildInt32ByName("byteLength", -1); + + // #TODO: proper uri reading. right now, assuming its a file name. + if (OVR::OVR_stricmp(uri.c_str() + (uri.length() - 4), ".bin") != 0) { + // #TODO: support loading buffers from data other then a bin file. + // i.e. inline buffers etc. + ALOGW("Loading buffers other then bin files currently unsupported"); + loaded = false; + } + int bufferLength = 0; + uint8_t* tempbuffer = ReadFileBufferFromZipFile( + zfp, uri.c_str(), bufferLength, (const uint8_t*)fileData); + if (tempbuffer == nullptr) { + ALOGW("could not load buffer for gltfBuffer"); + loaded = false; + } else { + // ensure the buffer is aligned. + size_t alignedBufferSize = (bufferLength / 4 + 1) * 4; + newGltfBuffer.bufferData.resize(alignedBufferSize); + memcpy(newGltfBuffer.bufferData.data(), tempbuffer, bufferLength); + } + + if (newGltfBuffer.byteLength > (size_t)bufferLength) { + ALOGW( + "%d byteLength > bufferLength loading gltfBuffer %d", + (int)newGltfBuffer.byteLength, + bufferLength); + loaded = false; + } + + const char* bufferName; + if (name.length() > 0) { + bufferName = name.c_str(); + } else { + bufferName = uri.c_str(); + } + + newGltfBuffer.name = bufferName; + + modelFile.Buffers.push_back(newGltfBuffer); + } + } + } + } // END BUFFERS + + if (loaded) { // BUFFERVIEW + LOGV("Loading bufferviews"); + const OVR::JsonReader bufferViews(models.GetChildByName("bufferViews")); + if (bufferViews.IsArray()) { + while (!bufferViews.IsEndOfArray() && loaded) { + const OVR::JsonReader bufferview(bufferViews.GetNextArrayElement()); + if (bufferview.IsObject()) { + ModelBufferView newBufferView; + + newBufferView.name = bufferview.GetChildStringByName("name"); + const int buffer = bufferview.GetChildInt32ByName("buffer"); + newBufferView.byteOffset = bufferview.GetChildInt32ByName("byteOffset"); + newBufferView.byteLength = bufferview.GetChildInt32ByName("byteLength"); + newBufferView.byteStride = bufferview.GetChildInt32ByName("byteStride"); + newBufferView.target = bufferview.GetChildInt32ByName("target"); + + if (buffer < 0 || buffer >= (const int)modelFile.Buffers.size()) { + ALOGW("Error: Invalid buffer Index in gltfBufferView"); + loaded = false; + } + if (newBufferView.byteStride < 0 || newBufferView.byteStride > 255) { + ALOGW("Error: Invalid byeStride in gltfBufferView"); + loaded = false; + } + if (newBufferView.target < 0) { + ALOGW("Error: Invalid target in gltfBufferView"); + loaded = false; + } + + newBufferView.buffer = &modelFile.Buffers[buffer]; + modelFile.BufferViews.push_back(newBufferView); + } + } + } + } // END BUFFERVIEWS + + if (loaded) { // IMAGES + // LOGCPUTIME( "Loading image textures" ); + // gather all the images, and try to load them from the zip file. + const OVR::JsonReader images(models.GetChildByName("images")); + if (images.IsArray()) { + while (!images.IsEndOfArray()) { + const OVR::JsonReader image(images.GetNextArrayElement()); + if (image.IsObject()) { + const std::string name = image.GetChildStringByName("name"); + const std::string uri = image.GetChildStringByName("uri"); + int bufferView = image.GetChildInt32ByName("bufferView", -1); + if (bufferView >= 0) { + // #TODO: support bufferView index for image files. + ALOGW( + "Loading images from bufferView currently unsupported, defaulting image"); + // Create a default texture. + LoadModelFileTexture( + modelFile, "DefaultImage", nullptr, 0, materialParms); + } else { + // check to make sure the image is ktx. + if (OVR::OVR_stricmp(uri.c_str() + (uri.length() - 4), ".ktx") != + 0) { + // #TODO: Try looking for a ktx image before we load the non ktx + // image. + ALOGW( + "Loading images other then ktx is not advised. %s", + uri.c_str()); + + int bufferLength = 0; + uint8_t* buffer = ReadFileBufferFromZipFile( + zfp, uri.c_str(), bufferLength, (const uint8_t*)fileData); + const char* imageName = uri.c_str(); + + LoadModelFileTexture( + modelFile, + imageName, + (const char*)buffer, + bufferLength, + materialParms); + } else { + int bufferLength = 0; + uint8_t* buffer = ReadFileBufferFromZipFile( + zfp, uri.c_str(), bufferLength, (const uint8_t*)fileData); + const char* imageName = uri.c_str(); + + LoadModelFileTexture( + modelFile, + imageName, + (const char*)buffer, + bufferLength, + materialParms); + } + } + } + } + } + } // END images + // End of section dependent on zip file. + } else { + ALOGW("error: could not parse json for gltf"); + loaded = false; + } + + if (loaded) { + loaded = + LoadModelFile_glTF_Json(modelFile, gltfJson, programs, materialParms, outModelGeo); + } + } + + if (gltfJson != nullptr && (gltfJson < fileData || gltfJson > fileData + fileDataLength)) { + delete gltfJson; + } + + return loaded; +} + +ModelFile* LoadModelFile_glB( + const char* fileName, + const char* fileData, + const int fileDataLength, + const ModelGlPrograms& programs, + const MaterialParms& materialParms, + ModelGeo* outModelGeo) { + // LOGCPUTIME( "LoadModelFile_glB" ); + + ModelFile* modelFilePtr = new ModelFile; + ModelFile& modelFile = *modelFilePtr; + + modelFile.FileName = fileName; + modelFile.UsingSrgbTextures = materialParms.UseSrgbTextureFormats; + + bool loaded = true; + + uint32_t fileDataIndex = 0; + uint32_t fileDataRemainingLength = fileDataLength; + glTFBinaryHeader header; + if (fileDataRemainingLength < sizeof(header)) { + ALOGW("Error: could not load glb gltfHeader"); + loaded = false; + } + + if (loaded) { + memcpy(&header, &fileData[fileDataIndex], sizeof(header)); + fileDataIndex += sizeof(header); + fileDataRemainingLength -= sizeof(header); + + if (header.magic != GLTF_BINARY_MAGIC) { + ALOGW("Error: invalid glb gltfHeader magic"); + loaded = false; + } + + if (header.version != GLTF_BINARY_VERSION) { + ALOGW("Error: invalid glb gltfHeader version"); + loaded = false; + } + + if (header.length != (uint32_t)fileDataLength) { + ALOGW("Error: invalid glb gltfHeader length"); + loaded = false; + } + } + + if (loaded && fileDataRemainingLength > sizeof(uint32_t) * 2) { + uint32_t chunkType = 0; + uint32_t chunkLength = 0; + + memcpy(&chunkLength, &fileData[fileDataIndex], sizeof(uint32_t)); + fileDataIndex += sizeof(uint32_t); + fileDataRemainingLength -= sizeof(uint32_t); + memcpy(&chunkType, &fileData[fileDataIndex], sizeof(uint32_t)); + fileDataIndex += sizeof(uint32_t); + fileDataRemainingLength -= sizeof(uint32_t); + + if (chunkType != GLTF_BINARY_CHUNKTYPE_JSON) { + ALOGW("Error: glb first chunk not JSON"); + loaded = false; + } + + std::shared_ptr json = nullptr; + const char* gltfJson = nullptr; + if (loaded) { + const char* error = nullptr; + gltfJson = &fileData[fileDataIndex]; + json = OVR::JSON::Parse(gltfJson, &error); + fileDataIndex += chunkLength; + fileDataRemainingLength -= chunkLength; + + if (json == nullptr) { + ALOGW( + "LoadModelFile_glB: Error Parsing JSON %s : %s", + modelFilePtr->FileName.c_str(), + error); + loaded = false; + } + } + + const char* buffer = nullptr; + uint32_t bufferLength = 0; + if (loaded) { + if (fileDataRemainingLength > sizeof(uint32_t) * 2) { + uint32_t bufferChunkType = 0; + memcpy(&bufferLength, &fileData[fileDataIndex], sizeof(uint32_t)); + fileDataIndex += sizeof(uint32_t); + fileDataRemainingLength -= sizeof(uint32_t); + memcpy(&bufferChunkType, &fileData[fileDataIndex], sizeof(uint32_t)); + fileDataIndex += sizeof(uint32_t); + fileDataRemainingLength -= sizeof(uint32_t); + + if (bufferChunkType != GLTF_BINARY_CHUNKTYPE_BINARY) { + ALOGW("Error: glb second chunk not binary"); + loaded = false; + } else if (bufferLength > fileDataRemainingLength) { + ALOGW("Error: glb binary chunk length greater then remaining buffer"); + loaded = false; + } else { + if (bufferLength < fileDataRemainingLength) { + ALOGW("Error: glb binary chunk length less then remaining buffer"); + } + buffer = &fileData[fileDataIndex]; + } + } else { + ALOGW("Not enough data remaining to parse glB buffer"); + loaded = false; + } + } + + if (loaded) { + const OVR::JsonReader models(json); + if (models.IsObject()) { + // Buffers BufferViews and Images need access to the data location, in this case the + // buffer inside the glb file. + // after they are loaded it should be identical wether the input is a zip file, a + // folder structure or a glb file. + if (loaded) { // BUFFERS + LOGV("Loading buffers"); + // gather all the buffers, and try to load them from the zip file. + const OVR::JsonReader buffers(models.GetChildByName("buffers")); + if (buffers.IsArray()) { + while (!buffers.IsEndOfArray() && loaded) { + if (static_cast(modelFile.Buffers.size()) > 0) { + ALOGW("Error: glB file contains more then one buffer"); + loaded = false; + } + + const OVR::JsonReader bufferReader(buffers.GetNextArrayElement()); + if (bufferReader.IsObject() && loaded) { + ModelBuffer newGltfBuffer; + + const std::string name = bufferReader.GetChildStringByName("name"); + const std::string uri = bufferReader.GetChildStringByName("uri"); + newGltfBuffer.byteLength = + bufferReader.GetChildInt32ByName("byteLength", -1); + + // #TODO: proper uri reading. right now, assuming its a file name. + if (!uri.empty()) { + ALOGW( + "Loading buffers with an uri currently unsupported in glb"); + loaded = false; + } + + if (newGltfBuffer.byteLength > (size_t)bufferLength) { + ALOGW( + "%d byteLength > bufferLength loading gltfBuffer %d", + (int)newGltfBuffer.byteLength, + bufferLength); + loaded = false; + } + + // ensure the buffer is aligned. + size_t alignedBufferSize = (bufferLength / 4 + 1) * 4; + newGltfBuffer.bufferData.resize(alignedBufferSize); + memcpy( + newGltfBuffer.bufferData.data(), + buffer, + newGltfBuffer.byteLength); + + const char* bufferName; + if (name.length() > 0) { + bufferName = name.c_str(); + } else { + bufferName = "glB_Buffer"; + } + + newGltfBuffer.name = bufferName; + + modelFile.Buffers.push_back(newGltfBuffer); + } + } + } + } // END BUFFERS + + if (loaded) { // BUFFERVIEW + LOGV("Loading bufferviews"); + const OVR::JsonReader bufferViews(models.GetChildByName("bufferViews")); + if (bufferViews.IsArray()) { + while (!bufferViews.IsEndOfArray() && loaded) { + const OVR::JsonReader bufferview(bufferViews.GetNextArrayElement()); + if (bufferview.IsObject()) { + ModelBufferView newBufferView; + + newBufferView.name = bufferview.GetChildStringByName("name"); + const int bufferIndex = bufferview.GetChildInt32ByName("buffer"); + newBufferView.byteOffset = + bufferview.GetChildInt32ByName("byteOffset"); + newBufferView.byteLength = + bufferview.GetChildInt32ByName("byteLength"); + newBufferView.byteStride = + bufferview.GetChildInt32ByName("byteStride"); + newBufferView.target = bufferview.GetChildInt32ByName("target"); + + if (bufferIndex < 0 || + bufferIndex >= (const int)modelFile.Buffers.size()) { + ALOGW("Error: Invalid buffer Index in gltfBufferView"); + loaded = false; + } + if (newBufferView.byteStride < 0 || + newBufferView.byteStride > 255) { + ALOGW("Error: Invalid byeStride in gltfBufferView"); + loaded = false; + } + if (newBufferView.target < 0) { + ALOGW("Error: Invalid target in gltfBufferView"); + loaded = false; + } + + newBufferView.buffer = &modelFile.Buffers[bufferIndex]; + modelFile.BufferViews.push_back(newBufferView); + } + } + } + } // END BUFFERVIEWS + + if (loaded) { // IMAGES + LOGV("Loading image textures"); + // gather all the images, and try to load them from the zip file. + const OVR::JsonReader images(models.GetChildByName("images")); + if (images.IsArray()) { + while (!images.IsEndOfArray()) { + const OVR::JsonReader image(images.GetNextArrayElement()); + if (image.IsObject()) { + const std::string name = image.GetChildStringByName("name"); + const std::string uri = image.GetChildStringByName("uri"); + const std::string mimeType = image.GetChildStringByName("mimeType"); + int bufferView = image.GetChildInt32ByName("bufferView", -1); + LOGV( + "LoadModelFile_glB: %s, %s, %d", + name.c_str(), + uri.c_str(), + bufferView); + if (bufferView >= 0 && + bufferView < static_cast(modelFile.BufferViews.size())) { + ModelBufferView* pBufferView = + &modelFile.BufferViews[bufferView]; + int imageBufferLength = (int)pBufferView->byteLength; + uint8_t* imageBuffer = + (uint8_t*)pBufferView->buffer->bufferData.data() + + pBufferView->byteOffset; + + std::string path = name; + const char* ext = strrchr(mimeType.c_str(), '/'); + if (ext) { + path += "."; + path += ext + 1; + } + + LoadModelFileTexture( + modelFile, + path.c_str(), + (const char*)imageBuffer, + imageBufferLength, + materialParms); + } else if ( + materialParms.ImageUriHandler && + materialParms.ImageUriHandler(modelFile, uri)) { + LOGV("LoadModelFile_glB: uri processed by custom handler"); + } else { + ALOGW( + "Loading images from othen then bufferView currently unsupported in glBfd, defaulting image"); + // Create a default texture. + LoadModelFileTexture( + modelFile, "DefaultImage", nullptr, 0, materialParms); + } + } + } + } + } // END images + + // End of section dependent on buffer data in the glB file. + } + } + + if (loaded) { + loaded = + LoadModelFile_glTF_Json(modelFile, gltfJson, programs, materialParms, outModelGeo); + } + } + + // delete fileData; + + if (!loaded) { + ALOGW("Error: failed to load %s", fileName); + delete modelFilePtr; + modelFilePtr = nullptr; + } + + return modelFilePtr; +} + +} // namespace OVRFW diff --git a/Samples/SampleXrFramework/Src/Model/ModelRender.cpp b/Samples/SampleXrFramework/Src/Model/ModelRender.cpp new file mode 100755 index 0000000..b3c8303 --- /dev/null +++ b/Samples/SampleXrFramework/Src/Model/ModelRender.cpp @@ -0,0 +1,298 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/************************************************************************************ + +Filename : ModelRender.cpp +Content : Optimized OpenGL rendering path +Created : August 9, 2013 +Authors : John Carmack + +************************************************************************************/ + +#include "ModelRender.h" + +#include +#include + +#include "Misc/Log.h" +#include "Render/Egl.h" + +using OVR::Bounds3f; +using OVR::Matrix4f; +using OVR::Vector2f; +using OVR::Vector3f; +using OVR::Vector4f; + +namespace OVRFW { + +// Returns 0 if the bounds is culled by the mvp, otherwise returns the max W +// value of the bounds corners so it can be sorted into roughly front to back +// order for more efficient Z cull. Sorting bounds in increasing order of +// their farthest W value usually makes characters and objects draw before +// the environments they are in, and draws sky boxes last, which is what we want. +static float BoundsSortCullKey(const Bounds3f& bounds, const Matrix4f& mvp) { + // Always cull empty bounds, which can be used to disable a surface. + // Don't just check a single axis, or billboards would be culled. + if (bounds.b[1].x == bounds.b[0].x && bounds.b[1].y == bounds.b[0].y) { + return 0; + } + + // Not very efficient code... + Vector4f c[8]; + for (int i = 0; i < 8; i++) { + Vector4f world; + world.x = bounds.b[(i & 1)].x; + world.y = bounds.b[(i & 2) >> 1].y; + world.z = bounds.b[(i & 4) >> 2].z; + world.w = 1.0f; + + c[i] = mvp.Transform(world); + } + + int i; + for (i = 0; i < 8; i++) { + if (c[i].x > -c[i].w) { + break; + } + } + if (i == 8) { + return 0; // all off one side + } + for (i = 0; i < 8; i++) { + if (c[i].x < c[i].w) { + break; + } + } + if (i == 8) { + return 0; // all off one side + } + + for (i = 0; i < 8; i++) { + if (c[i].y > -c[i].w) { + break; + } + } + if (i == 8) { + return 0; // all off one side + } + for (i = 0; i < 8; i++) { + if (c[i].y < c[i].w) { + break; + } + } + if (i == 8) { + return 0; // all off one side + } + + for (i = 0; i < 8; i++) { + if (c[i].z > -c[i].w) { + break; + } + } + if (i == 8) { + return 0; // all off one side + } + for (i = 0; i < 8; i++) { + if (c[i].z < c[i].w) { + break; + } + } + if (i == 8) { + return 0; // all off one side + } + + // calculate the farthest W point for front to back sorting + float maxW = 0; + for (i = 0; i < 8; i++) { + const float w = c[i].w; + if (w > maxW) { + maxW = w; + } + } + + return maxW; // couldn't cull +} + +struct bsort_t { + float key; + Matrix4f modelMatrix; + const std::vector* joints; + const ovrSurfaceDef* surface; + bool transparent; + + bool operator<(const bsort_t& b2) const { + const bsort_t& b1 = *this; + bool trans1 = b1.transparent; + bool trans2 = b2.transparent; + if (trans1 == trans2) { + float f1 = b1.key; + float f2 = b2.key; + if (!trans1) { + // both are solid, sort front-to-back + return (f1 < f2); + } else { + // both are transparent, sort back-to-front + return (f2 < f1); + } + } + // otherwise, one is solid and one is translucent... the solid is always rendered first + return !trans1; + }; +}; + +void BuildModelSurfaceList( + std::vector& surfaceList, + const std::vector& emitNodes, + const std::vector& emitSurfaces, + const Matrix4f& viewMatrix, + const Matrix4f& projectionMatrix) { + // A mobile GPU will be in trouble if it draws more than this. + static const int MAX_DRAW_SURFACES = 1024; + bsort_t bsort[MAX_DRAW_SURFACES]; + + const Matrix4f vpMatrix = projectionMatrix * viewMatrix; + + int numSurfaces = 0; + + for (int nodeNum = 0; nodeNum < static_cast(emitNodes.size()); nodeNum++) { + const ModelNodeState& nodeState = *emitNodes[nodeNum]; + if (nodeState.GetNode() != NULL && nodeState.GetNode()->model != NULL) { + // #TODO currently we aren't properly updating the geo local bounds for skinned animated + // objects. Fix that. + bool allowCulling = true; + if (nodeState.node->skinIndex >= 0) { + allowCulling = false; + } + + if (nodeState.GetNode()->model != nullptr) { + const Model& modelDef = *nodeState.GetNode()->model; + for (int surfaceNum = 0; surfaceNum < static_cast(modelDef.surfaces.size()); + surfaceNum++) { + const ovrSurfaceDef& surfaceDef = modelDef.surfaces[surfaceNum].surfaceDef; + const float sort = BoundsSortCullKey( + surfaceDef.geo.localBounds, vpMatrix * nodeState.GetGlobalTransform()); + if (sort == 0) { + if (allowCulling) { + if (LogRenderSurfaces) { + ALOG("Culled %s", surfaceDef.surfaceName.c_str()); + } + continue; + } else { + if (LogRenderSurfaces) { + ALOG("Skipped Culling of %s", surfaceDef.surfaceName.c_str()); + } + } + } + + if (numSurfaces == MAX_DRAW_SURFACES) { + break; + } + + /* + // Update the Joint Uniform Buffer + if ( nodeState.node->skinIndex >= 0 ) + { + const ModelSkin & skin = + nodeState.state->mf->Skins[nodeState.node->skinIndex]; + + static Matrix4f transposedJoints[MAX_JOINTS]; + const int numJoints = std::min( static_cast< int >( + skin.jointIndexes.size() ), MAX_JOINTS ); + + + ALOGW( "### Skinning using skin #%d", + nodeState.node->skinIndex ); + + Matrix4f inverseGlobalSkeletonTransform; + if ( skin.skeletonRootIndex >= 0 ) + { + inverseGlobalSkeletonTransform = + nodeState.state->nodeStates[skin.skeletonRootIndex].GetGlobalTransform().Inverted(); + } + else + { + inverseGlobalSkeletonTransform = + nodeState.state->nodeStates[nodeState.node->parentIndex].GetGlobalTransform().Inverted(); + } + + for ( int j = 0; j < numJoints; j++ ) + { + Matrix4f globalTransform = + nodeState.state->nodeStates[skin.jointIndexes[j]].GetGlobalTransform(); + Matrix4f tempTransform; + Matrix4f::Multiply( &tempTransform, + inverseGlobalSkeletonTransform, globalTransform ); Matrix4f + localJointTransform; + + if ( skin.inverseBindMatrices.size() > 0 ) + { + Matrix4f::Multiply( &localJointTransform, + tempTransform, skin.inverseBindMatrices[j] ); + } + else + { + ALOGW( "No inverse bind on modle" ); + localJointTransform = tempTransform; + } + + transposedJoints[j] = + localJointTransform.Transposed(); + } + const size_t updateSize = numJoints * sizeof( Matrix4f + ); surfaceDef.graphicsCommand.uniformJoints.Update( updateSize, + &transposedJoints[0] ); + } + */ + + bsort[numSurfaces].key = sort; + bsort[numSurfaces].modelMatrix = nodeState.GetGlobalTransform(); + bsort[numSurfaces].surface = &surfaceDef; + bsort[numSurfaces].transparent = + (surfaceDef.graphicsCommand.GpuState.blendEnable != + ovrGpuState::BLEND_DISABLE); + numSurfaces++; + } + } + } + } + + for (int i = 0; i < static_cast(emitSurfaces.size()); i++) { + const ovrDrawSurface& drawSurf = emitSurfaces[i]; + const ovrSurfaceDef& surfaceDef = *drawSurf.surface; + const float sort = + BoundsSortCullKey(surfaceDef.geo.localBounds, vpMatrix * drawSurf.modelMatrix); + if (sort == 0) { + if (LogRenderSurfaces) { + ALOG("Culled %s", surfaceDef.surfaceName.c_str()); + } + continue; + } + + if (numSurfaces == MAX_DRAW_SURFACES) { + break; + } + + bsort[numSurfaces].key = sort; + bsort[numSurfaces].modelMatrix = drawSurf.modelMatrix; + bsort[numSurfaces].surface = &surfaceDef; + bsort[numSurfaces].transparent = + (surfaceDef.graphicsCommand.GpuState.blendEnable != ovrGpuState::BLEND_DISABLE); + numSurfaces++; + } + + // sort by the far W and transparency + // IMPORTANT: use a stable sort so surfaces with identical bounds + // will sort consistently from frame to frame, rather than randomly + // as happens with qsort. + std::stable_sort(bsort, bsort + numSurfaces); + + // ----TODO_DRAWEYEVIEW : don't overwrite surfaces which may have already been added to the + // surfaceList. + surfaceList.resize(numSurfaces); + for (int i = 0; i < numSurfaces; i++) { + surfaceList[i].modelMatrix = bsort[i].modelMatrix; + surfaceList[i].surface = bsort[i].surface; + } +} + +} // namespace OVRFW diff --git a/Samples/SampleXrFramework/Src/Model/ModelRender.h b/Samples/SampleXrFramework/Src/Model/ModelRender.h new file mode 100755 index 0000000..37f8bff --- /dev/null +++ b/Samples/SampleXrFramework/Src/Model/ModelRender.h @@ -0,0 +1,32 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/************************************************************************************ + +Filename : ModelRender.h +Content : Optimized OpenGL rendering path +Created : August 9, 2013 +Authors : John Carmack + +************************************************************************************/ + +#pragma once + +#include "OVR_Math.h" +#include "Render/SurfaceRender.h" +#include "ModelFile.h" + +#include + +namespace OVRFW { +// The model surfaces are culled and added to the sorted surface list. +// Application specific surfaces from the emit list are also added to the sorted surface list. +// The surface list is sorted such that opaque surfaces come first, sorted front-to-back, +// and transparent surfaces come last, sorted back-to-front. +void BuildModelSurfaceList( + std::vector& surfaceList, + const std::vector& emitNodes, + const std::vector& emitSurfaces, + const OVR::Matrix4f& viewMatrix, + const OVR::Matrix4f& projectionMatrix); + +} // namespace OVRFW diff --git a/Samples/SampleXrFramework/Src/Model/ModelTrace.cpp b/Samples/SampleXrFramework/Src/Model/ModelTrace.cpp new file mode 100755 index 0000000..93e33a0 --- /dev/null +++ b/Samples/SampleXrFramework/Src/Model/ModelTrace.cpp @@ -0,0 +1,510 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/************************************************************************************ + +Filename : ModelTrace.cpp +Content : Ray tracer using a KD-Tree. +Created : May, 2014 +Authors : J.M.P. van Waveren + +*************************************************************************************/ + +#include "ModelTrace.h" + +#include +#include +#include +#include + +#include "Misc/Log.h" + +using OVR::Bounds3f; +using OVR::Matrix4f; +using OVR::Vector2f; +using OVR::Vector3f; +using OVR::Vector4f; + +namespace OVRFW { + +/* + An Efficient and Robust Ray–Box Intersection Algorithm + Amy Williams, Steve Barrus, R. Keith Morley, Peter Shirley + Journal of Graphics Tools, Issue 10, Pages 49-54, June 2005 + + Returns true if the ray intersects the bounds. + 't0' and 't1' are the distances along the ray where the intersections occurs. + 1st intersection = rayStart + t0 * rayDir + 2nd intersection = rayStart + t1 * rayDir +*/ +bool Intersect_RayBounds( + const Vector3f& rayStart, + const Vector3f& rayDir, + const Vector3f& mins, + const Vector3f& maxs, + float& t0, + float& t1) { + const float rcpDirX = (fabsf(rayDir.x) > MATH_FLOAT_SMALLEST_NON_DENORMAL) + ? (1.0f / rayDir.x) + : MATH_FLOAT_HUGE_NUMBER; + const float rcpDirY = (fabsf(rayDir.y) > MATH_FLOAT_SMALLEST_NON_DENORMAL) + ? (1.0f / rayDir.y) + : MATH_FLOAT_HUGE_NUMBER; + const float rcpDirZ = (fabsf(rayDir.z) > MATH_FLOAT_SMALLEST_NON_DENORMAL) + ? (1.0f / rayDir.z) + : MATH_FLOAT_HUGE_NUMBER; + + const float sX = (mins.x - rayStart.x) * rcpDirX; + const float sY = (mins.y - rayStart.y) * rcpDirY; + const float sZ = (mins.z - rayStart.z) * rcpDirZ; + + const float tX = (maxs.x - rayStart.x) * rcpDirX; + const float tY = (maxs.y - rayStart.y) * rcpDirY; + const float tZ = (maxs.z - rayStart.z) * rcpDirZ; + + const float minX = std::min(sX, tX); + const float minY = std::min(sY, tY); + const float minZ = std::min(sZ, tZ); + + const float maxX = std::max(sX, tX); + const float maxY = std::max(sY, tY); + const float maxZ = std::max(sZ, tZ); + + t0 = std::max(minX, std::max(minY, minZ)); + t1 = std::min(maxX, std::min(maxY, maxZ)); + + return (t0 <= t1); +} + +/* + Fast, Minimum Storage Ray/Triangle Intersection + Tomas Möller, Ben Trumbore + Journal of Graphics Tools, 1997 + + Triangles are back-face culled. + Returns true if the ray intersects the triangle. + 't0' is the distance along the ray where the intersection occurs. + intersection = rayStart + t0 * rayDir; + 'u' and 'v' are the barycentric coordinates. + intersection = ( 1 - u - v ) * v0 + u * v1 + v * v2 +*/ +bool Intersect_RayTriangle( + const Vector3f& rayStart, + const Vector3f& rayDir, + const Vector3f& v0, + const Vector3f& v1, + const Vector3f& v2, + float& t0, + float& u, + float& v) { + assert(rayDir.IsNormalized()); + + const Vector3f edge1 = v1 - v0; + const Vector3f edge2 = v2 - v0; + + const Vector3f tv = rayStart - v0; + const Vector3f pv = rayDir.Cross(edge2); + const Vector3f qv = tv.Cross(edge1); + const float det = edge1.Dot(pv); + + // If the determinant is negative then the triangle is backfacing. + if (det <= 0.0f) { + return false; + } + + // This code has been modified to only perform a floating-point + // division if the ray actually hits the triangle. If back facing + // triangles are not culled then the sign of 's' and 't' need to + // be flipped. This can be accomplished by multiplying the values + // with the determinant instead of the reciprocal determinant. + + const float s = tv.Dot(pv); + const float t = rayDir.Dot(qv); + + if (s >= 0.0f && s <= det) { + if (t >= 0.0f && s + t <= det) { + // If the determinant is almost zero then the ray lies in the triangle plane. + // This comparison is done last because it is usually rare for + // the ray to lay in the triangle plane. + if (fabsf(det) > MATH_FLOAT_SMALLEST_NON_DENORMAL) { + const float rcpDet = 1.0f / det; + t0 = edge2.Dot(qv) * rcpDet; + u = s * rcpDet; + v = t * rcpDet; + return true; + } + } + } + + return false; +} + +/* + + Stackless KD-Tree Traversal for High Performance GPU Ray Tracing + Stefan Popov, Johannes Günther, Hans-Peter Seidel, Philipp Slusallek + Eurographics, Volume 26, Number 3, 2007 + +*/ + +const int RT_KDTREE_MAX_ITERATIONS = 128; + +bool ModelTrace::Validate(const bool fullVerify) const { + bool invalid = false; + + invalid |= header.numVertices != static_cast(vertices.size()); + invalid |= header.numUvs != static_cast(uvs.size()); + invalid |= header.numIndices != static_cast(indices.size()); + invalid |= header.numNodes != static_cast(nodes.size()); + invalid |= header.numLeafs != static_cast(leafs.size()); + invalid |= header.numOverflow != static_cast(overflow.size()); + if (!invalid) { + ALOG("ModelTrace::Verify - invalid header"); + return false; + } + // the number of uvs must be either equal to the number of vertices, or 0 + if (static_cast(uvs.size()) != 0 && + static_cast(uvs.size()) != static_cast(vertices.size())) { + ALOG("ModelTrace::Verify - model must have no uvs, or the same number of uvs as vertices"); + return false; + } + if (fullVerify) { + // verify that all child indices are valid in each node + for (int i = 0; i < static_cast(nodes.size()); ++i) { + const kdtree_node_t& node = nodes[i]; + const bool isLeaf = (node.data & 1) != 0; + if (isLeaf) { + // leaves have no children to verify + continue; + } + int const leftChildIndex = node.data >> 3; + int const rightChildIndex = leftChildIndex + 1; + if (leftChildIndex < 0 || leftChildIndex >= static_cast(nodes.size())) { + ALOG( + "ModelTrace::Verify - leftChildIndex of %i for node %i is out of range, max %i", + leftChildIndex, + i, + static_cast(nodes.size()) - 1); + return false; + } + if (rightChildIndex < 0 || rightChildIndex >= static_cast(nodes.size())) { + ALOG( + "ModelTrace::Verify - rightChildIndex of %i for node %i is out of range, max %i", + leftChildIndex, + i, + static_cast(nodes.size()) - 1); + return false; + } + } + const int numTris = static_cast(indices.size()) / 3; + if (numTris * 3 != static_cast(indices.size()) * 3) { + ALOG("ModelTrace::Verify - Orphaned indices"); + return false; + } + // verify leaves don't point to any out-of-range triangles + for (int i = 0; i < static_cast(leafs.size()); ++i) { + const kdtree_leaf_t& leaf = leafs[i]; + for (int j = 0; j < RT_KDTREE_MAX_LEAF_TRIANGLES; ++j) { + // if the triangle index is < 1 this is either the end of + // the triangle list, or an index into the overflow list + const int triIndex = leaf.triangles[j]; + if (triIndex < 0) { + if (triIndex == -1) { + break; // no more triangles + } + // this is an index into the overflow -- verify it is in range + const int overflowIndex = triIndex & 0x7FFFFFFF; + if (overflowIndex < 0 || overflowIndex >= static_cast(overflow.size())) { + ALOG( + "ModelTrace::Verify - Leaf %i has an out of range overflow index %i at index %i, max %i", + i, + overflowIndex, + j, + numTris - 1); + return false; + } + // we don't verify the overflow indices are in range of the triangle list here + // here because we'll do that explicity below to make sure we hit even overflow + // indices that aren't referenced by a leaf + } else if (triIndex >= numTris) { + ALOG( + "ModelTrace::Verify - Leaf %i has an out of range triangle of index %i at index %i, max %i", + i, + triIndex, + j, + numTris - 1); + return false; + } + } + } + // verify overflow list doesn't point to any out-of-range triangles + for (int i = 0; i < static_cast(overflow.size()); ++i) { + if (overflow[i] < 0 || overflow[i] >= numTris) { + ALOG( + "ModelTrace::Verify - overflow index %i value %i is out of range, max %i", + i, + overflow[i], + numTris - 1); + return false; + } + } + // verify indices do not point to any out-of-range vertices + for (int i = 0; i < static_cast(indices.size()); ++i) { + if (indices[i] < 0 || indices[i] >= static_cast(vertices.size())) { + ALOG( + "Index %i value %i is out of range, max %i", + i, + indices[i], + static_cast(vertices.size()) - 1); + return false; + } + } + } + return true; +} + +traceResult_t ModelTrace::Trace(const Vector3f& start, const Vector3f& end) const { + // in debug, at least warn programmers if they're loading a model + // that fails simple validation. + assert(Validate(false)); + + traceResult_t result; + result.triangleIndex = -1; + result.fraction = 1.0f; + result.uv = Vector2f(0.0f); + result.normal = Vector3f(0.0f); + + const Vector3f rayDelta = end - start; + const float rayLengthSqr = rayDelta.LengthSq(); + const float rayLengthRcp = OVR::RcpSqrt(rayLengthSqr); + const float rayLength = rayLengthSqr * rayLengthRcp; + const Vector3f rayDir = rayDelta * rayLengthRcp; + + const float rcpRayDirX = (fabsf(rayDir.x) > MATH_FLOAT_SMALLEST_NON_DENORMAL) + ? (1.0f / rayDir.x) + : MATH_FLOAT_HUGE_NUMBER; + const float rcpRayDirY = (fabsf(rayDir.y) > MATH_FLOAT_SMALLEST_NON_DENORMAL) + ? (1.0f / rayDir.y) + : MATH_FLOAT_HUGE_NUMBER; + const float rcpRayDirZ = (fabsf(rayDir.z) > MATH_FLOAT_SMALLEST_NON_DENORMAL) + ? (1.0f / rayDir.z) + : MATH_FLOAT_HUGE_NUMBER; + + const float sX = (header.bounds.GetMins()[0] - start.x) * rcpRayDirX; + const float sY = (header.bounds.GetMins()[1] - start.y) * rcpRayDirY; + const float sZ = (header.bounds.GetMins()[2] - start.z) * rcpRayDirZ; + + const float tX = (header.bounds.GetMaxs()[0] - start.x) * rcpRayDirX; + const float tY = (header.bounds.GetMaxs()[1] - start.y) * rcpRayDirY; + const float tZ = (header.bounds.GetMaxs()[2] - start.z) * rcpRayDirZ; + + const float minX = std::min(sX, tX); + const float minY = std::min(sY, tY); + const float minZ = std::min(sZ, tZ); + + const float maxX = std::max(sX, tX); + const float maxY = std::max(sY, tY); + const float maxZ = std::max(sZ, tZ); + + const float t0 = std::max(minX, std::max(minY, minZ)); + const float t1 = std::min(maxX, std::min(maxY, maxZ)); + + if (t0 >= t1) { + return result; + } + + float entryDistance = std::max(t0, 0.0f); + float bestDistance = std::min(t1 + 0.00001f, rayLength); + Vector2f uv; + + const kdtree_node_t* currentNode = &nodes[0]; + + for (int i = 0; i < RT_KDTREE_MAX_ITERATIONS; i++) { + const Vector3f rayEntryPoint = start + rayDir * entryDistance; + + // Step down the tree until a leaf node is found. + while ((currentNode->data & 1) == 0) { + // Select the child node based on whether the entry point is left or right of the split + // plane. If the entry point is directly at the split plane then choose the side based + // on the ray direction. + const int nodePlane = ((currentNode->data >> 1) & 3); + int child; + if (rayEntryPoint[nodePlane] - currentNode->dist < 0.00001f) + child = 0; + else if (rayEntryPoint[nodePlane] - currentNode->dist > 0.00001f) + child = 1; + else + child = (rayDelta[nodePlane] > 0.0f); + currentNode = &nodes[(currentNode->data >> 3) + child]; + } + + // Check for an intersection with a triangle in this leaf. + const kdtree_leaf_t* currentLeaf = &leafs[(currentNode->data >> 3)]; + const int* leafTriangles = currentLeaf->triangles; + int leafTriangleCount = RT_KDTREE_MAX_LEAF_TRIANGLES; + for (int j = 0; j < leafTriangleCount; j++) { + int currentTriangle = leafTriangles[j]; + if (currentTriangle < 0) { + if (currentTriangle == -1) { + break; + } + + const int offset = (currentTriangle & 0x7FFFFFFF); + leafTriangles = &overflow[offset]; + leafTriangleCount = header.numOverflow - offset; + j = 0; + currentTriangle = leafTriangles[0]; + } + + float distance; + float u; + float v; + + if (Intersect_RayTriangle( + start, + rayDir, + vertices[indices[currentTriangle * 3 + 0]], + vertices[indices[currentTriangle * 3 + 1]], + vertices[indices[currentTriangle * 3 + 2]], + distance, + u, + v)) { + if (distance >= 0.0f && distance < bestDistance) { + bestDistance = distance; + + result.triangleIndex = currentTriangle * 3; + uv.x = u; + uv.y = v; + } + } + } + + // Calculate the distance along the ray where the next leaf is entered. + const float sXX = (currentLeaf->bounds.GetMins()[0] - start.x) * rcpRayDirX; + const float sYY = (currentLeaf->bounds.GetMins()[1] - start.y) * rcpRayDirY; + const float sZZ = (currentLeaf->bounds.GetMins()[2] - start.z) * rcpRayDirZ; + + const float tXX = (currentLeaf->bounds.GetMaxs()[0] - start.x) * rcpRayDirX; + const float tYY = (currentLeaf->bounds.GetMaxs()[1] - start.y) * rcpRayDirY; + const float tZZ = (currentLeaf->bounds.GetMaxs()[2] - start.z) * rcpRayDirZ; + + const float maxXX = std::max(sXX, tXX); + const float maxYY = std::max(sYY, tYY); + const float maxZZ = std::max(sZZ, tZZ); + + entryDistance = std::min(maxXX, std::min(maxYY, maxZZ)); + if (entryDistance >= bestDistance) { + break; + } + + // Calculate the exit plane. + const int exitX = (0 << 1) | ((sXX < tXX) ? 1 : 0); + const int exitY = (1 << 1) | ((sYY < tYY) ? 1 : 0); + const int exitZ = (2 << 1) | ((sZZ < tZZ) ? 1 : 0); + const int exitPlane = + (maxXX < maxYY) ? (maxXX < maxZZ ? exitX : exitZ) : (maxYY < maxZZ ? exitY : exitZ); + + // Use a rope to enter the adjacent leaf. + const int exitNodeIndex = currentLeaf->ropes[exitPlane]; + if (exitNodeIndex == -1) { + break; + } + + currentNode = &nodes[exitNodeIndex]; + } + + if (result.triangleIndex != -1) { + result.fraction = bestDistance * rayLengthRcp; + // return default uvs if the model has no uvs + if (static_cast(uvs.size()) == 0) { + result.uv = Vector2f(0.0f, 0.0f); + } else { + result.uv = uvs[indices[result.triangleIndex + 0]] * (1.0f - uv.x - uv.y) + + uvs[indices[result.triangleIndex + 1]] * uv.x + + uvs[indices[result.triangleIndex + 2]] * uv.y; + } + const Vector3f d1 = vertices[indices[result.triangleIndex + 1]] - + vertices[indices[result.triangleIndex + 0]]; + const Vector3f d2 = vertices[indices[result.triangleIndex + 2]] - + vertices[indices[result.triangleIndex + 0]]; + result.normal = d1.Cross(d2).Normalized(); + } + + return result; +} + +traceResult_t ModelTrace::Trace_Exhaustive(const Vector3f& start, const Vector3f& end) const { + // in debug, at least warn programmers if they're loading a model + // that fails simple validation. + assert(Validate(false)); + + traceResult_t result; + result.triangleIndex = -1; + result.fraction = 1.0f; + result.uv = Vector2f(0.0f); + result.normal = Vector3f(0.0f); + + const Vector3f rayDelta = end - start; + const float rayLengthSqr = rayDelta.LengthSq(); + const float rayLengthRcp = OVR::RcpSqrt(rayLengthSqr); + const float rayLength = rayLengthSqr * rayLengthRcp; + const Vector3f rayStart = start; + const Vector3f rayDir = rayDelta * rayLengthRcp; + + float bestDistance = rayLength; + Vector2f uv; + + for (int i = 0; i < header.numIndices; i += 3) { + float distance; + float u; + float v; + + if (Intersect_RayTriangle( + rayStart, + rayDir, + vertices[indices[i + 0]], + vertices[indices[i + 1]], + vertices[indices[i + 2]], + distance, + u, + v)) { + if (distance >= 0.0f && distance < bestDistance) { + bestDistance = distance; + + result.triangleIndex = i; + uv.x = u; + uv.y = v; + } + } + } + + if (result.triangleIndex != -1) { + result.fraction = bestDistance * rayLengthRcp; + // return default uvs if the model has no uvs + if (static_cast(uvs.size()) == 0) { + result.uv = Vector2f(0.0f, 0.0f); + } else { + result.uv = uvs[indices[result.triangleIndex + 0]] * (1.0f - uv.x - uv.y) + + uvs[indices[result.triangleIndex + 1]] * uv.x + + uvs[indices[result.triangleIndex + 2]] * uv.y; + } + const Vector3f d1 = vertices[indices[result.triangleIndex + 1]] - + vertices[indices[result.triangleIndex + 0]]; + const Vector3f d2 = vertices[indices[result.triangleIndex + 2]] - + vertices[indices[result.triangleIndex + 0]]; + result.normal = d1.Cross(d2).Normalized(); + } + + return result; +} + +void ModelTrace::PrintStatsToLog() const { + ALOG("ModelTrace Stats:"); + ALOG(" Vertices: %i", static_cast(vertices.size())); + ALOG(" UVs : %i", static_cast(uvs.size())); + ALOG(" Indices : %i", static_cast(indices.size())); + ALOG(" Nodes : %i", static_cast(nodes.size())); + ALOG(" Leaves : %i", static_cast(leafs.size())); + ALOG(" Overflow: %i", static_cast(overflow.size())); +} + +} // namespace OVRFW diff --git a/Samples/SampleXrFramework/Src/Model/ModelTrace.h b/Samples/SampleXrFramework/Src/Model/ModelTrace.h new file mode 100755 index 0000000..3657b91 --- /dev/null +++ b/Samples/SampleXrFramework/Src/Model/ModelTrace.h @@ -0,0 +1,76 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/************************************************************************************ + +Filename : ModelTrace.h +Content : Ray tracer using a KD-Tree. +Created : May, 2014 +Authors : J.M.P. van Waveren + +*************************************************************************************/ + +#pragma once + +#include "OVR_Math.h" + +#include + +namespace OVRFW { + +const int RT_KDTREE_MAX_LEAF_TRIANGLES = 4; + +struct kdtree_header_t { + int numVertices; + int numUvs; + int numIndices; + int numNodes; + int numLeafs; + int numOverflow; + OVR::Bounds3f bounds; +}; + +struct kdtree_node_t { + // bits [ 0,0] = leaf flag + // bits [ 2,1] = split plane (0 = x, 1 = y, 2 = z, 3 = invalid) + // bits [31,3] = index of left child (+1 = right child index), or index of leaf data + unsigned int data; + float dist; +}; + +struct kdtree_leaf_t { + int triangles[RT_KDTREE_MAX_LEAF_TRIANGLES]; + int ropes[6]; + OVR::Bounds3f bounds; +}; + +struct traceResult_t { + int triangleIndex; + float fraction; + OVR::Vector2f uv; + OVR::Vector3f normal; +}; + +class ModelTrace { + public: + ModelTrace() {} + ~ModelTrace() {} + + bool Validate(const bool fullVerify) const; + + traceResult_t Trace(const OVR::Vector3f& start, const OVR::Vector3f& end) const; + traceResult_t Trace_Exhaustive(const OVR::Vector3f& start, const OVR::Vector3f& end) const; + + void PrintStatsToLog() const; + + public: + kdtree_header_t header; + std::vector vertices; + std::vector uvs; + std::vector indices; + std::vector nodes; + std::vector leafs; + std::vector overflow; // this is a flat array that stores extra triangle indices for leaves + // with > RT_KDTREE_MAX_LEAF_TRIANGLES +}; + +} // namespace OVRFW diff --git a/Samples/SampleXrFramework/Src/Model/SceneView.cpp b/Samples/SampleXrFramework/Src/Model/SceneView.cpp new file mode 100755 index 0000000..636911f --- /dev/null +++ b/Samples/SampleXrFramework/Src/Model/SceneView.cpp @@ -0,0 +1,1080 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/************************************************************************************ + +Filename : SceneView.cpp +Content : Basic viewing and movement in a scene. +Created : December 19, 2013 +Authors : John Carmack + +*************************************************************************************/ + +#include "SceneView.h" +#include "ModelAnimationUtils.h" +#include "ModelRender.h" + +#include + +#include "Misc/Log.h" + +using OVR::Axis_X; +using OVR::Axis_Y; +using OVR::Axis_Z; +using OVR::Bounds3f; +using OVR::Matrix4f; +using OVR::Quatf; +using OVR::Vector2f; +using OVR::Vector3d; +using OVR::Vector3f; +using OVR::Vector4f; + +namespace OVRFW { + +/* + Vertex Color +*/ + +const char* VertexColorVertexShaderSrc = R"glsl( +attribute highp vec4 Position; +attribute lowp vec4 VertexColor; +varying lowp vec4 oColor; +void main() +{ + gl_Position = TransformVertex( Position ); + oColor = VertexColor; +} +)glsl"; + +const char* VertexColorSkinned1VertexShaderSrc = R"glsl( +uniform JointMatrices +{ + highp mat4 Joints[64]; +} jb; +attribute highp vec4 Position; +attribute lowp vec4 VertexColor; +attribute highp vec4 JointWeights; +attribute highp vec4 JointIndices; +varying lowp vec4 oColor; +void main() +{ + highp vec4 localPos = jb.Joints[int(JointIndices.x)] * Position; + gl_Position = TransformVertex( localPos ); + oColor = VertexColor; +} +)glsl"; + +const char* VertexColorFragmentShaderSrc = R"glsl( +varying lowp vec4 oColor; +void main() +{ + gl_FragColor = oColor; +} +)glsl"; + +/* + Single Texture +*/ + +const char* SingleTextureVertexShaderSrc = R"glsl( +attribute highp vec4 Position; +attribute highp vec2 TexCoord; +varying highp vec2 oTexCoord; +void main() +{ + gl_Position = TransformVertex( Position ); + oTexCoord = TexCoord; +} +)glsl"; + +const char* SingleTextureSkinned1VertexShaderSrc = R"glsl( +uniform JointMatrices +{ + highp mat4 Joints[64]; +} jb; +attribute highp vec4 Position; +attribute highp vec2 TexCoord; +attribute highp vec4 JointWeights; +attribute highp vec4 JointIndices; +varying highp vec2 oTexCoord; +void main() +{ + highp vec4 localPos = jb.Joints[int(JointIndices.x)] * Position; + gl_Position = TransformVertex( localPos ); + oTexCoord = TexCoord; +} +)glsl"; + +const char* SingleTextureFragmentShaderSrc = R"glsl( +uniform sampler2D Texture0; +varying highp vec2 oTexCoord; +void main() +{ + gl_FragColor = texture2D( Texture0, oTexCoord ); +} +)glsl"; + +/* + Light Mapped +*/ + +const char* LightMappedVertexShaderSrc = R"glsl( +attribute highp vec4 Position; +attribute highp vec2 TexCoord; +attribute highp vec2 TexCoord1; +varying highp vec2 oTexCoord; +varying highp vec2 oTexCoord1; +void main() +{ + gl_Position = TransformVertex( Position ); + oTexCoord = TexCoord; + oTexCoord1 = TexCoord1; +} +)glsl"; + +const char* LightMappedSkinned1VertexShaderSrc = R"glsl( +uniform JointMatrices +{ + highp mat4 Joints[64]; +} jb; +attribute highp vec4 Position; +attribute highp vec2 TexCoord; +attribute highp vec2 TexCoord1; +attribute highp vec4 JointWeights; +attribute highp vec4 JointIndices; +varying highp vec2 oTexCoord; +varying highp vec2 oTexCoord1; +void main() +{ + highp vec4 localPos = jb.Joints[int(JointIndices.x)] * Position; + gl_Position = TransformVertex( localPos ); + oTexCoord = TexCoord; + oTexCoord1 = TexCoord1; +} +)glsl"; + +const char* LightMappedFragmentShaderSrc = R"glsl( +uniform sampler2D Texture0; +uniform sampler2D Texture1; +varying highp vec2 oTexCoord; +varying highp vec2 oTexCoord1; +void main() +{ + lowp vec4 diffuse = texture2D( Texture0, oTexCoord ); + lowp vec4 emissive = texture2D( Texture1, oTexCoord1 ); + gl_FragColor.xyz = diffuse.xyz * emissive.xyz * 1.5; + gl_FragColor.w = diffuse.w; +} +)glsl"; + +/* + Reflection Mapped +*/ + +const char* ReflectionMappedVertexShaderSrc = R"glsl( +uniform highp mat4 Modelm; +attribute highp vec4 Position; +attribute highp vec3 Normal; +attribute highp vec3 Tangent; +attribute highp vec3 Binormal; +attribute highp vec2 TexCoord; +attribute highp vec2 TexCoord1; +varying highp vec3 oEye; +varying highp vec3 oNormal; +varying highp vec3 oTangent; +varying highp vec3 oBinormal; +varying highp vec2 oTexCoord; +varying highp vec2 oTexCoord1; +vec3 multiply( mat4 m, vec3 v ) +{ + return vec3( + m[0].x * v.x + m[1].x * v.y + m[2].x * v.z, + m[0].y * v.x + m[1].y * v.y + m[2].y * v.z, + m[0].z * v.x + m[1].z * v.y + m[2].z * v.z ); +} +vec3 transposeMultiply( mat4 m, vec3 v ) +{ + return vec3( + m[0].x * v.x + m[0].y * v.y + m[0].z * v.z, + m[1].x * v.x + m[1].y * v.y + m[1].z * v.z, + m[2].x * v.x + m[2].y * v.y + m[2].z * v.z ); +} +void main() +{ + gl_Position = TransformVertex( Position ); + vec3 eye = transposeMultiply( sm.ViewMatrix[VIEW_ID], -vec3( sm.ViewMatrix[VIEW_ID][3] ) ); + oEye = eye - vec3( Modelm * Position ); + oNormal = multiply( Modelm, Normal ); + oTangent = multiply( Modelm, Tangent ); + oBinormal = multiply( Modelm, Binormal ); + oTexCoord = TexCoord; + oTexCoord1 = TexCoord1; +} +)glsl"; + +const char* ReflectionMappedSkinned1VertexShaderSrc = R"glsl( +uniform highp mat4 Modelm; +uniform JointMatrices +{ + highp mat4 Joints[64]; +} jb; +attribute highp vec4 Position; +attribute highp vec3 Normal; +attribute highp vec3 Tangent; +attribute highp vec3 Binormal; +attribute highp vec2 TexCoord; +attribute highp vec2 TexCoord1; +attribute highp vec4 JointWeights; +attribute highp vec4 JointIndices; +varying highp vec3 oEye; +varying highp vec3 oNormal; +varying highp vec3 oTangent; +varying highp vec3 oBinormal; +varying highp vec2 oTexCoord; +varying highp vec2 oTexCoord1; +vec3 multiply( mat4 m, vec3 v ) +{ + return vec3( + m[0].x * v.x + m[1].x * v.y + m[2].x * v.z, + m[0].y * v.x + m[1].y * v.y + m[2].y * v.z, + m[0].z * v.x + m[1].z * v.y + m[2].z * v.z ); +} +vec3 transposeMultiply( mat4 m, vec3 v ) +{ + return vec3( + m[0].x * v.x + m[0].y * v.y + m[0].z * v.z, + m[1].x * v.x + m[1].y * v.y + m[1].z * v.z, + m[2].x * v.x + m[2].y * v.y + m[2].z * v.z ); +} +void main() +{ + highp vec4 localPos = jb.Joints[int(JointIndices.x)] * Position; + gl_Position = TransformVertex( localPos ); + vec3 eye = transposeMultiply( sm.ViewMatrix[VIEW_ID], -vec3( sm.ViewMatrix[VIEW_ID][3] ) ); + oEye = eye - vec3( Modelm * ( jb.Joints[int(JointIndices.x)] * Position ) ); + oNormal = multiply( Modelm, multiply( jb.Joints[int(JointIndices.x)], Normal ) ); + oTangent = multiply( Modelm, multiply( jb.Joints[int(JointIndices.x)], Tangent ) ); + oBinormal = multiply( Modelm, multiply( jb.Joints[int(JointIndices.x)], Binormal ) ); + oTexCoord = TexCoord; + oTexCoord1 = TexCoord1; +} +)glsl"; + +const char* ReflectionMappedFragmentShaderSrc = R"glsl( +uniform sampler2D Texture0; +uniform sampler2D Texture1; +uniform sampler2D Texture2; +uniform sampler2D Texture3; +uniform samplerCube Texture4; +varying highp vec3 oEye; +varying highp vec3 oNormal; +varying highp vec3 oTangent; +varying highp vec3 oBinormal; +varying highp vec2 oTexCoord; +varying highp vec2 oTexCoord1; +void main() +{ + mediump vec3 normal = texture2D( Texture2, oTexCoord ).xyz * 2.0 - 1.0; + mediump vec3 surfaceNormal = normal.x * oTangent + normal.y * oBinormal + normal.z * oNormal; + mediump vec3 eyeDir = normalize( oEye.xyz ); + mediump vec3 reflectionDir = dot( eyeDir, surfaceNormal ) * 2.0 * surfaceNormal - eyeDir; + lowp vec3 specular = texture2D( Texture3, oTexCoord ).xyz * textureCube( Texture4, reflectionDir ).xyz; + lowp vec4 diffuse = texture2D( Texture0, oTexCoord ); + lowp vec4 emissive = texture2D( Texture1, oTexCoord1 ); + gl_FragColor.xyz = diffuse.xyz * emissive.xyz * 1.5 + specular; + gl_FragColor.w = diffuse.w; +} +)glsl"; + +/* +PBR +Currently this is flat shaded with emmissive so not really PBR +*/ + +const char* SimplePBRVertexShaderSrc = R"glsl( +attribute highp vec4 Position; +attribute highp vec2 TexCoord; +varying highp vec2 oTexCoord; +void main() +{ + gl_Position = TransformVertex( Position ); + oTexCoord = TexCoord; +} +)glsl"; + +const char* SimplePBRSkinned1VertexShaderSrc = R"glsl( +uniform JointMatrices +{ + highp mat4 Joints[64]; +} jb; +attribute highp vec4 Position; +attribute highp vec2 TexCoord; +attribute highp vec4 JointWeights; +attribute highp vec4 JointIndices; +varying highp vec2 oTexCoord; +void main() +{ + highp vec4 localPos1 = jb.Joints[int(JointIndices.x)] * Position; + highp vec4 localPos2 = jb.Joints[int(JointIndices.y)] * Position; + highp vec4 localPos3 = jb.Joints[int(JointIndices.z)] * Position; + highp vec4 localPos4 = jb.Joints[int(JointIndices.w)] * Position; + highp vec4 localPos = localPos1 * JointWeights.x + localPos2 * JointWeights.y + localPos3 * JointWeights.z + localPos4 * JointWeights.w; + gl_Position = TransformVertex( localPos ); + oTexCoord = TexCoord; +} +)glsl"; + +const char* SimplePBRFragmentShaderSrc = R"glsl( +uniform lowp vec4 BaseColorFactor; +void main() +{ + gl_FragColor = BaseColorFactor; +} +)glsl"; + +const char* BaseColorPBRFragmentShaderSrc = R"glsl( +uniform sampler2D BaseColorTexture; +uniform lowp vec4 BaseColorFactor; +varying highp vec2 oTexCoord; +void main() +{ + lowp vec4 BaseColor = texture2D( BaseColorTexture, oTexCoord ); + gl_FragColor.r = BaseColor.r * BaseColorFactor.r; + gl_FragColor.g = BaseColor.g * BaseColorFactor.g; + gl_FragColor.b = BaseColor.b * BaseColorFactor.b; + gl_FragColor.w = BaseColor.w * BaseColorFactor.w; +} +)glsl"; + +const char* BaseColorEmissivePBRFragmentShaderSrc = R"glsl( +uniform sampler2D Texture0; +uniform sampler2D Texture1; +uniform lowp vec4 BaseColorFactor; +uniform lowp vec4 EmissiveFactor; +varying highp vec2 oTexCoord; +void main() +{ + lowp vec4 BaseColor = texture2D( Texture0, oTexCoord ); + BaseColor.r = BaseColor.r * BaseColorFactor.r; + BaseColor.g = BaseColor.g * BaseColorFactor.g; + BaseColor.b = BaseColor.b * BaseColorFactor.b; + BaseColor.w = BaseColor.w * BaseColorFactor.w; + lowp vec4 EmissiveColor = texture2D( Texture1, oTexCoord ); + EmissiveColor.r = EmissiveColor.r * EmissiveFactor.r; + EmissiveColor.g = EmissiveColor.g * EmissiveFactor.g; + EmissiveColor.b = EmissiveColor.b * EmissiveFactor.b; + EmissiveColor.w = EmissiveColor.w * EmissiveFactor.w; + gl_FragColor = BaseColor + EmissiveColor; +} +)glsl"; + +void ModelInScene::SetModelFile(const ModelFile* mf) { + Definition = mf; + if (mf != NULL) { + State.GenerateStateFromModelFile(mf); + } +}; + +void ModelInScene::AnimateJoints(const double timeInSeconds) { + // new animation method. + { + if (State.animationTimelineStates.size() > 0) { + State.CalculateAnimationFrameAndFraction( + MODEL_ANIMATION_TIME_TYPE_LOOP_FORWARD, (float)timeInSeconds); + + for (int i = 0; i < static_cast(State.mf->Animations.size()); i++) { + ApplyAnimation(State, i); + } + + for (int i = 0; i < static_cast(State.nodeStates.size()); i++) { + State.nodeStates[i].RecalculateMatrix(); + } + } + } +} + +//------------------------------------------------------------------------------------- + +OvrSceneView::OvrSceneView() + : FreeWorldModelOnChange(false), + LoadedPrograms(false), + Paused(false), + SuppressModelsWithClientId(-1), + // FIXME: ideally EyeHeight and IPD properties would default initialize to 0.0f, but there are + // a handful of menus which cache these values before a frame has had a chance to run. + EyeHeight(1.6750f), // average eye height above the ground when standing + InterPupillaryDistance(0.0640f), // average interpupillary distance + Znear(0.1f), + StickYaw(0.0f), + StickPitch(0.0f), + SceneYaw(0.0f), + YawVelocity(0.0f), + MoveSpeed(3.0f), + FreeMove(false), + FootPos(0.0f), + EyeYaw(0.0f), + EyePitch(0.0f), + EyeRoll(0.0f), + YawMod(-1.0f) { + CenterEyeTransform = Matrix4f::Identity(); + CenterEyeViewMatrix = Matrix4f::Identity(); +} + +ModelGlPrograms OvrSceneView::GetDefaultGLPrograms() { + ModelGlPrograms programs; + + if (!LoadedPrograms) { + ProgVertexColor = OVRFW::GlProgram::Build( + VertexColorVertexShaderSrc, VertexColorFragmentShaderSrc, nullptr, 0); + + { + OVRFW::ovrProgramParm uniformParms[] = { + /// Vertex + /// Fragment + {"Texture0", OVRFW::ovrProgramParmType::TEXTURE_SAMPLED}, + }; + const int uniformCount = sizeof(uniformParms) / sizeof(OVRFW::ovrProgramParm); + ProgSingleTexture = OVRFW::GlProgram::Build( + SingleTextureVertexShaderSrc, + SingleTextureFragmentShaderSrc, + uniformParms, + uniformCount); + } + + { + OVRFW::ovrProgramParm uniformParms[] = { + /// Vertex + /// Fragment + {"Texture0", OVRFW::ovrProgramParmType::TEXTURE_SAMPLED}, + {"Texture1", OVRFW::ovrProgramParmType::TEXTURE_SAMPLED}, + }; + const int uniformCount = sizeof(uniformParms) / sizeof(OVRFW::ovrProgramParm); + ProgLightMapped = OVRFW::GlProgram::Build( + LightMappedVertexShaderSrc, + LightMappedFragmentShaderSrc, + uniformParms, + uniformCount); + } + + { + OVRFW::ovrProgramParm uniformParms[] = { + /// Vertex + {"Modelm", OVRFW::ovrProgramParmType::FLOAT_MATRIX4}, + /// Fragment + {"Texture0", OVRFW::ovrProgramParmType::TEXTURE_SAMPLED}, + {"Texture1", OVRFW::ovrProgramParmType::TEXTURE_SAMPLED}, + {"Texture2", OVRFW::ovrProgramParmType::TEXTURE_SAMPLED}, + {"Texture3", OVRFW::ovrProgramParmType::TEXTURE_SAMPLED}, + {"Texture4", OVRFW::ovrProgramParmType::TEXTURE_SAMPLED}, + }; + const int uniformCount = sizeof(uniformParms) / sizeof(OVRFW::ovrProgramParm); + ProgReflectionMapped = OVRFW::GlProgram::Build( + ReflectionMappedVertexShaderSrc, + ReflectionMappedFragmentShaderSrc, + uniformParms, + uniformCount); + } + + { + OVRFW::ovrProgramParm uniformParms[] = { + /// Vertex + /// Fragment + {"BaseColorFactor", OVRFW::ovrProgramParmType::FLOAT_VECTOR4}, + }; + const int uniformCount = sizeof(uniformParms) / sizeof(OVRFW::ovrProgramParm); + ProgSimplePBR = OVRFW::GlProgram::Build( + SimplePBRVertexShaderSrc, SimplePBRFragmentShaderSrc, uniformParms, uniformCount); + } + + { + OVRFW::ovrProgramParm uniformParms[] = { + /// Vertex + /// Fragment + {"BaseColorFactor", OVRFW::ovrProgramParmType::FLOAT_VECTOR4}, + {"BaseColorTexture", OVRFW::ovrProgramParmType::TEXTURE_SAMPLED}, + }; + const int uniformCount = sizeof(uniformParms) / sizeof(OVRFW::ovrProgramParm); + ProgBaseColorPBR = OVRFW::GlProgram::Build( + SimplePBRVertexShaderSrc, + BaseColorPBRFragmentShaderSrc, + uniformParms, + uniformCount); + } + + { + OVRFW::ovrProgramParm uniformParms[] = { + /// Vertex + /// Fragment + {"BaseColorFactor", OVRFW::ovrProgramParmType::FLOAT_VECTOR4}, + {"EmissiveFactor", OVRFW::ovrProgramParmType::FLOAT_VECTOR4}, + {"Texture0", OVRFW::ovrProgramParmType::TEXTURE_SAMPLED}, + {"Texture1", OVRFW::ovrProgramParmType::TEXTURE_SAMPLED}, + }; + const int uniformCount = sizeof(uniformParms) / sizeof(OVRFW::ovrProgramParm); + ProgBaseColorEmissivePBR = OVRFW::GlProgram::Build( + SimplePBRVertexShaderSrc, + BaseColorEmissivePBRFragmentShaderSrc, + uniformParms, + uniformCount); + } + + { + OVRFW::ovrProgramParm uniformParms[] = { + /// Vertex + {"JointMatrices", OVRFW::ovrProgramParmType::BUFFER_UNIFORM}, + /// Fragment + }; + const int uniformCount = sizeof(uniformParms) / sizeof(OVRFW::ovrProgramParm); + ProgSkinnedVertexColor = OVRFW::GlProgram::Build( + VertexColorSkinned1VertexShaderSrc, + VertexColorFragmentShaderSrc, + uniformParms, + uniformCount); + } + + { + OVRFW::ovrProgramParm uniformParms[] = { + /// Vertex + {"JointMatrices", OVRFW::ovrProgramParmType::BUFFER_UNIFORM}, + /// Fragment + {"Texture0", OVRFW::ovrProgramParmType::TEXTURE_SAMPLED}, + }; + const int uniformCount = sizeof(uniformParms) / sizeof(OVRFW::ovrProgramParm); + ProgSkinnedSingleTexture = OVRFW::GlProgram::Build( + SingleTextureSkinned1VertexShaderSrc, + SingleTextureFragmentShaderSrc, + uniformParms, + uniformCount); + } + + { + OVRFW::ovrProgramParm uniformParms[] = { + /// Vertex + {"JointMatrices", OVRFW::ovrProgramParmType::BUFFER_UNIFORM}, + /// Fragment + {"Texture0", OVRFW::ovrProgramParmType::TEXTURE_SAMPLED}, + {"Texture1", OVRFW::ovrProgramParmType::TEXTURE_SAMPLED}, + }; + const int uniformCount = sizeof(uniformParms) / sizeof(OVRFW::ovrProgramParm); + ProgSkinnedLightMapped = OVRFW::GlProgram::Build( + LightMappedSkinned1VertexShaderSrc, + LightMappedFragmentShaderSrc, + uniformParms, + uniformCount); + } + + { + OVRFW::ovrProgramParm uniformParms[] = { + /// Vertex + {"JointMatrices", OVRFW::ovrProgramParmType::BUFFER_UNIFORM}, + {"Modelm", OVRFW::ovrProgramParmType::FLOAT_MATRIX4}, + /// Fragment + {"Texture0", OVRFW::ovrProgramParmType::TEXTURE_SAMPLED}, + {"Texture1", OVRFW::ovrProgramParmType::TEXTURE_SAMPLED}, + {"Texture2", OVRFW::ovrProgramParmType::TEXTURE_SAMPLED}, + {"Texture3", OVRFW::ovrProgramParmType::TEXTURE_SAMPLED}, + {"Texture4", OVRFW::ovrProgramParmType::TEXTURE_SAMPLED}, + }; + const int uniformCount = sizeof(uniformParms) / sizeof(OVRFW::ovrProgramParm); + ProgSkinnedReflectionMapped = OVRFW::GlProgram::Build( + ReflectionMappedSkinned1VertexShaderSrc, + ReflectionMappedFragmentShaderSrc, + uniformParms, + uniformCount); + } + + { + OVRFW::ovrProgramParm uniformParms[] = { + /// Vertex + {"JointMatrices", OVRFW::ovrProgramParmType::BUFFER_UNIFORM}, + /// Fragment + {"BaseColorFactor", OVRFW::ovrProgramParmType::FLOAT_VECTOR4}, + }; + const int uniformCount = sizeof(uniformParms) / sizeof(OVRFW::ovrProgramParm); + ProgSkinnedSimplePBR = OVRFW::GlProgram::Build( + SimplePBRSkinned1VertexShaderSrc, + SimplePBRFragmentShaderSrc, + uniformParms, + uniformCount); + } + + { + OVRFW::ovrProgramParm uniformParms[] = { + /// Vertex + {"JointMatrices", OVRFW::ovrProgramParmType::BUFFER_UNIFORM}, + /// Fragment + {"BaseColorFactor", OVRFW::ovrProgramParmType::FLOAT_VECTOR4}, + {"BaseColorTexture", OVRFW::ovrProgramParmType::TEXTURE_SAMPLED}, + }; + const int uniformCount = sizeof(uniformParms) / sizeof(OVRFW::ovrProgramParm); + ProgSkinnedBaseColorPBR = OVRFW::GlProgram::Build( + SimplePBRSkinned1VertexShaderSrc, + BaseColorPBRFragmentShaderSrc, + uniformParms, + uniformCount); + } + + { + OVRFW::ovrProgramParm uniformParms[] = { + /// Vertex + {"JointMatrices", OVRFW::ovrProgramParmType::BUFFER_UNIFORM}, + /// Fragment + {"BaseColorFactor", OVRFW::ovrProgramParmType::FLOAT_VECTOR4}, + {"EmissiveFactor", OVRFW::ovrProgramParmType::FLOAT_VECTOR4}, + {"Texture0", OVRFW::ovrProgramParmType::TEXTURE_SAMPLED}, + {"Texture1", OVRFW::ovrProgramParmType::TEXTURE_SAMPLED}, + }; + const int uniformCount = sizeof(uniformParms) / sizeof(OVRFW::ovrProgramParm); + ProgSkinnedBaseColorEmissivePBR = OVRFW::GlProgram::Build( + SimplePBRSkinned1VertexShaderSrc, + BaseColorEmissivePBRFragmentShaderSrc, + uniformParms, + uniformCount); + } + + LoadedPrograms = true; + } + + programs.ProgVertexColor = &ProgVertexColor; + programs.ProgSingleTexture = &ProgSingleTexture; + programs.ProgLightMapped = &ProgLightMapped; + programs.ProgReflectionMapped = &ProgReflectionMapped; + programs.ProgSimplePBR = &ProgSimplePBR; + programs.ProgBaseColorPBR = &ProgBaseColorPBR; + programs.ProgBaseColorEmissivePBR = &ProgBaseColorEmissivePBR; + programs.ProgSkinnedVertexColor = &ProgSkinnedVertexColor; + programs.ProgSkinnedSingleTexture = &ProgSkinnedSingleTexture; + programs.ProgSkinnedLightMapped = &ProgSkinnedLightMapped; + programs.ProgSkinnedReflectionMapped = &ProgSkinnedReflectionMapped; + programs.ProgSkinnedSimplePBR = &ProgSkinnedSimplePBR; + programs.ProgSkinnedBaseColorPBR = &ProgSkinnedBaseColorPBR; + programs.ProgSkinnedBaseColorEmissivePBR = &ProgSkinnedBaseColorEmissivePBR; + + return programs; +} + +void OvrSceneView::LoadWorldModel( + const char* sceneFileName, + const MaterialParms& materialParms, + const bool fromApk) { + ALOG("OvrSceneView::LoadScene( %s )", sceneFileName); + + if (GlPrograms.ProgSingleTexture == NULL) { + GlPrograms = GetDefaultGLPrograms(); + } + + ModelFile* model = nullptr; + // Load the scene we are going to draw + if (fromApk) { + model = LoadModelFileFromApplicationPackage(sceneFileName, GlPrograms, materialParms); + } else { + model = LoadModelFile(sceneFileName, GlPrograms, materialParms); + } + + if (model == nullptr) { + ALOGW("OvrSceneView::LoadScene( %s ) failed", sceneFileName); + return; + } + + SetWorldModel(*model); + + FreeWorldModelOnChange = true; +} + +void OvrSceneView::LoadWorldModel( + class ovrFileSys& fileSys, + const char* uri, + const MaterialParms& materialParms) { + ALOG("OvrSceneView::LoadScene( %s )", uri); + + if (GlPrograms.ProgSingleTexture == NULL) { + GlPrograms = GetDefaultGLPrograms(); + } + + ModelFile* model = nullptr; + // Load the scene we are going to draw + model = LoadModelFile(fileSys, uri, GlPrograms, materialParms); + + if (model == nullptr) { + ALOGW("OvrSceneView::LoadScene( %s ) failed", uri); + return; + } + + SetWorldModel(*model); + + FreeWorldModelOnChange = true; +} + +void OvrSceneView::LoadWorldModelFromApplicationPackage( + const char* sceneFileName, + const MaterialParms& materialParms) { + LoadWorldModel(sceneFileName, materialParms, true); +} + +void OvrSceneView::LoadWorldModel(const char* sceneFileName, const MaterialParms& materialParms) { + LoadWorldModel(sceneFileName, materialParms, false); +} + +void OvrSceneView::SetWorldModel(ModelFile& world) { + ALOG("OvrSceneView::SetWorldModel( %s )", world.FileName.c_str()); + + if (FreeWorldModelOnChange && static_cast(Models.size()) > 0) { + delete WorldModel.Definition; + FreeWorldModelOnChange = false; + } + Models.clear(); + + WorldModel.SetModelFile(&world); + AddModel(&WorldModel); + + // Set the initial player position + FootPos = Vector3f(0.0f, 0.0f, 0.0f); + StickYaw = 0.0f; + StickPitch = 0.0f; + SceneYaw = 0.0f; +} + +void OvrSceneView::ClearStickAngles() { + StickYaw = 0.0f; + StickPitch = 0.0f; +} + +ovrSurfaceDef* OvrSceneView::FindNamedSurface(const char* name) const { + return (WorldModel.Definition == NULL) ? NULL : WorldModel.Definition->FindNamedSurface(name); +} + +const ModelTexture* OvrSceneView::FindNamedTexture(const char* name) const { + return (WorldModel.Definition == NULL) ? NULL : WorldModel.Definition->FindNamedTexture(name); +} + +const ModelTag* OvrSceneView::FindNamedTag(const char* name) const { + return (WorldModel.Definition == NULL) ? NULL : WorldModel.Definition->FindNamedTag(name); +} + +Bounds3f OvrSceneView::GetBounds() const { + return (WorldModel.Definition == NULL) + ? Bounds3f(Vector3f(0.0f, 0.0f, 0.0f), Vector3f(0.0f, 0.0f, 0.0f)) + : WorldModel.Definition->GetBounds(); +} + +int OvrSceneView::AddModel(ModelInScene* model) { + const int modelsSize = static_cast(Models.size()); + + // scan for a NULL entry + for (int i = 0; i < modelsSize; ++i) { + if (Models[i] == NULL) { + Models[i] = model; + return i; + } + } + + Models.push_back(model); + + return static_cast(Models.size()) - 1; +} + +void OvrSceneView::RemoveModelIndex(int index) { + Models[index] = NULL; +} + +void OvrSceneView::GetFrameMatrices( + const float fovDegreesX, + const float fovDegreesY, + FrameMatrices& frameMatrices) const { + frameMatrices.CenterView = GetCenterEyeViewMatrix(); + frameMatrices.EyeView[0] = GetEyeViewMatrix(0); + frameMatrices.EyeView[1] = GetEyeViewMatrix(1); + frameMatrices.EyeProjection[0] = GetEyeProjectionMatrix(0, fovDegreesX, fovDegreesY); + frameMatrices.EyeProjection[1] = GetEyeProjectionMatrix(1, fovDegreesX, fovDegreesY); +} + +void OvrSceneView::GenerateFrameSurfaceList( + const FrameMatrices& frameMatrices, + std::vector& surfaceList) const { + Matrix4f symmetricEyeProjectionMatrix = frameMatrices.EyeProjection[0]; + symmetricEyeProjectionMatrix.M[0][0] = frameMatrices.EyeProjection[0].M[0][0] / + (fabsf(frameMatrices.EyeProjection[0].M[0][2]) + 1.0f); + symmetricEyeProjectionMatrix.M[0][2] = 0.0f; + + const float moveBackDistance = + 0.5f * InterPupillaryDistance * symmetricEyeProjectionMatrix.M[0][0]; + Matrix4f centerEyeCullViewMatrix = + Matrix4f::Translation(0, 0, -moveBackDistance) * frameMatrices.CenterView; + + std::vector emitNodes; + for (int i = 0; i < static_cast(Models.size()); i++) { + if (Models[i] != NULL) { + ModelState& state = Models[i]->State; + if (state.DontRenderForClientUid == SuppressModelsWithClientId) { + continue; + } + for (int j = 0; j < static_cast(state.subSceneStates.size()); j++) { + ModelSubSceneState& subSceneState = state.subSceneStates[j]; + if (subSceneState.visible) { + for (int k = 0; k < static_cast(subSceneState.nodeStates.size()); k++) { + state.nodeStates[subSceneState.nodeStates[k]].AddNodesToEmitList(emitNodes); + } + } + } + } + } + + BuildModelSurfaceList( + surfaceList, + emitNodes, + EmitSurfaces, + centerEyeCullViewMatrix, + symmetricEyeProjectionMatrix); +} + +void OvrSceneView::SetFootPos(const Vector3f& pos, bool updateCenterEye /*= true*/) { + FootPos = pos; + if (updateCenterEye) { + UpdateCenterEye(); + } +} + +Vector3f OvrSceneView::GetNeutralHeadCenter() const { + /// This works for SPACE_LOCAL_FLOOR + return Vector3f(FootPos.x, FootPos.y, FootPos.z); + + /// This works for PACE_LOCAL + /// return Vector3f( FootPos.x, FootPos.y + EyeHeight, FootPos.z ); +} + +Vector3f OvrSceneView::GetCenterEyePosition() const { + return Vector3f( + CenterEyeTransform.M[0][3], CenterEyeTransform.M[1][3], CenterEyeTransform.M[2][3]); +} + +Vector3f OvrSceneView::GetCenterEyeForward() const { + return Vector3f( + -CenterEyeViewMatrix.M[2][0], -CenterEyeViewMatrix.M[2][1], -CenterEyeViewMatrix.M[2][2]); +} + +Matrix4f OvrSceneView::GetCenterEyeTransform() const { + return CenterEyeTransform; +} + +Matrix4f OvrSceneView::GetCenterEyeViewMatrix() const { + return CenterEyeViewMatrix; +} + +Matrix4f OvrSceneView::GetEyeViewMatrix(const int eye) const { + // World space head rotation + const Matrix4f head_rotation = Matrix4f(CurrentTracking.HeadPose.Rotation); + + // Convert the eye view to world-space and remove translation + Matrix4f eye_view_rot = CurrentTracking.Eye[eye].ViewMatrix; + eye_view_rot.M[0][3] = 0; + eye_view_rot.M[1][3] = 0; + eye_view_rot.M[2][3] = 0; + const Matrix4f eye_rotation = eye_view_rot.Inverted(); + + // Compute the rotation tranform from head to eye (in case of rotated screens) + const Matrix4f head_rot_inv = head_rotation.Inverted(); + Matrix4f head_eye_rotation = head_rot_inv * eye_rotation; + + // Add the IPD translation from head to eye + const float eye_shift = ((eye == 0) ? -0.5f : 0.5f) * InterPupillaryDistance; + const Matrix4f head_eye_translation = Matrix4f::Translation(eye_shift, 0.0f, 0.0f); + + // The full transform from head to eye in world + const Matrix4f head_eye_transform = head_eye_translation * head_eye_rotation; + + // Compute the new eye-pose using the input center eye view + const Matrix4f center_eye_pose_m = CenterEyeViewMatrix.Inverted(); // convert to world + const Matrix4f eye_pose_m = center_eye_pose_m * head_eye_transform; + + // Convert to view matrix + Matrix4f eye_view = eye_pose_m.Inverted(); + return eye_view; +} + +Matrix4f OvrSceneView::GetEyeProjectionMatrix( + const int eye, + const float fovDegreesX, + const float fovDegreesY) const { + // OVR_UNUSED( eye ); + + // We may want to make per-eye projection matrices if we move away from nearly-centered lenses. + // Use an infinite projection matrix because, except for things right up against the near plane, + // it provides better precision: + // "Tightening the Precision of Perspective Rendering" + // Paul Upchurch, Mathieu Desbrun + // Journal of Graphics Tools, Volume 16, Issue 1, 2012 + // return ovrMatrix4f_CreateProjectionFov(fovDegreesX, fovDegreesY, 0.0f, 0.0f, Znear, 0.0f); + + // Use the incoming default projection since our headset are using asymmetric fov + return CurrentTracking.Eye[eye].ProjectionMatrix; +} + +Matrix4f OvrSceneView::GetEyeViewProjectionMatrix( + const int eye, + const float fovDegreesX, + const float fovDegreesY) const { + return GetEyeProjectionMatrix(eye, fovDegreesX, fovDegreesY) * GetEyeViewMatrix(eye); +} + +float OvrSceneView::GetEyeHeight() const { + return EyeHeight; +} + +// This is called by Frame(), but it must be explicitly called when FootPos is +// updated, or calls to GetCenterEyePosition() won't reflect changes until the +// following frame. +void OvrSceneView::UpdateCenterEye() { + Matrix4f input; + if (YawMod > 0.0f) { + input = Matrix4f::Translation(GetNeutralHeadCenter()) * + Matrix4f::RotationY((StickYaw - fmodf(StickYaw, YawMod)) + SceneYaw) * + Matrix4f::RotationX(StickPitch); + } else { + input = Matrix4f::Translation(GetNeutralHeadCenter()) * + Matrix4f::RotationY(StickYaw + SceneYaw) * Matrix4f::RotationX(StickPitch); + } + + const Matrix4f transform(CurrentTracking.HeadPose); + CenterEyeTransform = input * transform; + CenterEyeViewMatrix = CenterEyeTransform.Inverted(); +} + +inline void ApplyDeadZone(Vector2f& v, float deadZoneRadius) { + const float radiusSquared = deadZoneRadius * deadZoneRadius; + if (v.LengthSq() < radiusSquared) { + v.x = 0.0f; + v.y = 0.0f; + } +} + +void OvrSceneView::Frame( + const ovrApplFrameIn& vrFrame, + const long long suppressModelsWithClientId_) { + SuppressModelsWithClientId = suppressModelsWithClientId_; + CurrentTracking = vrFrame; + InterPupillaryDistance = vrFrame.IPD; + + // trim height to 1m at a minimum if the reported height from the API is too low + static const float minEyeHeight = 1.0f; + EyeHeight = std::max(vrFrame.EyeHeight, minEyeHeight); + + static double LastPredictedDisplayTime = vrFrame.PredictedDisplayTime; + + // Delta time in seconds since last frame. + float dt = float(vrFrame.PredictedDisplayTime - LastPredictedDisplayTime); + const float angleSpeed = 1.5f; + + // Update this + LastPredictedDisplayTime = vrFrame.PredictedDisplayTime; + + // Controller sticks + Vector2f LeftStick(0.0f, 0.0f); + Vector2f RightStick(0.0f, 0.0f); + + /// Flip Y on quest + if (vrFrame.LeftRemoteTracked) { + LeftStick.x += vrFrame.LeftRemoteJoystick.x; + LeftStick.y -= vrFrame.LeftRemoteJoystick.y; + } + if (vrFrame.RightRemoteTracked) { + RightStick.x += vrFrame.RightRemoteJoystick.x; + RightStick.y -= vrFrame.RightRemoteJoystick.y; + } + + /// Apply dead zone + static constexpr float deadZoneRadius = 0.5f; + ApplyDeadZone(LeftStick, deadZoneRadius); + ApplyDeadZone(RightStick, deadZoneRadius); + + // + // Player view angles + // + + // Turn based on the look stick + // Because this can be predicted ahead by async TimeWarp, we apply + // the yaw from the previous frame's controls, trading a frame of + // latency on stick controls to avoid a bounce-back. + StickYaw -= YawVelocity * dt; + if (StickYaw < 0.0f) { + StickYaw += 2.0f * MATH_FLOAT_PI; + } else if (StickYaw > 2.0f * MATH_FLOAT_PI) { + StickYaw -= 2.0f * MATH_FLOAT_PI; + } + YawVelocity = angleSpeed * (RightStick.x); + + // We extract Yaw, Pitch, Roll instead of directly using the orientation + // to allow "additional" yaw manipulation with mouse/controller and scene offsets. + const Quatf quat = vrFrame.HeadPose.Rotation; + + quat.GetEulerAngles(&EyeYaw, &EyePitch, &EyeRoll); + + // Yaw is modified by both joystick and application-set scene yaw. + // Pitch is only modified by joystick when no head tracking sensor is active. + if (YawMod > 0.0f) { + EyeYaw += (StickYaw - fmodf(StickYaw, YawMod)) + SceneYaw; + } else { + EyeYaw += StickYaw + SceneYaw; + } + EyePitch += StickPitch; + + // + // Player movement + // + + float allSticksY = LeftStick.y + RightStick.y; + allSticksY = std::max(-1.0f, std::min(1.0f, allSticksY)); + + // Allow up / down movement if there is no floor collision model or in 'free move' mode. + const bool upDown = (WorldModel.Definition == NULL || FreeMove); + Vector3f gamepadMove( + LeftStick.x, upDown ? LeftStick.y : 0.0f, upDown ? RightStick.y : allSticksY); + + // Perform player movement if there is input. + if (gamepadMove.LengthSq() > 0.0f) { + const Matrix4f yawRotate = Matrix4f::RotationY(EyeYaw); + const Vector3f orientationVector = yawRotate.Transform(gamepadMove); + + // Don't let move get too crazy fast + const float moveDistance = std::min(MoveSpeed * (float)dt, 1.0f); + if (WorldModel.Definition != NULL && !FreeMove) { + FootPos = SlideMove( + FootPos, + GetEyeHeight(), + orientationVector, + moveDistance, + WorldModel.Definition->Collisions, + WorldModel.Definition->GroundCollisions); + } else { // no scene loaded, walk without any collisions + ModelCollision collisionModel; + ModelCollision groundCollisionModel; + FootPos = SlideMove( + FootPos, + GetEyeHeight(), + orientationVector, + moveDistance, + collisionModel, + groundCollisionModel); + } + } + + // + // Center eye transform + // + UpdateCenterEye(); + + // + // Model animations + // + + if (!Paused) { + for (int i = 0; i < static_cast(Models.size()); i++) { + if (Models[i] != NULL) { + Models[i]->AnimateJoints(vrFrame.PredictedDisplayTime); + } + } + } + + // External systems can add surfaces to this list before drawing. + EmitSurfaces.resize(0); +} + +} // namespace OVRFW diff --git a/Samples/SampleXrFramework/Src/Model/SceneView.h b/Samples/SampleXrFramework/Src/Model/SceneView.h new file mode 100755 index 0000000..39b218d --- /dev/null +++ b/Samples/SampleXrFramework/Src/Model/SceneView.h @@ -0,0 +1,266 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/************************************************************************************ + +Filename : SceneView.h +Content : Basic viewing and movement in a scene. +Created : December 19, 2013 +Authors : John Carmack + +************************************************************************************/ + +#pragma once + +#include "FrameParams.h" +#include "ModelFile.h" + +namespace OVRFW { + +//----------------------------------------------------------------------------------- +// ModelInScene +// +class ModelInScene { + public: + ModelInScene() : Definition(NULL) {} + + void SetModelFile(const ModelFile* mf); + void AnimateJoints(const double timeInSeconds); + + ModelState State; // passed to rendering code + const ModelFile* Definition; // will not be freed by OvrSceneView +}; + +//----------------------------------------------------------------------------------- +// OvrSceneView +// +class OvrSceneView { + public: + OvrSceneView(); + + // The default view will be located at the origin, looking down the -Z axis, + // with +X to the right and +Y up. + // Increasing yaw looks to the left (rotation around Y axis). + + // loads the default GL shader programs + ModelGlPrograms GetDefaultGLPrograms(); + + // Blocking load of a scene from the filesystem. + // This model will be freed when a new world model is set. + void LoadWorldModel(const char* sceneFileName, const MaterialParms& materialParms); + void LoadWorldModelFromApplicationPackage( + const char* sceneFileName, + const MaterialParms& materialParms); + void + LoadWorldModel(class ovrFileSys& fileSys, const char* uri, const MaterialParms& materialParms); + + // Set an already loaded scene, which will not be freed when a new + // world model is set. + void SetWorldModel(ModelFile& model); + ModelInScene* GetWorldModel() { + return &WorldModel; + } + + // Passed on to world model + ovrSurfaceDef* FindNamedSurface(const char* name) const; + const ModelTexture* FindNamedTexture(const char* name) const; + const ModelTag* FindNamedTag(const char* name) const; + OVR::Bounds3f GetBounds() const; + + // Returns the new modelIndex + int AddModel(ModelInScene* model); + void RemoveModelIndex(int index); + + void PauseAnimations(bool pauseAnimations) { + Paused = pauseAnimations; + } + + // Allow movement inside the scene based on the joypad. + // Models that have DontRenderForClientUid == suppressModelsWithClientId will be skipped + // to prevent the client's own head model from drawing in their view. + void Frame(const ovrApplFrameIn& vrFrame, const long long suppressModelsWithClientId = -1); + + // Populate frameMatrices with the view and projection matrices for the scene. + void GetFrameMatrices( + const float fovDegreesX, + const float fovDegreesY, + FrameMatrices& frameMatrices) const; + // Generates a sorted surface list for the scene (including emit surfaces). + void GenerateFrameSurfaceList( + const FrameMatrices& matrices, + std::vector& surfaceList) const; + + // Systems that want to manage individual surfaces instead of complete models + // can add surfaces to this list during Frame(). They will be drawn for + // both eyes, then the list will be cleared. + std::vector& GetEmitList() { + return EmitSurfaces; + } + + float GetEyeYaw() const { + return EyeYaw; + } + float GetEyePitch() const { + return EyePitch; + } + float GetEyeRoll() const { + return EyeRoll; + } + + float GetYawOffset() const { + return SceneYaw; + } + void SetYawOffset(const float yaw) { + EyeYaw += (yaw - SceneYaw); + SceneYaw = yaw; + } + + float GetZnear() const { + return Znear; + } + void SetZnear(float z) { + Znear = z; + } + + void SetMoveSpeed(const float speed) { + MoveSpeed = speed; + } + float GetMoveSpeed() const { + return MoveSpeed; + } + + void SetFreeMove(const bool allowFreeMovement) { + FreeMove = allowFreeMovement; + } + + // Derived from state after last Frame() + const OVR::Vector3f& GetFootPos() const { + return FootPos; + } + void SetFootPos(const OVR::Vector3f& pos, bool updateCenterEye = true); + + OVR::Vector3f GetNeutralHeadCenter() const; // FootPos + EyeHeight + OVR::Vector3f GetCenterEyePosition() const; + OVR::Vector3f GetCenterEyeForward() const; + OVR::Matrix4f GetCenterEyeTransform() const; + OVR::Matrix4f GetCenterEyeViewMatrix() const; + + OVR::Matrix4f GetEyeViewMatrix(const int eye) const; + OVR::Matrix4f + GetEyeProjectionMatrix(const int eye, const float fovDegreesX, const float fovDegreesY) const; + OVR::Matrix4f GetEyeViewProjectionMatrix( + const int eye, + const float fovDegreesX, + const float fovDegreesY) const; + + float GetEyeHeight() const; + + // When head tracking is reset, any joystick offsets should be cleared + // so the viewer is looking ehere the application wants. + void ClearStickAngles(); + + void UpdateCenterEye(); + + // Mod stick turning by this to help with sickness. If <= 0 then ignored + void SetYawMod(const float yawMod) { + YawMod = yawMod; + } + + private: + void LoadWorldModel( + const char* sceneFileName, + const MaterialParms& materialParms, + const bool fromApk); + + // The only ModelInScene that OvrSceneView actually owns. + bool FreeWorldModelOnChange; + ModelInScene WorldModel; + + // Entries can be NULL. + // None of these will be directly freed by OvrSceneView. + std::vector Models; + + // Externally generated surfaces + std::vector EmitSurfaces; + + GlProgram ProgVertexColor; + GlProgram ProgSingleTexture; + GlProgram ProgLightMapped; + GlProgram ProgReflectionMapped; + GlProgram ProgSimplePBR; + GlProgram ProgBaseColorPBR; + GlProgram ProgBaseColorEmissivePBR; + GlProgram ProgSkinnedVertexColor; + GlProgram ProgSkinnedSingleTexture; + GlProgram ProgSkinnedLightMapped; + GlProgram ProgSkinnedReflectionMapped; + GlProgram ProgSkinnedSimplePBR; + GlProgram ProgSkinnedBaseColorPBR; + GlProgram ProgSkinnedBaseColorEmissivePBR; + bool LoadedPrograms; + + ModelGlPrograms GlPrograms; + + // Don't animate if true. + bool Paused; + + // Updated each Frame() + long long SuppressModelsWithClientId; + + float EyeHeight; + float InterPupillaryDistance; + + float Znear; + + // Angle offsets in radians for joystick movement, which is + // the moral equivalent of head tracking. Reset head tracking + // should also clear these. + float StickYaw; // added on top of the sensor reading + float StickPitch; // only applied if the tracking sensor isn't active + + // An application can turn the primary view direction, which is where + // the view will go if head tracking is reset. Should only be changed + // at discrete transition points to avoid sickness. + float SceneYaw; + + // Applied one frame later to avoid bounce-back from async time warp yaw velocity prediction. + float YawVelocity; + + // 3.0 m/s by default. Different apps may want different move speeds + float MoveSpeed; + + // Allows vertical movement when holding right shoulder button + bool FreeMove; + + // Modified by joypad movement and collision detection + OVR::Vector3f FootPos; + + // Calculated in Frame() + OVR::Matrix4f CenterEyeTransform; + OVR::Matrix4f CenterEyeViewMatrix; + 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 read from sensor. + ovrApplFrameIn CurrentTracking; + + float YawMod; +}; + +// It probably isn't worth keeping these shared here, each user +// should just duplicate them. +extern const char* VertexColorVertexShaderSrc; +extern const char* VertexColorSkinned1VertexShaderSrc; +extern const char* VertexColorFragmentShaderSrc; + +extern const char* SingleTextureVertexShaderSrc; +extern const char* SingleTextureSkinned1VertexShaderSrc; +extern const char* SingleTextureFragmentShaderSrc; + +extern const char* LightMappedVertexShaderSrc; +extern const char* LightMappedSkinned1VertexShaderSrc; +extern const char* LightMappedFragmentShaderSrc; + +extern const char* ReflectionMappedVertexShaderSrc; +extern const char* ReflectionMappedSkinned1VertexShaderSrc; +extern const char* ReflectionMappedFragmentShaderSrc; + +} // namespace OVRFW diff --git a/Samples/SampleXrFramework/Src/OVR_BinaryFile2.cpp b/Samples/SampleXrFramework/Src/OVR_BinaryFile2.cpp new file mode 100755 index 0000000..3db8852 --- /dev/null +++ b/Samples/SampleXrFramework/Src/OVR_BinaryFile2.cpp @@ -0,0 +1,84 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/************************************************************************************ + +Filename : OVR_BinaryFile2.cpp +Content : Simple helper class to read a binary file. +Created : Jun, 2014 +Authors : J.M.P. van Waveren + +*************************************************************************************/ + +#include "OVR_BinaryFile2.h" +#include + +namespace OVRFW { + +BinaryReader::~BinaryReader() { + if (Allocated && nullptr != Data) { + delete[] Data; + } +} + +BinaryReader::BinaryReader(const char* path, const char** perror) + : Data(NULL), Size(0), Offset(0), Allocated(true) { + std::ifstream is; + is.open(path, std::ios::binary | std::ios::in); + if (!is.is_open()) { + if (perror != NULL) { + *perror = "Failed to open file."; + } + return; + } + + // get size + is.seekg(0, is.end); + Size = static_cast(is.tellg()); + is.seekg(0, is.beg); + + // allocate buffer + Data = (uint8_t*)new uint8_t[(Size + 1)]; + if (nullptr != Data) { + static_assert( + sizeof(char) == sizeof(uint8_t), + "allocation size of char and uint8_t mismatch - BinaryReader will fail"); + + // read all file + is.read((char*)Data, Size); + if (!is) { + if (perror != NULL) { + *perror = "Failed to read file."; + } + } + } else { + if (perror != NULL) { + *perror = "Failed to allocate backing buffer."; + } + } + + // close + is.close(); +} + +std::vector MemBufferFile(const char* fileName) { + std::vector buffer; + + std::ifstream is; + is.open(fileName, std::ios::binary | std::ios::in); + if (is.is_open()) { + // get size & allocate + is.seekg(0, is.end); + buffer.resize(static_cast(is.tellg())); + is.seekg(0, is.beg); + + // read all file + is.read((char*)buffer.data(), buffer.size()); + + // close + is.close(); + } + + return buffer; +} + +} // namespace OVRFW diff --git a/Samples/SampleXrFramework/Src/OVR_BinaryFile2.h b/Samples/SampleXrFramework/Src/OVR_BinaryFile2.h new file mode 100755 index 0000000..a32c68e --- /dev/null +++ b/Samples/SampleXrFramework/Src/OVR_BinaryFile2.h @@ -0,0 +1,66 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/************************************************************************************ + +Filename : OVR_BinaryFile2.h +Content : Simple helper class to read a binary file. +Created : Jun, 2014 +Authors : J.M.P. van Waveren + +*************************************************************************************/ + +#pragma once + +#include "OVR_Types.h" +#include + +/* + This is a simple helper class to read binary data next to a JSON file. +*/ + +namespace OVRFW { + +class BinaryReader { + public: + BinaryReader(const uint8_t* binData, const int binSize) + : Data(binData), Size(binSize), Offset(0), Allocated(false) {} + ~BinaryReader(); + + BinaryReader(const char* path, const char** perror); + + uint32_t ReadUInt32() const { + const int bytes = sizeof(uint32_t); + if (Data == NULL || bytes > Size - Offset) { + return 0; + } + Offset += bytes; + return *(uint32_t*)(Data + Offset - bytes); + } + + template + bool ReadArray(std::vector<_type_>& out, const int numElements) const { + const int bytes = numElements * sizeof(out[0]); + if (Data == NULL || bytes > Size - Offset) { + out.resize(0); + return false; + } + out.resize(numElements); + memcpy(out.data(), &Data[Offset], bytes); + Offset += bytes; + return true; + } + + bool IsAtEnd() const { + return (Offset == Size); + } + + private: + const uint8_t* Data; + int32_t Size; + mutable int32_t Offset; + bool Allocated; +}; + +std::vector MemBufferFile(const char* fileName); + +} // namespace OVRFW diff --git a/Samples/SampleXrFramework/Src/OVR_FileSys.cpp b/Samples/SampleXrFramework/Src/OVR_FileSys.cpp new file mode 100755 index 0000000..6a0ca69 --- /dev/null +++ b/Samples/SampleXrFramework/Src/OVR_FileSys.cpp @@ -0,0 +1,535 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/************************************************************************************ + +Filename : OVR_FileSys.cpp +Content : Abraction layer for file systems. +Created : July 1, 2015 +Authors : Jonathan E. Wright + +*************************************************************************************/ + +#include "OVR_FileSys.h" + +#include +#include // for isdigit, isalpha +#include + +#include "Misc/Log.h" + +#include "OVR_Stream_Impl.h" +#include "OVR_UTF8Util.h" +#include "OVR_Uri.h" +#include "OVR_Std.h" +#include "JniUtils.h" + +#if defined(OVR_OS_WIN32) +#include // _access +#include "windows.h" +#include +#pragma comment(lib, "shlwapi.lib") +#endif + +namespace OVRFW { + +bool HasPermission(const char* fileOrDirName, const permissionFlags_t flags) { + OVR_ASSERT(flags.GetValue() != 0); + + std::string s(fileOrDirName); + int len = static_cast(s.length()); + if (len <= 0) + return false; + + if (s[len - 1] != '/') { // directory ends in a slash + int end = len - 1; + for (; end > 0 && s[end] != '/'; end--) + ; + s = std::string(&s[0], end); + } + + int mode = 0; +#if !defined(OVR_OS_WIN32) + if (flags & PERMISSION_WRITE) { + mode |= W_OK; + } + if (flags & PERMISSION_READ) { + mode |= R_OK; + } + if (flags & PERMISSION_EXECUTE) { + mode |= X_OK; + } + return access(s.c_str(), mode) == 0; +#else + if (flags & PERMISSION_WRITE) { + mode |= 0x2; + } + if (flags & PERMISSION_READ) { + mode |= 0x4; + } + if (flags & PERMISSION_EXECUTE) { + // No execute permission + mode |= 0x4; + } + return _access(s.c_str(), mode) == 0; +#endif // !defined(OVR_OS_WIN32) +} + +static const char* StorageName[EST_COUNT] = { + "Phone Internal", // "/data/data/" + "Phone External", // "/storage/emulated/0" or "/sdcard" + "SD Card External" // "/storage/extSdCard", "", or a dynamic UUID if Android-M +}; + +static const char* FolderName[EFT_COUNT] = {"Root", "Files", "Cache"}; + +bool ovrFileSys::GetPathIfValidPermission( + xrJava const& java, + ovrStorageType storageType, + ovrFolderType folderType, + const char* subfolder, + permissionFlags_t permission, + std::string& outPath) { + std::string storageBasePath = ""; + // Hard-coding these values for now + if (storageType == EST_SECONDARY_EXTERNAL_STORAGE && folderType == EFT_ROOT) { + storageBasePath += "/sdcard/"; + } else if (storageType == EST_PRIMARY_EXTERNAL_STORAGE && folderType == EFT_ROOT) { + storageBasePath += "/sdcard/"; + } else if (storageType == EST_INTERNAL_STORAGE && folderType == EFT_ROOT) { + storageBasePath += "/data/data/"; + } else { + // TODO ... figure this out. + ALOGW( + "Failed to get permission for %s storage in %s folder ", + StorageName[storageType], + FolderName[folderType]); + return false; + } + std::string checkPath = storageBasePath + subfolder; + return HasPermission(checkPath.c_str(), permission); +} + +#if defined(OVR_OS_WIN32) +std::string exeDirAsUri() { + char path[MAX_PATH]; + if (::GetModuleFileNameA(NULL, path, MAX_PATH) == 0) { + return std::string(); + } + if (::PathRemoveFileSpecA(path) == 0) { + // 0 return indicates nothing was removed which should not be possible. + return std::string(); + } + size_t pathLen = strlen(path); + for (size_t i = 0; i < pathLen; ++i) { + if (path[i] == '\\') { + path[i] = '/'; + } + } + char uri[MAX_PATH]; + DWORD uriSize = MAX_PATH; + if (::UrlCreateFromPathA(path, uri, &uriSize, NULL) != S_OK) { + return std::string(); + } + // Windows doesn't recognize '%20' as a space, so replace it with '\x20' which it does. + return std::regex_replace(uri, std::regex("%20"), "\x20"); +} +#endif // defined(OVR_OS_WIN32) + +void ovrFileSys::PushBackSearchPathIfValid( + xrJava const& java, + ovrStorageType storageType, + ovrFolderType folderType, + const char* subfolder, + std::vector& searchPaths) { + std::string storageBasePath = ""; + if (storageType == EST_SECONDARY_EXTERNAL_STORAGE && folderType == EFT_ROOT) { + storageBasePath += "/sdcard/"; + } else if (storageType == EST_PRIMARY_EXTERNAL_STORAGE && folderType == EFT_ROOT) { + storageBasePath += "/sdcard/"; + } else if (storageType == EST_INTERNAL_STORAGE && folderType == EFT_ROOT) { + storageBasePath += "/data/data/"; + } else { + // TODO ... figure this out. + ALOGW( + "Failed to get permission for %s storage in %s folder ", + StorageName[storageType], + FolderName[folderType]); + return; + } + std::string checkPath = storageBasePath + subfolder; + + if (HasPermission(checkPath.c_str(), permissionFlags_t(PERMISSION_READ))) { + searchPaths.push_back(checkPath); + } +} + +//============================================================== +// ovrFileSysLocal +class ovrFileSysLocal : public ovrFileSys { + public: + // this is yucky right now because it's Java-specific, even though windows doesn't care about + // it. + ovrFileSysLocal(xrJava const& javaContext); + virtual ~ovrFileSysLocal(); + + virtual ovrStream* OpenStream(char const* uri, ovrStreamMode const mode); + virtual void CloseStream(ovrStream*& stream); + virtual bool ReadFile(char const* uri, std::vector& outBuffer); + virtual bool FileExists(char const* uri); + virtual bool GetLocalPathForURI(char const* uri, std::string& outputPath); + + virtual void Shutdown(); + + private: + std::vector Schemes; + JavaVM* Jvm{nullptr}; + jobject ActivityObject{0}; + + private: + int FindSchemeIndexForName(char const* schemeName) const; + ovrUriScheme* FindSchemeForName(char const* name) const; +}; + +#define PUI_PACKAGE_NAME "com.oculus.systemactivities" + +//============================== +// ovrFileSysLocal::ovrFileSysLocal +ovrFileSysLocal::ovrFileSysLocal(xrJava const& javaContext) : Jvm(javaContext.Vm) { + // always do unit tests on startup to assure nothing has been broken + ovrUri::DoUnitTest(); + +#if defined(OVR_OS_ANDROID) + ActivityObject = javaContext.Env->NewGlobalRef(javaContext.ActivityObject); + + // add the apk scheme + ovrUriScheme_Apk* scheme = new ovrUriScheme_Apk("apk"); + + // add a host for the executing application's scheme + char curPackageName[OVR_MAX_PATH_LEN]; + ovr_GetCurrentPackageName( + javaContext.Env, javaContext.ActivityObject, curPackageName, sizeof(curPackageName)); + + char curPackageCodePath[OVR_MAX_PATH_LEN]; + ovr_GetPackageCodePath( + javaContext.Env, + javaContext.ActivityObject, + curPackageCodePath, + sizeof(curPackageCodePath)); + + char curPackageUri[OVR_MAX_URI_LEN]; + OVR::OVR_sprintf(curPackageUri, sizeof(curPackageUri), "file://%s", curPackageCodePath); + if (!scheme->OpenHost("localhost", curPackageUri)) { + ALOG("Failed to OpenHost for host '%s', uri '%s'", "localhost", curPackageUri); + assert(false); + } + + { + for (int i = 0; i < 2; ++i) { + char const* packageName = (i == 0) ? PUI_PACKAGE_NAME : curPackageName; + char packagePath[OVR_MAX_PATH_LEN]; + packagePath[0] = '\0'; + if (ovr_GetInstalledPackagePath( + javaContext.Env, + javaContext.ActivityObject, + packageName, + packagePath, + sizeof(packagePath))) { + char packageUri[sizeof(packagePath) + 7]; + OVR::OVR_sprintf(packageUri, sizeof(packageUri), "file://%s", packagePath); + ALOG("ovrFileSysLocal - scheme adding name='%s' uri='%s'", packageName, packageUri); + scheme->OpenHost(packageName, packageUri); + break; + } + } + } + + // add the host for font assets by opening a stream and trying to load res/raw/font_location.txt + // from the System Activites apk. If this file exists then + { + std::vector buffer; + char fileName[256]; + OVR::OVR_sprintf( + fileName, sizeof(fileName), "apk://%s/res/raw/font_location.txt", PUI_PACKAGE_NAME); + char fontPackageName[1024]; + bool success = ReadFile(fileName, buffer); + if (success && buffer.size() > 0) { + OVR::OVR_strncpy( + fontPackageName, + sizeof(fontPackageName), + (char const*)(static_cast(buffer.data())), + buffer.size()); + ALOG("Found font package name '%s'", fontPackageName); + } else { + // default to the SystemActivities apk. + OVR::OVR_strcpy(fontPackageName, sizeof(fontPackageName), PUI_PACKAGE_NAME); + } + + char packagePath[OVR_MAX_PATH_LEN]; + packagePath[0] = '\0'; + if (ovr_GetInstalledPackagePath( + javaContext.Env, + javaContext.ActivityObject, + fontPackageName, + packagePath, + sizeof(packagePath))) { + // add this package to our scheme as a host so that fonts can be loaded from it + char packageUri[sizeof(packagePath) + 7]; + OVR::OVR_sprintf(packageUri, sizeof(packageUri), "file://%s", packagePath); + + // add the package name as an explict host if it doesn't already exists -- it will + // already exist if the package name is not overrloaded by font_location.txt (i.e. the + // fontPackageName will have defaulted to PUI_PACKAGE_NAME ) + if (!scheme->HostExists(fontPackageName)) { + scheme->OpenHost(fontPackageName, packageUri); + } + scheme->OpenHost("font", packageUri); + + ALOG("ovrFileSysLocal - Added host '%s' for fonts @'%s'", fontPackageName, packageUri); + } + } + + ALOG("ovrFileSysLocal - apk scheme OpenHost done uri '%s'", curPackageUri); + Schemes.push_back(scheme); +#elif defined(OVR_OS_WIN32) + (void)Jvm; + (void)ActivityObject; + + // On windows we will treat "apk" as relative to the exe + std::string exeDirUri = exeDirAsUri(); + std::string fontUri = exeDirAsUri() + "/font"; + + ovrUriScheme_File* scheme = new ovrUriScheme_File("apk"); + if (!scheme->OpenHost("localhost", exeDirUri.c_str())) { + ALOG( + "Failed to OpenHost for file_scheme host '%s', uri '%s'", + "localhost", + exeDirUri.c_str()); + assert(false); + } + if (!scheme->OpenHost("font", fontUri.c_str())) { + ALOG( + "Failed to OpenHost for file_scheme host '%s', uri '%s'", "localhost", fontUri.c_str()); + assert(false); + } + Schemes.push_back(scheme); +#endif + + ovrUriScheme_File* file_scheme = new ovrUriScheme_File("file"); + if (!file_scheme->OpenHost("localhost", "")) { + ALOG("Failed to OpenHost for file_scheme host '%s', uri '%s'", "localhost", ""); + assert(false); + } + ALOG("ovrFileSysLocal - file scheme OpenHost done uri '%s'", ""); + Schemes.push_back(file_scheme); + + ALOG("ovrFileSysLocal - done "); +} + +//============================== +// ovrFileSysLocal::ovrFileSysLocal +ovrFileSysLocal::~ovrFileSysLocal() { +#if defined(OVR_OS_ANDROID) + TempJniEnv env{Jvm}; + env->DeleteGlobalRef(ActivityObject); +#endif +} + +//============================== +// ovrFileSysLocal::OpenStream +ovrStream* ovrFileSysLocal::OpenStream(char const* uri, ovrStreamMode const mode) { + // parse the Uri to find the scheme + char scheme[OVR_MAX_SCHEME_LEN]; + char host[OVR_MAX_HOST_NAME_LEN]; + char path[OVR_MAX_PATH_LEN]; + int port = 0; + ovrUri::ParseUri( + uri, + scheme, + sizeof(scheme), + nullptr, + 0, + nullptr, + 0, + host, + sizeof(host), + port, + path, + sizeof(path), + nullptr, + 0, + nullptr, + 0); + + // ALOG( "Uri='%s' scheme='%s' host='%s'", uri, scheme, host ); + + ovrUriScheme* uriScheme = FindSchemeForName(scheme); + if (uriScheme == nullptr) { + ALOG("Uri '%s' missing scheme! Assuming apk scheme!", uri); + uriScheme = FindSchemeForName("apk"); + if (uriScheme == nullptr) { + return nullptr; + } + } + +#if defined(OVR_OS_ANDROID) + // If apk scheme, need to check if this is a package we haven't seen before, and add a host if + // so. + if (OVR::OVR_stricmp(scheme, "apk") == 0) { + if (!uriScheme->HostExists(host)) { + TempJniEnv env{Jvm}; + char packagePath[OVR_MAX_PATH_LEN]; + packagePath[0] = '\0'; + if (ovr_GetInstalledPackagePath( + env, ActivityObject, host, packagePath, sizeof(packagePath))) { + char packageUri[sizeof(packagePath) + 7]; + OVR::OVR_sprintf(packageUri, sizeof(packageUri), "file://%s", packagePath); + uriScheme->OpenHost(host, packageUri); + } + } + } +#endif + + ovrStream* stream = uriScheme->AllocStream(); + if (stream == nullptr) { + // ALOG( "Uri='%s' AllocStream failed!", uri ); + assert(stream != nullptr); + return nullptr; + } + if (!stream->Open(uri, mode)) { + // ALOG( "Uri='%s' stream->Open failed!", uri ); + delete stream; + return nullptr; + } + return stream; +} + +//============================== +// ovrFileSysLocal::CloseStream +void ovrFileSysLocal::CloseStream(ovrStream*& stream) { + if (stream != nullptr) { + stream->Close(); + delete stream; + stream = nullptr; + } +} + +//============================== +// ovrFileSysLocal::ReadFile +bool ovrFileSysLocal::ReadFile(char const* uri, std::vector& outBuffer) { + ovrStream* stream = OpenStream(uri, OVR_STREAM_MODE_READ); + if (stream == nullptr) { + return false; + } + bool success = stream->ReadFile(uri, outBuffer); + CloseStream(stream); + return success; +} + +//============================== +// ovrFileSysLocal::FileExists +bool ovrFileSysLocal::FileExists(char const* uri) { + ovrStream* stream = OpenStream(uri, OVR_STREAM_MODE_READ); + if (stream == nullptr) { + return false; + } + CloseStream(stream); + return true; +} + +//============================== +// ovrFileSysLocal::GetLocalPathForURI +bool ovrFileSysLocal::GetLocalPathForURI(char const* uri, std::string& outputPath) { + // parse the Uri to find the scheme + char scheme[OVR_MAX_SCHEME_LEN]; + char host[OVR_MAX_HOST_NAME_LEN]; + char path[OVR_MAX_PATH_LEN]; + int port = 0; + ovrUri::ParseUri( + uri, + scheme, + sizeof(scheme), + nullptr, + 0, + nullptr, + 0, + host, + sizeof(host), + port, + path, + sizeof(path), + nullptr, + 0, + nullptr, + 0); + + ovrUriScheme* uriScheme = FindSchemeForName(scheme); + if (uriScheme == nullptr) { + ALOG("GetLocalPathForURI: Uri '%s' missing scheme!", uri); + return false; + } + + // FIXME: It would be better to not have to allocate a stream to just get the path + ovrStream* stream = uriScheme->AllocStream(); + if (stream == nullptr) { + assert(stream != nullptr); + return false; + } + + const bool result = stream->GetLocalPathFromUri(uri, outputPath); + delete stream; + + return result; +} + +//============================== +// ovrFileSysLocal::FindSchemeIndexForName +int ovrFileSysLocal::FindSchemeIndexForName(char const* schemeName) const { + for (int i = 0; i < static_cast(Schemes.size()); ++i) { + if (OVR::OVR_stricmp(Schemes[i]->GetSchemeName(), schemeName) == 0) { + return i; + } + } + return -1; +} + +//============================== +// ovrFileSysLocal::FindSchemeForName +ovrUriScheme* ovrFileSysLocal::FindSchemeForName(char const* name) const { + int index = FindSchemeIndexForName(name); + return index < 0 ? nullptr : Schemes[index]; +} + +//============================== +// ovrFileSysLocal::Shutdown +void ovrFileSysLocal::Shutdown() { + for (int i = 0; i < static_cast(Schemes.size()); ++i) { + Schemes[i]->Shutdown(); + delete Schemes[i]; + Schemes[i] = nullptr; + } + Schemes.clear(); +} + +//============================================================================================== +// ovrFileSys +//============================================================================================== + +//============================== +// ovrFileSys::Create +ovrFileSys* ovrFileSys::Create(xrJava const& javaContext) { + ovrFileSys* fs = new ovrFileSysLocal(javaContext); + return fs; +} + +//============================== +// ovrFileSys::Destroy +void ovrFileSys::Destroy(ovrFileSys*& fs) { + if (fs != nullptr) { + ovrFileSysLocal* fsl = static_cast(fs); + fsl->Shutdown(); + delete fs; + fs = nullptr; + } +} + +} // namespace OVRFW diff --git a/Samples/SampleXrFramework/Src/OVR_FileSys.h b/Samples/SampleXrFramework/Src/OVR_FileSys.h new file mode 100755 index 0000000..9b2ef9f --- /dev/null +++ b/Samples/SampleXrFramework/Src/OVR_FileSys.h @@ -0,0 +1,214 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/************************************************************************************ + +Filename : OVR_FileSys.h +Content : Abraction layer for file systems. +Created : July 1, 2015 +Authors : Jonathan E. Wright + +*************************************************************************************/ + +#pragma once + +#include "OVR_Stream.h" +#include "OVR_BitFlags.h" +#include "OVR_Std.h" +#include "OVR_Types.h" + +#include +#include +#include + +#if defined(OVR_OS_ANDROID) +#include +#endif // defined(OVR_OS_ANDROID) + +typedef struct xrJava_ { + JavaVM* Vm = nullptr; //< Java Virtual Machine + JNIEnv* Env = nullptr; //< Thread specific environment + jobject ActivityObject = 0; //< Java activity object +} xrJava; + +namespace OVRFW { + +enum ovrPermission { PERMISSION_READ, PERMISSION_WRITE, PERMISSION_EXECUTE }; +typedef OVR::BitFlagsT permissionFlags_t; + +enum ovrStorageType { + // By default data here is private and other apps shouldn't be able to access data from here + // Path => "/data/data/", in Note 4 this is 24.67GB + EST_INTERNAL_STORAGE = 0, + + // Also known as emulated internal storage, as this is part of phone memory( that can't be + // removed ) which is emulated as external storage in Note 4 this is = 24.64GB, with + // WRITE_EXTERNAL_STORAGE permission can write anywhere in this storage Path => + // "/storage/emulated/0" or "/sdcard", + EST_PRIMARY_EXTERNAL_STORAGE, + + // Path => "/storage/extSdCard" + // Can only write to app specific folder - /storage/extSdCard/Android/obb/ + EST_SECONDARY_EXTERNAL_STORAGE, + + EST_COUNT +}; + +enum ovrFolderType { + // Root folder, for example: + // internal => "/data" + // primary external => "/storage/emulated/0" + // secondary external => "/storage/extSdCard" + EFT_ROOT = 0, + + // Files folder + EFT_FILES, + + // Cache folder, data in this folder can be flushed by OS when it needs more memory. + EFT_CACHE, + + EFT_COUNT +}; + +//============================================================== +// ovrFileSys +class ovrFileSys { + public: + static const int OVR_MAX_SCHEME_LEN = 128; + static const int OVR_MAX_HOST_NAME_LEN = 256; + static const int OVR_MAX_PATH_LEN = 1024; + static const int OVR_MAX_URI_LEN = 1024; + + virtual ~ovrFileSys() {} + + // FIXME: java-specific context should eventually be abstracted + static ovrFileSys* Create(xrJava const& javaContext); + static void Destroy(ovrFileSys*& fs); + + // Opens a stream for the specified Uri. + virtual ovrStream* OpenStream(char const* uri, ovrStreamMode const mode) = 0; + // Closes the specified stream. + virtual void CloseStream(ovrStream*& stream) = 0; + + virtual bool ReadFile(char const* uri, std::vector& outBuffer) = 0; + + virtual bool FileExists(char const* uri) = 0; + // Gets the local path for the specified URI. File must exist. Returns false if path is not + // accessible directly by the file system. + virtual bool GetLocalPathForURI(char const* uri, std::string& outputPath) = 0; + + static void PushBackSearchPathIfValid( + xrJava const& java, + ovrStorageType toStorage, + ovrFolderType toFolder, + const char* subfolder, + std::vector& searchPaths); + static bool GetPathIfValidPermission( + xrJava const& java, + ovrStorageType toStorage, + ovrFolderType toFolder, + const char* subfolder, + permissionFlags_t permission, + std::string& outPath); +}; + +inline std::string ExtractDirectory(const std::string& s) { + const int l = static_cast(s.length()); + if (l == 0) { + return std::string(""); + } + + int end; + if (s[l - 1] == '/') { // directory ends in a slash + end = l - 1; + } else { + for (end = l - 1; end > 0 && s[end] != '/'; end--) + ; + if (end == 0) { + end = l - 1; + } + } + int start; + for (start = end - 1; start > -1 && s[start] != '/'; start--) + ; + start++; + + return std::string(&s[start], end - start); +} + +inline bool FileExists(const char* filename) { + struct stat st; + int result = stat(filename, &st); + return result == 0; +} + +inline bool GetFullPath( + const std::vector& searchPaths, + char const* relativePath, + char* outPath, + const int outMaxLen) { + assert(outPath != NULL && outMaxLen >= 1); + + if (FileExists(relativePath)) { + OVR::OVR_sprintf(outPath, OVR::OVR_strlen(relativePath) + 1, "%s", relativePath); + return true; + } + + for (const auto& searchPath : searchPaths) { + OVR::OVR_sprintf(outPath, outMaxLen, "%s%s", searchPath.c_str(), relativePath); + if (FileExists(outPath)) { + return true; // outpath is now set to the full path + } + } + // just return the relative path if we never found the file + OVR::OVR_sprintf(outPath, outMaxLen, "%s", relativePath); + return false; +} + +inline bool GetFullPath( + const std::vector& searchPaths, + char const* relativePath, + std::string& outPath) { + char largePath[1024]; + bool result = GetFullPath(searchPaths, relativePath, largePath, sizeof(largePath)); + if (result) { + outPath = largePath; + } + return result; +} + +inline bool ToRelativePath( + const std::vector& searchPaths, + char const* fullPath, + char* outPath, + const int outMaxLen) { + // check if the path starts with any of the search paths + const int n = static_cast(searchPaths.size()); + for (int i = 0; i < n; ++i) { + char const* path = searchPaths[i].c_str(); + if (strstr(fullPath, path) == fullPath) { + size_t len = OVR::OVR_strlen(path); + OVR::OVR_sprintf(outPath, outMaxLen, "%s", fullPath + len); + return true; + } + } + OVR::OVR_sprintf(outPath, outMaxLen, "%s", fullPath); + return false; +} + +inline bool ToRelativePath( + const std::vector& searchPaths, + char const* fullPath, + std::string& outPath) { + char largePath[1024]; + bool result = ToRelativePath(searchPaths, fullPath, largePath, sizeof(largePath)); + outPath = largePath; + return result; +} + +bool HasPermission(const char* fileOrDirName, const permissionFlags_t flags); + +#if defined(OVR_OS_WIN32) +std::string exeDirAsUri(); +#endif + +} // namespace OVRFW diff --git a/Samples/SampleXrFramework/Src/OVR_Lexer2.cpp b/Samples/SampleXrFramework/Src/OVR_Lexer2.cpp new file mode 100755 index 0000000..cf1dbcf --- /dev/null +++ b/Samples/SampleXrFramework/Src/OVR_Lexer2.cpp @@ -0,0 +1,780 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +/********************************************************************************** + +Filename : OVR_Lexer.h +Content : A light-weight lexer +Created : May 1, 2015 +Authors : Jonathan E. Wright + +************************************************************************************/ + +#include "OVR_Lexer2.h" + +#include "OVR_Std.h" +#include "OVR_UTF8Util.h" +#include // for strto* functions +#include +#include +#include + +namespace OVRFW { + +//============================== +// ovrLexer::ovrLexer +ovrLexer::ovrLexer(const char* source, const size_t sourceLength, char const* punctuation) + : Source(source), + SourceLength(sourceLength), + p(Source), + Error(LEX_RESULT_OK), + Punctuation(NULL) { + size_t len = punctuation == NULL ? 0 : OVR::OVR_strlen(punctuation); + if (len == 0) { + Punctuation = new char[16]; + Punctuation[0] = '\0'; + } else { + Punctuation = new char[len + 1]; + OVR::OVR_strcpy(Punctuation, len + 1, punctuation); + } +} + +//============================== +// ovrLexer::ovrLexer +ovrLexer::ovrLexer(const char* source) : ovrLexer(source, OVR::OVR_strlen(source), NULL) {} + +//============================== +// ovrLexer::ovrLexer +ovrLexer::ovrLexer(const char* source, char const* punctuation) + : ovrLexer(source, OVR::OVR_strlen(source), punctuation) {} + +//============================== +// ovrLexer::ovrLexer +ovrLexer::ovrLexer(std::vector const& source, char const* punctuation) + : ovrLexer( + (const char*)(static_cast(source.data())), + source.size(), + punctuation) {} + +//============================== +// ovrLexer::ovrLexer +ovrLexer::ovrLexer(const ovrLexer& other) { + operator=(other); +} + +//============================== +// ovrLexer::ovrLexer +ovrLexer::ovrLexer(ovrLexer&& other) { + operator=(std::move(other)); +} + +//============================== +// ovrLexer::~ovrLexer +ovrLexer::~ovrLexer() { + assert(Error == LEX_RESULT_OK || Error == LEX_RESULT_EOF); + delete Punctuation; + Punctuation = NULL; +} + +//============================== +// ovrLexer::operator= +ovrLexer& ovrLexer::operator=(const ovrLexer& other) { + // self assignment + if (this == &other) { + return *this; + } + + Source = other.Source; + SourceLength = other.SourceLength; + p = other.p; + Error = other.Error; + + size_t len = other.Punctuation == NULL ? 0 : OVR::OVR_strlen(other.Punctuation); + if (len == 0) { + Punctuation = new char[16]; + Punctuation[0] = '\0'; + } else { + Punctuation = new char[len + 1]; + OVR::OVR_strcpy(Punctuation, len + 1, other.Punctuation); + } + + return *this; +} + +//============================== +// ovrLexer::operator= +ovrLexer& ovrLexer::operator=(ovrLexer&& other) { + // self assignment + if (this == &other) { + return *this; + } + + Source = other.Source; + SourceLength = other.SourceLength; + p = other.p; + Error = other.Error; + Punctuation = other.Punctuation; + + other.Source = nullptr; + other.SourceLength = 0; + other.p = nullptr; + other.Punctuation = nullptr; + + return *this; +} + +//============================== +// ovrLexer::FindChar +bool ovrLexer::FindChar(char const* buffer, uint32_t const ch) { + const char* curPtr = buffer; + for (;;) { + uint32_t const curChar = UTF8Util::DecodeNextChar(&curPtr); + if (curChar == '\0') { + return false; + } else if (curChar == ch) { + return true; + } + } +} + +//============================== +// ovrLexer::IsWhitespace +bool ovrLexer::IsWhitespace(uint32_t const ch) { + return (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n'); +} + +//============================== +// ovrLexer::IsQuote +bool ovrLexer::IsQuote(uint32_t const ch) { + bool result = ch == '\"'; + assert( + result == false || ch <= 255); // quotes are assumed elsewhere to be single-byte characters! + return result; +} + +//============================== +// ovrLexer::SkipWhitespace +ovrLexer::ovrResult +ovrLexer::SkipWhitespace(char const*& p, char const* source, size_t const sourceLength) { + const char* cur = p; // copy p because we only want to advance if it is whitespace + uint32_t ch; + for (;;) { + if (p >= source + sourceLength) { + return LEX_RESULT_EOF; + } + + ch = UTF8Util::DecodeNextChar(&cur); + + if (ch == '\0' || !IsWhitespace(ch)) { + return LEX_RESULT_OK; + } + p = cur; + } +} + +//============================== +// ovrLexer::IsPunctuation +bool ovrLexer::IsPunctuation(char const* punctuation, uint32_t const ch) { + const char* p = punctuation; + uint32_t curPunc = UTF8Util::DecodeNextChar(&p); + while (curPunc != '\0') { + if (curPunc == ch) { + return true; + } + curPunc = UTF8Util::DecodeNextChar(&p); + } + return false; +} + +//============================== +// ovrLexer::CopyResult +void ovrLexer::CopyResult(char const* buffer, char* token, size_t const maxTokenSize) { + // NOTE: if any multi-byte characters are ever treated as quotes, this code must change + if (IsQuote(*buffer)) { + size_t len = UTF8Util::GetLength(buffer); + const uint32_t lastChar = UTF8Util::GetCharAt(len - 1, buffer); + if (IsQuote(lastChar)) { + // The first char and last char are single-byte quotes, we can now just step past the + // first and not copy the last. + char const* start = buffer + 1; + len = OVR::OVR_strlen(start); // We do not care about UTF length here since we know the + // quotes are a single bytes + OVR::OVR_strncpy(token, maxTokenSize, start, len - 1); + return; + } + } + + OVR::OVR_strcpy(token, maxTokenSize, buffer); +} + +//============================== +// ovrLexer::PeekNextChar +uint32_t ovrLexer::PeekNextChar() { + if (p >= Source + SourceLength) { + return '\0'; + } + + // save state + ovrResult error = Error; + const char* tp = p; + + uint32_t ch = UTF8Util::DecodeNextChar(&p); + + // restore state + Error = error; + p = tp; + + return ch; +} + +//============================== +// ovrLexer::SkipToEndOfLine +ovrLexer::ovrResult ovrLexer::SkipToEndOfLine() { + uint32_t ch; + do { + if (p >= Source + SourceLength) { + return LEX_RESULT_EOF; + } + + ch = UTF8Util::DecodeNextChar(&p); + } while (ch != '\n' && ch != '\0'); + + return LEX_RESULT_OK; +} + +//============================== +// ovrLexer::EmitCodePoint +// helper function to ensure we never emit a character beyond the end of the buffer and that if we +// try to we always 0-terminate at the end of the buffer. +void ovrLexer::EmitCodePoint( + uint32_t const ch, + char* buffer, + ptrdiff_t& bufferOfs, + size_t const bufferSize, + char* token, + size_t const maxTokenSize) const { + if (static_cast(bufferOfs) < bufferSize) { + UTF8Util::EncodeChar(buffer, &bufferOfs, ch); + } else { + // not enough room to emit, so stuff a null character at the end + bufferOfs = bufferSize - 1; + UTF8Util::EncodeChar(buffer, &bufferOfs, '\0'); + } +} + +//============================== +// ovrLexer::TranslateEscapeCode +uint32_t ovrLexer::TranslateEscapeCode(uint32_t const inCh) { + switch (inCh) { + case 'n': + return '\n'; + case 'r': + return '\r'; + case 't': + return '\t'; + case '"': + return '\"'; + case '\'': + return '\''; + default: + return '\0'; + } +} + +//============================== +// ovrLexer::NextToken +ovrLexer::ovrResult ovrLexer::NextToken(char* token, size_t const maxTokenSize) { + if (token == NULL || maxTokenSize <= 0) { + assert(token != NULL && maxTokenSize > 0); + return LEX_RESULT_ERROR; + } + + token[0] = '\0'; + + size_t const BUFF_SIZE = 8192; + char buffer[BUFF_SIZE]; + + SkipWhitespace(p, Source, SourceLength); + + bool inQuotes = false; + bool inComment = false; + bool isPunc = false; + + char const* lastp = p; + + ptrdiff_t bufferOfs = 0; + for (;;) { + if (p > Source + SourceLength) { + EmitCodePoint('\0', buffer, bufferOfs, BUFF_SIZE, token, maxTokenSize); + CopyResult(buffer, token, maxTokenSize); + return LEX_RESULT_EOF; + } + + lastp = p; + uint32_t ch = UTF8Util::DecodeNextChar(&p); + + // exit if we just read whitespace or a null byte + if (ch == '\0' || (!inQuotes && !inComment && IsWhitespace(ch))) { + break; + } + + isPunc = IsPunctuation(Punctuation, ch); + if (inComment) { + if (ch == '*' && PeekNextChar() == '/') { + inComment = false; + // consume the '/' character + ch = UTF8Util::DecodeNextChar(&p); + // skip any whitespace that may follow the comment + ovrResult res = SkipWhitespace(p, Source, SourceLength); + if (res != LEX_RESULT_OK) { + return res; + } + } + continue; + } else if (inQuotes && ch == '\\') { + ch = TranslateEscapeCode(PeekNextChar()); + if (ch == '\0') { + return LEX_RESULT_UNKNOWN_ESCAPE; + } + UTF8Util::DecodeNextChar(&p); // consume the escape code + } else if (!inQuotes && !inComment && isPunc) { + if (ch == '/' && PeekNextChar() == '*') { + inComment = true; + continue; + } else if (ch == '/' && PeekNextChar() == '/') { + SkipToEndOfLine(); + // skip any whitespace that may start the next line + ovrResult res = SkipWhitespace(p, Source, SourceLength); + if (res != LEX_RESULT_OK) { + return res; + } + continue; + } else if (bufferOfs > 0) { + // we're already in a token, undo the read of the punctuation and exit + p = lastp; + break; + } else { + // if this is the first character of a token, just emit the punctuation + EmitCodePoint(ch, buffer, bufferOfs, BUFF_SIZE, token, maxTokenSize); + break; + } + } else if (IsQuote(ch)) { + if (inQuotes) // if we were in quotes, end the token at the closing quote + { + break; + } + // otherwise set the quote flag and skip emission of the quote character + inQuotes = true; + continue; + } + + int encodeSize = UTF8Util::GetEncodeCharSize(ch); + if (static_cast(bufferOfs + encodeSize) >= BUFF_SIZE - 1 || + static_cast(bufferOfs + encodeSize + 1) >= maxTokenSize) { + // truncation + UTF8Util::EncodeChar(buffer, &bufferOfs, '\0'); + CopyResult(buffer, token, maxTokenSize); + return LEX_RESULT_ERROR; + } + UTF8Util::EncodeChar(buffer, &bufferOfs, ch); + } + + // always emit a null byte + EmitCodePoint('\0', buffer, bufferOfs, BUFF_SIZE, token, maxTokenSize); + CopyResult(buffer, token, maxTokenSize); + return LEX_RESULT_OK; +} + +//============================== +// ovrLexer::PeekToken +ovrLexer::ovrResult ovrLexer::PeekToken(char* token, size_t const maxTokenSize) { + // save state + ovrResult error = Error; + const char* tp = p; + + ovrResult res = NextToken(token, maxTokenSize); + + // restore state + Error = error; + p = tp; + + return res; +} + +//============================== +// ovrLexer::ParseInt +ovrLexer::ovrResult ovrLexer::ParseInt(int& value, int const defaultVal) { + char token[128]; + ovrResult r = NextToken(token, sizeof(token)); + if (r != LEX_RESULT_OK) { + value = defaultVal; + return r; + } + + errno = 0; + char* endptr = nullptr; + value = strtol(token, &endptr, 10); + + // Did we overflow? + if (errno == ERANGE) { + value = defaultVal; + return LEX_RESULT_VALUE_OUT_OF_RANGE; + } + + // Did we fail to parse any characters at all? + if (endptr == token) { + value = defaultVal; + return LEX_RESULT_EOF; + } + + // Did we hit a non-digit character before the end of the string? + if (*endptr != '\0') { + value = defaultVal; + return LEX_RESULT_UNEXPECTED_TOKEN; + } + + // any other unexpected error + if (errno != 0) { + value = defaultVal; + return LEX_RESULT_UNEXPECTED_TOKEN; + } + + return LEX_RESULT_OK; +} + +//============================== +// ovrLexer::ParseUnsignedInt +ovrLexer::ovrResult ovrLexer::ParseUnsignedInt(unsigned int& value, unsigned int const defaultVal) { + char token[128]; + ovrResult r = NextToken(token, sizeof(token)); + if (r != LEX_RESULT_OK) { + value = defaultVal; + return r; + } + + errno = 0; + char* endptr = nullptr; + value = strtoul(token, &endptr, 10); + + // Did we overflow? + if (errno == ERANGE) { + value = defaultVal; + return LEX_RESULT_VALUE_OUT_OF_RANGE; + } + + // Did we fail to parse any characters at all? + if (endptr == token) { + value = defaultVal; + return LEX_RESULT_EOF; + } + + // Did we hit a non-digit character before the end of the string? + if (*endptr != '\0') { + value = defaultVal; + return LEX_RESULT_UNEXPECTED_TOKEN; + } + + // any other unexpected error + if (errno != 0) { + value = defaultVal; + return LEX_RESULT_UNEXPECTED_TOKEN; + } + + return LEX_RESULT_OK; +} + +//============================== +// ovrLexer::ParseLongLong +ovrLexer::ovrResult ovrLexer::ParseLongLong(long long& value, long long const defaultVal) { + char token[128]; + ovrResult r = NextToken(token, sizeof(token)); + if (r != LEX_RESULT_OK) { + value = defaultVal; + return r; + } + + errno = 0; + char* endptr = nullptr; + value = strtoll(token, &endptr, 10); + + // Did we overflow? + if (errno == ERANGE) { + value = defaultVal; + return LEX_RESULT_VALUE_OUT_OF_RANGE; + } + + // Did we fail to parse any characters at all? + if (endptr == token) { + value = defaultVal; + return LEX_RESULT_EOF; + } + + // Did we hit a non-digit character before the end of the string? + if (*endptr != '\0') { + value = defaultVal; + return LEX_RESULT_UNEXPECTED_TOKEN; + } + + // any other unexpected error + if (errno != 0) { + value = defaultVal; + return LEX_RESULT_UNEXPECTED_TOKEN; + } + + return LEX_RESULT_OK; +} + +//============================== +// ovrLexer::ParseUnsignedLongLong +ovrLexer::ovrResult ovrLexer::ParseUnsignedLongLong( + unsigned long long& value, + unsigned long long const defaultVal) { + char token[128]; + ovrResult r = NextToken(token, sizeof(token)); + if (r != LEX_RESULT_OK) { + value = defaultVal; + return r; + } + + errno = 0; + char* endptr = nullptr; + value = strtoull(token, &endptr, 10); + + // Did we overflow? + if (errno == ERANGE) { + value = defaultVal; + return LEX_RESULT_VALUE_OUT_OF_RANGE; + } + + // Did we fail to parse any characters at all? + if (endptr == token) { + value = defaultVal; + return LEX_RESULT_EOF; + } + + // Did we hit a non-digit character before the end of the string? + if (*endptr != '\0') { + value = defaultVal; + return LEX_RESULT_UNEXPECTED_TOKEN; + } + + // any other unexpected error + if (errno != 0) { + value = defaultVal; + return LEX_RESULT_UNEXPECTED_TOKEN; + } + + return LEX_RESULT_OK; +} + +//============================== +// ovrLexer::ParseFloat +ovrLexer::ovrResult ovrLexer::ParseFloat(float& value, float const defaultVal) { + char token[128]; + ovrResult r = NextToken(token, sizeof(token)); + if (r != LEX_RESULT_OK) { + value = defaultVal; + return r; + } + + errno = 0; + char* endptr = nullptr; + value = strtof(token, &endptr); + + // Did we overflow? + if (errno == ERANGE) { + value = defaultVal; + return LEX_RESULT_VALUE_OUT_OF_RANGE; + } + + // Did we fail to parse any characters at all? + if (endptr == token) { + value = defaultVal; + return LEX_RESULT_EOF; + } + + // Did we hit a non-digit character before the end of the string? + if (*endptr != '\0' && *endptr != 'f') { + value = defaultVal; + return LEX_RESULT_UNEXPECTED_TOKEN; + } + + // any other unexpected error + if (errno != 0) { + value = defaultVal; + return LEX_RESULT_UNEXPECTED_TOKEN; + } + + return LEX_RESULT_OK; +} + +//============================== +// ovrLexer::ParseDouble +ovrLexer::ovrResult ovrLexer::ParseDouble(double& value, double const defaultVal) { + char token[128]; + ovrResult r = NextToken(token, sizeof(token)); + if (r != LEX_RESULT_OK) { + value = defaultVal; + return r; + } + + errno = 0; + char* endptr = nullptr; + value = strtod(token, &endptr); + + // Did we overflow? + if (errno == ERANGE) { + value = defaultVal; + return LEX_RESULT_VALUE_OUT_OF_RANGE; + } + + // Did we fail to parse any characters at all? + if (endptr == token) { + value = defaultVal; + return LEX_RESULT_EOF; + } + + // Did we hit a non-digit character before the end of the string? + if (*endptr != '\0') { + value = defaultVal; + return LEX_RESULT_UNEXPECTED_TOKEN; + } + + // any other unexpected error + if (errno != 0) { + value = defaultVal; + return LEX_RESULT_UNEXPECTED_TOKEN; + } + + return LEX_RESULT_OK; +} + +//============================== +// ovrLexer::ParsePointer +ovrLexer::ovrResult ovrLexer::ParsePointer(unsigned char*& ptr, unsigned char* defaultVal) { + char token[128]; + ovrResult r = NextToken(token, sizeof(token)); + if (r != LEX_RESULT_OK) { + ptr = defaultVal; + return r; + } + const int result = sscanf(token, "%p", &ptr); + if (result != 1) { + ptr = defaultVal; + return LEX_RESULT_UNEXPECTED_TOKEN; + } + + return LEX_RESULT_OK; +} + +ovrLexer::ovrResult ovrLexer::ParseToEndOfLine(char* buffer, size_t const maxBufferSize) { + size_t remainingLen = SourceLength - (p - Source); + if (maxBufferSize > remainingLen) { + memcpy(buffer, p, remainingLen); + buffer[remainingLen] = '\0'; + return LEX_RESULT_OK; + } + + return LEX_RESULT_ERROR; +} + +//============================== +// ovrLexer::ExpectToken +ovrLexer::ovrResult +ovrLexer::ExpectToken(char const* expectedToken, char* token, size_t const maxTokenSize) { + ovrResult res = NextToken(token, maxTokenSize); + if (res != LEX_RESULT_OK) { + return res; + } + + if (OVR::OVR_strcmp(token, expectedToken) != 0) { + return LEX_RESULT_UNEXPECTED_TOKEN; + } + return LEX_RESULT_OK; +} + +//============================== +// ovrLexer::ExpectPunctuation +ovrLexer::ovrResult +ovrLexer::ExpectPunctuation(char const* punc, char* token, size_t const maxTokenSize) { + ovrResult res = NextToken(token, maxTokenSize); + if (res != LEX_RESULT_OK) { + return res; + } + size_t tokenLen = OVR::OVR_strlen(token); + if (tokenLen == 1) { + int len = static_cast(OVR::OVR_strlen(punc)); + for (int i = 0; i < len; ++i) { + if (punc[i] == token[0]) { + return LEX_RESULT_OK; + } + } + } + return LEX_RESULT_UNEXPECTED_TOKEN; +} + +#if 0 // enable for unit tests at static initialization time + +class ovrLexerUnitTest { +public: + ovrLexerUnitTest() + { + TestLex( "Single-token." ); + TestLex( "This is a test of simple parsing." ); + TestLex( " Test leading whitespace." ); + TestLex( "Test trailing whitespace. " ); + TestLex( "Test token truncation 012345678901234567890123456789ABCDEFGHIJ." ); + TestLex( "Test \"quoted strings\"!" ); + TestLex( "Test quoted strings with token truncation \"012345678901234567890123456789ABCDEFGHIJ\"!" ); + TestEOF( "This is a test of EOF" ); + } + + void TestLexInternal( ovrLexer & lex, char const * text ) + { + char out[8192]; + char token[32]; + + out[0] = '\0'; + while( lex.NextToken( token, sizeof( token ) ) == ovrLexer::LEX_RESULT_OK ) + { + OVR::OVR_strcat( out, sizeof( out ), token ); + OVR::OVR_strcat( out, sizeof( out ), " " ); + } + LOG( "%s", out ); + } + + void TestLex( char const * text ) + { + ovrLexer lex( text ); + TestLexInternal( lex, text ); + } + + void TestEOF( char const * text ) + { + ovrLexer lex( text ); + TestLexInternal( lex, text ); + + char token[32]; + if ( lex.NextToken( token, sizeof( token ) ) != ovrLexer::LEX_RESULT_EOF ) + { + LOG( "Did not get EOF!" ); + } + if ( lex.NextToken( token, sizeof( token ) ) == ovrLexer::LEX_RESULT_EOF ) + { + LOG( "Got expected EOF" ); + } + } + +}; + +static ovrLexerUnitTest unitTest; + +#endif + +} // namespace OVRFW diff --git a/Samples/SampleXrFramework/Src/OVR_Lexer2.h b/Samples/SampleXrFramework/Src/OVR_Lexer2.h new file mode 100755 index 0000000..9d31c7e --- /dev/null +++ b/Samples/SampleXrFramework/Src/OVR_Lexer2.h @@ -0,0 +1,112 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +/********************************************************************************** + +Filename : OVR_Lexer.h +Content : A light-weight lexer +Created : May 1, 2015 +Authors : Jonathan E. Wright + +************************************************************************************/ + +#pragma once + +#include +#include + +namespace OVRFW { + +//============================================================== +// ovrLexer +// +// This was originally intended to get rid of sscanf usage which +// has security issues (buffer overflows) and cannot parse strings containing +// spaces without using less-than-ideal workarounds. Supports keeping quoted +// strings as single tokens, UTF-8 encoded text, but not much else. +// +// If a punctuation string is passed in, each character in the string +// is treated as a punctuation mark. Punctuation marks will always be +// lexed to a single punctuation per token, independent of whitespace. +// +// If the / and * characters are passed as punctuation, then the lexer +// will also treat // and /* */ as C-style comments. +//============================================================== +class ovrLexer { + public: + enum ovrResult { + LEX_RESULT_OK, + LEX_RESULT_ERROR, // ran out of buffer space + LEX_RESULT_EOF, // tried to read past the end of the buffer + LEX_RESULT_UNKNOWN_ESCAPE, // unrecognized escape code + LEX_RESULT_UNEXPECTED_TOKEN, // did get the expected token + LEX_RESULT_VALUE_OUT_OF_RANGE, // numeric value was out of range for the given data size + }; + + // length of source must be specified + ovrLexer(const char* source, const size_t sourceLength, char const* punctuation); + ovrLexer(std::vector const& source, char const* punctuation); + // expects a 0-terminated string as input and length is determined as lenght of a string. + ovrLexer(const char* source); + ovrLexer(const char* source, char const* punctuation); + ovrLexer(const ovrLexer& other); + ovrLexer(ovrLexer&& other); + + ~ovrLexer(); + + ovrLexer& operator=(const ovrLexer& other); + ovrLexer& operator=(ovrLexer&& other); + + ovrResult NextToken(char* token, size_t const maxTokenSize); + ovrResult PeekToken(char* token, size_t const maxTokenSize); + ovrResult ExpectToken(char const* expectedToken, char* token, size_t const maxTokenSize); + ovrResult ExpectPunctuation(char const* punc, char* token, size_t const maxTokenSize); + + ovrResult ParseInt(int& value, int const defaultVal); + ovrResult ParseUnsignedInt(unsigned int& value, unsigned int const defaultVal); + ovrResult ParseLongLong(long long& value, long long const defaultVal); + ovrResult ParseUnsignedLongLong(unsigned long long& value, unsigned long long const defaultVal); + ovrResult ParseFloat(float& value, float const defaultVal); + ovrResult ParseDouble(double& value, double const defaultVal); + ovrResult ParsePointer(unsigned char*& ptr, unsigned char* defaultVal); + ovrResult ParseToEndOfLine(char* buffer, size_t const maxBufferSize); + + ovrResult GetError() const { + return Error; + } + + private: + static bool FindChar(char const* buffer, uint32_t const ch); + static bool IsWhitespace(uint32_t const ch); + static bool IsQuote(uint32_t const ch); + static ovrResult SkipWhitespace(char const*& p, char const* source, size_t const sourceLength); + static bool IsPunctuation(char const* punctuation, uint32_t const ch); + static void CopyResult(char const* buffer, char* token, size_t const maxTokenSize); + static uint32_t TranslateEscapeCode(uint32_t const inCh); + + uint32_t PeekNextChar(); + ovrResult SkipToEndOfLine(); + + void EmitCodePoint( + uint32_t const ch, + char* buffer, + ptrdiff_t& bufferOfs, + size_t const bufferSize, + char* token, + size_t const maxTokenSize) const; + + private: + const char* Source; + size_t SourceLength; + const char* p; // pointer to current position + ovrResult Error; + char* Punctuation; // UTF-8 string holding characters to lex as punctuation (may be empty) +}; + +} // namespace OVRFW diff --git a/Samples/SampleXrFramework/Src/OVR_MappedFile.cpp b/Samples/SampleXrFramework/Src/OVR_MappedFile.cpp new file mode 100755 index 0000000..62ce325 --- /dev/null +++ b/Samples/SampleXrFramework/Src/OVR_MappedFile.cpp @@ -0,0 +1,218 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/************************************************************************************ + +Filename : OVR_MappedFile.cpp +Content : Cross-platform memory-mapped file wrapper. +Created : May 12, 2014 +Authors : Chris Taylor + +*************************************************************************************/ + +#include "OVR_MappedFile.h" +#include "OVR_Types.h" + +#if defined(OVR_OS_ANDROID) + +#if defined(OVR_OS_ANDROID) +// disable warnings on implicit type conversion where value may be changed by conversion for +// sys/stat.h +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wconversion" +#endif + +#include +#include +#include +#include +#include + +#if defined(OVR_OS_ANDROID) +// restore warnings on implicit type conversion where value may be changed by conversion for +// sys/stat.h +#pragma GCC diagnostic pop +#endif + +namespace OVRFW { + +static uint32_t DEFAULT_ALLOCATION_GRANULARITY = 65536; + +static uint32_t GetAllocationGranularity() { + uint32_t alloc_gran = 0; + +#if defined(OVR_OS_WIN32) + + SYSTEM_INFO sys_info; + GetSystemInfo(&sys_info); + alloc_gran = sys_info.dwAllocationGranularity; + +#elif defined(OVR_OS_MAC) || defined(OVR_OS_IPHONE) + + alloc_gran = (uint32_t)getpagesize(); + +#elif defined(_SC_PAGE_SIZE) + + alloc_gran = (uint32_t)sysconf(_SC_PAGE_SIZE); + +#endif + + return (alloc_gran > 0) ? alloc_gran : DEFAULT_ALLOCATION_GRANULARITY; +} + +/* + MappedFile +*/ + +MappedFile::MappedFile() { + Length = 0; + File = -1; +} + +MappedFile::~MappedFile() { + Close(); +} + +bool MappedFile::OpenRead(const char* path, bool read_ahead, bool no_cache) { + Close(); + + ReadOnly = true; + + // Don't allow private files to be read by other applications. + File = open(path, O_RDONLY); + + if (File == -1) { + return false; + } else { + Length = lseek(File, 0, SEEK_END); + + if (Length <= 0) { + return false; + } else { +#if defined(F_RDAHEAD) + if (read_ahead) { + fcntl(File, F_RDAHEAD, 1); + } +#endif + +#if defined(F_NOCACHE) + if (no_cache) { + fcntl(File, F_NOCACHE, 1); + } +#endif + } + } + + return true; +} + +bool MappedFile::OpenWrite(const char* path, size_t size) { + Close(); + + ReadOnly = false; + Length = size; + + // Don't allow private files to be read or written by + // other applications. + File = open(path, O_RDWR | O_CREAT | O_TRUNC, (mode_t)0660); + + if (File == -1) { + return false; + } else { + if (-1 == lseek(File, size - 1, SEEK_SET)) { + return false; + } else { + if (1 != write(File, "", 1)) { + return false; + } + } + } + + return true; +} + +void MappedFile::Close() { + if (File != -1) { + close(File); + File = -1; + } + + Length = 0; +} + +/* + MappedView +*/ + +MappedView::MappedView() { + Data = 0; + Length = 0; + Offset = 0; + File = 0; + Map = MAP_FAILED; +} + +MappedView::~MappedView() { + Close(); +} + +bool MappedView::Open(MappedFile* file) { + Close(); + + if (!file || !file->IsValid()) { + return false; + } + + File = file; + return true; +} + +uint8_t* MappedView::MapView(size_t offset, uint32_t length) { + if (length == 0) { + length = static_cast(File->GetLength()); + } + + if (offset) { + uint32_t granularity = GetAllocationGranularity(); + + // Bring offset back to the previous allocation granularity + uint32_t mask = granularity - 1; + uint32_t masked = (uint32_t)offset & mask; + if (masked) { + offset -= masked; + length += masked; + } + } + + int prot = PROT_READ; + if (!File->ReadOnly) { + prot |= PROT_WRITE; + } + + // Use MAP_PRIVATE so that memory is not exposed to other processes. + Map = mmap(0, length, prot, MAP_PRIVATE, File->File, offset); + + if (Map == MAP_FAILED) { + return 0; + } + + Data = reinterpret_cast(Map); + + Offset = offset; + Length = length; + + return Data; +} + +void MappedView::Close() { + if (Map != MAP_FAILED) { + munmap(Map, Length); + Map = MAP_FAILED; + } + Data = 0; + Length = 0; + Offset = 0; +} + +} // namespace OVRFW + +#endif // defined(OVR_OS_ANDROID) diff --git a/Samples/SampleXrFramework/Src/OVR_MappedFile.h b/Samples/SampleXrFramework/Src/OVR_MappedFile.h new file mode 100755 index 0000000..0846351 --- /dev/null +++ b/Samples/SampleXrFramework/Src/OVR_MappedFile.h @@ -0,0 +1,105 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/************************************************************************************ + +Filename : OVR_MappedFile.h +Content : Cross-platform memory-mapped file wrapper. +Created : May 12, 2014 +Authors : Chris Taylor + +*************************************************************************************/ + +#pragma once + +#include +#include + +/* + Memory-mapped files are a fairly good compromise between performance and flexibility. + + Compared with asynchronous io, memory-mapped files are: + + Much easier to implement in a portable way + + Automatically paged in and out of RAM + + Automatically read-ahead cached + + When asynch IO is not available or blocking is acceptable then this is a + great alternative with low overhead and similar performance. + + For random file access, use MappedView with a MappedFile that has been + opened with random_access = true. Random access is usually used for a + database-like file type, which is much better implemented using asynch IO. +*/ + +namespace OVRFW { + +// Read-only memory mapped file +class MappedFile { + friend class MappedView; + + public: + MappedFile(); + ~MappedFile(); + + // Opens the file for shared read-only access with other applications + // Returns false on error (file not found, etc) + bool OpenRead(const char* path, bool read_ahead = false, bool no_cache = false); + + // Creates and opens the file for exclusive read/write access + bool OpenWrite(const char* path, size_t size); + + void Close(); + + bool IsReadOnly() const { + return ReadOnly; + } + size_t GetLength() const { + return Length; + } + bool IsValid() const { + return (Length != 0); + } + + private: + int File; + bool ReadOnly; + size_t Length; +}; + +// View of a portion of the memory mapped file +class MappedView { + public: + MappedView(); + ~MappedView(); + + bool Open(MappedFile* file); // Returns false on error + uint8_t* MapView( + size_t offset = 0, + uint32_t length = 0); // Returns 0 on error, 0 length means whole file + void Close(); + + bool IsValid() const { + return (Data != 0); + } + size_t GetOffset() const { + return Offset; + } + uint32_t GetLength() const { + return Length; + } + MappedFile* GetFile() { + return File; + } + uint8_t* GetFront() { + return Data; + } + + private: + void* Map; + + MappedFile* File; + uint8_t* Data; + size_t Offset; + uint32_t Length; +}; + +} // namespace OVRFW diff --git a/Samples/SampleXrFramework/Src/OVR_Stream.cpp b/Samples/SampleXrFramework/Src/OVR_Stream.cpp new file mode 100755 index 0000000..2ed1965 --- /dev/null +++ b/Samples/SampleXrFramework/Src/OVR_Stream.cpp @@ -0,0 +1,1065 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/************************************************************************************ + +Filename : OVR_Stream.cpp +Content : Abstraction for file streams. +Created : July 1, 2015 +Authors : Jonathan E. Wright + +*************************************************************************************/ + +#include "OVR_Stream.h" + +#include "Misc/Log.h" + +#include +#include + +#include "PackageFiles.h" +#include "OVR_Uri.h" +#include "OVR_UTF8Util.h" +#include "OVR_FileSys.h" +#include "OVR_Stream_Impl.h" + +#include "OVR_Std.h" + +namespace OVRFW { + +bool UriPathStartsWithDriveLetter(char const* uriPath) { + if (uriPath == NULL || uriPath[0] == '\0') { + return false; + } + if (uriPath[0] != '/') { + return false; + } + size_t len = UTF8Util::GetLength(uriPath); + if (len < 3) { + return false; + } + if (uriPath[0] != '/' || uriPath[2] != ':') { + return false; + } + return true; +} + +// this takes a pointer to a buffer containing a URI path as input and outputs a path that is safe +// to use on Windows. In particular, if the path starts with the form "/C:", then the returned path +// skips past the '/' and starts with the drive letter. +const char* SafePathFromUriPath(char const* uriPath) { + if (UriPathStartsWithDriveLetter(uriPath)) { + return uriPath + 1; + } + return uriPath; +} + +static uint32_t DecodeNextChar(char const* p, intptr_t& offset) { + char const* t = p + offset; + uint32_t ch = UTF8Util::DecodeNextChar(&t); + offset = t - p; + return ch; +} + +static bool EncodeChar(char* p, size_t const& maxOffset, intptr_t& offset, uint32_t ch) { + // test for buffer overflow by encoding to a temp buffer and seeing how far the offset moved + char temp[6]; + intptr_t tempOfs = 0; + UTF8Util::EncodeChar(temp, &tempOfs, ch); + if (static_cast(maxOffset) - offset <= tempOfs) { + // just encode a null byte at the current offset + assert(false); + p[offset] = '\0'; + offset++; + return false; + } + UTF8Util::EncodeChar(p, &offset, ch); + return true; +} + +bool AppendUriPath( + char const* inPath, + char const* appendPath, + char* outPath, + size_t const outPathSize) { + const char WIN_PATH_SEPARATOR = '\\'; + const char NIX_PATH_SEPARATOR = '/'; + const char URI_PATH_SEPARATOR = '/'; + + if (inPath == NULL || outPath == NULL || appendPath == NULL || outPathSize < 2) { + assert(inPath != NULL && outPath != NULL && appendPath != NULL && outPathSize > 1); + return false; + } + intptr_t inOfs = 0; + intptr_t outOfs = 0; + uint32_t lastCh = 0xffffffff; + uint32_t ch = 0xffffffff; + for (;;) { + lastCh = ch; + ch = DecodeNextChar(inPath, inOfs); + if (ch == '\0') { + break; + } + if (!EncodeChar(outPath, outPathSize, outOfs, ch)) { + return false; + } + } + + // ensure there's always a path separator after inPath + if (lastCh != WIN_PATH_SEPARATOR && lastCh != NIX_PATH_SEPARATOR && + lastCh != URI_PATH_SEPARATOR) { + // emit a separator + if (!EncodeChar(outPath, outPathSize, outOfs, URI_PATH_SEPARATOR)) { + return false; // buffer overflow + } + } + + // skip past any path separators at the start of append path + intptr_t appendOfs = 0; + char const* appendPathStart = &appendPath[0]; + for (;;) { + ch = DecodeNextChar(appendPath, appendOfs); + if (ch != WIN_PATH_SEPARATOR && ch != NIX_PATH_SEPARATOR && ch != URI_PATH_SEPARATOR) { + break; + } + appendPathStart = appendPath + appendOfs; + } + + appendOfs = 0; + for (;;) { + ch = DecodeNextChar(appendPathStart, appendOfs); + if (!EncodeChar(outPath, outPathSize, outOfs, ch)) { + return false; + } + if (ch == 0) { + return true; + } + } +} + +//============================================================================================== +// ovrUriScheme +//============================================================================================== + +//============================== +// ovrUriScheme::ovrUriScheme +ovrUriScheme::ovrUriScheme(char const* schemeName) : SchemeName(), Uri(""), NumOpenStreams(0) { + OVR::OVR_strcpy(SchemeName, sizeof(SchemeName), schemeName); +} + +//============================== +// ovrUriScheme::~ovrUriScheme +ovrUriScheme::~ovrUriScheme() {} + +//============================== +// ovrUriScheme::GetSchemeName +char const* ovrUriScheme::GetSchemeName() const { + return SchemeName; +} + +//============================== +// ovrUriScheme::AllocStream +ovrStream* ovrUriScheme::AllocStream() const { + return AllocStream_Internal(); +} + +//============================== +// ovrUriScheme::Open +bool ovrUriScheme::OpenHost(char const* hostName, char const* sourceUri) { + assert(ovrUri::IsValidUri(sourceUri)); + if (!OpenHost_Internal(hostName, sourceUri)) { + ALOG("Failed to OpenHost for host '%s', uri '%s'", hostName, sourceUri); + assert(false); + return false; + } + return true; +} + +//============================== +// ovrUriScheme::CloseHost +void ovrUriScheme::CloseHost(char const* hostName) { + CloseHost_Internal(hostName); +} + +//============================== +// ovrUriScheme::Shutdown +void ovrUriScheme::Shutdown() { + assert(NumOpenStreams == 0); // this should never happen -- CLOSE ALL STREAMS AFTER USE. + Shutdown_Internal(); +} + +//============================== +// ovrUriScheme::StreamOpened +void ovrUriScheme::StreamOpened(ovrStream& stream) const { + NumOpenStreams++; + StreamOpened_Internal(stream); +} + +//============================== +// ovrUriScheme::StreamClosed +void ovrUriScheme::StreamClosed(ovrStream& stream) const { + StreamClosed_Internal(stream); + NumOpenStreams--; + assert(NumOpenStreams >= 0); // if this goes negative a stream was closed twice +} + +//============================== +// ovrUriScheme::HostExists +bool ovrUriScheme::HostExists(char const* hostName) const { + return HostExists_Internal(hostName); +} + +//============================================================================================== +// ovrStream +//============================================================================================== + +//============================== +// ovrStream::ovrStream +ovrStream::ovrStream(ovrUriScheme const& scheme) + : Scheme(scheme), Uri(""), Mode(OVR_STREAM_MODE_MAX) {} + +//============================== +// ovrStream::~ovrStream +ovrStream::~ovrStream() { + assert(Mode == OVR_STREAM_MODE_MAX); // if this hits, file was not closed first +} + +//============================== +// ovrStream::Open +bool ovrStream::Open(char const* uri, ovrStreamMode const mode) { + if (IsOpen()) { + ALOG( + "ovrStream::Open: tried to open Uri '%s' when Uri '%s' is already open", + uri, + Uri.c_str()); + assert(!IsOpen()); + return false; + } + + bool success = Open_Internal(uri, mode); + if (success) { + Uri = uri; + Mode = mode; + Scheme.StreamOpened(*this); + } + return success; +} + +//============================== +// ovrStream::Close +void ovrStream::Close() { + Close_Internal(); + Scheme.StreamClosed(*this); + Mode = OVR_STREAM_MODE_MAX; +} + +//============================== +// ovrStream::Flush +void ovrStream::Flush() { + Flush_Internal(); +} + +//============================== +// ovrStream::GetLocalPathFromUri +bool ovrStream::GetLocalPathFromUri(const char* uri, std::string& outputPath) { + return GetLocalPathFromUri_Internal(uri, outputPath); +} + +//============================== +// ovrStream::Read +bool ovrStream::Read( + std::vector& outBuffer, + size_t const bytesToRead, + size_t& outBytesRead) { + if (!IsOpen()) { + ALOG("ovrStream::Read: stream is not open!"); + assert(IsOpen()); + return false; + } + + if (Mode != OVR_STREAM_MODE_READ) { + ALOG("ovrStream::Read: stream is not open for reading!"); + assert(Mode == OVR_STREAM_MODE_READ); + return false; + } + return Read_Internal(outBuffer, bytesToRead, outBytesRead); +} + +//============================== +// ovrStream::ReadFile +bool ovrStream::ReadFile(char const* uri, std::vector& outBuffer) { + assert(IsOpen()); + + return ReadFile_Internal(outBuffer); +} + +//============================== +// ovrStream::Write +bool ovrStream::Write(void const* inBuffer, size_t const bytesToWrite) { + if (!IsOpen()) { + ALOG("ovrStream::Read: stream is not open!"); + assert(IsOpen()); + return false; + } + + if (Mode != OVR_STREAM_MODE_WRITE) { + ALOG("ovrStream::Read: stream is not open for writing!"); + assert(Mode == OVR_STREAM_MODE_WRITE); + return false; + } + return Write_Internal(inBuffer, bytesToWrite); +} + +//============================== +// ovrStream::Tell +size_t ovrStream::Tell() const { + return Tell_Internal(); +} + +//============================== +// ovrStream::Length +size_t ovrStream::Length() const { + return Length_Internal(); +} + +//============================== +// ovrStream::GetUri +char const* ovrStream::GetUri() const { + return Uri.c_str(); +} + +//============================== +// ovrStream::IsOpen +bool ovrStream::IsOpen() const { + return Mode != OVR_STREAM_MODE_MAX; +} + +//============================================================================================== +// ovrUriScheme_File +//============================================================================================== + +//============================== +// ovrUriScheme_File::ovrUriScheme_File +ovrUriScheme_File::ovrUriScheme_File(char const* schemeName) : ovrUriScheme(schemeName) {} + +//============================== +// ovrUriScheme_File::AllocStream_Internal +ovrStream* ovrUriScheme_File::AllocStream_Internal() const { + return new ovrStream_File(*this); +} + +//============================== +// ovrUriScheme_File::HostExists_Internal +bool ovrUriScheme_File::HostExists_Internal(char const* hostName) const { + return FindHostIndexByHostName(hostName) >= 0; +} + +//============================== +// ovrUriScheme_File::OpenHost_Internal +bool ovrUriScheme_File::OpenHost_Internal(char const* hostName, char const* uriSource) { + int index = FindHostIndexByHostName(hostName); + if (index >= 0) { + assert(index < 0); // host already exists + return false; + } + // TODO: Add AllocHost() / AllocHost_Internal() to ovrUriScheme? Requires an ovrUriHost base + // class, though... + ovrFileHost* host = new ovrFileHost(hostName, uriSource); + assert(host != NULL); + + if (!host->Open()) { + return false; + } + Hosts.push_back(host); + return true; +} + +//============================== +// ovrUriScheme_File::CloseHost_Internal +void ovrUriScheme_File::CloseHost_Internal(char const* hostName) { + ovrFileHost* host = FindHostByHostName(hostName); + assert(host != NULL); + if (host != NULL) { + host->Close(); + } +} + +//============================== +// ovrUriScheme_File:: +void ovrUriScheme_File::Shutdown_Internal() {} + +//============================== +// ovrUriScheme_File::FindHostIndexByHostName +int ovrUriScheme_File::FindHostIndexByHostName(char const* hostName) const { + if ((hostName == NULL || hostName[0] == '\0') && Hosts.size() > 0) { + return 0; + } + + for (int i = 0; i < static_cast(Hosts.size()); ++i) { + assert(Hosts[i] != NULL); + if (OVR::OVR_strcmp(Hosts[i]->GetHostName(), hostName) == 0) { + return i; + } + } + return -1; +} + +//============================== +// ovrUriScheme_File::FindHostByHostName +ovrUriScheme_File::ovrFileHost* ovrUriScheme_File::FindHostByHostName(char const* hostName) const { + int index = FindHostIndexByHostName(hostName); + if (index < 0) { + return NULL; + } + return Hosts[index]; +} + +//============================== +// ovruriScheme_File::AddHostSourceUri +void ovrUriScheme_File::AddHostSourceUri(char const* hostName, char const* sourceUri) { + ovrFileHost* host = FindHostByHostName(hostName); + if (host == NULL) { + OpenHost(hostName, sourceUri); + return; + } + host->AddSourceUri(sourceUri); +} + +//============================== +// ovrUriScheme_File::ovrFileHost::Open +bool ovrUriScheme_File::ovrFileHost::Open() { + return true; // file schemes don't need to do anything to open a host +} + +//============================== +// ovrUriScheme_File::ovrFileHost::Close +void ovrUriScheme_File::ovrFileHost::Close() { + // file schemes don't need to do anything to open a host +} + +//============================== +// ovrUriScheme_File::ovrFileHost::AddSourceUri +void ovrUriScheme_File::ovrFileHost::AddSourceUri(char const* sourceUri) { + SourceUris.push_back(std::string(sourceUri)); +} + +//============================================================================================== +// ovrStream_File +//============================================================================================== + +//============================== +// ovrStream_File::ovrStream_File +ovrStream_File::ovrStream_File(ovrUriScheme const& scheme) : ovrStream(scheme), F(NULL) {} + +//============================== +// ovrStream_File::~ovrStream_File +ovrStream_File::~ovrStream_File() {} + +//============================== +// ovrStream_File::GetLocalPathFromUri_Internal +bool ovrStream_File::GetLocalPathFromUri_Internal(const char* uri, std::string& outputPath) { + // require a fully-qualified Uri for now? + char schemeName[128]; + char hostName[128]; + int port; + char uriPath[ovrFileSys::OVR_MAX_SCHEME_LEN]; + if (!ovrUri::ParseUri( + uri, + schemeName, + sizeof(schemeName), + NULL, + 0, + NULL, + 0, + hostName, + sizeof(hostName), + port, + uriPath, + sizeof(uriPath), + NULL, + 0, + NULL, + 0)) { + ALOG("ovrStream_File::GetLocalPathFromUri_Internal: invalid uri '%s'", uri); + assert(false); + return false; + } + size_t uriPathLen = UTF8Util::GetLength(uriPath); + if (uriPathLen < 1) { + assert(uriPathLen > 0); + return false; + } + // on Windows, the URI path may have a /C:/ pattern, in which case we must skip over the leading + // slash AND not prepend the host's sourceUri + char fullPath[ovrFileSys::OVR_MAX_PATH_LEN]; + if (UriPathStartsWithDriveLetter(uriPath)) { + OVR::OVR_sprintf(fullPath, sizeof(fullPath), "%s", SafePathFromUriPath(uriPath)); + outputPath = fullPath; + return true; + } + + ovrUriScheme_File::ovrFileHost* host = GetFileScheme().FindHostByHostName(hostName); + if (host == NULL) { + assert(host != NULL); + return false; + } + + // return the path from the last host + int i = host->GetNumSourceUris() - 1; + if (i >= 0) { + // find the host's base path + char const* sourceUri = host->GetSourceUri(i); + assert(sourceUri != NULL); + // in this case, the URI path should ALWAYS have a leading slash! + assert(uriPath[0] == '/'); + // convert the source uri into a system path + char basePath[ovrFileSys::OVR_MAX_PATH_LEN]; + if (!ovrUri::ParseUri( + sourceUri, + NULL, + 0, + NULL, + 0, + NULL, + 0, + NULL, + 0, + port, + basePath, + sizeof(basePath), + NULL, + 0, + NULL, + 0)) { + ALOG( + "ovrStream_File::GetLocalPathFromUri_Internal: invalid source uri '%s'", sourceUri); + assert(false); + return false; + } + AppendUriPath(SafePathFromUriPath(basePath), uriPath, fullPath, sizeof(fullPath)); + outputPath = fullPath; + return true; + } + + return false; +} + +//============================== +// ovrStream_File::Open_Internal +bool ovrStream_File::Open_Internal(char const* uri, ovrStreamMode const mode) { + if (F != NULL) { + assert(F == NULL); + ALOG("Attempted to open file '%s' with an already open file handle.", uri); + return false; + } + char const* fmode = NULL; + switch (mode) { + case OVR_STREAM_MODE_READ: + fmode = "rb"; + break; + case OVR_STREAM_MODE_WRITE: + fmode = "wb"; + break; + default: + assert(false); + return false; + } + if (fmode == NULL) { + assert(fmode != NULL); + return false; + } + + // require a fully-qualified Uri for now? + char schemeName[128]; + char hostName[128]; + int port; + char uriPath[ovrFileSys::OVR_MAX_SCHEME_LEN]; + if (!ovrUri::ParseUri( + uri, + schemeName, + sizeof(schemeName), + NULL, + 0, + NULL, + 0, + hostName, + sizeof(hostName), + port, + uriPath, + sizeof(uriPath), + NULL, + 0, + NULL, + 0)) { + ALOG("ovrStream_File::Open_Internal: invalid uri '%s'", uri); + assert(false); + return false; + } + size_t uriPathLen = UTF8Util::GetLength(uriPath); + if (uriPathLen < 1) { + assert(uriPathLen > 0); + return false; + } + // on Windows, the URI path may have a /C:/ pattern, in which case we must skip over the leading + // slash AND not prepend the host's sourceUri + char fullPath[ovrFileSys::OVR_MAX_PATH_LEN]; + if (UriPathStartsWithDriveLetter(uriPath)) { + OVR::OVR_sprintf(fullPath, sizeof(fullPath), "%s", SafePathFromUriPath(uriPath)); + F = fopen(fullPath, fmode); + if (F != NULL) { + Uri = uri; + return true; + } else { + ALOGE( + "ovrStream_File::Open_Internal: Failed to open file errno=%d, fullPath=%s", + errno, + fullPath); + } + return false; + } + + ovrUriScheme_File::ovrFileHost* host = GetFileScheme().FindHostByHostName(hostName); + if (host == NULL) { + assert(host != NULL); + return false; + } + + // open the file in the first host path where it exists + for (int i = host->GetNumSourceUris() - 1; i >= 0; --i) { + // find the host's base path + char const* sourceUri = host->GetSourceUri(i); + assert(sourceUri != NULL); + // in this case, the URI path should ALWAYS have a leading slash! + assert(uriPath[0] == '/'); + // convert the source uri into a system path + char basePath[ovrFileSys::OVR_MAX_PATH_LEN]; + if (!ovrUri::ParseUri( + sourceUri, + NULL, + 0, + NULL, + 0, + NULL, + 0, + NULL, + 0, + port, + basePath, + sizeof(basePath), + NULL, + 0, + NULL, + 0)) { + ALOG("ovrStream_File::Open_Internal: invalid source uri '%s'", sourceUri); + assert(false); + return false; + } + AppendUriPath(SafePathFromUriPath(basePath), uriPath, fullPath, sizeof(fullPath)); + // OVR_sprintf( fullPath, sizeof( fullPath ), "%s%s", ovrPathUtils::SafePathFromUriPath( + // basePath ), uriPath ); + F = fopen(fullPath, fmode); + if (F != NULL) { + Uri = uri; + return true; + } else { + ALOGE( + "ovrStream_File::Open_Internal: Failed to open file errno=%d, fullPath=%s", + errno, + fullPath); + } + } + return false; +} + +//============================== +// ovrStream_File::Close_Internal +void ovrStream_File::Close_Internal() { + if (F != NULL) { + fclose(F); + F = NULL; + } +} + +//============================== +// ovrStream_File::Flush_Internal +void ovrStream_File::Flush_Internal() { + if (F != NULL) { + fflush(F); + } +} + +//============================== +// ovrStream_File::Read_Internal +bool ovrStream_File::Read_Internal( + std::vector& outBuffer, + size_t const bytesToRead, + size_t& outBytesRead) { + size_t numRead; + numRead = fread(outBuffer.data(), bytesToRead, 1, F); + outBytesRead = numRead * bytesToRead; + if (numRead != 1) { + ALOG( + "Tried to read %zu bytes from file '%s', but only read %zu bytes.", + bytesToRead, + Uri.c_str(), + outBytesRead); + return false; + } + return true; +} + +//============================== +// ovrStream_File::ReadFile_Internal +bool ovrStream_File::ReadFile_Internal(std::vector& outBuffer) { + std::vector buffer(Length()); + outBuffer = buffer; + size_t bytesRead = 0; + return Read_Internal(outBuffer, outBuffer.size(), bytesRead); +} + +//============================== +// ovrStream_File::Write_Internal +bool ovrStream_File::Write_Internal(void const* inBuffer, size_t const bytesToWrite) { + size_t recsWritten = fwrite(inBuffer, bytesToWrite, 1, F); + if (recsWritten != 1) { + ALOG("Failed to write %zu bytes to file '%s'", bytesToWrite, Uri.c_str()); + return false; + } + return true; +} + +//============================== +// ovrStream_File::Tell_Internal +size_t ovrStream_File::Tell_Internal() const { + return ftell(F); +} + +//============================== +// ovrStream_File::Length_Internal +size_t ovrStream_File::Length_Internal() const { + // remember the current offset + size_t curOfs = ftell(F); + // seek to the end + fseek(F, 0, SEEK_END); + // get the position at the end + size_t len = ftell(F); + // seek back to the original offset + fseek(F, curOfs, SEEK_SET); + return len; +} + +//============================== +// ovrStream_File::AtEnd_Internal +bool ovrStream_File::AtEnd_Internal() const { + return feof(F) != 0; +} + +//============================================================================================== +// ovrUriScheme_Apk +//============================================================================================== + +//============================== +// ovrUriScheme_Apk::ovrUriScheme_Apk +ovrUriScheme_Apk::ovrUriScheme_Apk(char const* schemeName) : ovrUriScheme(schemeName) {} + +//============================== +// ovrUriScheme_Apk::~ovrUriScheme_Apk +ovrUriScheme_Apk::~ovrUriScheme_Apk() {} + +//============================== +// ovrUriScheme_Apk::AllocStream_Internal +ovrStream* ovrUriScheme_Apk::AllocStream_Internal() const { + return new ovrStream_Apk(*this); +} + +//============================== +// ovrUriScheme_Apk:: +bool ovrUriScheme_Apk::HostExists_Internal(char const* hostName) const { + return FindHostIndexByHostName(hostName) >= 0; +} + +//============================== +// ovrUriScheme_Apk::OpenHost_Internal +bool ovrUriScheme_Apk::OpenHost_Internal(char const* hostName, char const* sourceUri) { + int index = FindHostIndexByHostName(hostName); + if (index >= 0) { + assert(index < 0); // host already exists + return false; + } + ovrApkHost* host = new ovrApkHost(hostName, sourceUri); + assert(host != NULL); + + if (!host->Open()) { + return false; + } + Hosts.push_back(host); + return true; +} + +//============================== +// ovrUriScheme_Apk::CloseHost_Internal +void ovrUriScheme_Apk::CloseHost_Internal(char const* hostName) { + ovrApkHost* host = FindHostByHostName(hostName); + assert(host != NULL); + if (host != NULL) { + host->Close(); + } +} + +//============================== +// ovrUriScheme_Apk::FindHostIndexByHostName +int ovrUriScheme_Apk::FindHostIndexByHostName(char const* hostName) const { + if ((hostName == NULL || hostName[0] == '\0') && Hosts.size() > 0) { + return 0; + } + + for (int i = 0; i < static_cast(Hosts.size()); ++i) { + assert(Hosts[i] != NULL); + if (OVR::OVR_strcmp(Hosts[i]->GetHostName(), hostName) == 0) { + return i; + } + } + return -1; +} + +//============================== +// ovrUriScheme_Apk::FindHostByHostName +ovrUriScheme_Apk::ovrApkHost* ovrUriScheme_Apk::FindHostByHostName(char const* hostName) const { + int index = FindHostIndexByHostName(hostName); + if (index < 0) { + return NULL; + } + return Hosts[index]; +} + +//============================== +// ovrUriScheme_Apk::GetZipFileForHostName +void* ovrUriScheme_Apk::GetZipFileForHostName(char const* hostName) const { + ovrApkHost* host = FindHostByHostName(hostName); + if (host == NULL) { + assert(host != NULL); + return NULL; + } + return host->GetZipFile(); +} + +//============================== +// ovrUriScheme_Apk::ovrApkHost::Open +bool ovrUriScheme_Apk::ovrApkHost::Open() { + char scheme[ovrFileSys::OVR_MAX_SCHEME_LEN]; + char username[128]; + char password[128]; + char hostName[ovrFileSys::OVR_MAX_HOST_NAME_LEN]; + int port; + char path[ovrFileSys::OVR_MAX_PATH_LEN]; + char query[1024]; + char fragment[1024]; + + bool const valid = ovrUri::ParseUri( + SourceUri.c_str(), + scheme, + sizeof(scheme), + username, + sizeof(username), + password, + sizeof(password), + hostName, + sizeof(hostName), + port, + path, + sizeof(path), + query, + sizeof(query), + fragment, + sizeof(fragment)); + + // we expect apks to use a file scheme + OVR_UNUSED(valid); + assert(valid && OVR::OVR_stricmp(scheme, "file") == 0); + + ZipFile = ovr_OpenOtherApplicationPackage(path); + if (ZipFile == NULL) { + ALOG("Failed to open apk: '%s'", SourceUri.c_str()); + return false; + } + return true; +} + +//============================== +// ovrUriScheme_Apk::ovrApkHost::Close +void ovrUriScheme_Apk::ovrApkHost::Close() { + assert(ZipFile != NULL); + if (ZipFile != NULL) { + ovr_CloseOtherApplicationPackage(ZipFile); + } + assert(ZipFile == NULL); +} + +//============================== +// ovrUriScheme_Apk::Shutdown_Internal +void ovrUriScheme_Apk::Shutdown_Internal() { + // close all hosts + for (int i = 0; i < static_cast(Hosts.size()); ++i) { + Hosts[i]->Close(); + delete Hosts[i]; + Hosts[i] = NULL; + } + Hosts.clear(); +} + +//============================================================================================== +// ovrStream_Apk +//============================================================================================== + +//============================== +// ovrStream_Apk::ovrStream_Apk +ovrStream_Apk::ovrStream_Apk(ovrUriScheme const& scheme) : ovrStream(scheme), IsOpen(false) {} + +//============================== +// ovrStream_Apk::~ovrStream_Apk +ovrStream_Apk::~ovrStream_Apk() {} + +//============================== +// ovrStream_Apk::GetLocalPathFromUri_Internal +bool ovrStream_Apk::GetLocalPathFromUri_Internal(const char* uri, std::string& outputPath) { + // can't get path to file inside of zip + return false; +} + +//============================== +// ovrStream_Apk::Open_Internal +bool ovrStream_Apk::Open_Internal(char const* uri, ovrStreamMode const mode) { + if (IsOpen) { + ALOG("ovrStream_Apk: tried to open uri '%s' when '%s' is already open", uri, GetUri()); + assert(!IsOpen); + return false; + } + + if (mode != OVR_STREAM_MODE_READ) { + assert(mode == OVR_STREAM_MODE_READ); + ALOG("Only OVR_STREAM_MODE_READ is supported for apks! Uri: '%s'", uri); + return false; + } + + char hostName[ovrFileSys::OVR_MAX_HOST_NAME_LEN]; + int port; + char path[ovrFileSys::OVR_MAX_SCHEME_LEN]; + if (!ovrUri::ParseUri( + uri, + NULL, + 0, + NULL, + 0, + NULL, + 0, + hostName, + sizeof(hostName), + port, + path, + sizeof(path), + NULL, + 0, + NULL, + 0)) { + ALOG("ovrStream_Apk::Open_Internal: invalid Uri '%s'", uri); + return false; + } + + // get the zip file for this host + void* zipFile = GetApkScheme().GetZipFileForHostName(hostName); + if (zipFile == NULL) { + ALOG("ovrStream_Apk::Open_Internal: no zip file for uri '%s', host '%s'", uri, hostName); + return false; + } + + // inside of zip files, the leading slash will cause the file to not be found, so skip it + char const* pathStart = (path[0] == '/') ? path + 1 : path; + IsOpen = ovr_OtherPackageFileExists(zipFile, pathStart); + return IsOpen; +} + +//============================== +// ovrStream_Apk::Close_Internal +void ovrStream_Apk::Close_Internal() { + IsOpen = false; +} + +//============================== +// ovrStream_Apk::Flush_Internal +void ovrStream_Apk::Flush_Internal() { + // no-op +} + +//============================== +// ovrStream_Apk::Read_Internal +bool ovrStream_Apk::Read_Internal( + std::vector& outBuffer, + size_t const bytesToRead, + size_t& outBytesRead) { + assert(false); // TODO: cannot read partial files from an apk yet + return false; +} + +//============================== +// ovrStream_Apk::ReadFile_Internal +bool ovrStream_Apk::ReadFile_Internal(std::vector& outBuffer) { + char hostName[ovrFileSys::OVR_MAX_HOST_NAME_LEN]; + int port; + char path[ovrFileSys::OVR_MAX_SCHEME_LEN]; + if (!ovrUri::ParseUri( + GetUri(), + NULL, + 0, + NULL, + 0, + NULL, + 0, + hostName, + sizeof(hostName), + port, + path, + sizeof(path), + NULL, + 0, + NULL, + 0)) { + ALOG("ovrStream_Apk::ReadFile_Internal: invalid Uri '%s'", GetUri()); + return false; + } + + void* zipFile = GetApkScheme().GetZipFileForHostName(hostName); + + // inside of zip files, the leading slash will cause the file to not be found, so skip it + char const* pathStart = (path[0] == '/') ? path + 1 : path; + + return ovr_ReadFileFromOtherApplicationPackage(zipFile, pathStart, outBuffer); +} + +//============================== +// ovrStream_Apk::Write_Internal +bool ovrStream_Apk::Write_Internal(void const* inBuffer, size_t const bytesToWrite) { + assert(false); // can never write to an apk + return false; +} + +//============================== +// ovrStream_Apk::Tell_Internal +size_t ovrStream_Apk::Tell_Internal() const { + assert(false); // TODO: cannot read partial files from an apk yet + return 0; +} + +//============================== +// ovrStream_Apk::Length_Internal +size_t ovrStream_Apk::Length_Internal() const { + assert(false); // TODO: cannot read partial files from an apk yet + return 0; +} + +//============================== +// ovrStream_Apk::AtEnd_Internal +bool ovrStream_Apk::AtEnd_Internal() const { + assert(false); // TODO: cannot read partial files from an apk yet + return true; +} + +} // namespace OVRFW diff --git a/Samples/SampleXrFramework/Src/OVR_Stream.h b/Samples/SampleXrFramework/Src/OVR_Stream.h new file mode 100755 index 0000000..d31a7e6 --- /dev/null +++ b/Samples/SampleXrFramework/Src/OVR_Stream.h @@ -0,0 +1,104 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/************************************************************************************ + +Filename : OVR_Stream.h +Content : Abstraction for file streams. +Created : July 1, 2015 +Authors : Jonathan E. Wright + +*************************************************************************************/ + +#pragma once + +#include +#include + +namespace OVRFW { + +enum ovrStreamMode { OVR_STREAM_MODE_READ, OVR_STREAM_MODE_WRITE, OVR_STREAM_MODE_MAX }; + +class ovrUriScheme; + +//============================================================== +// ovrStream +// base class for streams +// Uses non-public virtual interface idiom so that the base class +// can preform pre- and post- operations when calling virtualized +// methods. +class ovrStream { + public: + ovrStream(ovrUriScheme const& scheme); + virtual ~ovrStream(); + + // Opens a stream for the specified Uri. + bool Open(char const* Uri, ovrStreamMode const mode); + + // Closes the currently open stream. + void Close(); + + // Flushes the stream + void Flush(); + + bool GetLocalPathFromUri(const char* uri, std::string& outputPath); + + // Reads the specified number of bytes from the stream into outBuffer. + // outBytesRead will contain the number of bytes read into outBuffer. + // - If the number of bytes specified is read into the buffer successfully, true is returned. + // - If the number of bytes read is too large for the buffer, the buffer is filled and false is + // returned. + // - If the number of bytes in the file is less than the number requested, the buffer is filled + // with the + // remaining bytes and false is returned. + bool Read(std::vector& outBuffer, size_t const bytesToRead, size_t& outBytesRead); + + // Allocates a buffer large enough to fit the stream resource and reads the stream into it. + bool ReadFile(char const* uri, std::vector& outBuffer); + + // Writes the specified number of bytes to the stream. + // - If writing fails, false is returned. + bool Write(void const* inBuffer, size_t const bytesToWrite); + + // Returns the current offset in the file + size_t Tell() const; + + // Returns the length of the file. + size_t Length() const; + + // returns true if at the end of the stream + bool AtEnd() const; + + char const* GetUri() const; + + bool IsOpen() const; + + protected: + ovrUriScheme const& GetScheme() const { + return Scheme; + } + + private: + ovrUriScheme const& Scheme; + std::string Uri; + ovrStreamMode Mode; + + private: + virtual bool GetLocalPathFromUri_Internal(const char* uri, std::string& outputPath) = 0; + virtual bool Open_Internal(char const* Uri, ovrStreamMode const mode) = 0; + virtual void Close_Internal() = 0; + virtual void Flush_Internal() = 0; + virtual bool Read_Internal( + std::vector& outBuffer, + size_t const bytesToRead, + size_t& outBytesRead) = 0; + virtual bool ReadFile_Internal(std::vector& outBuffer) = 0; + virtual bool Write_Internal(void const* inBuffer, size_t const bytesToWrite) = 0; + virtual size_t Tell_Internal() const = 0; + virtual size_t Length_Internal() const = 0; + virtual bool AtEnd_Internal() const = 0; + + // Private assignment operator to prevent copying. + ovrStream& operator=(ovrStream& rhs); +}; + +} // namespace OVRFW diff --git a/Samples/SampleXrFramework/Src/OVR_Stream_Impl.h b/Samples/SampleXrFramework/Src/OVR_Stream_Impl.h new file mode 100755 index 0000000..ee77cc3 --- /dev/null +++ b/Samples/SampleXrFramework/Src/OVR_Stream_Impl.h @@ -0,0 +1,266 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/************************************************************************************ + +Filename : OVR_Stream_Impl.h +Content : Implementations of file streams classes. +Created : July 2, 2015 +Authors : Jonathan E. Wright + +*************************************************************************************/ + +#pragma once + +#include + +#include +#include + +#include "OVR_Types.h" +#include "OVR_Stream.h" +// #include "OVR_FileSys.h" + +namespace OVRFW { +//============================================================== +// ovrUriScheme +// +// Uses the non-public virtual interface idiom so that the base class +// can do pre- and post- operations when calling overloaded methods on +// derived classes. +class ovrUriScheme { + public: + ovrUriScheme(char const* schemeName); + virtual ~ovrUriScheme(); + + char const* GetSchemeName() const; + + ovrStream* AllocStream() const; + bool OpenHost(char const* hostName, char const* sourceUri); + void CloseHost(char const* hostName); + + void Shutdown(); + + void StreamOpened(ovrStream& stream) const; + void StreamClosed(ovrStream& stream) const; + + bool HostExists(char const* hostName) const; + + private: + char SchemeName[ovrFileSys::OVR_MAX_SCHEME_LEN]; + std::string Uri; + + mutable std::atomic + NumOpenStreams; // this is used to catch the case where a stream is open when we shutdown. + + private: + virtual ovrStream* AllocStream_Internal() const = 0; + virtual bool OpenHost_Internal(char const* hostName, char const* sourceUri) = 0; + virtual void CloseHost_Internal(char const* hostName) = 0; + virtual void Shutdown_Internal() = 0; + virtual void StreamOpened_Internal(ovrStream& stream) const {} + virtual void StreamClosed_Internal(ovrStream& stream) const {} + virtual bool HostExists_Internal(char const* hostName) const = 0; +}; + +//============================================================== +// ovrUriScheme_File +class ovrUriScheme_File : public ovrUriScheme { + public: + ovrUriScheme_File(char const* schemeName); + + void AddHostSourceUri(char const* hostName, char const* sourceUri); + + class ovrFileHost { + public: + ovrFileHost() : HostName("") {} + + ovrFileHost(char const* hostName, char const* sourceUri) : HostName(hostName) { + SourceUris.push_back(std::string(sourceUri)); + } + + ovrFileHost(ovrFileHost& other) : HostName(other.HostName) {} + + ~ovrFileHost() {} + + ovrFileHost& operator=(ovrFileHost& rhs) { + if (this != &rhs) { + this->HostName = rhs.HostName; + this->SourceUris = rhs.SourceUris; + rhs.HostName = ""; + rhs.SourceUris.clear(); + } + return *this; + } + + bool Open(); + void Close(); + + char const* GetHostName() const { + return HostName.c_str(); + } + char const* GetSourceUri(int const index) const { + return SourceUris[index].c_str(); + } + int GetNumSourceUris() const { + return static_cast(SourceUris.size()); + } + void AddSourceUri(char const* sourceUri); + + private: + std::string HostName; // localhost or machine name on Windows + std::vector + SourceUris; // all the base paths for files loaded through this host + }; + + int FindHostIndexByHostName(char const* hostName) const; + ovrFileHost* FindHostByHostName(char const* hostName) const; + + private: + std::vector Hosts; + + private: + virtual ovrStream* AllocStream_Internal() const OVR_OVERRIDE; + virtual bool OpenHost_Internal(char const* hostName, char const* sourceUri) OVR_OVERRIDE; + virtual void CloseHost_Internal(char const* hostName) OVR_OVERRIDE; + virtual void Shutdown_Internal() OVR_OVERRIDE; + virtual bool HostExists_Internal(char const* hostName) const OVR_OVERRIDE; +}; + +//============================================================== +// ovrUriScheme_Apk +class ovrUriScheme_Apk : public ovrUriScheme { + public: + ovrUriScheme_Apk(char const* schemeName); + ~ovrUriScheme_Apk() override; + + void* GetZipFileForHostName(char const* hostName) const; + + private: + class ovrApkHost { + public: + ovrApkHost() : HostName(""), SourceUri(""), ZipFile(NULL) {} + + ovrApkHost(char const* hostName, char const* sourceUri) + : HostName(hostName), SourceUri(sourceUri), ZipFile(NULL) {} + + ovrApkHost(ovrApkHost& other) + : HostName(other.HostName), SourceUri(other.SourceUri), ZipFile(other.ZipFile) { + // leave host and package names for debugging if we try to use a copied host. + // other.HostName = ""; + // other.SourceUri = ""; + other.ZipFile = NULL; + } + + ~ovrApkHost() { + OVR_ASSERT(ZipFile == NULL); // if this hits the host hasn't been closed on delete + } + + ovrApkHost& operator=(ovrApkHost& rhs) { + if (this != &rhs) { + this->HostName = rhs.HostName; + this->SourceUri = rhs.SourceUri; + this->ZipFile = rhs.ZipFile; + rhs.HostName = ""; + rhs.SourceUri = ""; + rhs.ZipFile = NULL; + } + return *this; + } + + bool Open(); + void Close(); + + char const* GetHostName() const { + return HostName.c_str(); + } + char const* GetSourceUri() const { + return SourceUri.c_str(); + } + void* GetZipFile() const { + return ZipFile; + } + + private: + std::string HostName; // com.oculus.appname + std::string SourceUri; // file:///data/app/com.oculus.appname-1.apk + void* ZipFile; // pointer to the open apk file + }; + + std::vector Hosts; + + private: + virtual ovrStream* AllocStream_Internal() const OVR_OVERRIDE; + virtual bool OpenHost_Internal(char const* hostName, char const* sourceUri) OVR_OVERRIDE; + virtual void CloseHost_Internal(char const* hostName) OVR_OVERRIDE; + virtual void Shutdown_Internal() OVR_OVERRIDE; + virtual bool HostExists_Internal(char const* hostName) const OVR_OVERRIDE; + + int FindHostIndexByHostName(char const* hostName) const; + ovrApkHost* FindHostByHostName(char const* hostName) const; +}; + +//============================================================== +// ovrStream_File +class ovrStream_File : public ovrStream { + public: + ovrStream_File(ovrUriScheme const& scheme); + ~ovrStream_File() override; + + private: + FILE* F; + std::string Uri; + + private: + virtual bool GetLocalPathFromUri_Internal(const char* uri, std::string& outputPath) + OVR_OVERRIDE; + virtual bool Open_Internal(char const* uri, ovrStreamMode const mode) OVR_OVERRIDE; + virtual void Close_Internal() OVR_OVERRIDE; + virtual void Flush_Internal() OVR_OVERRIDE; + virtual bool Read_Internal( + std::vector& outBuffer, + size_t const bytesToRead, + size_t& outBytesRead) OVR_OVERRIDE; + virtual bool ReadFile_Internal(std::vector& outBuffer) OVR_OVERRIDE; + virtual bool Write_Internal(void const* inBuffer, size_t const bytesToWrite) OVR_OVERRIDE; + virtual size_t Tell_Internal() const OVR_OVERRIDE; + virtual size_t Length_Internal() const OVR_OVERRIDE; + virtual bool AtEnd_Internal() const OVR_OVERRIDE; + + ovrUriScheme_File const& GetFileScheme() const { + return *static_cast(&GetScheme()); + } +}; + +//============================================================== +// ovrStream_Apk +class ovrStream_Apk : public ovrStream { + public: + ovrStream_Apk(ovrUriScheme const& scheme); + ~ovrStream_Apk() override; + + private: + std::string HostName; + bool IsOpen; + + private: + virtual bool GetLocalPathFromUri_Internal(const char* uri, std::string& outputPath) + OVR_OVERRIDE; + virtual bool Open_Internal(char const* uri, ovrStreamMode const mode) OVR_OVERRIDE; + virtual void Close_Internal() OVR_OVERRIDE; + virtual void Flush_Internal() OVR_OVERRIDE; + virtual bool Read_Internal( + std::vector& outBuffer, + size_t const bytesToRead, + size_t& outBytesRead) OVR_OVERRIDE; + virtual bool ReadFile_Internal(std::vector& outBuffer) OVR_OVERRIDE; + virtual bool Write_Internal(void const* inBuffer, size_t const bytesToWrite) OVR_OVERRIDE; + virtual size_t Tell_Internal() const OVR_OVERRIDE; + virtual size_t Length_Internal() const OVR_OVERRIDE; + virtual bool AtEnd_Internal() const OVR_OVERRIDE; + + ovrUriScheme_Apk const& GetApkScheme() const { + return *static_cast(&GetScheme()); + } +}; + +} // namespace OVRFW diff --git a/Samples/SampleXrFramework/Src/OVR_UTF8Util.cpp b/Samples/SampleXrFramework/Src/OVR_UTF8Util.cpp new file mode 100755 index 0000000..b366bcb --- /dev/null +++ b/Samples/SampleXrFramework/Src/OVR_UTF8Util.cpp @@ -0,0 +1,534 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * All rights reserved. + * + * Licensed under the Oculus SDK License Agreement (the "License"); + * you may not use the Oculus SDK except in compliance with the License, + * which is provided at the time of installation or download, or which + * otherwise accompanies this software in either electronic or hard copy form. + * + * You may obtain a copy of the License at + * + * https://developer.oculus.com/licenses/oculussdk/ + * + * Unless required by applicable law or agreed to in writing, the Oculus SDK + * 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. + */ + +/************************************************************************** + +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 + +************************************************************************************/ + +#include "OVR_UTF8Util.h" +#include + +namespace OVRFW { +namespace UTF8Util { + +intptr_t GetLength(const char* buf, intptr_t buflen) { + const char* p = buf; + intptr_t 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_t GetCharAt(intptr_t index, const char* putf8str, intptr_t length) { + const char* buf = putf8str; + uint32_t 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. + assert(index == 0); + return c; + } + } while (index >= 0); + + return c; +} + +intptr_t GetByteIndex(intptr_t index, const char* putf8str, intptr_t 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_t c = UTF8Util::DecodeNextChar_Advance0(&buf); + index--; + + if (c == 0) + return buf - putf8str; + }; + + return buf - putf8str; +} + +int GetEncodeCharSize(uint32_t 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_t DecodeNextChar_Advance0(const char** putf8Buffer) { + uint32_t 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_t)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 EncodeChar(char* pbuffer, intptr_t* pindex, uint32_t 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)++] = static_cast(0x80 | (char)((ucs_character >> 0) & 0x3F)); + } else if (ucs_character <= 0xFFFF) { + // Three bytes. + pbuffer[(*pindex)++] = 0xE0 | (char)(ucs_character >> 12); + pbuffer[(*pindex)++] = static_cast(0x80 | (char)((ucs_character >> 6) & 0x3F)); + pbuffer[(*pindex)++] = static_cast(0x80 | (char)((ucs_character >> 0) & 0x3F)); + } else if (ucs_character <= 0x1FFFFF) { + // Four bytes. + pbuffer[(*pindex)++] = 0xF0 | (char)(ucs_character >> 18); + pbuffer[(*pindex)++] = static_cast(0x80 | (char)((ucs_character >> 12) & 0x3F)); + pbuffer[(*pindex)++] = static_cast(0x80 | (char)((ucs_character >> 6) & 0x3F)); + pbuffer[(*pindex)++] = static_cast(0x80 | (char)((ucs_character >> 0) & 0x3F)); + } else if (ucs_character <= 0x3FFFFFF) { + // Five bytes. + pbuffer[(*pindex)++] = 0xF8 | (char)(ucs_character >> 24); + pbuffer[(*pindex)++] = static_cast(0x80 | (char)((ucs_character >> 18) & 0x3F)); + pbuffer[(*pindex)++] = static_cast(0x80 | (char)((ucs_character >> 12) & 0x3F)); + pbuffer[(*pindex)++] = static_cast(0x80 | (char)((ucs_character >> 6) & 0x3F)); + pbuffer[(*pindex)++] = static_cast(0x80 | (char)((ucs_character >> 0) & 0x3F)); + } else if (ucs_character <= 0x7FFFFFFF) { + // Six bytes. + pbuffer[(*pindex)++] = 0xFC | (char)(ucs_character >> 30); + pbuffer[(*pindex)++] = static_cast(0x80 | (char)((ucs_character >> 24) & 0x3F)); + pbuffer[(*pindex)++] = static_cast(0x80 | (char)((ucs_character >> 18) & 0x3F)); + pbuffer[(*pindex)++] = static_cast(0x80 | (char)((ucs_character >> 12) & 0x3F)); + pbuffer[(*pindex)++] = static_cast(0x80 | (char)((ucs_character >> 6) & 0x3F)); + pbuffer[(*pindex)++] = static_cast(0x80 | (char)((ucs_character >> 0) & 0x3F)); + } else { + // Invalid char; don't encode anything. + } +} + +intptr_t GetEncodeStringSize(const wchar_t* pchar, intptr_t length) { + intptr_t 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 EncodeString(char* pbuff, const wchar_t* pchar, intptr_t length) { + intptr_t 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; +} + +size_t DecodeString(wchar_t* pbuff, const char* putf8str, intptr_t bytesLen) { + wchar_t* pbegin = pbuff; + if (bytesLen == -1) { + while (1) { + uint32_t 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_t 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_t* ucs_in) { + for (;;) { + uint32_t next_ucs = *ucs_in++; + uint32_t next_ucs_from_utf8 = utf8::decode_next_unicode_character(&utf8_in); + if (next_ucs != next_ucs_from_utf8) { + return false; + } + if (next_ucs == 0) { + 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_t* line) { + for (;;) { + uint32_t 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_t test32[] = { + 0x49, + 0x67, + 0x6E, + 0x61, + 0x63, + 0x69, + 0x6F, + 0x20, + 0x43, + 0x61, + 0x73, + 0x74, + 0x61, + 0xF1, + 0x6F, + 0x00}; + + 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_t 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_t* q = line_buffer_ucs; + for (;;) { + uint32_t uc = UTF8Util::DecodeNextChar(&p); + *q++ = uc; + + assert(q < line_buffer_ucs + LINE_SIZE); + 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_t uc = *q++; + assert(index < LINE_SIZE * 6 - 6); + int last_index = index; + UTF8Util::EncodeChar(reencoded_utf8, &index, uc); + 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 + + assert(check_equal(line_buffer_utf8, line_buffer_ucs)); + assert(check_equal(reencoded_utf8, line_buffer_ucs)); + + // Start next line. + byte_counter = 0; + } + } + + fclose(fp); + } + + return 0; +} +#endif // UTF8_UNIT_TEST + +bool DecodePrevChar(char const* p, intptr_t& offset, uint32_t& ch) { + if (offset <= 0) { + ch = '\0'; + return false; + } + for (int i = 1; i < 6; ++i) { + intptr_t ofs = offset - i; + if (ofs < 0) { + ch = '\0'; + return false; + } + char t = *(p + ofs); + if ((t & 0x80) == 0) { + // normal ascii char + offset = ofs; + ch = t; + return true; + } + // if not a low-ascii, the the byte must start with 11 if it's a leading character byte, or + // 10 otherwise + else if ( + (t & 0xE0) == 0xC0 || (t & 0xF0) == 0xE0 || (t & 0xF8) == 0xF0 || (t & 0xFC) == 0xF8 || + (t & 0xFE) == 0xFC) { + // leading byte, decode from here + char const* tp = p + ofs; + ch = UTF8Util::DecodeNextChar(&tp); + offset = ofs; + return true; + } else if ((t & 0xC0) != 0x80) { + // this is not a UTF8 encoding + assert(false); + return false; + } + } + return false; +} + +void AppendChar(std::string& s, uint32_t ch) { + char buff[8] = {0}; + intptr_t encodeSize = 0; + + // Converts ch into UTF8 string and fills it into buff. + UTF8Util::EncodeChar(buff, &encodeSize, ch); + assert(encodeSize >= 0); + s += buff; +} + +} // namespace UTF8Util +} // namespace OVRFW diff --git a/Samples/SampleXrFramework/Src/OVR_UTF8Util.h b/Samples/SampleXrFramework/Src/OVR_UTF8Util.h new file mode 100755 index 0000000..ff5e895 --- /dev/null +++ b/Samples/SampleXrFramework/Src/OVR_UTF8Util.h @@ -0,0 +1,96 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * All rights reserved. + * + * Licensed under the Oculus SDK License Agreement (the "License"); + * you may not use the Oculus SDK except in compliance with the License, + * which is provided at the time of installation or download, or which + * otherwise accompanies this software in either electronic or hard copy form. + * + * You may obtain a copy of the License at + * + * https://developer.oculus.com/licenses/oculussdk/ + * + * Unless required by applicable law or agreed to in writing, the Oculus SDK + * 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. + */ + +/************************************************************************************ + * Filename : OVR_UTF8Util.h + * Content : UTF8 Unicode character encoding/decoding support + * Created : September 19, 2012 + * Notes : + ***********************************************************************************/ + +#pragma once + +#include + +namespace OVRFW { +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. +intptr_t GetLength(const char* putf8str, intptr_t 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_t GetCharAt(intptr_t index, const char* putf8str, intptr_t length = -1); + +// Converts UTF8 character index into byte offset. +// -1 is returned if index was out of bounds. +intptr_t GetByteIndex(intptr_t index, const char* putf8str, intptr_t 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. +intptr_t GetEncodeStringSize(const wchar_t* pchar, intptr_t length = -1); + +// Encodes a unicode (UCS-2 only) string into a buffer. The size of buffer must be at +// least GetEncodeStringSize() + 1. +void EncodeString(char* pbuff, const wchar_t* pchar, intptr_t 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) +size_t DecodeString(wchar_t* pbuff, const char* putf8str, intptr_t bytesLen = -1); + +// *** Individual character Encoding/Decoding. + +// Determined the number of bytes necessary to encode a UCS character. +int GetEncodeCharSize(uint32_t 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 EncodeChar(char* pbuffer, intptr_t* poffset, uint32_t 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_t DecodeNextChar_Advance0(const char** putf8Buffer); + +// Safer version of DecodeNextChar, which doesn't advance pointer if +// null character is hit. +inline uint32_t DecodeNextChar(const char** putf8Buffer) { + uint32_t ch = DecodeNextChar_Advance0(putf8Buffer); + if (ch == 0) + (*putf8Buffer)--; + return ch; +} + +bool DecodePrevChar(char const* p, intptr_t& offset, uint32_t& ch); + +void AppendChar(std::string& s, uint32_t ch); + +} // namespace UTF8Util +} // namespace OVRFW diff --git a/Samples/SampleXrFramework/Src/OVR_Uri.cpp b/Samples/SampleXrFramework/Src/OVR_Uri.cpp new file mode 100755 index 0000000..c5ee80b --- /dev/null +++ b/Samples/SampleXrFramework/Src/OVR_Uri.cpp @@ -0,0 +1,669 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/************************************************************************************ + +Filename : OVR_Uri.cpp +Content : URI parser. +Created : July 1, 2015 +Authors : Jonathan E. Wright + +*************************************************************************************/ + +#include "OVR_Uri.h" +#include +#include "Misc/Log.h" +#include "OVR_UTF8Util.h" +#include + +// If enabled, allow URI's missing a scheme to be considered valid. This is valuable +// if we want to allow for old paths to work by defaulting the scheme. +#define TOLERATE_MISSING_SCHEME +// If enabled, allow URI's that have a host but no path to be considered valid. +// #define TOLERATE_HOST_ONLY + +#if defined(OVR_BUILD_DEBUG) +#define URI_LOG(_fmt_, ...) ALOG(_fmt_, __VA_ARGS__) +#else +#define URI_LOG(_fmt_, ...) \ + if (!InUnitTest) { \ + ALOG(_fmt_, __VA_ARGS__); \ + } +#endif + +namespace OVRFW { + +bool ovrUri::InUnitTest = false; + +//============================================================== +// UTF8Decoder +// allows decoding of a UTF string, peeking ahead without changing the current decode +// location and "backing up" up to two code points if we want to decode characters again. +class UTF8Decoder { + public: + explicit UTF8Decoder(char const* source) + : Source(source), Cur(source), Prev(NULL), PrevPrev(NULL) {} + + explicit UTF8Decoder(UTF8Decoder const& other) + : Source(other.Source), Cur(other.Cur), Prev(other.Prev), PrevPrev(other.PrevPrev) {} + + UTF8Decoder& operator=(UTF8Decoder const& rhs) { + if (this != &rhs) { + Source = rhs.Source; + Cur = rhs.Cur; + Prev = rhs.Prev; + PrevPrev = rhs.PrevPrev; + } + return *this; + } + + uint32_t DecodeNext() { + PrevPrev = Prev; + Prev = Cur; + return UTF8Util::DecodeNextChar(&Cur); + } + + uint32_t PeekNext() { + char const* temp = Cur; + return UTF8Util::DecodeNextChar(&temp); + } + + uint32_t PeekNextNext() { + char const* temp = Cur; + UTF8Util::DecodeNextChar(&temp); + return UTF8Util::DecodeNextChar(&temp); + } + + void Backup() { + if (Prev == NULL) { + assert(Prev != NULL); + return; + } + Cur = Prev; + Prev = PrevPrev; + PrevPrev = NULL; + } + + char const* GetCur() const { + return Cur; + } + void Reset() { + Cur = Source; + Prev = NULL; + PrevPrev = NULL; + } + + private: + char const* Source; // the string we're decoding + char const* Cur; // the current decode point + char const* Prev; // the previous decode point + char const* PrevPrev; // the previos, previous decode point +}; + +//============================== +// ovrUri::ParseScheme +bool ovrUri::ParseScheme(char const* uri, char* outScheme, size_t const outSchemeSize) { + int port = 0; + return ParseUri( + uri, outScheme, outSchemeSize, NULL, 0, NULL, 0, NULL, 0, port, NULL, 0, NULL, 0, NULL, 0); +} + +//============================== +// EncodeCharToBuffer +// only returns false if the buffer overflows -- NULL out pointers are just skipped +static bool +EncodeCharToBuffer(uint32_t const ch, char* out, size_t const outSize, ptrdiff_t& outOffset) { + if (out == NULL) { + // if the output buffer is null, just skip + return true; + } + + assert(outSize > 1); + + // test to see if the character encoding will overflow the buffer + char encodeSizeBuff[4]; + ptrdiff_t encodeSize = 0; + UTF8Util::EncodeChar(encodeSizeBuff, &encodeSize, ch); + if (static_cast(outOffset + encodeSize) >= outSize) { + // output buffer is full + out[outOffset] = '\0'; + return false; + } + + // just re-encode into the real buffer + UTF8Util::EncodeChar(out, &outOffset, ch); + return true; +} + +static bool IsLegalHostCharacter(uint32_t const ch) { + return ch != ' '; +} + +//============================== +// ovrUri::ParseUri +bool ovrUri::ParseUri( + char const* uri, + char* outScheme, + size_t const outSchemeSize, + char* outUsername, + size_t const outUsernameSize, + char* outPassword, + size_t const outPasswordSize, + char* outHost, + size_t const outHostSize, + int& outPort, + char* outPath, + size_t const outPathSize, + char* outQuery, + size_t const outQuerySize, + char* outFragment, + size_t const outFragmentSize) { + // verify all parameters are either NULL or large enough to hold a meaningful path + if (outScheme != NULL) { + if (outSchemeSize < 2) { + assert(outSchemeSize > 1); + return false; + } + outScheme[0] = '\0'; + } + if (outHost != NULL) { + if (outHostSize < 2) { + assert(outHostSize > 1); + return false; + } + outHost[0] = '\0'; + } + if (outUsername != NULL) { + if (outUsernameSize < 2) { + assert(outUsernameSize > 1); + return false; + } + outUsername[0] = '\0'; + } + if (outPassword != NULL) { + if (outPasswordSize < 2) { + assert(outPasswordSize > 1); + return false; + } + outPassword[0] = '\0'; + } + if (outPath != NULL) { + if (outPathSize < 2) { + assert(outPathSize > 1); + return false; + } + outPath[0] = '\0'; + } + if (outQuery != NULL) { + if (outQuerySize < 2) { + assert(outQuerySize > 1); + return false; + } + outQuery[0] = '\0'; + } + if (outFragment != NULL) { + if (outFragmentSize < 2) { + assert(outFragmentSize > 1); + return false; + } + outFragment[0] = '\0'; + } + outPort = 0; + + // encode UTF8 to outputs as we go + UTF8Decoder decoder(uri); + uint32_t ch = 0; + + // Parse the scheme. The scheme is everything up to the first colon + { + ptrdiff_t schemeOffset = 0; + + for (;;) { + ch = decoder.DecodeNext(); + if (ch == '\0') { + // if we get here then we didn't have a colon and there's no scheme + if (outScheme != NULL) { + outScheme[0] = '\0'; + } +#if defined(TOLERATE_MISSING_SCHEME) // tolerant to missing scheme, but not clear if that's + // desirable + // try parsing as a host + decoder.Reset(); + break; +#else // error on scheme only + LOG("Uri '%s': scheme only!", uri); + return false; +#endif + } else if (ch == ':') { + // 0-terminate + if (outScheme != NULL) { + outScheme[schemeOffset] = '\0'; + // scheme must at least 1 character in length and start with a letter + if (schemeOffset < 1) { + // assert( schemeOffset >= 1 ); + ALOG("Uri '%s' has an empty scheme!", uri); + return false; + } + } + // a valid scheme must be followed by // + if (decoder.PeekNext() != '/' || decoder.PeekNextNext() != '/') { + // assert( false ); + ALOG( + "Uri '%s': valid scheme must be followed by a host separator ( '//' ).", + uri); + return false; + } + // done with scheme + break; + } + + if (schemeOffset == 0) { + if (ch == '/') { +#if defined(TOLERATE_MISSING_SCHEME) // tolerant to missing scheme, but not clear if that's + // desirable + // this may be the start of a path or host + // go back to the start of the string and continue parsing as if it were a host + decoder.Reset(); + break; +#else // error on missing scheme + LOG("Uri '%s': missing scheme", uri); + return false; +#endif + } else if (!isalpha(ch)) { + // assert( schemeOffset > 0 || isalpha( ch ) ); + ALOG("Uri '%s': scheme does not start with a letter.", uri); + if (outScheme != NULL) { + outScheme[0] = '\0'; + } + return false; + } + } + + if (!EncodeCharToBuffer(ch, outScheme, outSchemeSize, schemeOffset)) { + ALOG("Uri '%s': scheme buffer overflow!", uri); + return false; + } + } + } + + // the scheme must be followed by // if there is a host + if (decoder.PeekNext() == '/' && decoder.PeekNextNext() == '/') { + // skip the two forward slashes + decoder.DecodeNext(); + decoder.DecodeNext(); + + UTF8Decoder hostStartDecoder(decoder); + + bool parsedUsername = false; + bool usernameOverflowed = false; + bool passwordOverflowed = false; + + ptrdiff_t usernameOffset = 0; + ptrdiff_t passwordOffset = 0; + + // scan for username / password followed by @ + for (;;) { + ch = decoder.DecodeNext(); + if (ch == '\0' || ch == '/') { + // we reached the end of the host section or the uri without finding a username / + // password reset to the start of the host and continue trying to parse a host + decoder = hostStartDecoder; + if (outUsername != NULL) { + outUsername[0] = '\0'; + } + if (outPassword != NULL) { + outPassword[0] = '\0'; + } + break; + } + if (ch == ':') { + if (parsedUsername) { + ALOG("Uri '%s': Malformed authority!", uri); + return false; + } + // start parsing the password now + parsedUsername = true; + if (outUsername != NULL) { + outUsername[usernameOffset] = '\0'; + } + continue; + } else if (ch == '@') { + // continue to parse the host + if (usernameOverflowed) { + ALOG("Uri '%s': username buffer overflow!", uri); + } + if (passwordOverflowed) { + ALOG("Uri '%s': password buffer overflow!", uri); + } + if (outPassword != NULL) { + outPassword[passwordOffset] = '\0'; + } + break; + } + + if (!parsedUsername) { + if (!usernameOverflowed && + !EncodeCharToBuffer(ch, outUsername, outUsernameSize, usernameOffset)) { + // don't consider this an error until we're sure that we're parsing a username + // and not a host + usernameOverflowed = true; + break; + } + } else { + if (!passwordOverflowed && + !EncodeCharToBuffer(ch, outPassword, outPasswordSize, passwordOffset)) { + // don't consider this an error until we're sure that we're parsing a password + // and not a host + passwordOverflowed = true; + break; + } + } + } + + // There is a host so parse the host. The host is everything up to the next forward slash ( + // or colon if there is a port ) The host does not include the // + ptrdiff_t hostOffset = 0; + + for (;;) { + ch = decoder.DecodeNext(); + if (ch == ':') { + // parse out the port + ptrdiff_t portOffset = 0; + char portString[128]; + portString[0] = '\0'; + for (;;) { + ch = decoder.DecodeNext(); + if (ch == '/' || ch == '\0') { + // got to the end if the Uri or the start of the path + portString[portOffset] = '\0'; + outPort = atoi(portString); + if (ch == '\0') { + // finished parsing the Uri + if (outPath != NULL) { + // assert( outPath != NULL && ch != '\0' ); + ALOG( + "Uri '%s': found host:port without path when a path was expected!", + uri); + return false; + } else { + ALOG("Uri '%s': parsed host:port without a path!", uri); + } + return true; + } + // continue parsing + break; + } else if (!isdigit(ch)) { + ALOG("Uri '%s' has a non-numeric port!", uri); + // assert( isdigit( ch ) ); + return false; + } + + if (!EncodeCharToBuffer(ch, &portString[0], sizeof(portString), portOffset)) { + ALOG("Uri '%s': port buffer overflow!", uri); + return false; + } + } + } + + // check for the end of the host or the end of the string -- we may have just parsed the + // port at this point + if (ch == '/' || ch == '\0') { + // end of the host + if (outHost != NULL) { + outHost[hostOffset] = '\0'; + } + if (ch == '\0') { +#if defined(TOLERATE_HOST_ONLY) + // return hostOffset > 0 ? true : false; + // it's technically ok for a URI to only specify the host. + // It's not enough information by itself to find a resource, but we do not know + // if the client is going to compose a URI in parts outside of this. + return true; +#else + if (hostOffset < 1) { + ALOG("Uri '%s': missing host!", uri); + // assert( hostOffset > 0 ); + return false; + } +#endif + } + // un-parse the forward slash because it must be part of the path + decoder.Backup(); + break; // reached a '/' + } + + if (!IsLegalHostCharacter(ch)) { + ALOG("Uri '%s': illegal character 0x%x in host.", uri, ch); + if (outHost != NULL) { + outHost[0] = '\0'; + } + return false; + } + if (!EncodeCharToBuffer(ch, outHost, outHostSize, hostOffset)) { + ALOG("Uri '%s': host buffer overflow!", uri); + return false; + } + } + } + + // parse the path as everything up to the first query indicator (?) or the end of the string + { + ptrdiff_t pathOffset = 0; + + for (;;) { + ch = decoder.DecodeNext(); + if (ch == '\0' || ch == '?') { + // zero-terminate + if (outPath != NULL) { + outPath[pathOffset] = '\0'; + if (pathOffset == 1 && outPath[0] == '/') { + ALOG("Uri '%s': empty path!", uri); + return false; + } + } + if (ch == '\0') { + return true; + } + break; // reached the start of the query + } + + if (!EncodeCharToBuffer(ch, outPath, outPathSize, pathOffset)) { + ALOG("Uri '%s': path buffer overflow", uri); + return false; + } + } + } + + // parse the query up to the first fragment indicator (#) or the end of the string + { + intptr_t queryOffset = 0; + + for (;;) { + ch = decoder.DecodeNext(); + if (ch == '\0' || ch == '#') { + // zero-terminate + if (outQuery != NULL) { + outQuery[queryOffset] = '\0'; + } + if (ch == '\0') { + return true; + } + break; // reached the start of the fragment + } + + if (!EncodeCharToBuffer(ch, outQuery, outQuerySize, queryOffset)) { + ALOG("Uri '%s': query buffer overflow", uri); + return false; + } + } + } + + // parse the fragment to the end of the string + { + intptr_t fragmentOffset = 0; + + for (;;) { + ch = decoder.DecodeNext(); + if (ch == '\0') { + // zero-terminate + if (outFragment != NULL) { + outFragment[fragmentOffset] = '\0'; + } + return true; + } + + if (!EncodeCharToBuffer(ch, outFragment, outFragmentSize, fragmentOffset)) { + ALOG("Uri '%s': fragment buffer overflow", uri); + return false; + } + } + } +} + +bool ovrUri::IsValidUri(char const* uri) { + char scheme[128]; + char username[128]; + char password[128]; + char host[256]; + int port; + char path[1024]; + char query[1024]; + char fragment[1024]; + + bool const valid = ovrUri::ParseUri( + uri, + scheme, + sizeof(scheme), + username, + sizeof(username), + password, + sizeof(password), + host, + sizeof(host), + port, + path, + sizeof(path), + query, + sizeof(query), + fragment, + sizeof(fragment)); + return valid; +} + +static void LogResult(char const* name, char const* value) { +#if defined(OVR_BUILD_DEBUG) + ALOG("%s: %s", name, value != NULL ? value : ""); +#endif +} + +static void LogResult(char const* name, int const value) { +#if defined(OVR_BUILD_DEBUG) + ALOG("%s: %i", name, value); +#endif +} + +static void ReportTest( + char const* testName, + char const* uri, + bool const isValid, + bool const success, + char const* scheme, + char const* username, + char const* password, + char const* host, + int const port, + char const* path, + char const* query, + char const* fragment) { + char const* failMsg = NULL; + if (isValid) { + failMsg = !success ? "FAILED TO PARSE VALID URI" : NULL; + } else if (!isValid) { + failMsg = success ? "INCORRECTLY PARSED INVALID URI" : NULL; + } + + if (failMsg != NULL) { + ALOG("Test %s", testName); + ALOG("URI: %s", uri); + ALOG("%s", failMsg); + LogResult("scheme", scheme); + LogResult("username", username); + LogResult("password", password); + LogResult("host", host); + LogResult("port", port); + LogResult("path", path); + LogResult("query", query); + LogResult("fragment", fragment); + assert(failMsg == NULL); + } +} + +static void Test(char const* testName, char const* uri, bool const isValid) { + char scheme[128]; + char username[128]; + char password[128]; + char host[256]; + int port; + char path[1024]; + char query[1024]; + char fragment[1024]; + + bool const success = ovrUri::ParseUri( + uri, + scheme, + sizeof(scheme), + username, + sizeof(username), + password, + sizeof(password), + host, + sizeof(host), + port, + path, + sizeof(path), + query, + sizeof(query), + fragment, + sizeof(fragment)); + ReportTest( + testName, + uri, + isValid, + success, + scheme, + username, + password, + host, + port, + path, + query, + fragment); +} + +void ovrUri::DoUnitTest() { + InUnitTest = true; // don't show debug info in release + + Test("1", "file:///sdcard/oculus/360Photos/pic.jpg", true); + Test("2", "http://puzz.s3.amazonaws.com/2008/07/galerija_equirectangular.jpg", true); + Test("3", "apk:///assets/default.jpg", true); + Test("4", "http://puzz.s3.amazonaws.com:8008/2008/07/galerija_equirectangular.jpg", true); +#if defined(TOLERATE_MISSING_SCHEME) + bool const missingSchemeOk = true; +#else + bool const missingSchemeOk = false; +#endif + Test("Path without Scheme", "/sdcard/oculus/360Photos/pic.jpg", missingSchemeOk); + Test("Path without Scheme 2", "sdcard/oculus/360Photos/pic.jpg", missingSchemeOk); + Test("Malformed scheme", ":///sdcard/oculus/360Photos/pic.jpg", false); + Test("6", "No scheme colon", missingSchemeOk); + Test("7", "file:No host double slash", false); + Test("8", "file:/No host double slash", false); + Test("File scheme with illegal spaces in host", "file://No path slash", false); + Test("http scheme with a host but no path", "http://www.oculus.com", true); + Test("http scheme only", "http:", false); + Test("http scheme missing host", "http://", false); + Test("http scheme with empty host and empty path", "http:///", false); + + InUnitTest = false; +} + +} // namespace OVRFW diff --git a/Samples/SampleXrFramework/Src/OVR_Uri.h b/Samples/SampleXrFramework/Src/OVR_Uri.h new file mode 100755 index 0000000..96e8711 --- /dev/null +++ b/Samples/SampleXrFramework/Src/OVR_Uri.h @@ -0,0 +1,97 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/************************************************************************************ + +Filename : OVR_Uri.h +Content : URI parser. +Created : July 1, 2015 +Authors : Jonathan E. Wright + +*************************************************************************************/ + +#pragma once + +#include // for size_t +#include + +namespace OVRFW { + +//============================================================== +// ovrUri +// +// Parses URIs for validity and returns component pieces. The URI is considered valid +// even if it is only a partial URI. It is up to the caller to determine if it has +// enough of a URI to locate a resource. This is because we only want to validate +// structure with this class. This leaves a client of this class able to compose URIs +// from multiple sources and still able to use this class for validation of each +// piece. +// +// If a scheme and host are missing, the URI is parsed as if it were a path only. +// Clients of this class can then decide how to handle the path for a default case. +// +// Paths following a host name: file://windows_machine_name/c:/temp/readme.txt +// will always have the leading path separator at the beginning. In the above case +// the parse will result in: +// scheme: file +// host: windows_machine_name +// path: /c:/temp/readme.txt +// +// Which seems incorrect for the drive letter case. But is correct according to parse +// examples here: https://en.wikipedia.org/wiki/URI_scheme where the path does include +// the first slash after the host section. +// file://windows_machine_name/temp/readme.txt results in: +// scheme: file +// host: windows_machine_name +// path: /temp/readme.txt +// Clients need to take this into account when appending the path to what they consider +// to be the host root. On a Windows machine, the host root is unclear without the drive +// letter specification. +// +// Note that the scheme never includes the trailing colon and the host never includes +// the leading double forward slashes. +// Note spaces in host names are considered invalid. +// Spaces in paths are currently allowed but really these should have to be escaped. +// +// Features: +// - supports UTF-8 +// - supports authority (username and password) +// - supports queries +// - supports fragments +// +// TODO: support escaped characters (%20), etc. + +class ovrUri { + public: + static const int MAX_URI_SIZE = 1024; + + // if any of the out pointers are NULL, that part of the Uri will not be returned + static bool ParseUri( + char const* uri, + char* outScheme, + size_t const outSchemeSize, + char* outUsername, + size_t const outUsernameSize, + char* outPassword, + size_t const outPasswordSize, + char* outHost, + size_t const outHostSize, + int& outPort, + char* outPath, + size_t const outPathSize, + char* outQuery, + size_t outQuerySize, + char* outFragment, + size_t outFragmentSize); + + // Parses out just the scheme. This will fail if the URI is not properly formed (i.e. if it's + // only a scheme without a path. + static bool ParseScheme(char const* uri, char* outScheme, size_t const outSchemeSize); + + static bool IsValidUri(char const* uri); + + static void DoUnitTest(); + + static bool InUnitTest; +}; + +} // namespace OVRFW diff --git a/Samples/SampleXrFramework/Src/PackageFiles.cpp b/Samples/SampleXrFramework/Src/PackageFiles.cpp new file mode 100755 index 0000000..fabbb53 --- /dev/null +++ b/Samples/SampleXrFramework/Src/PackageFiles.cpp @@ -0,0 +1,292 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/************************************************************************************ + +Filename : PackageFiles.cpp +Content : Read files from the application package zip +Created : August 18, 2014 +Authors : John Carmack + +*************************************************************************************/ + +#include "PackageFiles.h" + +#include "Misc/Log.h" +#include "OVR_Std.h" + +#include + +#include +#include +#include + +#if !defined(OVR_OS_WIN32) +#include +#endif + +#include +#include +#include + +namespace OVRFW { + +// Decompressed files can be written here for faster access next launch +static char CachePath[1024]; + +const char* ovr_GetApplicationPackageCachePath() { + return CachePath; +} + +OvrApkFile::OvrApkFile(void* zipFile) : ZipFile(zipFile) {} + +OvrApkFile::~OvrApkFile() { + ovr_CloseOtherApplicationPackage(ZipFile); +} + +//-------------------------------------------------------------- +// Functions for reading assets from other application packages +//-------------------------------------------------------------- + +void* ovr_OpenOtherApplicationPackage(const char* packageCodePath) { + void* zipFile = unzOpen(packageCodePath); + +// enable the following block if you need to see the list of files in the application package +// This is useful for finding a file added in one of the res/ sub-folders (necesary if you want +// to include a resource file in every project that links VrAppFramework). +#if 0 + // enumerate the files in the package for us so we can see if the vrappframework res/raw files are in there + if ( unzGoToFirstFile( zipFile ) == UNZ_OK ) + { + LOG( "FilesInPackage", "Files in package:" ); + do + { + unz_file_info fileInfo; + char fileName[512]; + if ( unzGetCurrentFileInfo( zipFile, &fileInfo, fileName, sizeof( fileName ), NULL, 0, NULL, 0 ) == UNZ_OK ) + { + LOG( "FilesInPackage", "%s", fileName ); + } + } while ( unzGoToNextFile( zipFile ) == UNZ_OK ); + } +#endif + return zipFile; +} + +void ovr_CloseOtherApplicationPackage(void*& zipFile) { + if (zipFile == 0) { + return; + } + unzClose(zipFile); + zipFile = 0; +} + +static std::mutex PackageFileMutex; + +bool ovr_OtherPackageFileExists(void* zipFile, const char* nameInZip) { + std::lock_guard mutex(PackageFileMutex); + + const int locateRet = unzLocateFile(zipFile, nameInZip, 2 /* case insensitive */); + if (locateRet != UNZ_OK) { + ALOG("File '%s' not found in apk!", nameInZip); + return false; + } + + const int openRet = unzOpenCurrentFile(zipFile); + if (openRet != UNZ_OK) { + ALOGW("Error opening file '%s' from apk!", nameInZip); + return false; + } + + unzCloseCurrentFile(zipFile); + + return true; +} + +static bool ovr_ReadFileFromOtherApplicationPackageInternal( + void* zipFile, + const char* nameInZip, + int& length, + void*& buffer, + std::function allocBuffer, + std::function freeBuffer) { + length = 0; + buffer = NULL; + if (zipFile == 0) { + return false; + } + +#if !defined(OVR_OS_WIN32) + std::lock_guard mutex(PackageFileMutex); + + const int locateRet = unzLocateFile(zipFile, nameInZip, 2 /* case insensitive */); + + if (locateRet != UNZ_OK) { + ALOG("File '%s' not found in apk!", nameInZip); + return false; + } + + unz_file_info info; + const int getRet = unzGetCurrentFileInfo(zipFile, &info, NULL, 0, NULL, 0, NULL, 0); + + if (getRet != UNZ_OK) { + ALOGW("File info error reading '%s' from apk!", nameInZip); + return false; + } + + // Check for an already extracted cache file based on the CRC if + // the file is compressed. + if (info.compression_method != 0 && CachePath[0]) { + char cacheName[1024]; + snprintf(cacheName, sizeof(cacheName), "%s/%08x.bin", CachePath, (unsigned)info.crc); + const int fd = open(cacheName, O_RDONLY); + if (fd > 0) { + struct stat s = {}; + + if (fstat(fd, &s) != -1) { + // LOG( "Loading cached file for: %s", nameInZip ); + length = s.st_size; + if (length != (int)info.uncompressed_size) { + ALOG( + "Cached file for %s has length %i != %lu", + nameInZip, + length, + info.uncompressed_size); + // Fall through to normal load. + } else { + buffer = allocBuffer(length); + const int r = read(fd, buffer, length); + close(fd); + if (r != length) { + ALOG("Cached file for %s only read %i != %i", nameInZip, r, length); + freeBuffer(buffer); + // Fall through to normal load. + } else { // Got the cached file. + return true; + } + } + } + close(fd); + } + } else { + // LOG( "Not compressed: %s", nameInZip ); + } + + const int openRet = unzOpenCurrentFile(zipFile); + if (openRet != UNZ_OK) { + ALOGW("Error opening file '%s' from apk!", nameInZip); + return false; + } + + length = info.uncompressed_size; + buffer = allocBuffer(length); + + const int readRet = unzReadCurrentFile(zipFile, buffer, length); + if (readRet != length) { + ALOGW("Error reading file '%s' from apk!", nameInZip); + freeBuffer(buffer); + length = 0; + buffer = NULL; + return false; + } + + unzCloseCurrentFile(zipFile); + + // Optionally write out to the cache directory + if (info.compression_method != 0 && CachePath[0]) { + char tempName[1024]; + snprintf(tempName, sizeof(tempName), "%s/%08x.tmp", CachePath, (unsigned)info.crc); + + char cacheName[1024]; + snprintf(cacheName, sizeof(cacheName), "%s/%08x.bin", CachePath, (unsigned)info.crc); + const int fd = open(tempName, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR); + if (fd > 0) { + const int r = write(fd, buffer, length); + close(fd); + if (r == length) { + if (rename(tempName, cacheName) == -1) { + ALOG("Failed to rename cache file for %s", nameInZip); + } else { + ALOG("Cache file generated for %s", nameInZip); + } + } else { + ALOG("Only wrote %i of %i for cached %s", r, length, nameInZip); + } + } else { + ALOG("Failed to open new cache file for %s: %s", nameInZip, tempName); + } + } + + return true; +#else + // No equivalent of other application package on windows. + return false; +#endif // !defined(OVR_OS_WIN32) +} + +bool ovr_ReadFileFromOtherApplicationPackage( + void* zipFile, + const char* nameInZip, + std::vector& outBuffer) { + // Dummy parameters + int length = 0; + void* buffer = nullptr; + + // allocate using buffer resize + auto allocBuffer = [&](const size_t size) { + outBuffer.resize(size); + return outBuffer.data(); + }; + + auto freeBuffer = [&](void* buffer) { outBuffer.resize(0); }; + + return ovr_ReadFileFromOtherApplicationPackageInternal( + zipFile, nameInZip, length, buffer, allocBuffer, freeBuffer); +} + +bool ovr_ReadFileFromOtherApplicationPackage( + void* zipFile, + const char* nameInZip, + int& length, + void*& buffer) { + // allocate using malloc / free + auto allocBuffer = [](const size_t size) { return malloc(size); }; + + auto freeBuffer = [](void* buffer) { free(buffer); }; + + return ovr_ReadFileFromOtherApplicationPackageInternal( + zipFile, nameInZip, length, buffer, allocBuffer, freeBuffer); +} + +//-------------------------------------------------------------- +// Functions for reading assets from this process's application package +//-------------------------------------------------------------- + +static unzFile packageZipFile = 0; + +void* ovr_GetApplicationPackageFile() { + return packageZipFile; +} + +void ovr_OpenApplicationPackage(const char* packageCodePath, const char* cachePath_) { + if (packageZipFile) { + return; + } + if (cachePath_ != NULL) { + OVR::OVR_strncpy(CachePath, sizeof(CachePath), cachePath_, sizeof(CachePath) - 1); + } + packageZipFile = ovr_OpenOtherApplicationPackage(packageCodePath); +} + +bool ovr_PackageFileExists(const char* nameInZip) { + return ovr_OtherPackageFileExists(packageZipFile, nameInZip); +} + +bool ovr_ReadFileFromApplicationPackage(const char* nameInZip, int& length, void*& buffer) { + return ovr_ReadFileFromOtherApplicationPackage(packageZipFile, nameInZip, length, buffer); +} + +bool ovr_ReadFileFromApplicationPackage(const char* nameInZip, std::vector& buffer) { + return ovr_ReadFileFromOtherApplicationPackage(packageZipFile, nameInZip, buffer); +} + +} // namespace OVRFW diff --git a/Samples/SampleXrFramework/Src/PackageFiles.h b/Samples/SampleXrFramework/Src/PackageFiles.h new file mode 100755 index 0000000..cbac037 --- /dev/null +++ b/Samples/SampleXrFramework/Src/PackageFiles.h @@ -0,0 +1,96 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/************************************************************************************ + +Filename : PackageFiles.h +Content : Read files from the application package zip +Created : August 18, 2014 +Authors : John Carmack + +*************************************************************************************/ + +#pragma once + +#include + +// The application package is the moral equivalent of the filesystem, so +// I don't feel too bad about making it globally accessible, versus requiring +// an App pointer to be handed around to everything that might want to load +// a texture or model. + +namespace OVRFW { + +//============================================================== +// OvrApkFile +// RAII class for application packages +//============================================================== +class OvrApkFile { + public: + OvrApkFile(void* zipFile); + ~OvrApkFile(); + + operator void*() const { + return ZipFile; + } + operator bool() const { + return ZipFile != 0; + } + + private: + void* ZipFile; +}; + +//-------------------------------------------------------------- +// Functions for reading assets from other application packages +//-------------------------------------------------------------- + +// Call this to open a specific package and use the returned handle in calls to functions for +// loading from other application packages. +void* ovr_OpenOtherApplicationPackage(const char* packageName); + +// Call this to close another application package after loading resources from it. +void ovr_CloseOtherApplicationPackage(void*& zipFile); + +// These are probably NOT thread safe! +bool ovr_OtherPackageFileExists(void* zipFile, const char* nameInZip); + +// Returns NULL buffer if the file is not found. +bool ovr_ReadFileFromOtherApplicationPackage( + void* zipFile, + const char* nameInZip, + int& length, + void*& buffer); +bool ovr_ReadFileFromOtherApplicationPackage( + void* zipFile, + const char* nameInZip, + std::vector& buffer); + +//-------------------------------------------------------------- +// Functions for reading assets from this process's application package +//-------------------------------------------------------------- + +// returns the zip file for the applications own package +void* ovr_GetApplicationPackageFile(); + +// Returns something like "/data/data/com.oculus.vrscript/cache/" +// Applications can write files here and expect them to be cleaned up on +// application uninstall, unlike writing to /sdcard. Also, this is on a +// proper linux filesystem, so exec permissions can be set. +const char* ovr_GetApplicationPackageCachePath(); + +// App.cpp calls this very shortly after startup. +// If cachePath is not NULL, compressed files that are read will be written +// out to the cachePath with the CRC as the filename so they can be read +// back in much faster. +void ovr_OpenApplicationPackage(const char* packageName, const char* cachePath); + +// These are probably NOT thread safe! +bool ovr_PackageFileExists(const char* nameInZip); + +// Returns NULL buffer if the file is not found. +bool ovr_ReadFileFromApplicationPackage(const char* nameInZip, int& length, void*& buffer); + +// Returns an empty MemBufferFile if the file is not found. +bool ovr_ReadFileFromApplicationPackage(const char* nameInZip, std::vector& buffer); + +} // namespace OVRFW diff --git a/Samples/SampleXrFramework/Src/Render/BeamRenderer.cpp b/Samples/SampleXrFramework/Src/Render/BeamRenderer.cpp new file mode 100755 index 0000000..182facb --- /dev/null +++ b/Samples/SampleXrFramework/Src/Render/BeamRenderer.cpp @@ -0,0 +1,426 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/************************************************************************************ + +Filename : BeamRenderer.cpp +Content : Class that manages and renders view-oriented beams. +Created : October 23; 2015 +Authors : Jonathan E. Wright + +*************************************************************************************/ + +#include "BeamRenderer.h" +#include "TextureAtlas.h" + +#include "Misc/Log.h" + +using OVR::Matrix4f; +using OVR::Posef; +using OVR::Quatf; +using OVR::Vector2f; +using OVR::Vector3f; +using OVR::Vector4f; + +inline Vector3f GetViewMatrixPosition(Matrix4f const& m) { + return m.Inverted().GetTranslation(); +} + +namespace OVRFW { + +static const char* BeamVertexSrc = R"glsl( +attribute highp vec4 Position; +attribute lowp vec4 VertexColor; +attribute highp vec2 TexCoord; + +varying lowp vec4 outColor; +varying highp vec2 oTexCoord; + +void main() +{ + gl_Position = TransformVertex( Position ); + oTexCoord = TexCoord; + outColor = VertexColor; +} +)glsl"; + +static const char* TextureFragmentSrc = R"glsl( +uniform sampler2D Texture0; + +varying lowp vec4 outColor; +varying highp vec2 oTexCoord; + +void main() +{ + gl_FragColor = outColor * texture2D( Texture0, oTexCoord ); +} +)glsl"; + +static const char* ParametricFragmentSrc = R"glsl( +precision highp float; + +varying lowp vec4 outColor; +varying highp vec2 oTexCoord; + +void main() +{ + vec2 v = (oTexCoord - vec2(0.5)) * vec2(2.0); + + // Fade toward the end of the beam + float forwardFade = 1.0 - oTexCoord.y * oTexCoord.y; + + // Fade out from center of beam to the sides + float sideFade = 1.0 - abs((oTexCoord.x - 0.5) * 2.0); + + float r = sideFade * forwardFade; + gl_FragColor = outColor * vec4(r,r,r,r); +} +)glsl"; + +float ovrBeamRenderer::LIFETIME_INFINITE = FLT_MAX; + +//============================== +// ovrBeamRenderer::ovrBeamRenderer +ovrBeamRenderer::ovrBeamRenderer() : MaxBeams(0) {} + +//============================== +// ovrBeamRenderer::ovrBeamRenderer +ovrBeamRenderer::~ovrBeamRenderer() { + Shutdown(); +} + +//============================== +// ovrBeamRenderer::Init +void ovrBeamRenderer::Init(const int maxBeams, const bool depthTest) { + Shutdown(); + + MaxBeams = maxBeams; + + if (TextureProgram.VertexShader == 0 || TextureProgram.FragmentShader == 0) { + OVRFW::ovrProgramParm uniformParms[] = { + /// Vertex + /// Fragment + {"Texture0", OVRFW::ovrProgramParmType::TEXTURE_SAMPLED}, + }; + const int uniformCount = sizeof(uniformParms) / sizeof(OVRFW::ovrProgramParm); + TextureProgram = + OVRFW::GlProgram::Build(BeamVertexSrc, TextureFragmentSrc, uniformParms, uniformCount); + } + if (ParametricProgram.VertexShader == 0 || ParametricProgram.FragmentShader == 0) { + ParametricProgram = + OVRFW::GlProgram::Build(BeamVertexSrc, ParametricFragmentSrc, nullptr, 0); + } + + const int numVerts = maxBeams * 4; + + VertexAttribs attr; + attr.position.resize(numVerts); + attr.uv0.resize(numVerts); + attr.color.resize(numVerts); + + // the indices will never change once we've set them up; we just won't necessarily + // use all of the index buffer to render. + std::vector indices; + indices.resize(MaxBeams * 6); + + for (int i = 0; i < MaxBeams; i++) { + indices[i * 6 + 0] = static_cast(i * 4 + 0); + indices[i * 6 + 1] = static_cast(i * 4 + 1); + indices[i * 6 + 2] = static_cast(i * 4 + 3); + indices[i * 6 + 3] = static_cast(i * 4 + 0); + indices[i * 6 + 4] = static_cast(i * 4 + 3); + indices[i * 6 + 5] = static_cast(i * 4 + 2); + } + + Surf.surfaceName = "beams"; + Surf.geo.Create(attr, indices); + Surf.geo.primitiveType = GL_TRIANGLES; + Surf.geo.indexCount = 0; + + ovrGraphicsCommand& gc = Surf.graphicsCommand; + gc.GpuState.depthEnable = gc.GpuState.depthMaskEnable = depthTest; + gc.GpuState.blendEnable = ovrGpuState::BLEND_ENABLE; + gc.GpuState.blendSrc = GL_SRC_ALPHA; + gc.GpuState.blendDst = GL_ONE; + gc.Program = TextureProgram; + gc.GpuState.lineWidth = 1.0f; +} + +//============================== +// ovrBeamRenderer::Shutdown +void ovrBeamRenderer::Shutdown() { + Surf.geo.Free(); + OVRFW::GlProgram::Free(TextureProgram); + OVRFW::GlProgram::Free(ParametricProgram); + + MaxBeams = 0; + FreeBeams.resize(0); + ActiveBeams.resize(0); + BeamInfos.resize(0); +} + +//============================== +// ovrBeamRenderer::AddBeam +ovrBeamRenderer::handle_t ovrBeamRenderer::AddBeam( + const OVRFW::ovrApplFrameIn& frame, + const ovrTextureAtlas& atlas, + const int atlasIndex, + const float width, + const Vector3f& startPos, + const Vector3f& endPos, + const Vector4f& initialColor, + const float lifeTime) { + handle_t handle; + + // ALOG( "ovrBeamRenderer::AddDebugLine" ); + if (FreeBeams.size() > 0) { + handle = FreeBeams[static_cast(FreeBeams.size()) - 1]; + FreeBeams.pop_back(); + } else { + handle = handle_t(static_cast(BeamInfos.size())); + if (handle.Get() >= MaxBeams || handle.Get() >= MAX_BEAMS) { + return handle_t(); + } + BeamInfos.push_back(ovrBeamInfo()); + } + + assert(handle.IsValid()); + assert(handle.Get() < static_cast(BeamInfos.size())); + assert(handle.Get() < MAX_BEAMS); + + ActiveBeams.push_back(handle); + + UpdateBeamInternal( + frame, handle, &atlas, atlasIndex, width, startPos, endPos, initialColor, lifeTime); + + return (lifeTime == LIFETIME_INFINITE) ? handle : handle_t(); +} + +//============================== +// ovrBeamRenderer::AddBeam +ovrBeamRenderer::handle_t ovrBeamRenderer::AddBeam( + const OVRFW::ovrApplFrameIn& frame, + const float width, + const Vector3f& startPos, + const Vector3f& endPos, + const OVR::Vector4f& initialColor) { + handle_t handle; + + // ALOG( "ovrBeamRenderer::AddDebugLine" ); + if (FreeBeams.size() > 0) { + handle = FreeBeams[static_cast(FreeBeams.size()) - 1]; + FreeBeams.pop_back(); + } else { + handle = handle_t(static_cast(BeamInfos.size())); + if (handle.Get() >= MaxBeams || handle.Get() >= MAX_BEAMS) { + return handle_t(); + } + BeamInfos.push_back(ovrBeamInfo()); + } + + assert(handle.IsValid()); + assert(handle.Get() < static_cast(BeamInfos.size())); + assert(handle.Get() < MAX_BEAMS); + ActiveBeams.push_back(handle); + + UpdateBeamInternal( + frame, handle, nullptr, 0, width, startPos, endPos, initialColor, LIFETIME_INFINITE); + + return handle; +} + +//============================== +// ovrBeamRenderer::UpdateBeam +void ovrBeamRenderer::UpdateBeam( + const OVRFW::ovrApplFrameIn& frame, + const handle_t handle, + const ovrTextureAtlas& atlas, + const int atlasIndex, + const float width, + const Vector3f& startPos, + const Vector3f& endPos, + const Vector4f& initialColor) { + assert(BeamInfos[handle.Get()].Handle.IsValid()); + UpdateBeamInternal( + frame, + handle, + &atlas, + atlasIndex, + width, + startPos, + endPos, + initialColor, + LIFETIME_INFINITE); +} + +void ovrBeamRenderer::UpdateBeam( + const OVRFW::ovrApplFrameIn& frame, + const handle_t handle, + const float width, + const Vector3f& startPos, + const Vector3f& endPos, + const Vector4f& initialColor) { + assert(BeamInfos[handle.Get()].Handle.IsValid()); + UpdateBeamInternal( + frame, handle, nullptr, 0, width, startPos, endPos, initialColor, LIFETIME_INFINITE); +} + +void ovrBeamRenderer::RemoveBeam(const handle_t handle) { + if (!handle.IsValid() || handle.Get() >= BeamInfos.size()) { + return; + } + BeamInfos[handle.Get()].StartTime = -1.0; + BeamInfos[handle.Get()].LifeTime = -1.0f; +} + +//============================== +// ovrBeamRenderer::UpdateBeamInternal +void ovrBeamRenderer::UpdateBeamInternal( + const OVRFW::ovrApplFrameIn& frame, + const handle_t handle, + const ovrTextureAtlas* atlas, + const int atlasIndex, + const float width, + const Vector3f& startPos, + const Vector3f& endPos, + const Vector4f& initialColor, + float const lifeTime) { + if (!handle.IsValid()) { + assert(handle.IsValid()); + return; + } + + ovrBeamInfo& beam = BeamInfos[handle.Get()]; + + beam.Handle = handle; + beam.StartTime = frame.PredictedDisplayTime; + beam.LifeTime = lifeTime; + beam.Width = width; + beam.AtlasIndex = static_cast(atlasIndex); + beam.StartPos = startPos; + beam.EndPos = endPos; + beam.InitialColor = initialColor; + if (atlas == nullptr) { + beam.TexCoords[0] = {0.0f, 0.0f}; // min tex coords + beam.TexCoords[1] = {1.0f, 1.0f}; // max tex coords + } else { + const ovrTextureAtlas::ovrSpriteDef& sd = atlas->GetSpriteDef(atlasIndex); + beam.TexCoords[0] = sd.uvMins; // min tex coords + beam.TexCoords[1] = sd.uvMaxs; // max tex coords + } +} + +//============================== +// ovrBeamRenderer::Frame +void ovrBeamRenderer::Frame( + const OVRFW::ovrApplFrameIn& frame, + const Matrix4f& centerViewMatrix, + const ovrTextureAtlas& atlas) { + FrameInternal(frame, centerViewMatrix, &atlas); +} +void ovrBeamRenderer::Frame(const OVRFW::ovrApplFrameIn& frame, const Matrix4f& centerViewMatrix) { + FrameInternal(frame, centerViewMatrix, nullptr); +} + +//============================== +// ovrBeamRenderer::Frame +void ovrBeamRenderer::FrameInternal( + const OVRFW::ovrApplFrameIn& frame, + const OVR::Matrix4f& centerViewMatrix, + const class ovrTextureAtlas* atlas) { + /// we + if (atlas) { + Surf.geo.indexCount = 0; + Surf.graphicsCommand.Textures[0] = atlas->GetTexture(); + Surf.graphicsCommand.BindUniformTextures(); + Surf.graphicsCommand.Program = TextureProgram; + } else { + Surf.graphicsCommand.Program = ParametricProgram; + } + + VertexAttribs attr; + attr.position.resize(ActiveBeams.size() * 4); + attr.color.resize(ActiveBeams.size() * 4); + attr.uv0.resize(ActiveBeams.size() * 4); + + const Vector3f viewPos = GetViewMatrixPosition(centerViewMatrix); + + int quadIndex = 0; + for (int i = 0; i < static_cast(ActiveBeams.size()); ++i) { + const handle_t beamHandle = ActiveBeams[i]; + if (!beamHandle.IsValid()) { + continue; + } + + const ovrBeamInfo& cur = BeamInfos[beamHandle.Get()]; + double const timeAlive = frame.PredictedDisplayTime - cur.StartTime; + if (timeAlive > cur.LifeTime) { + BeamInfos[beamHandle.Get()].Handle = handle_t(); + FreeBeams.push_back(beamHandle); + ActiveBeams[i] = ActiveBeams.back(); + ActiveBeams.pop_back(); + i--; + continue; + } + + // Vector describing length and direction of beam (but not position) + const Vector3f beamVector = cur.EndPos - cur.StartPos; + // Center of the beam in "world space". Start + half-way along the beam + const Vector3f beamCenter = cur.StartPos + beamVector * 0.5f; + const Vector3f beamDir = beamVector.Normalized(); + // Vector from centerView to the center of the beam + const Vector3f viewToCenter = beamCenter - viewPos; + // Cross product gives us an offset direction for the beam such that the flat side is facing + // the viewer. Classic billboarding. + const Vector3f cross = beamDir.Cross(viewToCenter).Normalized() * cur.Width * 0.5f; + + const float t = static_cast(frame.PredictedDisplayTime - cur.StartTime); + + const Vector4f color = EaseFunctions[cur.EaseFunc](cur.InitialColor, t / cur.LifeTime); + const Vector2f uvOfs(0.0f); + + attr.position[quadIndex * 4 + 0] = cur.StartPos + cross; + attr.position[quadIndex * 4 + 1] = cur.StartPos - cross; + attr.position[quadIndex * 4 + 2] = cur.EndPos + cross; + attr.position[quadIndex * 4 + 3] = cur.EndPos - cross; + attr.color[quadIndex * 4 + 0] = color; + attr.color[quadIndex * 4 + 1] = color; + attr.color[quadIndex * 4 + 2] = color; + attr.color[quadIndex * 4 + 3] = color; + attr.uv0[quadIndex * 4 + 0] = Vector2f(cur.TexCoords[0].x, cur.TexCoords[0].y) + uvOfs; + attr.uv0[quadIndex * 4 + 1] = Vector2f(cur.TexCoords[1].x, cur.TexCoords[0].y) + uvOfs; + attr.uv0[quadIndex * 4 + 2] = Vector2f(cur.TexCoords[0].x, cur.TexCoords[1].y) + uvOfs; + attr.uv0[quadIndex * 4 + 3] = Vector2f(cur.TexCoords[1].x, cur.TexCoords[1].y) + uvOfs; + + quadIndex++; + } + + // Surf.graphicsCommand.GpuState.polygonMode = GL_LINE; + Surf.graphicsCommand.GpuState.cullEnable = false; + Surf.geo.indexCount = quadIndex * 6; + Surf.geo.Update(attr); +} + +//============================== +// ovrBeamRenderer::RenderEyeView +void ovrBeamRenderer::RenderEyeView( + const Matrix4f& /*viewMatrix*/, + const Matrix4f& /*projMatrix*/, + std::vector& surfaceList) { + if (Surf.geo.indexCount > 0) { + surfaceList.push_back(ovrDrawSurface(ModelMatrix, &Surf)); + } +} + +void ovrBeamRenderer::Render(std::vector& surfaceList) { + if (Surf.geo.indexCount > 0) { + surfaceList.push_back(ovrDrawSurface(ModelMatrix, &Surf)); + } +} + +//============================== +// ovrBeamRenderer::SetPose +void ovrBeamRenderer::SetPose(const Posef& pose) { + ModelMatrix = Matrix4f(pose); +} + +} // namespace OVRFW diff --git a/Samples/SampleXrFramework/Src/Render/BeamRenderer.h b/Samples/SampleXrFramework/Src/Render/BeamRenderer.h new file mode 100755 index 0000000..4bb1a26 --- /dev/null +++ b/Samples/SampleXrFramework/Src/Render/BeamRenderer.h @@ -0,0 +1,154 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/************************************************************************************ + +Filename : BeamRenderer.h +Content : Class that manages and renders view-oriented beams. +Created : October 23, 2015 +Authors : Jonathan E. Wright + +*************************************************************************************/ + +#pragma once + +#include + +#include "OVR_Math.h" +#include "OVR_TypesafeNumber.h" + +#include "FrameParams.h" +#include "Render/SurfaceRender.h" +#include "Render/GlProgram.h" + +#include "TextureAtlas.h" +#include "EaseFunctions.h" + +namespace OVRFW { + +//============================================================== +// ovrBeamRenderer +class ovrBeamRenderer { + public: + static const int MAX_BEAMS = (1ULL << (sizeof(uint16_t) * 8)) - 1; + enum ovrBeamHandle { INVALID_BEAM_HANDLE = MAX_BEAMS }; + typedef OVR::TypesafeNumberT handle_t; + + static float LIFETIME_INFINITE; + + ovrBeamRenderer(); + ~ovrBeamRenderer(); + + void Init(const int maxBeams, const bool depthTest); + void Shutdown(); + + void Frame( + const OVRFW::ovrApplFrameIn& frame, + const OVR::Matrix4f& centerViewMatrix, + const class ovrTextureAtlas& atlas); + void Frame(const OVRFW::ovrApplFrameIn& frame, const OVR::Matrix4f& centerViewMatrix); + + void SetPose(const OVR::Posef& pose); + + void RenderEyeView( + const OVR::Matrix4f& viewMatrix, + const OVR::Matrix4f& projMatrix, + std::vector& surfaceList); + void Render(std::vector& surfaceList); + + // If lifeTime == LIFETIME_INFINITE, then the beam will never be automatically removed and + // it can be referenced by handle. The handle will be returned from this function. + // If the lifeTime != LIFETIME_INFINITE, then this function will still add the beam (if the + // max beams has not been reached) but return a handle == MAX_BEAMS + handle_t AddBeam( + const OVRFW::ovrApplFrameIn& frame, + const ovrTextureAtlas& atlas, + const int atlasIndex, + const float width, + const OVR::Vector3f& start, + const OVR::Vector3f& end, + const OVR::Vector4f& initialColor, + const float lifeTime); + + // Updates the properties of the beam with the specified handle + void UpdateBeam( + const OVRFW::ovrApplFrameIn& frame, + const handle_t handle, + const ovrTextureAtlas& atlas, + const int atlasIndex, + const float width, + const OVR::Vector3f& start, + const OVR::Vector3f& end, + const OVR::Vector4f& initialColor); + + handle_t AddBeam( + const OVRFW::ovrApplFrameIn& frame, + const float width, + const OVR::Vector3f& start, + const OVR::Vector3f& end, + const OVR::Vector4f& initialColor); + void UpdateBeam( + const OVRFW::ovrApplFrameIn& frame, + const handle_t handle, + const float width, + const OVR::Vector3f& start, + const OVR::Vector3f& end, + const OVR::Vector4f& initialColor); + + // removes the beam with the specified handle + void RemoveBeam(handle_t const handle); + + private: + void FrameInternal( + const OVRFW::ovrApplFrameIn& frame, + const OVR::Matrix4f& centerViewMatrix, + const class ovrTextureAtlas* atlas); + void UpdateBeamInternal( + const OVRFW::ovrApplFrameIn& frame, + const handle_t handle, + const ovrTextureAtlas* atlas, + const int atlasIndex, + const float width, + const OVR::Vector3f& start, + const OVR::Vector3f& end, + const OVR::Vector4f& initialColor, + const float lifeTime); + + struct ovrBeamInfo { + ovrBeamInfo() + : StartTime(0.0), + LifeTime(0.0f), + Width(0.0f), + StartPos(0.0f), + EndPos(0.0f), + InitialColor(0.0f), + TexCoords(), + AtlasIndex(0), + Handle(MAX_BEAMS), + EaseFunc(ovrEaseFunc::NONE) {} + + double StartTime; + float LifeTime; + float Width; + OVR::Vector3f StartPos; + OVR::Vector3f EndPos; + OVR::Vector4f InitialColor; + OVR::Vector2f TexCoords[2]; // tex coords are in the space of the atlas entry + uint16_t AtlasIndex; // index in the texture atlas + handle_t Handle; + ovrEaseFunc EaseFunc; + }; + + ovrSurfaceDef Surf; + + std::vector BeamInfos; + std::vector ActiveBeams; + std::vector FreeBeams; + + int MaxBeams; + + GlProgram TextureProgram; + GlProgram ParametricProgram; + OVR::Matrix4f ModelMatrix; +}; + +} // namespace OVRFW diff --git a/Samples/SampleXrFramework/Src/Render/BillBoardRenderer.cpp b/Samples/SampleXrFramework/Src/Render/BillBoardRenderer.cpp new file mode 100755 index 0000000..2ef25e1 --- /dev/null +++ b/Samples/SampleXrFramework/Src/Render/BillBoardRenderer.cpp @@ -0,0 +1,416 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/************************************************************************************ + +Filename : BillBoardRenderer.cpp +Content : Class that manages and renders view-oriented billboards. +Created : October 23; 2015 +Authors : Jonathan E. Wright + +*************************************************************************************/ + +#include "BillBoardRenderer.h" +#include "TextureAtlas.h" + +#include "Misc/Log.h" + +using OVR::Matrix4f; +using OVR::Posef; +using OVR::Quatf; +using OVR::Vector2f; +using OVR::Vector3f; +using OVR::Vector4f; + +inline Vector3f GetViewMatrixPosition(Matrix4f const& m) { + return m.Inverted().GetTranslation(); +} + +inline Vector3f GetViewMatrixUp(Matrix4f const& m) { + return m.Inverted().GetYBasis(); +} + +inline Vector3f GetViewMatrixRight(Matrix4f const& m) { + return m.Inverted().GetXBasis(); +} + +inline Vector3f GetViewMatrixForward(Matrix4f const& m) { + return m.Inverted().GetZBasis(); +} + +namespace OVRFW { + +static const char* BillBoardVertexSrc = R"glsl( +attribute vec4 Position; +attribute vec4 VertexColor; +attribute vec2 TexCoord; + +varying lowp vec4 outColor; +varying highp vec2 oTexCoord; + +void main() +{ + gl_Position = TransformVertex( Position ); + oTexCoord = TexCoord; + outColor = VertexColor; +} +)glsl"; + +static const char* TextureFragmentSrc = R"glsl( +uniform sampler2D Texture0; + +varying lowp vec4 outColor; +varying highp vec2 oTexCoord; + +void main() +{ + gl_FragColor = outColor * texture2D( Texture0, oTexCoord ); +} +)glsl"; + +static const char* ParametricFragmentSrc = R"glsl( +varying lowp vec4 outColor; +varying highp vec2 oTexCoord; + +void main() +{ + gl_FragColor = outColor; +} +)glsl"; + +float ovrBillBoardRenderer::LIFETIME_INFINITE = FLT_MAX; + +//============================== +// ovrBillBoardRenderer::ovrBillBoardRenderer +ovrBillBoardRenderer::ovrBillBoardRenderer() : MaxBillBoards(0) {} + +//============================== +// ovrBillBoardRenderer::ovrBillBoardRenderer +ovrBillBoardRenderer::~ovrBillBoardRenderer() { + Shutdown(); +} + +//============================== +// ovrBillBoardRenderer::Init +void ovrBillBoardRenderer::Init(const int maxBillBoards, const bool depthTest) { + Shutdown(); + + MaxBillBoards = maxBillBoards; + + if (TextureProgram.VertexShader == 0 || TextureProgram.FragmentShader == 0) { + OVRFW::ovrProgramParm uniformParms[] = { + /// Vertex + /// Fragment + {"Texture0", OVRFW::ovrProgramParmType::TEXTURE_SAMPLED}, + }; + const int uniformCount = sizeof(uniformParms) / sizeof(OVRFW::ovrProgramParm); + TextureProgram = OVRFW::GlProgram::Build( + BillBoardVertexSrc, TextureFragmentSrc, uniformParms, uniformCount); + } + if (ParametricProgram.VertexShader == 0 || ParametricProgram.FragmentShader == 0) { + ParametricProgram = + OVRFW::GlProgram::Build(BillBoardVertexSrc, ParametricFragmentSrc, nullptr, 0); + } + + const int numVerts = maxBillBoards * 4; + + VertexAttribs attr; + attr.position.resize(numVerts); + attr.uv0.resize(numVerts); + attr.color.resize(numVerts); + + // the indices will never change once we've set them up; we just won't necessarily + // use all of the index buffer to render. + std::vector indices; + indices.resize(MaxBillBoards * 6); + + for (int i = 0; i < MaxBillBoards; i++) { + indices[i * 6 + 0] = static_cast(i * 4 + 0); + indices[i * 6 + 1] = static_cast(i * 4 + 1); + indices[i * 6 + 2] = static_cast(i * 4 + 3); + indices[i * 6 + 3] = static_cast(i * 4 + 0); + indices[i * 6 + 4] = static_cast(i * 4 + 3); + indices[i * 6 + 5] = static_cast(i * 4 + 2); + } + + Surf.surfaceName = "billboards"; + Surf.geo.Create(attr, indices); + Surf.geo.primitiveType = GL_TRIANGLES; + Surf.geo.indexCount = 0; + + ovrGraphicsCommand& gc = Surf.graphicsCommand; + gc.GpuState.depthEnable = gc.GpuState.depthMaskEnable = depthTest; + gc.GpuState.blendEnable = ovrGpuState::BLEND_DISABLE; + gc.GpuState.blendSrc = GL_ONE; + gc.Program = TextureProgram; + gc.GpuState.lineWidth = 2.0f; +} + +//============================== +// ovrBillBoardRenderer::Shutdown +void ovrBillBoardRenderer::Shutdown() { + Surf.geo.Free(); + OVRFW::GlProgram::Free(TextureProgram); + OVRFW::GlProgram::Free(ParametricProgram); + + MaxBillBoards = 0; + FreeBillBoards.resize(0); + ActiveBillBoards.resize(0); + BillBoardInfos.resize(0); +} + +//============================== +// ovrBillBoardRenderer::AddBillBoard +ovrBillBoardRenderer::handle_t ovrBillBoardRenderer::AddBillBoard( + const OVRFW::ovrApplFrameIn& frame, + const ovrTextureAtlas& atlas, + const int atlasIndex, + const float width, + const float height, + const Vector3f& pos, + const Vector4f& initialColor, + const float lifeTime) { + handle_t handle; + + // ALOG( "ovrBillBoardRenderer::AddDebugLine" ); + if (FreeBillBoards.size() > 0) { + handle = FreeBillBoards[static_cast(FreeBillBoards.size()) - 1]; + FreeBillBoards.pop_back(); + } else { + handle = handle_t(static_cast(BillBoardInfos.size())); + if (handle.Get() >= MaxBillBoards || handle.Get() >= MAX_BILLBOARDS) { + return handle_t(); + } + BillBoardInfos.push_back(ovrBillBoardInfo()); + } + + assert(handle.IsValid()); + assert(handle.Get() < static_cast(BillBoardInfos.size())); + assert(handle.Get() < MAX_BILLBOARDS); + + ActiveBillBoards.push_back(handle); + + UpdateBillBoardInternal( + frame, handle, &atlas, atlasIndex, width, height, pos, initialColor, lifeTime); + + return (lifeTime == LIFETIME_INFINITE) ? handle : handle_t(); +} + +//============================== +// ovrBillBoardRenderer::AddBillBoard +ovrBillBoardRenderer::handle_t ovrBillBoardRenderer::AddBillBoard( + const OVRFW::ovrApplFrameIn& frame, + const float width, + const float height, + const Vector3f& pos, + const OVR::Vector4f& initialColor) { + handle_t handle; + + // ALOG( "ovrBillBoardRenderer::AddDebugLine" ); + if (FreeBillBoards.size() > 0) { + handle = FreeBillBoards[static_cast(FreeBillBoards.size()) - 1]; + FreeBillBoards.pop_back(); + } else { + handle = handle_t(static_cast(BillBoardInfos.size())); + if (handle.Get() >= MaxBillBoards || handle.Get() >= MAX_BILLBOARDS) { + return handle_t(); + } + BillBoardInfos.push_back(ovrBillBoardInfo()); + } + + assert(handle.IsValid()); + assert(handle.Get() < static_cast(BillBoardInfos.size())); + assert(handle.Get() < MAX_BILLBOARDS); + ActiveBillBoards.push_back(handle); + + UpdateBillBoardInternal( + frame, handle, nullptr, 0, width, height, pos, initialColor, LIFETIME_INFINITE); + + return handle; +} + +//============================== +// ovrBillBoardRenderer::UpdateBillBoard +void ovrBillBoardRenderer::UpdateBillBoard( + const OVRFW::ovrApplFrameIn& frame, + const handle_t handle, + const ovrTextureAtlas& atlas, + const int atlasIndex, + const float width, + const float height, + const Vector3f& pos, + const Vector4f& initialColor) { + assert(BillBoardInfos[handle.Get()].Handle.IsValid()); + UpdateBillBoardInternal( + frame, handle, &atlas, atlasIndex, width, height, pos, initialColor, LIFETIME_INFINITE); +} + +void ovrBillBoardRenderer::UpdateBillBoard( + const OVRFW::ovrApplFrameIn& frame, + const handle_t handle, + const float width, + const float height, + const Vector3f& pos, + const Vector4f& initialColor) { + assert(BillBoardInfos[handle.Get()].Handle.IsValid()); + UpdateBillBoardInternal( + frame, handle, nullptr, 0, width, height, pos, initialColor, LIFETIME_INFINITE); +} + +void ovrBillBoardRenderer::RemoveBillBoard(const handle_t handle) { + if (!handle.IsValid() || handle.Get() >= BillBoardInfos.size()) { + return; + } + BillBoardInfos[handle.Get()].StartTime = -1.0; + BillBoardInfos[handle.Get()].LifeTime = -1.0f; +} + +//============================== +// ovrBillBoardRenderer::UpdateBillBoardInternal +void ovrBillBoardRenderer::UpdateBillBoardInternal( + const OVRFW::ovrApplFrameIn& frame, + const handle_t handle, + const ovrTextureAtlas* atlas, + const int atlasIndex, + const float width, + const float height, + const Vector3f& pos, + const Vector4f& initialColor, + float const lifeTime) { + if (!handle.IsValid()) { + assert(handle.IsValid()); + return; + } + + ovrBillBoardInfo& billboard = BillBoardInfos[handle.Get()]; + + billboard.Handle = handle; + billboard.StartTime = frame.PredictedDisplayTime; + billboard.LifeTime = lifeTime; + billboard.Width = width; + billboard.Height = height; + billboard.AtlasIndex = static_cast(atlasIndex); + billboard.Pos = pos; + billboard.InitialColor = initialColor; + if (atlas == nullptr) { + billboard.TexCoords[0] = {0.0f, 0.0f}; // min tex coords + billboard.TexCoords[1] = {1.0f, 1.0f}; // max tex coords + } else { + const ovrTextureAtlas::ovrSpriteDef& sd = atlas->GetSpriteDef(atlasIndex); + billboard.TexCoords[0] = sd.uvMins; // min tex coords + billboard.TexCoords[1] = sd.uvMaxs; // max tex coords + } +} + +//============================== +// ovrBillBoardRenderer::Frame +void ovrBillBoardRenderer::Frame( + const OVRFW::ovrApplFrameIn& frame, + const Matrix4f& centerViewMatrix, + const ovrTextureAtlas& atlas) { + FrameInternal(frame, centerViewMatrix, &atlas); +} +void ovrBillBoardRenderer::Frame( + const OVRFW::ovrApplFrameIn& frame, + const Matrix4f& centerViewMatrix) { + FrameInternal(frame, centerViewMatrix, nullptr); +} + +//============================== +// ovrBillBoardRenderer::Frame +void ovrBillBoardRenderer::FrameInternal( + const OVRFW::ovrApplFrameIn& frame, + const OVR::Matrix4f& centerViewMatrix, + const class ovrTextureAtlas* atlas) { + /// we + if (atlas) { + Surf.geo.indexCount = 0; + Surf.graphicsCommand.Textures[0] = atlas->GetTexture(); + Surf.graphicsCommand.BindUniformTextures(); + Surf.graphicsCommand.Program = TextureProgram; + } else { + Surf.graphicsCommand.Program = ParametricProgram; + } + + VertexAttribs attr; + attr.position.resize(ActiveBillBoards.size() * 4); + attr.color.resize(ActiveBillBoards.size() * 4); + attr.uv0.resize(ActiveBillBoards.size() * 4); + + const Vector3f viewPos = GetViewMatrixPosition(centerViewMatrix); + const Vector3f viewUp = Vector3f(0.0f, 1.0f, 0.0f); + + int quadIndex = 0; + for (int i = 0; i < static_cast(ActiveBillBoards.size()); ++i) { + const handle_t billboardHandle = ActiveBillBoards[i]; + if (!billboardHandle.IsValid()) { + continue; + } + + const ovrBillBoardInfo& cur = BillBoardInfos[billboardHandle.Get()]; + double const timeAlive = frame.PredictedDisplayTime - cur.StartTime; + if (timeAlive > cur.LifeTime) { + BillBoardInfos[billboardHandle.Get()].Handle = handle_t(); + FreeBillBoards.push_back(billboardHandle); + ActiveBillBoards[i] = ActiveBillBoards.back(); + ActiveBillBoards.pop_back(); + i--; + continue; + } + + const Vector3f viewForward = (cur.Pos - viewPos).Normalized(); + const Vector3f viewRight = viewForward.Cross(viewUp).Normalized(); + + const Vector3f up = viewUp * cur.Height * 0.5f; + const Vector3f right = viewRight * cur.Width * 0.5f; + + const float t = static_cast(frame.PredictedDisplayTime - cur.StartTime); + + const Vector4f color = EaseFunctions[cur.EaseFunc](cur.InitialColor, t / cur.LifeTime); + const Vector2f uvOfs(0.0f); + + attr.position[quadIndex * 4 + 0] = cur.Pos + up - right; + attr.position[quadIndex * 4 + 1] = cur.Pos - up - right; + attr.position[quadIndex * 4 + 2] = cur.Pos + up + right; + attr.position[quadIndex * 4 + 3] = cur.Pos - up + right; + attr.color[quadIndex * 4 + 0] = color; + attr.color[quadIndex * 4 + 1] = color; + attr.color[quadIndex * 4 + 2] = color; + attr.color[quadIndex * 4 + 3] = color; + attr.uv0[quadIndex * 4 + 0] = Vector2f(cur.TexCoords[0].x, cur.TexCoords[0].y) + uvOfs; + attr.uv0[quadIndex * 4 + 1] = Vector2f(cur.TexCoords[1].x, cur.TexCoords[0].y) + uvOfs; + attr.uv0[quadIndex * 4 + 2] = Vector2f(cur.TexCoords[0].x, cur.TexCoords[1].y) + uvOfs; + attr.uv0[quadIndex * 4 + 3] = Vector2f(cur.TexCoords[1].x, cur.TexCoords[1].y) + uvOfs; + + quadIndex++; + } + + // Surf.graphicsCommand.GpuState.polygonMode = GL_LINE; + Surf.graphicsCommand.GpuState.cullEnable = false; + Surf.geo.indexCount = quadIndex * 6; + Surf.geo.Update(attr); +} + +//============================== +// ovrBillBoardRenderer::RenderEyeView +void ovrBillBoardRenderer::RenderEyeView( + const Matrix4f& /*viewMatrix*/, + const Matrix4f& /*projMatrix*/, + std::vector& surfaceList) { + if (Surf.geo.indexCount > 0) { + surfaceList.push_back(ovrDrawSurface(ModelMatrix, &Surf)); + } +} + +void ovrBillBoardRenderer::Render(std::vector& surfaceList) { + if (Surf.geo.indexCount > 0) { + surfaceList.push_back(ovrDrawSurface(ModelMatrix, &Surf)); + } +} + +//============================== +// ovrBillBoardRenderer::SetPose +void ovrBillBoardRenderer::SetPose(const Posef& pose) { + ModelMatrix = Matrix4f(pose); +} + +} // namespace OVRFW diff --git a/Samples/SampleXrFramework/Src/Render/BillBoardRenderer.h b/Samples/SampleXrFramework/Src/Render/BillBoardRenderer.h new file mode 100755 index 0000000..ad6739d --- /dev/null +++ b/Samples/SampleXrFramework/Src/Render/BillBoardRenderer.h @@ -0,0 +1,154 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/************************************************************************************ + +Filename : BillBoardRenderer.h +Content : Class that manages and renders view-oriented billboards. +Created : October 23, 2015 +Authors : Jonathan E. Wright + +*************************************************************************************/ + +#pragma once + +#include + +#include "OVR_Math.h" +#include "OVR_TypesafeNumber.h" + +#include "FrameParams.h" +#include "Render/SurfaceRender.h" +#include "Render/GlProgram.h" + +#include "TextureAtlas.h" +#include "EaseFunctions.h" + +namespace OVRFW { + +//============================================================== +// ovrBillBoardRenderer +class ovrBillBoardRenderer { + public: + static const int MAX_BILLBOARDS = (1ULL << (sizeof(uint16_t) * 8)) - 1; + enum ovrBillBoardHandle { INVALID_BILLBOARD_HANDLE = MAX_BILLBOARDS }; + typedef OVR::TypesafeNumberT handle_t; + + static float LIFETIME_INFINITE; + + ovrBillBoardRenderer(); + ~ovrBillBoardRenderer(); + + void Init(const int maxBillBoards, const bool depthTest); + void Shutdown(); + + void Frame( + const OVRFW::ovrApplFrameIn& frame, + const OVR::Matrix4f& centerViewMatrix, + const class ovrTextureAtlas& atlas); + void Frame(const OVRFW::ovrApplFrameIn& frame, const OVR::Matrix4f& centerViewMatrix); + + void SetPose(const OVR::Posef& pose); + + void RenderEyeView( + const OVR::Matrix4f& viewMatrix, + const OVR::Matrix4f& projMatrix, + std::vector& surfaceList); + void Render(std::vector& surfaceList); + + // If lifeTime == LIFETIME_INFINITE, then the billboard will never be automatically removed and + // it can be referenced by handle. The handle will be returned from this function. + // If the lifeTime != LIFETIME_INFINITE, then this function will still add the billboard (if the + // max billboards has not been reached) but return a handle == MAX_BILLBOARDS + handle_t AddBillBoard( + const OVRFW::ovrApplFrameIn& frame, + const ovrTextureAtlas& atlas, + const int atlasIndex, + const float width, + const float height, + const OVR::Vector3f& pos, + const OVR::Vector4f& initialColor, + const float lifeTime); + + // Updates the properties of the billboard with the specified handle + void UpdateBillBoard( + const OVRFW::ovrApplFrameIn& frame, + const handle_t handle, + const ovrTextureAtlas& atlas, + const int atlasIndex, + const float width, + const float height, + const OVR::Vector3f& pos, + const OVR::Vector4f& initialColor); + + handle_t AddBillBoard( + const OVRFW::ovrApplFrameIn& frame, + const float width, + const float height, + const OVR::Vector3f& pos, + const OVR::Vector4f& initialColor); + void UpdateBillBoard( + const OVRFW::ovrApplFrameIn& frame, + const handle_t handle, + const float width, + const float height, + const OVR::Vector3f& pos, + const OVR::Vector4f& initialColor); + + // removes the billboard with the specified handle + void RemoveBillBoard(handle_t const handle); + + private: + void FrameInternal( + const OVRFW::ovrApplFrameIn& frame, + const OVR::Matrix4f& centerViewMatrix, + const class ovrTextureAtlas* atlas); + void UpdateBillBoardInternal( + const OVRFW::ovrApplFrameIn& frame, + const handle_t handle, + const ovrTextureAtlas* atlas, + const int atlasIndex, + const float width, + const float height, + const OVR::Vector3f& pos, + const OVR::Vector4f& initialColor, + const float lifeTime); + + struct ovrBillBoardInfo { + ovrBillBoardInfo() + : StartTime(0.0), + LifeTime(0.0f), + Width(0.0f), + Height(0.0f), + Pos(0.0f), + InitialColor(0.0f), + TexCoords(), + AtlasIndex(0), + Handle(MAX_BILLBOARDS), + EaseFunc(ovrEaseFunc::NONE) {} + + double StartTime; + float LifeTime; + float Width; + float Height; + OVR::Vector3f Pos; + OVR::Vector4f InitialColor; + OVR::Vector2f TexCoords[2]; // tex coords are in the space of the atlas entry + uint16_t AtlasIndex; // index in the texture atlas + handle_t Handle; + ovrEaseFunc EaseFunc; + }; + + ovrSurfaceDef Surf; + + std::vector BillBoardInfos; + std::vector ActiveBillBoards; + std::vector FreeBillBoards; + + int MaxBillBoards; + + GlProgram TextureProgram; + GlProgram ParametricProgram; + OVR::Matrix4f ModelMatrix; +}; + +} // namespace OVRFW diff --git a/Samples/SampleXrFramework/Src/Render/BitmapFont.cpp b/Samples/SampleXrFramework/Src/Render/BitmapFont.cpp new file mode 100755 index 0000000..ed7ae64 --- /dev/null +++ b/Samples/SampleXrFramework/Src/Render/BitmapFont.cpp @@ -0,0 +1,2457 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/************************************************************************************ + +Filename : BitmapFont.cpp +Content : Monospaced bitmap font rendering intended for debugging only. +Created : March 11, 2014 +Authors : Jonathan E. Wright + +*************************************************************************************/ + +// TODO: +// - add support for multiple fonts per surface using texture arrays (store texture in 3rd texture +// coord) +// - in-world text really should sort with all other transparent surfaces +// + +#include "BitmapFont.h" + +#include +#include +#include + +#include "OVR_UTF8Util.h" +#include "OVR_JSON.h" +#include "OVR_Math.h" + +#include "Misc/Log.h" + +#include "Egl.h" +#include "GlProgram.h" +#include "GlTexture.h" +#include "GlGeometry.h" + +#include "PackageFiles.h" +#include "OVR_FileSys.h" +#include "OVR_Uri.h" + +using OVR::Bounds3f; +using OVR::Matrix4f; +using OVR::Posef; +using OVR::Quatf; +using OVR::Vector2f; +using OVR::Vector3f; +using OVR::Vector4f; + +inline Vector3f GetViewMatrixUp(Matrix4f const& m) { + return Vector3f(-m.M[1][0], -m.M[1][1], -m.M[1][2]).Normalized(); +} + +inline std::string ExtractFile(const std::string& s) { + const int l = static_cast(s.length()); + if (l == 0) { + return std::string(""); + } + + int end = l; + if (s[l - 1] == '/') { // directory ends in a slash + end = l - 1; + } + + int start; + for (start = end - 1; start > -1 && s[start] != '/'; start--) + ; + start++; + + return std::string(&s[start], end - start); +} + +inline void StripFilename(char const* inPath, char* outPath, size_t const outPathSize) { + const char WIN_PATH_SEPARATOR = '\\'; + const char NIX_PATH_SEPARATOR = '/'; + const char URI_PATH_SEPARATOR = '/'; + + assert(inPath != NULL); + if (inPath[0] == '\0') { + OVR::OVR_strcpy(outPath, outPathSize, inPath); + return; + } + + intptr_t inOfs = OVR::OVR_strlen(inPath); + for (;;) { + uint32_t ch; + if (!OVRFW::UTF8Util::DecodePrevChar(inPath, inOfs, ch)) { + // invalid UTF-8 encoding + assert(false); + break; + } + if (ch == WIN_PATH_SEPARATOR || ch == NIX_PATH_SEPARATOR || ch == URI_PATH_SEPARATOR) { + OVR::OVR_strncpy(outPath, outPathSize, inPath, inOfs + 1); + return; + } + } + // never hit a path separator so copy the entire thing + OVR::OVR_strcpy(outPath, outPathSize, inPath); +} + +static uint32_t DecodeNextChar(char const* p, intptr_t& offset) { + char const* t = p + offset; + uint32_t ch = OVRFW::UTF8Util::DecodeNextChar(&t); + offset = t - p; + return ch; +} + +static bool EncodeChar(char* p, size_t const& maxOffset, intptr_t& offset, uint32_t ch) { + // test for buffer overflow by encoding to a temp buffer and seeing how far the offset moved + char temp[6]; + intptr_t tempOfs = 0; + OVRFW::UTF8Util::EncodeChar(temp, &tempOfs, ch); + if (static_cast(maxOffset) - offset <= tempOfs) { + // just encode a null byte at the current offset + assert(false); + p[offset] = '\0'; + offset++; + return false; + } + OVRFW::UTF8Util::EncodeChar(p, &offset, ch); + return true; +} + +bool AppendUriPath( + char const* inPath, + char const* appendPath, + char* outPath, + size_t const outPathSize) { + const char WIN_PATH_SEPARATOR = '\\'; + const char NIX_PATH_SEPARATOR = '/'; + const char URI_PATH_SEPARATOR = '/'; + + if (inPath == NULL || outPath == NULL || appendPath == NULL || outPathSize < 2) { + assert(inPath != NULL && outPath != NULL && appendPath != NULL && outPathSize > 1); + return false; + } + intptr_t inOfs = 0; + intptr_t outOfs = 0; + uint32_t lastCh = 0xffffffff; + uint32_t ch = 0xffffffff; + for (;;) { + lastCh = ch; + ch = DecodeNextChar(inPath, inOfs); + if (ch == '\0') { + break; + } + if (!EncodeChar(outPath, outPathSize, outOfs, ch)) { + return false; + } + } + + // ensure there's always a path separator after inPath + if (lastCh != WIN_PATH_SEPARATOR && lastCh != NIX_PATH_SEPARATOR && + lastCh != URI_PATH_SEPARATOR) { + // emit a separator + if (!EncodeChar(outPath, outPathSize, outOfs, URI_PATH_SEPARATOR)) { + return false; // buffer overflow + } + } + + // skip past any path separators at the start of append path + intptr_t appendOfs = 0; + char const* appendPathStart = &appendPath[0]; + for (;;) { + ch = DecodeNextChar(appendPath, appendOfs); + if (ch != WIN_PATH_SEPARATOR && ch != NIX_PATH_SEPARATOR && ch != URI_PATH_SEPARATOR) { + break; + } + appendPathStart = appendPath + appendOfs; + } + + appendOfs = 0; + for (;;) { + ch = DecodeNextChar(appendPathStart, appendOfs); + if (!EncodeChar(outPath, outPathSize, outOfs, ch)) { + return false; + } + if (ch == 0) { + return true; + } + } +} + +namespace OVRFW { + +static char const* FontSingleTextureVertexShaderSrc = R"glsl( + attribute vec4 Position; + attribute vec2 TexCoord; + attribute vec4 VertexColor; + attribute vec4 FontParms; + varying highp vec2 oTexCoord; + varying lowp vec4 oColor; + varying vec4 oFontParms; + void main() + { + gl_Position = TransformVertex( Position ); + oTexCoord = TexCoord; + oColor = VertexColor; + oFontParms = FontParms; + } +)glsl"; + +// Use derivatives to make the faded color and alpha boundaries a +// consistent thickness regardless of font scale. +static char const* SDFFontFragmentShaderSrc = R"glsl( + uniform sampler2D Texture0; + varying highp vec2 oTexCoord; + varying lowp vec4 oColor; + varying mediump vec4 oFontParms; + void main() + { + mediump float distance = texture2D( Texture0, oTexCoord ).r; + mediump float ds = oFontParms.z * 255.0; + mediump float dd = fwidth( oTexCoord.x ) * oFontParms.w * 10.0 * ds; + mediump float ALPHA_MIN = oFontParms.x - dd; + mediump float ALPHA_MAX = oFontParms.x + dd; + mediump float COLOR_MIN = oFontParms.y - dd; + mediump float COLOR_MAX = oFontParms.y + dd; + gl_FragColor.xyz = ( oColor * ( clamp( distance, COLOR_MIN, COLOR_MAX ) - COLOR_MIN ) / ( COLOR_MAX - COLOR_MIN ) ).xyz; + gl_FragColor.w = oColor.w * ( clamp( distance, ALPHA_MIN, ALPHA_MAX ) - ALPHA_MIN ) / ( ALPHA_MAX - ALPHA_MIN ); + } +)glsl"; + +class FontGlyphType { + public: + FontGlyphType() + : CharCode(0), + X(0.0f), + Y(0.0f), + Width(0.0f), + Height(0.0f), + AdvanceX(0.0f), + AdvanceY(0.0f), + BearingX(0.0f), + BearingY(0.0f) {} + + int32_t CharCode; + float X; + float Y; + float Width; + float Height; + float AdvanceX; + float AdvanceY; + float BearingX; + float BearingY; +}; + +class ovrFontWeight { + public: + ovrFontWeight(float const alphaCenterOffset = 0.0f, float const colorCenterOffset = 0.0f) + : AlphaCenterOffset(alphaCenterOffset), ColorCenterOffset(colorCenterOffset) {} + + float AlphaCenterOffset; + float ColorCenterOffset; +}; + +class FontInfoType { + public: + static const int FNT_FILE_VERSION; + + // This is used to scale the UVs to world units that work with the current scale values used + // throughout the native code. Unfortunately the original code didn't account for the image size + // before factoring in the user scale, so this keeps everything the same. + static const float DEFAULT_SCALE_FACTOR; + + FontInfoType() + : NaturalWidth(0.0f), + NaturalHeight(0.0f), + HorizontalPad(0), + VerticalPad(0), + FontHeight(0), + ScaleFactorX(1.0f), + ScaleFactorY(1.0f), + TweakScale(1.0f), + CenterOffset(0.0f), + MaxAscent(0.0f), + MaxDescent(0.0f), + EdgeWidth(32.0f) {} + + bool Load(ovrFileSys& fileSys, char const* uri); + bool Save(char const* filename); + + FontGlyphType const& GlyphForCharCode(uint32_t const charCode) const; + ovrFontWeight GetFontWeight(int const index) const; + + std::string FontName; // name of the font (not necessarily the file name) + std::string CommandLine; // command line used to generate this font + std::string ImageFileName; // the file name of the font image + float NaturalWidth; // width of the font image before downsampling to SDF + float NaturalHeight; // height of the font image before downsampling to SDF + float HorizontalPad; // horizontal padding for all glyphs + float VerticalPad; // vertical padding for all glyphs + float FontHeight; // vertical distance between two baselines (i.e. two lines of text) + float ScaleFactorX; // x-axis scale factor + float ScaleFactorY; // y-axis scale factor + float TweakScale; // additional scale factor used to tweak the size of other-language fonts + float CenterOffset; // +/- value applied to "center" distance in the signed distance field. + // Range [-1,1]. A negative offset will make the font appear bolder. + float MaxAscent; // maximum ascent of any character + float MaxDescent; // maximum descent of any character + float EdgeWidth; // adjust the edge falloff. Helps with fonts that have smaller glyph sizes in + // the texture (CJK) + std::vector Glyphs; // info about each glyph in the font + std::vector + CharCodeMap; // index by character code to get the index of a glyph for the character + std::vector FontWeights; + + private: + bool LoadFromBuffer(void const* buffer, size_t const bufferSize); +}; + +const int FontInfoType::FNT_FILE_VERSION = + 1; // initial version storing pixel locations and scaling post/load to fix some precision loss +// for now, we're not going to increment this so that we're less likely to have dependency issues +// with loading the font from Home const int FontInfoType::FNT_FILE_VERSION = 2; // added +// TweakScale for manual adjustment of other-language fonts +const float FontInfoType::DEFAULT_SCALE_FACTOR = 512.0f; + +class BitmapFontLocal : public BitmapFont { + public: + BitmapFontLocal() : FontTexture(), ImageWidth(0), ImageHeight(0) {} + ~BitmapFontLocal() { + FreeTexture(FontTexture); + GlProgram::Free(FontProgram); + } + + virtual bool Load(ovrFileSys& fileSys, const char* uri); + + // Calculates the native (unscaled) width of the text string. Line endings are ignored. + virtual float CalcTextWidth(char const* text) const; + + // Calculates the native (unscaled) width of the text string. Each '\n' will start a new line + // and will increase the height by FontInfo.FontHeight. For multi-line strings, lineWidths will + // contain the width of each individual line of text and width will be the width of the widest + // line of text. + virtual void CalcTextMetrics( + char const* text, + size_t& len, + float& width, + float& height, + float& ascent, + float& descent, + float& fontHeight, + float* lineWidths, + int const maxLines, + int& numLines) const; + + virtual void TruncateText(std::string& inOutText, int const maxLines) const; + + bool WordWrapText(std::string& inOutText, const float widthMeters, const float fontScale = 1.0f) + const; + bool WordWrapText( + std::string& inOutText, + const float widthMeters, + std::vector wholeStrsList, + const float fontScale = 1.0f) const; + float GetLastFitChars( + std::string& inOutText, + const float widthMeters, + const float fontScale = 1.0f) const; + float GetFirstFitChars( + std::string& inOutText, + const float widthMeters, + const int numLines, + const float fontScale = 1.0f) const; + + // Returns a newly allocated GlGeometry for the text, allowing it to be sorted + // or transformed with more control than the global BitmapFontSurface. + // + // The GlGeometry must be freed, but the GlProgram is shared by all users of the font. + virtual ovrSurfaceDef TextSurface( + const char* text, + float scale, + const Vector4f& color, + HorizontalJustification hjust, + VerticalJustification vjust, + fontParms_t const* fontParms = nullptr) const; + + FontGlyphType const& GlyphForCharCode(uint32_t const charCode) const { + return FontInfo.GlyphForCharCode(charCode); + } + virtual Vector2f GetScaleFactor() const { + return Vector2f(FontInfo.ScaleFactorX, FontInfo.ScaleFactorY); + } + virtual void GetGlyphMetrics( + const uint32_t charCode, + float& width, + float& height, + float& advancex, + float& advancey) const; + + FontInfoType const& GetFontInfo() const { + return FontInfo; + } + const GlProgram& GetFontProgram() const { + return FontProgram; + } + int GetImageWidth() const { + return ImageWidth; + } + int GetImageHeight() const { + return ImageHeight; + } + const GlTexture& GetFontTexture() const { + return FontTexture; + } + + private: + FontInfoType FontInfo; + GlTexture FontTexture; + int ImageWidth; + int ImageHeight; + + GlProgram FontProgram; + + private: + bool LoadImage(ovrFileSys& fileSys, char const* uri); + bool LoadImageFromBuffer( + char const* imageName, + std::vector& buffer, + bool const isASTC); + bool LoadFontInfo(char const* glyphFileName); + bool LoadFontInfoFromBuffer(unsigned char const* buffer, size_t const bufferSize); +}; + +// We cast BitmapFont to BitmapFontLocal internally so that we do not have to expose +// a lot of BitmapFontLocal methods in the BitmapFont interface just so BitmapFontSurfaceLocal +// can use them. This problem comes up because BitmapFontSurface specifies the interface as +// taking BitmapFont as a parameter, not BitmapFontLocal. This is safe right now because +// we know that BitmapFont cannot be instantiated, nor is there any class derived from it other +// than BitmapFontLocal. +static BitmapFontLocal const& AsLocal(BitmapFont const& font) { + return *static_cast(&font); +} + +struct fontVertex_t { + fontVertex_t() : xyz(0.0f), s(0.0f), t(0.0f), rgba(), fontParms() {} + + Vector3f xyz; + float s; + float t; + std::uint8_t rgba[4]; + std::uint8_t fontParms[4]; +}; + +typedef unsigned short fontIndex_t; + +//============================== +// ftoi +#if defined(OVR_CPU_X86_64) +#include +inline int ftoi(float const f) { + return _mm_cvtt_ss2si(_mm_set_ss(f)); +} +#elif defined(OVR_CPU_x86) +inline int ftoi(float const f) { + int i; + __asm + { + fld f + fistp i + } + return i; +} +#else +inline int ftoi(float const f) { + return (int)f; +} +#endif + +//============================== +// ColorToABGR +int32_t ColorToABGR(Vector4f const& color) { + // format is ABGR + return (ftoi(color.w * 255.0f) << 24) | (ftoi(color.z * 255.0f) << 16) | + (ftoi(color.y * 255.0f) << 8) | ftoi(color.x * 255.0f); +} + +static bool IsHexDigit(char const ch) { + return (ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'F') || (ch >= 'a' && ch <= 'f'); +} + +static bool DigitToHex(char const ch, uint32_t& hex) { + if (ch >= 'A' && ch <= 'F') { + hex = ch - 'A' + 10; + } else if (ch >= 'a' && ch <= 'f') { + hex = ch - 'a' + 10; + } else if (ch >= '0' && ch <= '9') { + hex = ch - '0'; + } else { + hex = 0; + return false; + } + return true; +} + +static bool CheckForColorEscape(char const** buffer, uint32_t& color) { + // Must be exactly 10 chars "~~XXXXXXXX" where X's are ABGR in hex + OVR_ASSERT(buffer != nullptr && *buffer != nullptr); + + char const* ptr = *buffer; + if (ptr[0] != '~') { + return false; + } + if (ptr[1] != '~') { + return false; + } + color = 0; + for (int i = 2; i < 10; ++i) { + uint32_t hex; + if (!DigitToHex(ptr[i], hex)) { + return false; + } + + color |= hex; + color <<= 4; + } + *buffer += 10; + return true; +} + +static bool CheckForFormatEscape(char const** buffer, uint32_t& color, uint32_t& weight) { + OVR_ASSERT(buffer != nullptr && *buffer != nullptr); + char const* ptr = *buffer; + if (ptr[0] == '\0' || ptr[1] == '\0' || ptr[2] == '\0') { + return false; + } else if (ptr[0] != '~' || ptr[1] != '~') { + return false; + } + // if the character after ~~ is a hex digit, this is a color + else if (IsHexDigit(ptr[2])) { + return CheckForColorEscape(buffer, color); + } else if (ptr[2] == 'w' || ptr[2] == 'W') { + // a single-digit weight command should follow + if (!DigitToHex(ptr[3], weight)) { + return false; + } + *buffer += 4; + return true; + } + return false; // if we got here we don't have a valid character after ~~ so return false. +} + +// The vertices in a vertex block are in local space and pre-scaled. They are transformed into +// world space and stuffed into the VBO before rendering (once the current MVP is known). +// The vertices can be pivoted around the Pivot point to face the camera, then an additional +// rotation applied. +class VertexBlockType { + public: + VertexBlockType() + : Font(NULL), + Verts(NULL), + NumVerts(0), + Pivot(0.0f), + Rotation(), + Billboard(true), + TrackRoll(false) {} + + VertexBlockType(VertexBlockType const& other) + : Font(NULL), + Verts(NULL), + NumVerts(0), + Pivot(0.0f), + Rotation(), + Billboard(true), + TrackRoll(false) { + Copy(other); + } + + VertexBlockType& operator=(VertexBlockType const& other) { + Copy(other); + return *this; + } + + void Copy(VertexBlockType const& other) { + if (&other == this) { + return; + } + delete[] Verts; + Font = other.Font; + Verts = other.Verts; + NumVerts = other.NumVerts; + Pivot = other.Pivot; + Rotation = other.Rotation; + Billboard = other.Billboard; + TrackRoll = other.TrackRoll; + + other.Font = NULL; + other.Verts = NULL; + other.NumVerts = 0; + } + + VertexBlockType( + BitmapFont const& font, + int const numVerts, + Vector3f const& pivot, + Quatf const& rot, + bool const billboard, + bool const trackRoll) + : Font(&font), + NumVerts(numVerts), + Pivot(pivot), + Rotation(rot), + Billboard(billboard), + TrackRoll(trackRoll) { + Verts = new fontVertex_t[numVerts]; + } + + ~VertexBlockType() { + Free(); + } + + void Free() { + Font = NULL; + delete[] Verts; + Verts = NULL; + NumVerts = 0; + } + + mutable BitmapFont const* Font; // the font used to render text into this vertex block + mutable fontVertex_t* Verts; // the vertices + mutable int NumVerts; // the number of vertices in the block + Vector3f Pivot; // postion this vertex block can be rotated around + Quatf Rotation; // additional rotation to apply + bool Billboard; // true to always face the camera + bool TrackRoll; // if true, when billboarded, roll with the camera +}; + +// Sets up VB and VAO for font drawing +GlGeometry FontGeometry(int maxQuads, Bounds3f& localBounds) { + GlGeometry Geo; + + Geo.indexCount = maxQuads * 6; + Geo.vertexCount = maxQuads * 4; + + Geo.localBounds = localBounds; + + // font VAO + glGenVertexArrays(1, &Geo.vertexArrayObject); + glBindVertexArray(Geo.vertexArrayObject); + + // vertex buffer + const int vertexByteCount = Geo.vertexCount * sizeof(fontVertex_t); + glGenBuffers(1, &Geo.vertexBuffer); + glBindBuffer(GL_ARRAY_BUFFER, Geo.vertexBuffer); + glBufferData(GL_ARRAY_BUFFER, vertexByteCount, NULL, GL_DYNAMIC_DRAW); + + glEnableVertexAttribArray(VERTEX_ATTRIBUTE_LOCATION_POSITION); // x, y and z + glVertexAttribPointer( + VERTEX_ATTRIBUTE_LOCATION_POSITION, 3, GL_FLOAT, GL_FALSE, sizeof(fontVertex_t), (void*)0); + + glEnableVertexAttribArray(VERTEX_ATTRIBUTE_LOCATION_UV0); // s and t + glVertexAttribPointer( + VERTEX_ATTRIBUTE_LOCATION_UV0, + 2, + GL_FLOAT, + GL_FALSE, + sizeof(fontVertex_t), + (void*)offsetof(fontVertex_t, s)); + + glEnableVertexAttribArray(VERTEX_ATTRIBUTE_LOCATION_COLOR); // color + glVertexAttribPointer( + VERTEX_ATTRIBUTE_LOCATION_COLOR, + 4, + GL_UNSIGNED_BYTE, + GL_TRUE, + sizeof(fontVertex_t), + (void*)offsetof(fontVertex_t, rgba)); + + glDisableVertexAttribArray(VERTEX_ATTRIBUTE_LOCATION_UV1); + + glEnableVertexAttribArray(VERTEX_ATTRIBUTE_LOCATION_FONT_PARMS); // outline parms + glVertexAttribPointer( + VERTEX_ATTRIBUTE_LOCATION_FONT_PARMS, + 4, + GL_UNSIGNED_BYTE, + GL_TRUE, + sizeof(fontVertex_t), + (void*)offsetof(fontVertex_t, fontParms)); + + fontIndex_t* indices = new fontIndex_t[Geo.indexCount]; + const int indexByteCount = Geo.indexCount * sizeof(fontIndex_t); + + // indices never change + fontIndex_t v = 0; + for (int i = 0; i < maxQuads; i++) { + indices[i * 6 + 0] = v + 2; + indices[i * 6 + 1] = v + 1; + indices[i * 6 + 2] = v + 0; + indices[i * 6 + 3] = v + 3; + indices[i * 6 + 4] = v + 2; + indices[i * 6 + 5] = v + 0; + v += 4; + } + + glGenBuffers(1, &Geo.indexBuffer); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, Geo.indexBuffer); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexByteCount, (void*)indices, GL_STATIC_DRAW); + + glBindVertexArray(0); + + delete[] indices; + + return Geo; +} + +struct ovrFormat { + ovrFormat(uint32_t const color) : Color(color), Weight(0xffffffff), LastWeight(0xffffffff) {} + + uint32_t Color; + uint32_t Weight; + uint32_t LastWeight; +}; + +static void UpdateFormat( + FontInfoType const& fontInfo, + fontParms_t const& fontParms, + char const** buffer, + ovrFormat& format, + uint8_t vertexParms[4]) { + while (CheckForFormatEscape(buffer, format.Color, format.Weight)) + ; + if (format.Weight != format.LastWeight && format.Weight != 0xffffffff) { + ovrFontWeight const& w = fontInfo.GetFontWeight(format.Weight); + vertexParms[1] = + (uint8_t)(std::clamp( + fontParms.ColorCenter + fontInfo.CenterOffset + w.ColorCenterOffset, + 0.0f, + 1.0f) * + 255); + vertexParms[0] = + (uint8_t)(std::clamp( + fontParms.AlphaCenter + fontInfo.CenterOffset + w.AlphaCenterOffset, + 0.0f, + 1.0f) * + 255); + } +} + +//============================== +// DrawText3D +VertexBlockType DrawTextToVertexBlock( + BitmapFont const& font, + fontParms_t const& fontParms, + Vector3f const& pos, + Vector3f const& normal, + Vector3f const& up, + float scale, + Vector4f const& color, + char const* text, + Vector3f* toNextLine = nullptr) { + if (toNextLine) { + *toNextLine = Vector3f::ZERO; + } + if (text == NULL || text[0] == '\0') { +#if defined(OVR_BUILD_DEBUG) + ALOG("DrawTextToVertexBlock: null or empty text!"); +#endif + return VertexBlockType(); // nothing to do here, move along + } + + // TODO: multiple line support -- we would need to calculate the horizontal width + // for each string ending in \n + size_t len; + float width; + float height; + float ascent; + float descent; + float fontHeight; + int const MAX_LINES = 128; + float lineWidths[MAX_LINES]; + int numLines; + AsLocal(font).CalcTextMetrics( + text, len, width, height, ascent, descent, fontHeight, lineWidths, MAX_LINES, numLines); + +#if defined(OVR_BUILD_DEBUG) +/// ALOG( "BitmapFontSurfaceLocal::DrawText3D( \"%s\" %s %s ) : width = %.2f, height = %.2f, +/// numLines = %i, fh = %.2f", text, ( fontParms.AlignVert == VERTICAL_CENTER ) ? +/// "cv" : ( ( fontParms.AlignVert == VERTICAL_CENTER_FIXEDHEIGHT ) ? "cvfh" : "tv" ), +/// ( fontParms.AlignHoriz == HORIZONTAL_CENTER ) ? "ch" : ( ( fontParms.AlignHoriz == +/// HORIZONTAL_LEFT ) ? "lh" : "rh" ), +/// width, height, numLines, AsLocal( font ).GetFontInfo().FontHeight ); +#endif + + if (len == 0) { +#if defined(OVR_BUILD_DEBUG) + ALOG("DrawTextToVertexBlock: zero-length text after metrics!"); +#endif + return VertexBlockType(); + } + + if (!normal.IsNormalized()) { + ALOG( + "DrawTextToVertexBlock: normal = ( %g, %g, %g ), text = '%s'", + normal.x, + normal.y, + normal.z, + text); + assert(normal.IsNormalized()); + } + if (!up.IsNormalized()) { + ALOG("DrawTextToVertexBlock: up = ( %g, %g, %g ), text = '%s'", up.x, up.y, up.z, text); + assert(up.IsNormalized()); + } + + const FontInfoType& fontInfo = AsLocal(font).GetFontInfo(); + + float imageWidth = (float)AsLocal(font).GetImageWidth(); + float const xScale = AsLocal(font).GetFontInfo().ScaleFactorX * scale; + float const yScale = AsLocal(font).GetFontInfo().ScaleFactorY * scale; + + // allocate a vertex block + const int numVerts = 4 * static_cast(len); + VertexBlockType vb(font, numVerts, pos, Quatf(), fontParms.Billboard, fontParms.TrackRoll); + + Vector3f const right = up.Cross(normal); + Vector3f const r = (fontParms.Billboard) ? Vector3f(1.0f, 0.0f, 0.0f) : right; + Vector3f const u = (fontParms.Billboard) ? Vector3f(0.0f, 1.0f, 0.0f) : up; + + Vector3f curPos(0.0f); + switch (fontParms.AlignVert) { + case VERTICAL_BASELINE: + break; + + case VERTICAL_CENTER: { + float const vofs = (height * 0.5f) - ascent; + curPos += u * (vofs * scale); + break; + } + case VERTICAL_CENTER_FIXEDHEIGHT: { + // for fixed height, we must adjust single-line text by the max ascent because fonts + // are rendered according to their baseline. For multiline text, the first line + // contributes max ascent only while the other lines are adjusted by font height. + float const ma = AsLocal(font).GetFontInfo().MaxAscent; + float const md = AsLocal(font).GetFontInfo().MaxDescent; + float const fh = AsLocal(font).GetFontInfo().FontHeight; + float const adjust = (ma - md) * 0.5f; + float const vofs = (fh * (numLines - 1) * 0.5f) - adjust; + curPos += u * (vofs * yScale); + break; + } + case VERTICAL_TOP: { + float const vofs = height - ascent; + curPos += u * (vofs * scale); + break; + } + } + + Vector3f basePos = curPos; + switch (fontParms.AlignHoriz) { + case HORIZONTAL_LEFT: + break; + + case HORIZONTAL_CENTER: { + curPos -= r * (lineWidths[0] * 0.5f * scale); + break; + } + case HORIZONTAL_RIGHT: { + curPos -= r * (lineWidths[0] * scale); + break; + } + } + +#if defined(_MSC_VER) && _MSC_VER >= 1920 && _MSC_VER < 1929 + // fatal error C1001: Internal compiler error with vs2019 + float s = fontInfo.FontHeight * yScale; + Vector3f lineInc = {u.x * s, u.y * s, u.z * s}; +#else + Vector3f lineInc = u * (fontInfo.FontHeight * yScale); +#endif // defined(_MSC_VER) && _MSC_VER >= 1920 && _MSC_VER < 1929 + + float const distanceScale = imageWidth / FontInfoType::DEFAULT_SCALE_FACTOR; + float const imageScaleFactor = 1024.0f / imageWidth; + + float const weightOffset = 0.0f; + float const edgeWidth = fontInfo.EdgeWidth * imageScaleFactor; + + uint8_t vertexParms[4] = { + (uint8_t)(std::clamp( + fontParms.AlphaCenter + fontInfo.CenterOffset + weightOffset, 0.0f, 1.0f) * + 255), + (uint8_t)(std::clamp( + fontParms.ColorCenter + fontInfo.CenterOffset + weightOffset, 0.0f, 1.0f) * + 255), + //(uint8_t)( 0 ), + (uint8_t)(std::clamp(distanceScale, 1.0f, 255.0f)), + (uint8_t)(std::clamp(edgeWidth / 16.0f, 0.0f, 1.0f) * 255.0f)}; + + ovrFormat format(ColorToABGR(color)); + + int curLine = 0; + fontVertex_t* v = vb.Verts; + char const* p = text; + size_t i = 0; + + UpdateFormat(fontInfo, fontParms, &p, format, vertexParms); + uint32_t charCode = UTF8Util::DecodeNextChar(&p); + + for (; charCode != '\0'; i++, charCode = UTF8Util::DecodeNextChar(&p)) { + OVR_ASSERT(i < len); + if (charCode == '\n' && curLine < numLines && curLine < MAX_LINES) { + // move to next line + curLine++; + basePos -= lineInc; + if (toNextLine) { + *toNextLine -= lineInc; + } + curPos = basePos; + switch (fontParms.AlignHoriz) { + case HORIZONTAL_LEFT: + break; + + case HORIZONTAL_CENTER: { + curPos -= r * (lineWidths[curLine] * 0.5f * scale); + break; + } + case HORIZONTAL_RIGHT: { + curPos -= r * (lineWidths[curLine] * scale); + break; + } + } + } + + FontGlyphType const& g = AsLocal(font).GlyphForCharCode(charCode); + + float s0 = g.X; + float t0 = g.Y; + float s1 = (g.X + g.Width); + float t1 = (g.Y + g.Height); + + float bearingX = g.BearingX * xScale; + float bearingY = g.BearingY * yScale; + + float rw = (g.Width + g.BearingX) * xScale; + float rh = (g.Height - g.BearingY) * yScale; + + // lower left + v[i * 4 + 0].xyz = curPos + (r * bearingX) - (u * rh); + v[i * 4 + 0].s = s0; + v[i * 4 + 0].t = t1; + *(std::uint32_t*)(&v[i * 4 + 0].rgba[0]) = format.Color; + *(std::uint32_t*)(&v[i * 4 + 0].fontParms[0]) = *(std::uint32_t*)(&vertexParms[0]); + // upper left + v[i * 4 + 1].xyz = curPos + (r * bearingX) + (u * bearingY); + v[i * 4 + 1].s = s0; + v[i * 4 + 1].t = t0; + *(std::uint32_t*)(&v[i * 4 + 1].rgba[0]) = format.Color; + *(std::uint32_t*)(&v[i * 4 + 1].fontParms[0]) = *(std::uint32_t*)(&vertexParms[0]); + // upper right + v[i * 4 + 2].xyz = curPos + (r * rw) + (u * bearingY); + v[i * 4 + 2].s = s1; + v[i * 4 + 2].t = t0; + *(std::uint32_t*)(&v[i * 4 + 2].rgba[0]) = format.Color; + *(std::uint32_t*)(&v[i * 4 + 2].fontParms[0]) = *(std::uint32_t*)(&vertexParms[0]); + // lower right + v[i * 4 + 3].xyz = curPos + (r * rw) - (u * rh); + v[i * 4 + 3].s = s1; + v[i * 4 + 3].t = t1; + *(std::uint32_t*)(&v[i * 4 + 3].rgba[0]) = format.Color; + *(std::uint32_t*)(&v[i * 4 + 3].fontParms[0]) = *(std::uint32_t*)(&vertexParms[0]); + // advance to start of next char + curPos += r * (g.AdvanceX * xScale); + + UpdateFormat(fontInfo, fontParms, &p, format, vertexParms); + } + + if (toNextLine) { + *toNextLine -= lineInc; + } + +#if defined(OVR_BUILD_DEBUG) +/// ALOG( "DrawTextToVertexBlock: drawn %d vertices lineInc = ", vb.NumVerts ); +#endif + + return vb; +} + +ovrFontWeight FontInfoType::GetFontWeight(const int index) const { + if (index < 0 || static_cast(index) >= FontWeights.size()) { + return ovrFontWeight(); + } + return FontWeights[index]; +} + +ovrSurfaceDef BitmapFontLocal::TextSurface( + const char* text, + float scale, + const Vector4f& color, + HorizontalJustification hjust, + VerticalJustification vjust, + fontParms_t const* fontParms) const { + fontParms_t fp; + if (fontParms != nullptr) { + fp = *fontParms; + } else { + fp.AlignHoriz = hjust; + fp.AlignVert = vjust; + } + VertexBlockType vb = DrawTextToVertexBlock( + *this, + fp, + Vector3f(0.0f), // origin + Vector3f(0.0f, 0.0f, 1.0f), // normal + Vector3f(0.0f, 1.0f, 0.0f), // up + scale, + color, + text); + + Bounds3f blockBounds(Bounds3f::Init); + for (int i = 0; i < vb.NumVerts; i++) { + blockBounds.AddPoint(vb.Verts[i].xyz); + } + + ovrSurfaceDef s; + s.geo = FontGeometry(vb.NumVerts / 4, blockBounds); + + glBindVertexArray(s.geo.vertexArrayObject); + glBindBuffer(GL_ARRAY_BUFFER, s.geo.vertexBuffer); + glBufferSubData(GL_ARRAY_BUFFER, 0, vb.NumVerts * sizeof(fontVertex_t), (void*)vb.Verts); + glBindVertexArray(0); + + vb.Free(); + + // for now we set up both the gpu state, program, and uniformdata. + + // Special blend mode to also work over underlay layers + s.graphicsCommand.GpuState.blendEnable = ovrGpuState::BLEND_ENABLE_SEPARATE; + s.graphicsCommand.GpuState.blendSrc = GL_SRC_ALPHA; + s.graphicsCommand.GpuState.blendDst = GL_ONE_MINUS_SRC_ALPHA; + s.graphicsCommand.GpuState.blendSrcAlpha = GL_ONE; + s.graphicsCommand.GpuState.blendDstAlpha = GL_ONE_MINUS_SRC_ALPHA; + s.graphicsCommand.GpuState.depthMaskEnable = false; + + s.graphicsCommand.Program = FontProgram; + s.graphicsCommand.UniformData[0].Data = (void*)&FontTexture; + + s.surfaceName = text; + return s; +} + +//================================================================================================== +// BitmapFontSurfaceLocal +// +class BitmapFontSurfaceLocal : public BitmapFontSurface { + public: + BitmapFontSurfaceLocal(); + virtual ~BitmapFontSurfaceLocal(); + + virtual void Init(const int maxVertices); + void Free(); + + // add text to the VBO that will render in a 2D pass. + virtual Vector3f DrawText3D( + BitmapFont const& font, + const fontParms_t& flags, + const Vector3f& pos, + Vector3f const& normal, + Vector3f const& up, + float const scale, + Vector4f const& color, + char const* text); + virtual Vector3f DrawText3Df( + BitmapFont const& font, + const fontParms_t& flags, + const Vector3f& pos, + Vector3f const& normal, + Vector3f const& up, + float const scale, + Vector4f const& color, + char const* text, + ...); + + virtual Vector3f DrawTextBillboarded3D( + BitmapFont const& font, + fontParms_t const& flags, + Vector3f const& pos, + float const scale, + Vector4f const& color, + char const* text); + virtual Vector3f DrawTextBillboarded3Df( + BitmapFont const& font, + fontParms_t const& flags, + Vector3f const& pos, + float const scale, + Vector4f const& color, + char const* fmt, + ...); + + // transform the billboarded font strings + virtual void Finish(Matrix4f const& viewMatrix); + + // add the VBO to the surface render list + virtual void AppendSurfaceList(BitmapFont const& font, std::vector& surfaceList) + const; + + virtual bool IsInitialized() const { + return Initialized; + } + + virtual void SetCullEnabled(const bool enabled); + + private: + // This limitation may not exist anymore now that ModelMatrix is no longer a member. + BitmapFontSurfaceLocal& operator=(BitmapFontSurfaceLocal const& rhs); + + mutable ovrSurfaceDef FontSurfaceDef; + + fontVertex_t* Vertices; // vertices that are written to the VBO. Fixed size to not overflow VBO + int MaxVertices; + int MaxIndices; + int CurVertex; // reset every Render() + int CurIndex; // reset every Render() + bool Initialized; + + std::vector + VertexBlocks; // each pointer in the array points to an allocated block ov +}; + +//================================================================================================== +// FontInfoType +//================================================================================================== + +//============================== +// FontInfoType::Load +bool FontInfoType::Load(ovrFileSys& fileSys, char const* uri) { + std::vector buffer; + if (!fileSys.ReadFile(uri, buffer)) { + ALOG("FontInfoType::Load ReadFile FAIL uri = '%s' ", uri); + return false; + } + ALOG("FontInfoType::Load ReadFile OK buffer size = '%d' ", (int)buffer.size()); + + bool result = LoadFromBuffer(buffer.data(), buffer.size()); + // enable the block below to rewrite the font + /* + if ( result ) + { + Save( "c:/temp/" ); + } + */ + + ALOG("FontInfoType::Load ReadFile DONE result = '%d' ", (int)result); + return result; +} + +//============================== +// FontInfoType::LoadFromBuffer +bool FontInfoType::LoadFromBuffer(void const* buffer, size_t const bufferSize) { + char const* errorMsg = NULL; + std::shared_ptr jsonRoot = + OVR::JSON::Parse(reinterpret_cast(buffer), &errorMsg); + if (jsonRoot == NULL) { + ALOGW("OVR::JSON Error: %s", (errorMsg != NULL) ? errorMsg : ""); + ALOG( + "FontInfoType::LoadFromBuffer FAIL OVR::JSON ERROR = '%s' ", + (errorMsg != NULL) ? errorMsg : ""); + return false; + } + + int32_t maxCharCode = -1; + // currently we're only supporting the first unicode plane up to 65k. If we were to support + // other planes we could conceivably end up with a very sparse 1,114,111 byte type for mapping + // character codes to glyphs and if that's the case we may just want to use a hash, or use a + // combination of tables for the first 65K and hashes for the other, less-frequently-used + // characters. + static const int MAX_GLYPHS = 0xffff; + + // load the glyphs + const OVR::JsonReader jsonGlyphs(jsonRoot); + if (!jsonGlyphs.IsObject()) { + ALOG("FontInfoType::LoadFromBuffer FAIL ==> jsonGlyphs.IsObject() = FALSE "); + return false; + } + + // OVR::JSON doesn't have ints so cast from float to an int + int Version = static_cast(jsonGlyphs.GetChildFloatByName("Version")); + if (Version != FNT_FILE_VERSION) { + ALOG("FontInfoType::LoadFromBuffer FAIL ==> Version != FNT_FILE_VERSION "); + return false; + } + + FontName = jsonGlyphs.GetChildStringByName("FontName"); + CommandLine = jsonGlyphs.GetChildStringByName("CommandLine"); + ImageFileName = jsonGlyphs.GetChildStringByName("ImageFileName"); + const int numGlyphs = jsonGlyphs.GetChildInt32ByName("NumGlyphs"); + if (numGlyphs < 0 || numGlyphs > MAX_GLYPHS) { + OVR_ASSERT(numGlyphs > 0 && numGlyphs <= MAX_GLYPHS); + ALOG("FontInfoType::LoadFromBuffer FAIL ==> numGlyphs < 0 || numGlyphs > MAX_GLYPHS "); + return false; + } + + NaturalWidth = jsonGlyphs.GetChildFloatByName("NaturalWidth"); + NaturalHeight = jsonGlyphs.GetChildFloatByName("NaturalHeight"); + + // we scale everything after loading integer values from the OVR::JSON file because the OVR + // OVR::JSON writer loses precision on floats + float nwScale = 1.0f / NaturalWidth; + float nhScale = 1.0f / NaturalHeight; + + HorizontalPad = jsonGlyphs.GetChildFloatByName("HorizontalPad") * nwScale; + VerticalPad = jsonGlyphs.GetChildFloatByName("VerticalPad") * nhScale; + FontHeight = jsonGlyphs.GetChildFloatByName("FontHeight") * nhScale; + CenterOffset = jsonGlyphs.GetChildFloatByName("CenterOffset"); + TweakScale = jsonGlyphs.GetChildFloatByName("TweakScale", 1.0f); + EdgeWidth = jsonGlyphs.GetChildFloatByName("EdgeWidth", 32.0f); + +#if defined(OVR_BUILD_DEBUG) + ALOG("FontName = %s", FontName.c_str()); + ALOG("CommandLine = %s", CommandLine.c_str()); + ALOG("HorizontalPad = %.4f", HorizontalPad); + ALOG("VerticalPad = %.4f", VerticalPad); + ALOG("FontHeight = %.4f", FontHeight); + ALOG("CenterOffset = %.4f", CenterOffset); + ALOG("TweakScale = %.4f", TweakScale); + ALOG("EdgeWidth = %.4f", EdgeWidth); + ALOG("ImageFileName = %s", ImageFileName.c_str()); + ALOG("Loading %i glyphs.", numGlyphs); +#endif + + /// HACK: this is hard-coded until we do not have a dependcy on reading the font from Home + /// TODO: I believe this can now be removed. + if (OVR::OVR_stricmp(FontName.c_str(), "korean.fnt") == 0) { + TweakScale = 0.75f; + CenterOffset = -0.02f; + } + /// HACK: end hack + + const OVR::JsonReader jsonWeightArray(jsonGlyphs.GetChildByName("Weights")); + if (jsonWeightArray.IsValid()) { +#if defined(OVR_BUILD_DEBUG) + ALOG("jsonWeightArray valid"); +#endif + + for (; !jsonWeightArray.IsEndOfArray();) { + const OVR::JsonReader jsonWeight(jsonWeightArray.GetNextArrayElement()); + if (jsonWeight.IsObject()) { + ovrFontWeight w; + w.AlphaCenterOffset = jsonWeight.GetChildFloatByName("AlphaCenterOffset"); + w.ColorCenterOffset = jsonWeight.GetChildFloatByName("ColorCenterOffset"); + FontWeights.push_back(w); + } + } + } + +#if defined(OVR_BUILD_DEBUG) + ALOG("jsonWeightArray DONE"); +#endif + + Glyphs.resize(numGlyphs); + const OVR::JsonReader jsonGlyphArray(jsonGlyphs.GetChildByName("Glyphs")); + + double oWidth = 0.0; + double oHeight = 0.0; + + if (jsonGlyphArray.IsArray()) { + for (int i = 0; i < static_cast(Glyphs.size()) && !jsonGlyphArray.IsEndOfArray(); + i++) { + const OVR::JsonReader jsonGlyph(jsonGlyphArray.GetNextArrayElement()); + if (jsonGlyph.IsObject()) { + FontGlyphType& g = Glyphs[i]; + g.CharCode = jsonGlyph.GetChildInt32ByName("CharCode"); + g.X = jsonGlyph.GetChildFloatByName("X"); + g.Y = jsonGlyph.GetChildFloatByName("Y"); + g.Width = jsonGlyph.GetChildFloatByName("Width"); + g.Height = jsonGlyph.GetChildFloatByName("Height"); + g.AdvanceX = jsonGlyph.GetChildFloatByName("AdvanceX"); + g.AdvanceY = jsonGlyph.GetChildFloatByName("AdvanceY"); + g.BearingX = jsonGlyph.GetChildFloatByName("BearingX"); + g.BearingY = jsonGlyph.GetChildFloatByName("BearingY"); + + if (g.CharCode == 'O') { + oWidth = g.Width; + oHeight = g.Height; + } + + g.X *= nwScale; + g.Y *= nhScale; + g.Width *= nwScale; + g.Height *= nhScale; + g.AdvanceX *= nwScale; + g.AdvanceY *= nhScale; + g.BearingX *= nwScale; + g.BearingY *= nhScale; + + float const ascent = g.BearingY; + float const descent = g.Height - g.BearingY; + if (ascent > MaxAscent) { + MaxAscent = ascent; + } + if (descent > MaxDescent) { + MaxDescent = descent; + } + +#if defined(OVR_BUILD_DEBUG) +/// ALOG( "Glyphs[%d] --> X=%.3f Y=%.3f CharCode=%d", i, g.X, g.Y, g.CharCode ); +#endif + + maxCharCode = std::max(maxCharCode, g.CharCode); + } + } + } + +#if defined(OVR_BUILD_DEBUG) + ALOG("jsonGlyphArray DONE maxCharCode =%d", maxCharCode); +#endif + + float const DEFAULT_TEXT_SCALE = 0.0025f; + + double const NATURAL_WIDTH_SCALE = NaturalWidth / 4096.0; + double const NATURAL_HEIGHT_SCALE = NaturalHeight / 3820.0; + double const DEFAULT_O_WIDTH = 325.0; + double const DEFAULT_O_HEIGHT = 322.0; + double const OLD_WIDTH_FACTOR = 1.04240608; + float const widthScaleFactor = + static_cast(DEFAULT_O_WIDTH / oWidth * OLD_WIDTH_FACTOR * NATURAL_WIDTH_SCALE); + float const heightScaleFactor = + static_cast(DEFAULT_O_HEIGHT / oHeight * OLD_WIDTH_FACTOR * NATURAL_HEIGHT_SCALE); + + ScaleFactorX = DEFAULT_SCALE_FACTOR * DEFAULT_TEXT_SCALE * widthScaleFactor * TweakScale; + ScaleFactorY = DEFAULT_SCALE_FACTOR * DEFAULT_TEXT_SCALE * heightScaleFactor * TweakScale; + + // This is not intended for wide or ucf character sets -- depending on the size range of + // character codes lookups may need to be changed to use a hash. + if (maxCharCode >= MAX_GLYPHS) { + OVR_ASSERT(maxCharCode <= MAX_GLYPHS); + maxCharCode = MAX_GLYPHS; + } + + // resize the array to the maximum glyph value + CharCodeMap.resize(maxCharCode + 1); + + // init to empty value + CharCodeMap.assign(CharCodeMap.size(), -1); + + for (int i = 0; i < static_cast(Glyphs.size()); ++i) { + FontGlyphType const& g = Glyphs[i]; + CharCodeMap[g.CharCode] = i; + } + + ALOG("FontInfoType load SUCCESS"); + return true; +} + +class ovrGlyphSort { + public: + void SortGlyphIndicesByCharacterCode( + std::vector const& glyphs, + std::vector& glyphIndices) { + Glyphs = &glyphs; + qsort(glyphIndices.data(), glyphIndices.size(), sizeof(int), CompareByCharacterCode); + Glyphs = nullptr; + } + + private: + static int CompareByCharacterCode(const void* a, const void* b) { + int const aIndex = *(int*)a; + int const bIndex = *(int*)b; + FontGlyphType const& glyphA = (*Glyphs)[aIndex]; + FontGlyphType const& glyphB = (*Glyphs)[bIndex]; + if (glyphA.CharCode < glyphB.CharCode) { + return -1; + } else if (glyphA.CharCode > glyphB.CharCode) { + return 1; + } + return 0; + } + + static std::vector const* Glyphs; +}; + +std::vector const* ovrGlyphSort::Glyphs = nullptr; + +bool FontInfoType::Save(char const* path) { + std::shared_ptr joFont = OVR::JSON::CreateObject(); + joFont->AddStringItem("FontName", FontName.c_str()); + joFont->AddStringItem("CommandLine", CommandLine.c_str()); + joFont->AddNumberItem("Version", FNT_FILE_VERSION); + joFont->AddStringItem("ImageFileName", ImageFileName.c_str()); + joFont->AddNumberItem("NaturalWidth", NaturalWidth); + joFont->AddNumberItem("NaturalHeight", NaturalHeight); + joFont->AddNumberItem("HorizontalPad", HorizontalPad); + joFont->AddNumberItem("VerticalPad", VerticalPad); + joFont->AddNumberItem("FontHeight", FontHeight); + joFont->AddNumberItem("CenterOffset", CenterOffset); + joFont->AddNumberItem("TweakScale", TweakScale); + joFont->AddNumberItem("EdgeWidth", EdgeWidth); + joFont->AddNumberItem("NumGlyphs", Glyphs.size()); + + std::vector glyphIndices; + glyphIndices.resize(Glyphs.size()); + for (int i = 0; i < static_cast(Glyphs.size()); ++i) { + glyphIndices[i] = i; + } + + ovrGlyphSort sort; + sort.SortGlyphIndicesByCharacterCode(Glyphs, glyphIndices); + + std::shared_ptr joGlyphsArray = OVR::JSON::CreateArray(); + joFont->AddItem("Glyphs", joGlyphsArray); + // add all glyphs + for (const int& index : glyphIndices) { + FontGlyphType const& g = Glyphs[index]; + + std::shared_ptr joGlyph = OVR::JSON::CreateObject(); + joGlyph->AddNumberItem("CharCode", g.CharCode); + joGlyph->AddNumberItem( + "X", g.X); // now in natural units because the OVR::JSON writer loses precision + joGlyph->AddNumberItem( + "Y", g.Y); // now in natural units because the OVR::JSON writer loses precision + joGlyph->AddNumberItem("Width", g.Width); + joGlyph->AddNumberItem("Height", g.Height); + joGlyph->AddNumberItem("AdvanceX", g.AdvanceX); + joGlyph->AddNumberItem("AdvanceY", g.AdvanceY); + joGlyph->AddNumberItem("BearingX", g.BearingX); + joGlyph->AddNumberItem("BearingY", g.BearingY); + joGlyphsArray->AddArrayElement(joGlyph); + } + + char filePath[1024]; + AppendUriPath(path, FontName.c_str(), filePath, sizeof(filePath)); + ALOG("Writing font file '%s'\n...", filePath); + joFont->Save(filePath); + + return true; +} + +//============================== +// FontInfoType::GlyphForCharCode +FontGlyphType const& FontInfoType::GlyphForCharCode(uint32_t const charCode) const { + auto lookupGlyph = [this](uint32_t const ch) { + return ch >= CharCodeMap.size() ? -1 : CharCodeMap[ch]; + }; + + int glyphIndex = + lookupGlyph(charCode); // charCode >= CharCodeMap.size() ? -1 : CharCodeMap[charCode]; + if (glyphIndex < 0 || glyphIndex >= static_cast(Glyphs.size())) { +#if defined(OVR_BUILD_DEBUG) + OVR_WARN( + "FontInfoType::GlyphForCharCode FAILED TO FIND GLYPH FOR CHARACTER! charCode %u => %i [mapsize=%zu] [glyphsize=%i]", + charCode, + glyphIndex, + CharCodeMap.size(), + static_cast(Glyphs.size())); +#endif + + switch (charCode) { + case '*': { + static FontGlyphType emptyGlyph; + return emptyGlyph; + } + // Some fonts don't include special punctuation marks but translators are apt to use + // absolutely every obscure character there is. Alternatively this could be done when + // the text is loaded from the string resource, but at that point we do not necessarily + // know if the font contains the special characters. + case 0x201C: // left double quote + case 0x201D: // right double quote + { + return GlyphForCharCode('\"'); + } + case 0x2013: // English Dash + case 0x2014: // Em Dash + { + return GlyphForCharCode('-'); + } + default: { + // if we have a glyph for "replacement character" U+FFFD, use that, otherwise use + // "black diamond" U+25C6. If that doesn't exists, use "halfwidth black square" + // U+FFED, and if that doesn't exist, use the asterisk. + glyphIndex = lookupGlyph(0xFFFD); + if (glyphIndex < 0 || glyphIndex >= static_cast(Glyphs.size())) { + glyphIndex = lookupGlyph(0x25C6); + if (glyphIndex < 0 || glyphIndex >= static_cast(Glyphs.size())) { + glyphIndex = lookupGlyph(0xFFED); + if (glyphIndex < 0 || glyphIndex >= static_cast(Glyphs.size())) { +#if 0 // enable to make unknown characters obvious + static const char unknownGlyphs[] = { '!', '@', '#', '$', '%', '^', '&', '*', '(', ')' }; + static int curGlyph = 0; + curGlyph++; + curGlyph = curGlyph % sizeof( unknownGlyphs ); + return GlyphForCharCode( unknownGlyphs[curGlyph] ); +#else + return GlyphForCharCode('*'); +#endif + } + } + } + } + } + } + + OVR_ASSERT(glyphIndex >= 0 && glyphIndex < static_cast(Glyphs.size())); + return Glyphs[glyphIndex]; +} + +//================================================================================================== +// BitmapFontLocal +//================================================================================================== + +static bool ExtensionMatches(char const* fileName, char const* ext) { + if (fileName == NULL || ext == NULL) { + return false; + } + size_t extLen = OVR::OVR_strlen(ext); + size_t fileNameLen = OVR::OVR_strlen(fileName); + if (extLen > fileNameLen) { + return false; + } + return OVR::OVR_stricmp(fileName + fileNameLen - extLen, ext) == 0; +} + +//============================== +// BitmapFontLocal::Load +bool BitmapFontLocal::Load(ovrFileSys& fileSys, char const* uri) { + char scheme[128]; + char host[128]; + int port; + char path[1024]; + + ALOG("Load Uri = %s", uri); + + if (!ovrUri::ParseUri( + uri, + scheme, + sizeof(scheme), + NULL, + 0, + NULL, + 0, + host, + sizeof(host), + port, + path, + sizeof(path), + NULL, + 0, + NULL, + 0)) { + ALOG("ParseUri FAILED Uri = %s", uri); + return false; + } + + if (!FontInfo.Load(fileSys, uri)) { + ALOG("FontInfo.Load FAILED Uri = %s", uri); + return false; + } + + char imagePath[1024]; + { + ALOG("FontInfo file Uri = %s", uri); + ALOG("FontInfo file path = %s", path); + std::string imageBaseName = ExtractFile(FontInfo.ImageFileName); + ALOG("image base name = %s", imageBaseName.c_str()); + + StripFilename(path, imagePath, sizeof(imagePath)); + ALOG("FontInfo base path = %s", imagePath); + + AppendUriPath(imagePath, imageBaseName.c_str(), imagePath, sizeof(imagePath)); + ALOG("imagePath = %s", imagePath); + } + + char imageUri[1024]; + OVR::OVR_sprintf(imageUri, sizeof(imageUri), "%s://%s%s", scheme, host, imagePath); + ALOG("imageUri = %s", imageUri); + if (!LoadImage(fileSys, imageUri)) { + ALOG("BitmapFont image load FAILED: imageUri = '%s' uri = '%s'", imageUri, uri); + return false; + } + + // create the shaders for font rendering if not already created + if (FontProgram.VertexShader == 0 || FontProgram.FragmentShader == 0) { + static ovrProgramParm fontUniformParms[] = { + {"Texture0", ovrProgramParmType::TEXTURE_SAMPLED}, + }; + FontProgram = GlProgram::Build( + FontSingleTextureVertexShaderSrc, + SDFFontFragmentShaderSrc, + fontUniformParms, + sizeof(fontUniformParms) / sizeof(ovrProgramParm)); + } + +#if defined(OVR_BUILD_DEBUG) + ALOG("BitmapFont for uri = %s load SUCCESS", uri); +#endif + + return true; +} + +//============================== +// BitmapFontLocal::Load +bool BitmapFontLocal::LoadImage(ovrFileSys& fileSys, char const* uri) { + std::vector imageBuffer; + if (!fileSys.ReadFile(uri, imageBuffer)) { + return false; + } + bool success = LoadImageFromBuffer(uri, imageBuffer, ExtensionMatches(uri, ".astc")); + if (!success) { + ALOG("BitmapFontLocal::LoadImage: failed to load image '%s'", uri); + } + return success; +} + +//============================== +// BitmapFontLocal::LoadImageFromBuffer +bool BitmapFontLocal::LoadImageFromBuffer( + char const* imageName, + std::vector& buffer, + bool const isASTC) { + DeleteTexture(FontTexture); + + if (isASTC) { + FontTexture = LoadASTCTextureFromMemory(buffer.data(), buffer.size(), 1, false); + } else { + FontTexture = LoadTextureFromBuffer( + imageName, buffer, TextureFlags_t(TEXTUREFLAG_NO_DEFAULT), ImageWidth, ImageHeight); + } + if (FontTexture.IsValid() == false) { + ALOGW("BitmapFontLocal::Load: failed to load '%s'", imageName); + return false; + } + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, FontTexture.texture); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + ALOG("BitmapFontLocal::LoadImageFromBuffer: success"); + return true; +} + +//============================== +// BitmapFontLocal::GetGlyphMetrics +void BitmapFontLocal::GetGlyphMetrics( + const uint32_t charCode, + float& width, + float& height, + float& advanceX, + float& advanceY) const { + FontGlyphType const& glyph = GlyphForCharCode(charCode); + width = glyph.Width; + height = glyph.Height; + advanceX = glyph.AdvanceX; + advanceY = glyph.AdvanceY; +} + +//============================== +// BitmapFontLocal::WordWrapText +bool BitmapFontLocal::WordWrapText( + std::string& inOutText, + const float widthMeters, + const float fontScale) const { + return WordWrapText(inOutText, widthMeters, std::vector(), fontScale); +} + +//============================== +// BitmapFontLocal::WordWrapText +bool BitmapFontLocal::WordWrapText( + std::string& inOutText, + const float widthMeters, + std::vector wholeStrsList, + const float fontScale) const { + char const* source = inOutText.c_str(); + if (*source == '\0') { + // ALOG( "Tried to word-wrap NULL text!" ); + return false; + } + +#if defined(OVR_BUILD_DEBUG) +/// ALOG( "Word-wrapping '%s' ... ", source ); +#endif + + // we will change characters in the new string and can potentially add line breaks after some + // characters so it may grow larger than the original string. While determining the length of + // the string, find any characters that may potentially have a line break added after them and + // increase the string length by 1 for each. + auto IsPostLineBreakChar = [](uint32_t const ch) { + // array of characters after which we can add line breaks. + uint32_t const postLineBreakChars[] = { + ',', + '.', + ':', + ';', + '>', + '!', + '?', + ')', + ']', + '-', + '=', + '+', + '*', + '\\', + '/', + 0x3002, // Chinese 'full-stop + '\0' // list terminator + }; + + for (int i = 0; postLineBreakChars[i] != 0; ++i) { + if (ch == postLineBreakChars[i]) { + return true; + } + } + return false; + }; + + size_t lengthInBytes = 0; + char const* cur = source; + for (;;) { + char const* prev = cur; + uint32_t const charCode = UTF8Util::DecodeNextChar(&cur); + ptrdiff_t const numBytes = cur - prev; + + if (charCode == '\0') { + break; + } + + lengthInBytes += static_cast(numBytes); + + if (IsPostLineBreakChar(charCode)) { + ++lengthInBytes; // add one byte for '\n' + } + } + + // we now have the maximum possible length of the string with line-breaks. + char* dest = new char[lengthInBytes + 1]; + dest[lengthInBytes] = '\0'; + intptr_t destOffset = 0; + + cur = source; // reset + intptr_t lastLineBreakOfs = -1; + intptr_t lastPostLineBreakOfs = -1; + + float const xScale = FontInfo.ScaleFactorX * fontScale; + double lineWidthAtLastBreak = 0.0; + double lineWidth = 0.0; + + while (*cur != '\0') { + // skip over formatting + uint32_t color; + uint32_t weight; + char const* p = cur; + while (CheckForFormatEscape(&p, color, weight)) { + } + ptrdiff_t numFormatChars = p - cur; + if (numFormatChars > 0) { + // copy the formatting to the destination + memcpy(dest + destOffset, cur, numFormatChars); + cur += numFormatChars; + destOffset += numFormatChars; + } + + double lastLineWidth = lineWidth; + intptr_t lastDestOffset = destOffset; + + char const* pre = cur; + uint32_t charCode = UTF8Util::DecodeNextChar(&cur); + if (charCode == '\0') { + UTF8Util::EncodeChar(dest, &destOffset, '\0'); + break; + } + intptr_t const charCodeSize = cur - pre; + + // replace tabs with a space + if (charCode == '\t') { + charCode = ' '; + } + + if (charCode == ' ') { + lastLineBreakOfs = destOffset; + lineWidthAtLastBreak = lineWidth; // line width *before* the space + } else if (charCode == '\\') { + // replace verbatim and "\r" with explicit breaks + char const* temp = cur; + uint32_t nextCode = UTF8Util::DecodeNextChar(&temp); + if (nextCode == 'r' || nextCode == 'n') { + lineWidth = 0.0; + lineWidthAtLastBreak = 0.0; + cur = temp; // skip the next character + // output a linefeed + UTF8Util::EncodeChar(dest, &destOffset, '\n'); + continue; + } + } else if (charCode == '\r' || charCode == '\n') { + lineWidth = 0.0; + lineWidthAtLastBreak = 0.0; + // output a linefeed + UTF8Util::EncodeChar(dest, &destOffset, '\n'); + continue; + } + + FontGlyphType const& g = GlyphForCharCode(charCode); + lineWidth += g.AdvanceX * xScale; + + if (lineWidth >= widthMeters) { + if (charCode == ' ') { + // just turn the emitted character into a linefeed + charCode = '\n'; + } else { + if (lastLineBreakOfs < 0 && lastPostLineBreakOfs < 0) { + // we're unable to wrap based on punctuation or whitespace, so we must break at + // some arbitrary character. This is fine for Chinese, but not for western + // languages. It's also relatively rare for western languages since they use + // spaces between each word. + /*if ( !isCJK ) + { + return false; + }*/ + // just break at the last character that didn't exceed the line width + lastPostLineBreakOfs = lastDestOffset; + lineWidthAtLastBreak = lastLineWidth; + } + + intptr_t bestBreakOfs = lastLineBreakOfs > lastPostLineBreakOfs + ? lastLineBreakOfs + : lastPostLineBreakOfs; + + char* destBreakPoint = dest + bestBreakOfs; + + bool const overwrite = bestBreakOfs != lastPostLineBreakOfs; + if (overwrite) { + // we overwrote with a single byte, but we could have overwritten a multi-byte + // unicode character so we may need to shift the entire destination string down + // by (sizeof overwritten char) - 1 bytes. decode the character at the bestBreak + // point to determine how many bytes we need to skip + char const* afterDestBreakPoint = destBreakPoint; + uint32_t const overwrittenCode = UTF8Util::DecodeNextChar(&afterDestBreakPoint); + OVR_UNUSED(overwrittenCode); + + // encode the line feed over the last break point. + UTF8Util::EncodeChar(dest, &bestBreakOfs, '\n'); + + // determine if we need to shift the dest text down + ptrdiff_t numBytesToSkip = afterDestBreakPoint - destBreakPoint; + if (numBytesToSkip > 1) { + memcpy( + dest + bestBreakOfs + 1, + dest + bestBreakOfs + numBytesToSkip, + lengthInBytes - bestBreakOfs); + destOffset -= numBytesToSkip - 1; + } + } else { + // move the buffer contents after the insertion point to make room for 1 byte + for (intptr_t ofs = destOffset; ofs > bestBreakOfs; --ofs) { + dest[ofs] = dest[ofs - 1]; + } + // encode the line feed after the last break point + UTF8Util::EncodeChar(dest, &bestBreakOfs, '\n'); + destOffset++; + } + } + + lastLineBreakOfs = -1; + lastPostLineBreakOfs = -1; + + // subtract the width after the last whitespace so that we don't lose any accumulated + // width. + lineWidth -= lineWidthAtLastBreak; + } + + if (IsPostLineBreakChar(charCode)) { + lastPostLineBreakOfs = destOffset + charCodeSize; + lineWidthAtLastBreak = lineWidth; // line width *after* the break char + } + + // output the current character to the destination buffer + UTF8Util::EncodeChar(dest, &destOffset, charCode); + + // in CJK cases we cannot be absolutely certain of the number of breaks until we've scanned + // everything and computed the width (because we may break at any character). In that case, + // we may need to expand the buffer. + if (destOffset + 6 > + static_cast(lengthInBytes)) // 6 bytes is max UTF-8 encoding size + { + size_t newLen = (lengthInBytes * 3) / 2; + char* newDest = new char[newLen + 1]; + memcpy(newDest, dest, destOffset); + newDest[newLen] = '\0'; + delete[] dest; + dest = newDest; + lengthInBytes = newLen; + } + } + + dest[destOffset] = '\0'; + inOutText = dest; + delete[] dest; + +#if defined(OVR_BUILD_DEBUG) +/// ALOG( "Word-wrapping '%s' -> '%s' DONE", source, inOutText.c_str() ); +#endif + + return true; +} + +float BitmapFontLocal::GetFirstFitChars( + std::string& inOutText, + const float widthMeters, + const int numLines, + const float fontScale) const { + if (inOutText.empty()) { + return 0; + } + + float const xScale = FontInfo.ScaleFactorX * fontScale; + float lineWidth = 0.0f; + int remainingLines = numLines; + + for (int32_t pos = 0; pos < static_cast(inOutText.length()); ++pos) { + uint32_t charCode = UTF8Util::GetCharAt(pos, inOutText.c_str()); + if (charCode == '\n') { + remainingLines--; + if (remainingLines == 0) { + inOutText = inOutText.substr(0, pos); + return widthMeters - lineWidth; + } + } else if (charCode != '\0') { + FontGlyphType const& glyph = GlyphForCharCode(charCode); + lineWidth += glyph.AdvanceX * xScale; + if (lineWidth > widthMeters) { + inOutText = + inOutText.substr(0, pos - 1); // -1 to not include current char that didn't fit. + return widthMeters - (lineWidth - glyph.AdvanceX * xScale); + } + } + } + // Entire text fits, leave inOutText unchanged + return 0; +} + +float BitmapFontLocal::GetLastFitChars( + std::string& inOutText, + const float widthMeters, + const float fontScale) const { + if (inOutText.empty()) { + return 0; + } + + float const xScale = FontInfo.ScaleFactorX * fontScale; + float lineWidth = 0.0f; + + for (int32_t pos = static_cast(inOutText.length()) - 1; pos >= 0; --pos) { + uint32_t charCode = UTF8Util::GetCharAt(pos, inOutText.c_str()); + FontGlyphType const& glyph = GlyphForCharCode(charCode); + lineWidth += glyph.AdvanceX * xScale; + if (lineWidth > widthMeters) { + inOutText = inOutText.substr( + pos + 1, inOutText.length()); // +1 to not include current char that didn't fit. + return widthMeters - (lineWidth - glyph.AdvanceX * xScale); + } + } + // Entire text fits, leave inOutText unchanged + return 0; +} + +//============================== +// BitmapFontLocal::CalcTextWidth +float BitmapFontLocal::CalcTextWidth(char const* text) const { + float width = 0.0f; + char const* p = text; + uint32_t color; + uint32_t weight; + while (CheckForFormatEscape(&p, color, weight)) + ; + for (uint32_t charCode = UTF8Util::DecodeNextChar(&p); charCode != '\0'; + charCode = UTF8Util::DecodeNextChar(&p)) { + if (charCode == '\r' || charCode == '\n') { + continue; // skip line endings + } + + FontGlyphType const& g = GlyphForCharCode(charCode); + width += g.AdvanceX * FontInfo.ScaleFactorX; + while (CheckForFormatEscape(&p, color, weight)) + ; + } + +#if defined(OVR_BUILD_DEBUG) +/// ALOG( "CalcTextWidth '%s' = %.3f ", text, width ); +#endif + + return width; +} + +//============================== +// BitmapFontLocal::CalcTextMetrics +void BitmapFontLocal::CalcTextMetrics( + char const* text, + size_t& len, + float& width, + float& height, + float& firstAscent, + float& lastDescent, + float& fontHeight, + float* lineWidths, + int const maxLines, + int& numLines) const { + len = 0; + numLines = 0; + width = 0.0f; + height = 0.0f; + + if (lineWidths == NULL || maxLines <= 0) { + return; + } + if (text == NULL || text[0] == '\0') { + return; + } + + float maxLineAscent = 0.0f; + float maxLineDescent = 0.0f; + firstAscent = 0.0f; + lastDescent = 0.0f; + fontHeight = FontInfo.FontHeight * FontInfo.ScaleFactorY; + numLines = 0; + int charsOnLine = 0; + lineWidths[0] = 0.0f; + uint32_t color; + uint32_t weight; + + char const* p = text; + for (;; len++) { + while (CheckForFormatEscape(&p, color, weight)) + ; + uint32_t charCode = UTF8Util::DecodeNextChar(&p); + if (charCode == '\r') { + continue; // skip carriage returns + } + if (charCode == '\n' || charCode == '\0') { + // keep track of the widest line, which will be the width of the entire text block + if (numLines < maxLines && lineWidths[numLines] > width) { + width = lineWidths[numLines]; + } + + firstAscent = (numLines == 0) ? maxLineAscent : firstAscent; + lastDescent = (charsOnLine > 0) ? maxLineDescent : lastDescent; + charsOnLine = 0; + + numLines++; + if (numLines < maxLines) { + // if we're not out of array space, advance and zero the width + lineWidths[numLines] = 0.0f; + maxLineAscent = 0.0f; + maxLineDescent = 0.0f; + } + + if (charCode == '\0') { + break; + } + continue; + } + + charsOnLine++; + + FontGlyphType const& g = GlyphForCharCode(charCode); + + if (numLines < maxLines) { + lineWidths[numLines] += g.AdvanceX * FontInfo.ScaleFactorX; + } + + if (numLines == 0) { + if (g.BearingY > maxLineAscent) { + maxLineAscent = g.BearingY; + } + } else { + // all lines after the first line are full height + maxLineAscent = FontInfo.FontHeight; + } + float descent = g.Height - g.BearingY; + if (descent > maxLineDescent) { + maxLineDescent = descent; + } + } + + OVR_ASSERT(numLines >= 1); + + firstAscent *= FontInfo.ScaleFactorY; + lastDescent *= FontInfo.ScaleFactorY; + height = firstAscent; + + if (numLines < maxLines) { + height += (numLines - 1) * FontInfo.FontHeight * FontInfo.ScaleFactorY; + } else { + height += (maxLines - 1) * FontInfo.FontHeight * FontInfo.ScaleFactorY; + } + + height += lastDescent; + + // OVR_ASSERT( numLines <= maxLines ); +} + +//============================== +// BitmapFontLocal::TruncateText +void BitmapFontLocal::TruncateText(std::string& inOutText, int const maxLines) const { + char const* p = inOutText.c_str(); + + if (p == nullptr || p[0] == '\0') { + return; + } + + uint32_t color; + uint32_t weight; + + int lineCount = 0; + size_t len = 0; + for (;; len++) { + while (CheckForFormatEscape(&p, color, weight)) + ; + uint32_t charCode = UTF8Util::DecodeNextChar(&p); + if (charCode == '\n') { + lineCount++; + if (lineCount == maxLines - 1) { + inOutText = inOutText.substr(0, len + 1); + inOutText += "..."; + break; + } + } + + if (charCode == '\0') { + break; + } + } + +#if defined(OVR_BUILD_DEBUG) +/// ALOG( "TruncateText '%s' -> '%s' ", p, inOutText.c_str() ); +#endif +} + +//================================================================================================== +// BitmapFontSurfaceLocal +//================================================================================================== + +//============================== +// BitmapFontSurfaceLocal::BitmapFontSurface +BitmapFontSurfaceLocal::BitmapFontSurfaceLocal() + : Vertices(NULL), + MaxVertices(0), + MaxIndices(0), + CurVertex(0), + CurIndex(0), + Initialized(false) {} + +//============================== +// BitmapFontSurfaceLocal::~BitmapFontSurfaceLocal +BitmapFontSurfaceLocal::~BitmapFontSurfaceLocal() { + FontSurfaceDef.geo.Free(); + delete[] Vertices; + Vertices = NULL; +} + +//============================== +// BitmapFontSurfaceLocal::Init +// Initializes the surface VBO +void BitmapFontSurfaceLocal::Init(const int maxVertices) { + OVR_ASSERT( + FontSurfaceDef.geo.vertexBuffer == 0 && FontSurfaceDef.geo.indexBuffer == 0 && + FontSurfaceDef.geo.vertexArrayObject == 0); + OVR_ASSERT(Vertices == NULL); + if (Vertices != NULL) { + delete[] Vertices; + Vertices = NULL; + } + OVR_ASSERT(maxVertices % 4 == 0); + + MaxVertices = maxVertices; + MaxIndices = (maxVertices / 4) * 6; + + Vertices = new fontVertex_t[maxVertices]; + + CurVertex = 0; + CurIndex = 0; + + Bounds3f localBounds(Bounds3f::Init); + FontSurfaceDef.geo = FontGeometry(MaxVertices / 4, localBounds); + FontSurfaceDef.geo.indexCount = 0; // if there's anything to render this will be modified + + FontSurfaceDef.surfaceName = "font"; + + // FontSurfaceDef.graphicsCommand.GpuState.blendMode = GL_FUNC_ADD; + FontSurfaceDef.graphicsCommand.GpuState.blendSrc = GL_SRC_ALPHA; + FontSurfaceDef.graphicsCommand.GpuState.blendDst = GL_ONE_MINUS_SRC_ALPHA; + +#if 0 + FontSurfaceDef.graphicsCommand.GpuState.blendSrcAlpha = GL_ONE; + FontSurfaceDef.graphicsCommand.GpuState.blendDstAlpha = GL_ONE_MINUS_SRC_ALPHA; + FontSurfaceDef.graphicsCommand.GpuState.blendEnable = ovrGpuState::BLEND_ENABLE_SEPARATE; +#else + FontSurfaceDef.graphicsCommand.GpuState.blendEnable = ovrGpuState::BLEND_ENABLE; +#endif + + FontSurfaceDef.graphicsCommand.GpuState.frontFace = GL_CCW; + + FontSurfaceDef.graphicsCommand.GpuState.depthEnable = true; + FontSurfaceDef.graphicsCommand.GpuState.depthMaskEnable = false; + FontSurfaceDef.graphicsCommand.GpuState.polygonOffsetEnable = false; + FontSurfaceDef.graphicsCommand.GpuState.cullEnable = true; + + Initialized = true; + + ALOG("BitmapFontSurfaceLocal::Init: success"); +} + +//============================== +// BitmapFontSurfaceLocal::DrawText3D +Vector3f BitmapFontSurfaceLocal::DrawText3D( + BitmapFont const& font, + fontParms_t const& parms, + Vector3f const& pos, + Vector3f const& normal, + Vector3f const& up, + float scale, + Vector4f const& color, + char const* text) { +#if defined(OVR_BUILD_DEBUG) +/// ALOG( "DrawText3D -> '%s'", text == NULL ? "" : text ); +#endif + + if (text == NULL || text[0] == '\0') { + return Vector3f::ZERO; // nothing to do here, move along + } + Vector3f toNextLine; + VertexBlockType vb = + DrawTextToVertexBlock(font, parms, pos, normal, up, scale, color, text, &toNextLine); + + // add the new vertex block to the array of vertex blocks + VertexBlocks.push_back(vb); + + return toNextLine; +} + +//============================== +// BitmapFontSurfaceLocal::DrawText3Df +Vector3f BitmapFontSurfaceLocal::DrawText3Df( + BitmapFont const& font, + fontParms_t const& parms, + Vector3f const& pos, + Vector3f const& normal, + Vector3f const& up, + float const scale, + Vector4f const& color, + char const* fmt, + ...) { + char buffer[256]; + va_list args; + va_start(args, fmt); + OVR::OVR_vsprintf(buffer, sizeof(buffer), fmt, args); + va_end(args); + return DrawText3D(font, parms, pos, normal, up, scale, color, buffer); +} + +//============================== +// BitmapFontSurfaceLocal::DrawTextBillboarded3D +Vector3f BitmapFontSurfaceLocal::DrawTextBillboarded3D( + BitmapFont const& font, + fontParms_t const& parms, + Vector3f const& pos, + float const scale, + Vector4f const& color, + char const* text) { + fontParms_t billboardParms = parms; + billboardParms.Billboard = true; + return DrawText3D( + font, + billboardParms, + pos, + Vector3f(1.0f, 0.0f, 0.0f), + Vector3f(0.0f, -1.0f, 0.0f), + scale, + color, + text); +} + +//============================== +// BitmapFontSurfaceLocal::DrawTextBillboarded3Df +Vector3f BitmapFontSurfaceLocal::DrawTextBillboarded3Df( + BitmapFont const& font, + fontParms_t const& parms, + Vector3f const& pos, + float const scale, + Vector4f const& color, + char const* fmt, + ...) { + char buffer[256]; + va_list args; + va_start(args, fmt); + OVR::OVR_vsprintf(buffer, sizeof(buffer), fmt, args); + va_end(args); + return DrawTextBillboarded3D(font, parms, pos, scale, color, buffer); +} + +//============================================================== +// vbSort_t +// small structure that is used to sort vertex blocks by their distance to the camera +//============================================================== +struct vbSort_t { + int VertexBlockIndex; + float DistanceSquared; +}; + +//============================== +// VertexBlockSortFn +// sort function for vertex blocks +int VertexBlockSortFn(void const* a, void const* b) { + return ftoi(((vbSort_t const*)a)->DistanceSquared - ((vbSort_t const*)b)->DistanceSquared); +} + +//============================== +// BitmapFontSurfaceLocal::Finish +// transform all vertex blocks into the vertices array so they're ready to be uploaded to the VBO +// We don't have to do this for each eye because the billboarded surfaces are sorted / aligned +// based on the center view matrix's view direction. +void BitmapFontSurfaceLocal::Finish(Matrix4f const& viewMatrix) { + // SPAM( "BitmapFontSurfaceLocal::Finish" ); + + FontSurfaceDef.geo.localBounds.Clear(); + + Matrix4f invViewMatrix = viewMatrix.Inverted(); // if the view is never scaled or sheared we + // could use Transposed() here instead + Vector3f viewPos = invViewMatrix.GetTranslation(); + Vector3f viewUp = GetViewMatrixUp(viewMatrix); + + // sort vertex blocks indices based on distance to pivot + int const MAX_VERTEX_BLOCKS = 256; + vbSort_t vbSort[MAX_VERTEX_BLOCKS]; + int const n = VertexBlocks.size(); + for (int i = 0; i < n; ++i) { + vbSort[i].VertexBlockIndex = i; + VertexBlockType& vb = VertexBlocks[i]; + vbSort[i].DistanceSquared = (vb.Pivot - viewPos).LengthSq(); + } + + qsort(vbSort, n, sizeof(vbSort[0]), VertexBlockSortFn); + + // transform the vertex blocks into the vertices array + CurIndex = 0; + CurVertex = 0; + + // TODO: + // To add multiple-font-per-surface support, we need to add a 3rd component to s and t, + // then get the font for each vertex block, and set the texture index on each vertex in + // the third texture coordinate. + for (int i = 0; i < static_cast(VertexBlocks.size()); ++i) { + VertexBlockType& vb = VertexBlocks[vbSort[i].VertexBlockIndex]; + Matrix4f transform; + if (vb.Billboard) { + if (vb.TrackRoll) { + transform = invViewMatrix; + } else { + Vector3f textNormal = viewPos - vb.Pivot; + float const len = textNormal.Length(); + if (len < MATH_FLOAT_SMALLEST_NON_DENORMAL) { + vb.Free(); + continue; + } + textNormal *= 1.0f / len; + transform = Matrix4f::CreateFromBasisVectors(textNormal, viewUp * -1.0f); + } + transform.SetTranslation(vb.Pivot); + } else { + transform.SetIdentity(); + transform.SetTranslation(vb.Pivot); + } + + for (int j = 0; j < vb.NumVerts; j++) { + fontVertex_t const& v = vb.Verts[j]; + Vector3f const position = transform.Transform(v.xyz); + + // If you hit this assert you're likely trying to draw + // too much text for the current buffer size, + // most likely need to update the value + // passed to fontSurface->Init() in OvrGuiSysLocal::Init + // + // Note: Vertices is *not* a dynamic std::vector + // because it gets copies into GPU memory, which is reserved + // separately. If you decide to make this system dynamic + // remember to scale re-allocate the OpenGL VBOs as well + if (CurVertex >= MaxVertices) { + OVR_FAIL( + "Application tried to draw more text than there is buffer for. " + "This failure protects agains a buffer overflow"); + } + + Vertices[CurVertex].xyz = position; + Vertices[CurVertex].s = v.s; + Vertices[CurVertex].t = v.t; + *(std::uint32_t*)(&Vertices[CurVertex].rgba[0]) = *(std::uint32_t*)(&v.rgba[0]); + *(std::uint32_t*)(&Vertices[CurVertex].fontParms[0]) = + *(std::uint32_t*)(&v.fontParms[0]); + CurVertex++; + + FontSurfaceDef.geo.localBounds.AddPoint(position); + } + CurIndex += (vb.NumVerts / 2) * 3; + // free this vertex block + vb.Free(); + } + // remove all elements from the vertex block (but don't free the memory since it's likely to be + // needed on the next frame. + VertexBlocks.clear(); + + glBindVertexArray(FontSurfaceDef.geo.vertexArrayObject); + glBindBuffer(GL_ARRAY_BUFFER, FontSurfaceDef.geo.vertexBuffer); + glBufferSubData(GL_ARRAY_BUFFER, 0, CurVertex * sizeof(fontVertex_t), (void*)Vertices); + glBindVertexArray(0); + FontSurfaceDef.geo.indexCount = CurIndex; +} + +//============================== +// BitmapFontSurfaceLocal::AppendSurfaceList +void BitmapFontSurfaceLocal::AppendSurfaceList( + BitmapFont const& font, + std::vector& surfaceList) const { + if (FontSurfaceDef.geo.indexCount == 0) { + return; + } + + ovrDrawSurface drawSurf; + + FontSurfaceDef.graphicsCommand.Program = AsLocal(font).GetFontProgram(); + FontSurfaceDef.graphicsCommand.UniformData[0].Data = (void*)&AsLocal(font).GetFontTexture(); + + drawSurf.surface = &FontSurfaceDef; + + surfaceList.push_back(drawSurf); +} + +void BitmapFontSurfaceLocal::SetCullEnabled(const bool enabled) { + FontSurfaceDef.graphicsCommand.GpuState.cullEnable = enabled; +} + +//============================== +// BitmapFont::Create +BitmapFont* BitmapFont::Create() { + return new BitmapFontLocal; +} +//============================== +// BitmapFont::Free +void BitmapFont::Free(BitmapFont*& font) { + if (font != NULL) { + delete font; + font = NULL; + } +} + +//============================== +// BitmapFontSurface::Create +BitmapFontSurface* BitmapFontSurface::Create() { + return new BitmapFontSurfaceLocal(); +} + +//============================== +// BitmapFontSurface::Free +void BitmapFontSurface::Free(BitmapFontSurface*& fontSurface) { + if (fontSurface != NULL) { + delete fontSurface; + fontSurface = NULL; + } +} + +} // namespace OVRFW diff --git a/Samples/SampleXrFramework/Src/Render/BitmapFont.h b/Samples/SampleXrFramework/Src/Render/BitmapFont.h new file mode 100755 index 0000000..c6b97c1 --- /dev/null +++ b/Samples/SampleXrFramework/Src/Render/BitmapFont.h @@ -0,0 +1,201 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/************************************************************************************ + +Filename : BitmapFont.h +Content : Bitmap font rendering. +Created : March 11, 2014 +Authors : Jonathan E. Wright + +*************************************************************************************/ + +#pragma once + +#include +#include + +#include "OVR_Math.h" +#include "SurfaceRender.h" // SurfaceDef + +namespace OVRFW { + +class ovrFileSys; +class BitmapFont; +class BitmapFontSurface; + +enum HorizontalJustification { HORIZONTAL_LEFT, HORIZONTAL_CENTER, HORIZONTAL_RIGHT }; + +enum VerticalJustification { + VERTICAL_BASELINE, // align text by baseline of first row + VERTICAL_CENTER, + VERTICAL_CENTER_FIXEDHEIGHT, // ignores ascenders/descenders + VERTICAL_TOP +}; + +// To get a black outline on fonts, AlphaCenter should be < ( ColorCenter = 0.5 ) +// To get non-outlined fonts, ColorCenter should be < ( AlphaCenter = 0.5 ) +struct fontParms_t { + fontParms_t() + : AlignHoriz(HORIZONTAL_LEFT), + AlignVert(VERTICAL_BASELINE), + Billboard(false), + TrackRoll(false), + AlphaCenter(0.425f), + ColorCenter(0.50f) {} + + HorizontalJustification + AlignHoriz; // horizontal justification around the specified x coordinate + VerticalJustification AlignVert; // vertical justification around the specified y coordinate + bool Billboard; // true to always face the camera + bool TrackRoll; // when billboarding, track with camera roll + float AlphaCenter; // below this distance, alpha is 0, above this alpha is 1 + float ColorCenter; // below this distance, color is 0, above this color is 1 +}; + +//============================================================== +// BitmapFont +class BitmapFont { + public: + static BitmapFont* Create(); + static void Free(BitmapFont*& font); + + virtual bool Load(ovrFileSys& fileSys, const char* uri) = 0; + + // Calculates the native (unscaled) width of the text string. Line endings are ignored. + virtual float CalcTextWidth(char const* text) const = 0; + // Calculates the native (unscaled) width of the text string. Each '\n' will start a new line + // and will increase the height by FontInfo.FontHeight. For multi-line strings, lineWidths will + // contain the width of each individual line of text and width will be the width of the widest + // line of text. + virtual void CalcTextMetrics( + char const* text, + size_t& len, + float& width, + float& height, + float& ascent, + float& descent, + float& fontHeight, + float* lineWidths, + int const maxLines, + int& numLines) const = 0; + + virtual void TruncateText(std::string& inOutText, int const maxLines) const = 0; + + // Word wraps passed in text based on the passed in width in meters. + // Turns any pre-existing escape characters into spaces. + virtual bool WordWrapText( + std::string& inOutText, + const float widthMeters, + const float fontScale = 1.0f) const = 0; + // Another version of WordWrapText which doesn't break in between strings that are listed in + // wholeStrsList array Ex : "Gear VR", we don't want to break in between "Gear" & "VR" so we + // need to pass "Gear VR" string in wholeStrsList + virtual bool WordWrapText( + std::string& inOutText, + const float widthMeters, + std::vector wholeStrsList, + const float fontScale = 1.0f) const = 0; + + // Get the last part of the string that will fit in the provided width. Returns an offset if the + // entire string doesn't fit. The offset can be used to help with right justification. It is the + // width of the part of the last character that would have fit. + virtual float GetLastFitChars( + std::string& inOutText, + const float widthMeters, + const float fontScale = 1.0f) const = 0; + virtual float GetFirstFitChars( + std::string& inOutText, + const float widthMeters, + const int numLines, + const float fontScale = 1.0f) const = 0; + + // Returns a drawable surface with a newly allocated GlGeometry for the text, + // allowing it to be sorted or transformed with more control than the global + // BitmapFontSurface. + // + // Geometry is laid out on the Z = 0.0f plane, around the origin based on + // the justification options, with faces oriented to be visible when viewing + // down -Z. + // + // The SurfaceDef GlGeometry must be freed, but the GlProgram is shared by all + // users of the font. + virtual ovrSurfaceDef TextSurface( + const char* text, + float scale, + const OVR::Vector4f& color, + HorizontalJustification hjust, + VerticalJustification vjust, + fontParms_t const* fp = nullptr) const = 0; + + virtual OVR::Vector2f GetScaleFactor() const = 0; + virtual void GetGlyphMetrics( + const uint32_t charCode, + float& width, + float& height, + float& advancex, + float& advancey) const = 0; + + protected: + virtual ~BitmapFont() {} +}; + +//============================================================== +// BitmapFontSurface +class BitmapFontSurface { + public: + static BitmapFontSurface* Create(); + static void Free(BitmapFontSurface*& fontSurface); + + virtual void Init(const int maxVertices) = 0; + // Draw functions returns the amount to modify position by for the next draw call if you want to + // render more lines and have them spaced normally. Will be along the vector up. + virtual OVR::Vector3f DrawText3D( + BitmapFont const& font, + const fontParms_t& flags, + const OVR::Vector3f& pos, + OVR::Vector3f const& normal, + OVR::Vector3f const& up, + float const scale, + OVR::Vector4f const& color, + char const* text) = 0; + virtual OVR::Vector3f DrawText3Df( + BitmapFont const& font, + const fontParms_t& flags, + const OVR::Vector3f& pos, + OVR::Vector3f const& normal, + OVR::Vector3f const& up, + float const scale, + OVR::Vector4f const& color, + char const* text, + ...) = 0; + + virtual OVR::Vector3f DrawTextBillboarded3D( + BitmapFont const& font, + fontParms_t const& flags, + OVR::Vector3f const& pos, + float const scale, + OVR::Vector4f const& color, + char const* text) = 0; + virtual OVR::Vector3f DrawTextBillboarded3Df( + BitmapFont const& font, + fontParms_t const& flags, + OVR::Vector3f const& pos, + float const scale, + OVR::Vector4f const& color, + char const* fmt, + ...) = 0; + + virtual void Finish(OVR::Matrix4f const& viewMatrix) = 0; + + virtual void AppendSurfaceList(BitmapFont const& font, std::vector& surfaceList) + const = 0; + + virtual bool IsInitialized() const = 0; + + virtual void SetCullEnabled(const bool enabled) = 0; + + protected: + virtual ~BitmapFontSurface() {} +}; + +} // namespace OVRFW diff --git a/Samples/SampleXrFramework/Src/Render/DebugLines.cpp b/Samples/SampleXrFramework/Src/Render/DebugLines.cpp new file mode 100755 index 0000000..56dfbe2 --- /dev/null +++ b/Samples/SampleXrFramework/Src/Render/DebugLines.cpp @@ -0,0 +1,418 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/************************************************************************************ + +Filename : DebugLines.cpp +Content : Class that manages and renders debug lines. +Created : April 22, 2014 +Authors : Jonathan E. Wright + +*************************************************************************************/ + +#include "DebugLines.h" + +#include + +#include "Egl.h" +#include "Misc/Log.h" + +#include "GlGeometry.h" +#include "GlProgram.h" + +using OVR::Bounds3f; +using OVR::Matrix4f; +using OVR::Posef; +using OVR::Quatf; +using OVR::Vector2f; +using OVR::Vector3f; +using OVR::Vector4f; + +namespace OVRFW { + +static const char* DebugLineVertexSrc = R"glsl( + attribute highp vec4 Position; + attribute lowp vec4 VertexColor; + varying lowp vec4 outColor; + void main() + { + gl_Position = TransformVertex( Position ); + outColor = VertexColor; + } +)glsl"; + +static const char* DebugLineFragmentSrc = R"glsl( + varying lowp vec4 outColor; + void main() + { + gl_FragColor = outColor; + } +)glsl"; + +//============================================================== +// OvrDebugLinesLocal +// +class OvrDebugLinesLocal : public OvrDebugLines { + public: + struct DebugLines_t { + DebugLines_t() : DrawSurf(&Surf) {} + + ovrSurfaceDef Surf; + ovrDrawSurface DrawSurf; + VertexAttribs Attr; + std::vector EndFrame; + }; + + typedef unsigned short LineIndex_t; + + static const int MAX_DEBUG_LINES = 2048; + + OvrDebugLinesLocal(); + virtual ~OvrDebugLinesLocal(); + + virtual void Init(float lineWidth); + virtual void Init(); + virtual void Shutdown(); + + virtual void BeginFrame(const long long frameNum); + virtual void AppendSurfaceList(std::vector& surfaceList); + + virtual void AddLine( + const Vector3f& start, + const Vector3f& end, + const Vector4f& startColor, + const Vector4f& endColor, + const long long endFrame, + const bool depthTest); + virtual void AddPoint( + const Vector3f& pos, + const float size, + const Vector4f& color, + const long long endFrame, + const bool depthTest); + // Add a debug point without a specified color. The axis lines will use default + // colors: X = red, Y = green, Z = blue (same as Maya). + virtual void + AddPoint(const Vector3f& pos, const float size, const long long endFrame, const bool depthTest); + + virtual void AddBounds(Posef const& pose, Bounds3f const& bounds, Vector4f const& color); + + virtual void AddAxes( + const Vector3f& origin, + const Matrix4f& axes, + const float size, + const long long endFrame, + const bool depthTest); + + virtual void + AddAxes(const Posef& pose, const float size, const long long endFrame, const bool depthTest); + + private: + DebugLines_t DepthTested; + DebugLines_t NonDepthTested; + + bool Initialized; + GlProgram LineProgram; + + void RemoveExpired(const long long frameNum, DebugLines_t& lines); +}; + +//============================== +// OvrDebugLinesLocal::OvrDebugLinesLocal +OvrDebugLinesLocal::OvrDebugLinesLocal() : Initialized(false) {} + +//============================== +// OvrDebugLinesLocal::OvrDebugLinesLocal +OvrDebugLinesLocal::~OvrDebugLinesLocal() { + Shutdown(); +} + +void OvrDebugLinesLocal::Init() { + Init(2.0f); +} + +//============================== +// OvrDebugLinesLocal::Init +void OvrDebugLinesLocal::Init(float lineWidth) { + if (Initialized) { + // JDC: multi-activity test ASSERT_WITH_TAG( !Initialized, "DebugLines" ); + return; + } + + // this is only freed by the OS when the program exits + if (LineProgram.VertexShader == 0 || LineProgram.FragmentShader == 0) { + LineProgram = GlProgram::Build(DebugLineVertexSrc, DebugLineFragmentSrc, NULL, 0); + } + + // the indices will never change once we've set them up, we just won't necessarily + // use all of the index buffer to render. + const int MAX_INDICES = MAX_DEBUG_LINES * 2; + std::vector indices; + indices.reserve(MAX_INDICES); + + for (LineIndex_t i = 0; i < MAX_INDICES; ++i) { + indices.push_back(i); + } + + for (int i = 0; i < 2; i++) { + DebugLines_t& dl = i == 0 ? NonDepthTested : DepthTested; + dl.Surf.geo.Create(dl.Attr, indices); + dl.Surf.geo.primitiveType = GL_LINES; + ovrGraphicsCommand& gc = dl.Surf.graphicsCommand; + gc.GpuState.blendDst = GL_ONE_MINUS_SRC_ALPHA; + gc.GpuState.depthEnable = gc.GpuState.depthMaskEnable = i == 1; + gc.GpuState.lineWidth = lineWidth; + gc.Program = LineProgram; + } + + Initialized = true; +} + +//============================== +// OvrDebugLinesLocal::Shutdown +void OvrDebugLinesLocal::Shutdown() { + if (!Initialized) { + // OVR_ASSERT_WITH_TAG( !Initialized, "DebugLines" ); + return; + } + DepthTested.Surf.geo.Free(); + NonDepthTested.Surf.geo.Free(); + GlProgram::Free(LineProgram); + Initialized = false; +} + +//============================== +// OvrDebugLinesLocal::AppendSurfaceList +void OvrDebugLinesLocal::AppendSurfaceList(std::vector& surfaceList) { + for (int j = 0; j < 2; j++) { + DebugLines_t& dl = j == 0 ? NonDepthTested : DepthTested; + int verts = dl.Attr.position.size(); + if (verts == 0) { + continue; + } + dl.Surf.geo.Update(dl.Attr); + dl.Surf.geo.indexCount = verts; + surfaceList.push_back(dl.DrawSurf); + } +} + +//============================== +// OvrDebugLinesLocal::AddLine +void OvrDebugLinesLocal::AddLine( + const Vector3f& start, + const Vector3f& end, + const Vector4f& startColor, + const Vector4f& endColor, + const long long endFrame, + const bool depthTest) { + // ALOG( "OvrDebugLinesLocal::AddDebugLine" ); + DebugLines_t& dl = depthTest ? DepthTested : NonDepthTested; + dl.Attr.position.push_back(start); + dl.Attr.position.push_back(end); + dl.Attr.color.push_back(startColor); + dl.Attr.color.push_back(endColor); + dl.EndFrame.push_back(endFrame); + // OVR_ASSERT( DepthTested.EndFrame.GetSizeI() < MAX_DEBUG_LINES ); + // OVR_ASSERT( NonDepthTested.EndFrame.GetSizeI() < MAX_DEBUG_LINES ); +} + +//============================== +// OvrDebugLinesLocal::AddPoint +void OvrDebugLinesLocal::AddPoint( + const Vector3f& pos, + const float size, + const Vector4f& color, + const long long endFrame, + const bool depthTest) { + float const hs = size * 0.5f; + Vector3f const fwd(0.0f, 0.0f, hs); + Vector3f const right(hs, 0.0f, 0.0f); + Vector3f const up(0.0f, hs, 0.0f); + + AddLine(pos - fwd, pos + fwd, color, color, endFrame, depthTest); + AddLine(pos - right, pos + right, color, color, endFrame, depthTest); + AddLine(pos - up, pos + up, color, color, endFrame, depthTest); +} + +//============================== +// OvrDebugLinesLocal::AddPoint +void OvrDebugLinesLocal::AddPoint( + const Vector3f& pos, + const float size, + const long long endFrame, + const bool depthTest) { + float const hs = size * 0.5f; + Vector3f const fwd(0.0f, 0.0f, hs); + Vector3f const right(hs, 0.0f, 0.0f); + Vector3f const up(0.0f, hs, 0.0f); + + AddLine( + pos - fwd, + pos + fwd, + Vector4f(0.0f, 0.0f, 1.0f, 1.0f), + Vector4f(0.0f, 0.0f, 1.0f, 1.0f), + endFrame, + depthTest); + AddLine( + pos - right, + pos + right, + Vector4f(1.0f, 0.0f, 0.0f, 1.0f), + Vector4f(1.0f, 0.0f, 0.0f, 1.0f), + endFrame, + depthTest); + AddLine( + pos - up, + pos + up, + Vector4f(0.0f, 1.0f, 0.0f, 1.0f), + Vector4f(0.0f, 1.0f, 0.0f, 1.0f), + endFrame, + depthTest); +} + +//============================== +// OvrDebugLinesLocal::AddAxes +void OvrDebugLinesLocal::AddAxes( + const Vector3f& origin, + const Matrix4f& axes, + const float size, + const long long endFrame, + const bool depthTest) { + const float half_size = size * 0.5f; + Vector3f const fwd = Vector3f(axes.M[2][0], axes.M[2][1], axes.M[2][2]) * half_size; + Vector3f const right = Vector3f(axes.M[0][0], axes.M[0][1], axes.M[0][2]) * half_size; + Vector3f const up = Vector3f(axes.M[1][0], axes.M[1][1], axes.M[1][2]) * half_size; + + AddLine( + origin - fwd, + origin + fwd, + Vector4f(0.0f, 0.0f, 1.0f, 1.0f), + Vector4f(0.0f, 0.0f, 1.0f, 1.0f), + endFrame, + depthTest); + AddLine( + origin - right, + origin + right, + Vector4f(1.0f, 0.0f, 0.0f, 1.0f), + Vector4f(1.0f, 0.0f, 0.0f, 1.0f), + endFrame, + depthTest); + AddLine( + origin - up, + origin + up, + Vector4f(0.0f, 1.0f, 0.0f, 1.0f), + Vector4f(0.0f, 1.0f, 0.0f, 1.0f), + endFrame, + depthTest); +} + +void OvrDebugLinesLocal::AddAxes( + const Posef& pose, + const float size, + const long long endFrame, + const bool depthTest) { + const auto origin = pose.Translation; + const OVR::Quatf rot(pose.Rotation.x, pose.Rotation.y, pose.Rotation.z, pose.Rotation.w); + + const auto axes = OVR::Matrix4f(rot); + + AddLine( + origin, + origin + axes.GetXBasis() * size, + Vector4f(0.0f, 0.0f, 1.0f, 1.0f), + Vector4f(0.0f, 0.0f, 1.0f, 1.0f), + endFrame, + depthTest); + AddLine( + origin, + origin + axes.GetYBasis() * size, + Vector4f(1.0f, 0.0f, 0.0f, 1.0f), + Vector4f(1.0f, 0.0f, 0.0f, 1.0f), + endFrame, + depthTest); + AddLine( + origin, + origin + axes.GetZBasis() * size, + Vector4f(0.0f, 1.0f, 0.0f, 1.0f), + Vector4f(0.0f, 1.0f, 0.0f, 1.0f), + endFrame, + depthTest); +} + +//============================== +// OvrDebugLinesLocal::AddBounds +void OvrDebugLinesLocal::AddBounds( + Posef const& pose, + Bounds3f const& bounds, + Vector4f const& color) { + Vector3f const& mins = bounds.GetMins(); + Vector3f const& maxs = bounds.GetMaxs(); + Vector3f corners[8]; + corners[0] = mins; + corners[7] = maxs; + corners[1] = Vector3f(mins.x, maxs.y, mins.z); + corners[2] = Vector3f(mins.x, maxs.y, maxs.z); + corners[3] = Vector3f(mins.x, mins.y, maxs.z); + corners[4] = Vector3f(maxs.x, mins.y, mins.z); + corners[5] = Vector3f(maxs.x, maxs.y, mins.z); + corners[6] = Vector3f(maxs.x, mins.y, maxs.z); + + // transform points + for (int i = 0; i < 8; ++i) { + corners[i] = pose.Rotation.Rotate(corners[i]); + corners[i] += pose.Translation; + } + + AddLine(corners[0], corners[1], color, color, 1, true); + AddLine(corners[1], corners[2], color, color, 1, true); + AddLine(corners[2], corners[3], color, color, 1, true); + AddLine(corners[3], corners[0], color, color, 1, true); + AddLine(corners[7], corners[6], color, color, 1, true); + AddLine(corners[6], corners[4], color, color, 1, true); + AddLine(corners[4], corners[5], color, color, 1, true); + AddLine(corners[5], corners[7], color, color, 1, true); + AddLine(corners[0], corners[4], color, color, 1, true); + AddLine(corners[1], corners[5], color, color, 1, true); + AddLine(corners[2], corners[7], color, color, 1, true); + AddLine(corners[3], corners[6], color, color, 1, true); +} + +//============================== +// OvrDebugLinesLocal::BeginFrame +void OvrDebugLinesLocal::BeginFrame(const long long frameNum) { + // LOG( "OvrDebugLinesLocal::RemoveExpired: frame %lli, removing %i lines", frameNum, + // DepthTestedLines.GetSizeI() + NonDepthTestedLines.GetSizeI() ); + DepthTested.Surf.geo.indexCount = 0; + NonDepthTested.Surf.geo.indexCount = 0; + RemoveExpired(frameNum, DepthTested); + RemoveExpired(frameNum, NonDepthTested); +} + +//============================== +// OvrDebugLinesLocal::RemoveExpired +void OvrDebugLinesLocal::RemoveExpired(const long long frameNum, DebugLines_t& lines) { + for (int i = lines.EndFrame.size() - 1; i >= 0; --i) { + if (frameNum >= lines.EndFrame[i]) { + lines.Attr.position.erase( + lines.Attr.position.cbegin() + (i * 2), lines.Attr.position.cbegin() + (i * 2) + 2); + lines.Attr.color.erase( + lines.Attr.color.cbegin() + (i * 2), lines.Attr.color.cbegin() + (i * 2) + 2); + lines.EndFrame.erase(lines.EndFrame.cbegin() + i); + } + } +} + +//============================== +// OvrDebugLines::Create +OvrDebugLines* OvrDebugLines::Create() { + return new OvrDebugLinesLocal; +} + +//============================== +// OvrDebugLines::Free +void OvrDebugLines::Free(OvrDebugLines*& debugLines) { + if (debugLines != NULL) { + delete debugLines; + debugLines = NULL; + } +} + +} // namespace OVRFW diff --git a/Samples/SampleXrFramework/Src/Render/DebugLines.h b/Samples/SampleXrFramework/Src/Render/DebugLines.h new file mode 100755 index 0000000..a6ff365 --- /dev/null +++ b/Samples/SampleXrFramework/Src/Render/DebugLines.h @@ -0,0 +1,77 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/************************************************************************************ + +Filename : DebugLines.h +Content : Class that manages and renders debug lines. +Created : April 22, 2014 +Authors : Jonathan E. Wright + +*************************************************************************************/ + +#pragma once + +#include +#include "OVR_Math.h" +#include "SurfaceRender.h" + +namespace OVRFW { + +//============================================================== +// OvrDebugLines +class OvrDebugLines { + public: + virtual ~OvrDebugLines() {} + + static OvrDebugLines* Create(); + static void Free(OvrDebugLines*& debugLines); + + virtual void Init(float lineWidth) = 0; + virtual void Init() = 0; + virtual void Shutdown() = 0; + + virtual void BeginFrame(const long long frameNum) = 0; + virtual void AppendSurfaceList(std::vector& surfaceList) = 0; + + virtual void AddLine( + const OVR::Vector3f& start, + const OVR::Vector3f& end, + const OVR::Vector4f& startColor, + const OVR::Vector4f& endColor, + const long long endFrame, + const bool depthTest) = 0; + + virtual void AddPoint( + const OVR::Vector3f& pos, + const float size, + const OVR::Vector4f& color, + const long long endFrame, + const bool depthTest) = 0; + + // Add a debug point without a specified color. The axis lines will use default + // colors: X = red, Y = green, Z = blue (same as Maya). + virtual void AddPoint( + const OVR::Vector3f& pos, + const float size, + const long long endFrame, + const bool depthTest) = 0; + + virtual void AddAxes( + const OVR::Vector3f& origin, + const OVR::Matrix4f& axes, + const float size, + const long long endFrame, + const bool depthTest) = 0; + + // Add axes at pose location, aligned the x, y, z of the axes to the pose orientation + virtual void AddAxes( + const OVR::Posef& pose, + const float size, + const long long endFrame, + const bool depthTest) = 0; + + virtual void + AddBounds(OVR::Posef const& pose, OVR::Bounds3f const& bounds, OVR::Vector4f const& color) = 0; +}; + +} // namespace OVRFW diff --git a/Samples/SampleXrFramework/Src/Render/EaseFunctions.cpp b/Samples/SampleXrFramework/Src/Render/EaseFunctions.cpp new file mode 100755 index 0000000..1269185 --- /dev/null +++ b/Samples/SampleXrFramework/Src/Render/EaseFunctions.cpp @@ -0,0 +1,57 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/************************************************************************************ + +Filename : EaseFunctions.h +Content : Functions for blending alpha over time. +Created : October 12, 2015 +Authors : Jonathan E. Wright + +************************************************************************************/ + +#include "EaseFunctions.h" +using OVR::Vector4f; + +namespace OVRFW { + +static Vector4f EaseFunc_None(const Vector4f& c, const float /*t*/) { + return c; +} + +static Vector4f EaseFunc_InOut_Linear(const Vector4f& c, const float t) { + const float s = EaseInOut_Linear(t); + return Vector4f(c.x * s, c.y * s, c.z * s, c.w * s); +} + +static Vector4f EaseFunc_InOut_Cubic(const Vector4f& c, const float t) { + const float s = EaseInOut_Cubic(t); + return Vector4f(c.x * s, c.y * s, c.z * s, c.w * s); +} + +static Vector4f EaseFunc_InOut_Quadratic(const Vector4f& c, const float t) { + const float s = EaseInOut_Quadratic(t); + return Vector4f(c.x * s, c.y * s, c.z * s, c.w * s); +} + +static Vector4f EaseFunc_Alpha_InOut_Linear(const Vector4f& c, const float t) { + return Vector4f(c.x, c.y, c.z, c.w * EaseInOut_Linear(t)); +} + +static Vector4f EaseFunc_Alpha_InOut_Cubic(const Vector4f& c, const float t) { + return Vector4f(c.x, c.y, c.z, c.w * EaseInOut_Cubic(t)); +} + +static Vector4f EaseFunc_Alpha_InOut_Quadratic(const Vector4f& c, const float t) { + return Vector4f(c.x, c.y, c.z, c.w * EaseInOut_Quadratic(t)); +} + +EaseFunction_t EaseFunctions[ovrEaseFunc::MAX] = { + EaseFunc_None, + EaseFunc_InOut_Linear, + EaseFunc_InOut_Cubic, + EaseFunc_InOut_Quadratic, + EaseFunc_Alpha_InOut_Linear, + EaseFunc_Alpha_InOut_Cubic, + EaseFunc_Alpha_InOut_Quadratic}; + +} // namespace OVRFW diff --git a/Samples/SampleXrFramework/Src/Render/EaseFunctions.h b/Samples/SampleXrFramework/Src/Render/EaseFunctions.h new file mode 100755 index 0000000..28effac --- /dev/null +++ b/Samples/SampleXrFramework/Src/Render/EaseFunctions.h @@ -0,0 +1,115 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/************************************************************************************ + +Filename : EaseFunctions.h +Content : Functions for blending alpha over time. +Created : October 12, 2015 +Authors : Jonathan E. Wright + +************************************************************************************/ + +#pragma once + +#include +#include +#include "OVR_Math.h" + +namespace OVRFW { + +enum ovrEaseFunc : uint8_t { + NONE, // just use initial color and alpha values + IN_OUT_LINEAR, // ease color and alpha in and out over life time linear + IN_OUT_CUBIC, // ease color and alpha in and out over life time cubic + IN_OUT_QUADRIC, // ease color and alpha in and out over life time quadratic + ALPHA_IN_OUT_LINEAR, // ease alpha channel in and out over life time linear + ALPHA_IN_OUT_CUBIC, // ease alpha channel in and out over life time cubic + ALPHA_IN_OUT_QUADRIC, // ease alpha channel in and out over life time quadratic + MAX +}; + +template +T EaseInOut_Linear(const T t) { + const T HALF = static_cast(0.5); + const T ONE = static_cast(1.0); + const T TWO = static_cast(2.0); + + if (t <= HALF) { + return TWO * t; + } + + return ONE - (TWO * (t - HALF)); +} + +// t must be between 0 and 1 +// The return value approaches 1 as t approaches 0.5 and +// begins to approach 0 again as t goes from 0.5 to 1 +template +T EaseInOut_Quadratic(const T t) { + const T HALF = static_cast(0.5); + const T ONE = static_cast(1.0); + const T TWO = static_cast(2.0); + + if (t <= HALF) { + return TWO * t * t; + } + + return ONE - (TWO * (t - HALF) * (t - HALF)); +} + +// t must be between 0 and 1 +// The return value is always 0 to 1. +// The output changes slowly near 0.0, most rapidly at 0.5 and slowly again near 1.0 +template +T EaseInOut_Quadratic2(const T t) { + const T HALF = static_cast(0.5); + const T ONE = static_cast(1.0); + const T TWO = static_cast(2.0); + + if (t <= HALF) { + return TWO * t * t; + } + + T const t2 = 1.0f - t; + return ONE - (TWO * (t2 * t2)); +} + +template +T EaseInOut_Cubic(T t) { + const T HALF = static_cast(0.5); + const T ONE = static_cast(1.0); + const T TWO = static_cast(2.0); + + if (t <= HALF) { + return TWO * t * t * t; + } + t -= HALF; + return ONE - (TWO * t * t * t); +} + +template +T EaseIn_Quadratic(T t) { + return t * t; +} + +// y = x^3 +template +T EaseIn_Cubic(T t) { + return t * t * t; +} + +// y = 1.0 - ( ( 1.0 - x )^3 ) +template +T EaseIn_CubicInverted(T t) { + const T ZERO = static_cast(0.0); + const T ONE = static_cast(1.0); + assert(t >= ZERO && t <= ONE); + T invT = ONE - t; + return ONE - (invT * invT * invT); +} + +typedef OVR::Vector4f (*EaseFunction_t)(const OVR::Vector4f& c, const float t); + +extern EaseFunction_t EaseFunctions[ovrEaseFunc::MAX]; + +} // namespace OVRFW diff --git a/Samples/SampleXrFramework/Src/Render/Egl.c b/Samples/SampleXrFramework/Src/Render/Egl.c new file mode 100755 index 0000000..9c4703c --- /dev/null +++ b/Samples/SampleXrFramework/Src/Render/Egl.c @@ -0,0 +1,423 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/************************************************************************************ + +Filename : Egl.cpp +Content : EGL utility functions. Originally part of the VrCubeWorld_NativeActivity + sample. +Created : March, 2015 +Authors : J.M.P. van Waveren +Language : C99 + + *************************************************************************************/ + +#include "Egl.h" +#include "Misc/Log.h" + +#include + +//============================================================================== +// Implementation +//============================================================================== + +#if defined(ANDROID) +PFNEGLCREATESYNCKHRPROC eglCreateSyncKHR_; +PFNEGLDESTROYSYNCKHRPROC eglDestroySyncKHR_; +PFNEGLCLIENTWAITSYNCKHRPROC eglClientWaitSyncKHR_; +PFNEGLSIGNALSYNCKHRPROC eglSignalSyncKHR_; +PFNEGLGETSYNCATTRIBKHRPROC eglGetSyncAttribKHR_; +#endif // defined(ANDROID) + +PFNGLINVALIDATEFRAMEBUFFER_ glInvalidateFramebuffer_; + +/* + ================================================================================ + + OpenGL-ES Utility Functions + + ================================================================================ + */ + +OpenGLExtensions_t glExtensions; + +void* EglGetExtensionProc(const char* functionName) { +#if defined(ANDROID) + void* ptr = (void*)eglGetProcAddress(functionName); +#elif defined(WIN32) + void* ptr = (void*)wglGetProcAddress(functionName); +#endif // defined(ANDROID) + if (ptr == NULL) { + ALOG("NOT FOUND: %s", functionName); + } + return ptr; +} + +void EglInitExtensions() { + const char* allExtensions = (const char*)glGetString(GL_EXTENSIONS); + if (allExtensions != NULL) { + glExtensions.multi_view = strstr(allExtensions, "GL_OVR_multiview2") && + strstr(allExtensions, "GL_OVR_multiview_multisampled_render_to_texture"); + + glExtensions.EXT_texture_border_clamp = + strstr(allExtensions, "GL_EXT_texture_border_clamp") || + strstr(allExtensions, "GL_OES_texture_border_clamp"); + + glExtensions.EXT_texture_filter_anisotropic = + strstr(allExtensions, "GL_EXT_texture_filter_anisotropic"); + } + +#if defined(ANDROID) + eglCreateSyncKHR_ = (PFNEGLCREATESYNCKHRPROC)EglGetExtensionProc("eglCreateSyncKHR"); + eglDestroySyncKHR_ = (PFNEGLDESTROYSYNCKHRPROC)EglGetExtensionProc("eglDestroySyncKHR"); + eglClientWaitSyncKHR_ = + (PFNEGLCLIENTWAITSYNCKHRPROC)EglGetExtensionProc("eglClientWaitSyncKHR"); + eglSignalSyncKHR_ = (PFNEGLSIGNALSYNCKHRPROC)EglGetExtensionProc("eglSignalSyncKHR"); + eglGetSyncAttribKHR_ = (PFNEGLGETSYNCATTRIBKHRPROC)EglGetExtensionProc("eglGetSyncAttribKHR"); +#endif // defined(ANDROID) + glInvalidateFramebuffer_ = + (PFNGLINVALIDATEFRAMEBUFFER_)EglGetExtensionProc("glInvalidateFramebuffer"); +} + +#if defined(ANDROID) + +const char* EglErrorString(const EGLint error) { + switch (error) { + case EGL_SUCCESS: + return "EGL_SUCCESS"; + case EGL_NOT_INITIALIZED: + return "EGL_NOT_INITIALIZED"; + case EGL_BAD_ACCESS: + return "EGL_BAD_ACCESS"; + case EGL_BAD_ALLOC: + return "EGL_BAD_ALLOC"; + case EGL_BAD_ATTRIBUTE: + return "EGL_BAD_ATTRIBUTE"; + case EGL_BAD_CONTEXT: + return "EGL_BAD_CONTEXT"; + case EGL_BAD_CONFIG: + return "EGL_BAD_CONFIG"; + case EGL_BAD_CURRENT_SURFACE: + return "EGL_BAD_CURRENT_SURFACE"; + case EGL_BAD_DISPLAY: + return "EGL_BAD_DISPLAY"; + case EGL_BAD_SURFACE: + return "EGL_BAD_SURFACE"; + case EGL_BAD_MATCH: + return "EGL_BAD_MATCH"; + case EGL_BAD_PARAMETER: + return "EGL_BAD_PARAMETER"; + case EGL_BAD_NATIVE_PIXMAP: + return "EGL_BAD_NATIVE_PIXMAP"; + case EGL_BAD_NATIVE_WINDOW: + return "EGL_BAD_NATIVE_WINDOW"; + case EGL_CONTEXT_LOST: + return "EGL_CONTEXT_LOST"; + default: + return "unknown"; + } +} + +#else + +const char* EglErrorString(const GLint err) { + return ovrGl_ErrorString_Windows(err); +} + +#endif // defined(ANDROID) + +const char* GlFrameBufferStatusString(GLenum status) { + switch (status) { + case GL_FRAMEBUFFER_UNDEFINED: + return "GL_FRAMEBUFFER_UNDEFINED"; + case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: + return "GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT"; + case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: + return "GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT"; + case GL_FRAMEBUFFER_UNSUPPORTED: + return "GL_FRAMEBUFFER_UNSUPPORTED"; + case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE: + return "GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE"; + default: + return "unknown"; + } +} + +#ifdef CHECK_GL_ERRORS + +static const char* GlErrorString(GLenum error) { + switch (error) { + case GL_NO_ERROR: + return "GL_NO_ERROR"; + case GL_INVALID_ENUM: + return "GL_INVALID_ENUM"; + case GL_INVALID_VALUE: + return "GL_INVALID_VALUE"; + case GL_INVALID_OPERATION: + return "GL_INVALID_OPERATION"; + case GL_INVALID_FRAMEBUFFER_OPERATION: + return "GL_INVALID_FRAMEBUFFER_OPERATION"; + case GL_OUT_OF_MEMORY: + return "GL_OUT_OF_MEMORY"; + default: + return "unknown"; + } +} + +void GLCheckErrors(int line) { + for (int i = 0; i < 10; i++) { + const GLenum error = glGetError(); + if (error == GL_NO_ERROR) { + break; + } + ALOGE("GL error on line %d: %s", line, GlErrorString(error)); + } +} + +#endif // CHECK_GL_ERRORS + +bool GLCheckErrorsWithTitle(const char* logTitle) { + bool hadError = false; + + // There can be multiple errors that need reporting. + do { + GLenum err = glGetError(); + if (err == GL_NO_ERROR) { + break; + } + hadError = true; + ALOGW( + "%s GL Error: #0x%04x -> %s", + (logTitle != NULL) ? logTitle : "", + (int)err, + EglErrorString(err)); + if (err == GL_OUT_OF_MEMORY) { + ALOGE_FAIL("GL_OUT_OF_MEMORY"); + } + } while (1); + return hadError; +} + +#if defined(ANDROID) + +EGLint GL_FlushSync(int timeout) { + // if extension not present, return NO_SYNC + if (eglCreateSyncKHR_ == NULL) { + return EGL_FALSE; + } + + EGLDisplay eglDisplay = eglGetCurrentDisplay(); + + const EGLSyncKHR sync = eglCreateSyncKHR_(eglDisplay, EGL_SYNC_FENCE_KHR, NULL); + if (sync == EGL_NO_SYNC_KHR) { + return EGL_FALSE; + } + + const EGLint wait = + eglClientWaitSyncKHR_(eglDisplay, sync, EGL_SYNC_FLUSH_COMMANDS_BIT_KHR, timeout); + + eglDestroySyncKHR_(eglDisplay, sync); + + return wait; +} + +void GL_Finish() { + // Given the common driver "optimization" of ignoring glFinish, we + // can't run reliably while drawing to the front buffer without + // the Sync extension. + if (eglCreateSyncKHR_ != NULL) { + // 100 milliseconds == 100000000 nanoseconds + const EGLint wait = GL_FlushSync(100000000); + if (wait == EGL_TIMEOUT_EXPIRED_KHR) { + ALOG("EGL_TIMEOUT_EXPIRED_KHR"); + } + if (wait == EGL_FALSE) { + ALOG("eglClientWaitSyncKHR returned EGL_FALSE"); + } + } +} + +void GL_Flush() { + if (eglCreateSyncKHR_ != NULL) { + const EGLint wait = GL_FlushSync(0); + if (wait == EGL_FALSE) { + ALOG("eglClientWaitSyncKHR returned EGL_FALSE"); + } + } + + // Also do a glFlush() so it shows up in logging tools that + // don't capture eglClientWaitSyncKHR_ calls. + // glFlush(); +} + +// This requires the isFBO parameter because GL ES 3.0's glInvalidateFramebuffer() uses +// different attachment values for FBO vs default framebuffer, unlike glDiscardFramebufferEXT() +void GL_InvalidateFramebuffer( + const enum invalidateTarget_t isFBO, + const bool colorBuffer, + const bool depthBuffer) { + const int offset = (int)!colorBuffer; + const int count = (int)colorBuffer + ((int)depthBuffer) * 2; + const GLenum fboAttachments[3] = { + GL_COLOR_ATTACHMENT0, GL_DEPTH_ATTACHMENT, GL_STENCIL_ATTACHMENT}; + const GLenum attachments[3] = {GL_COLOR_EXT, GL_DEPTH_EXT, GL_STENCIL_EXT}; + glInvalidateFramebuffer_( + GL_FRAMEBUFFER, count, (isFBO == INV_FBO ? fboAttachments : attachments) + offset); +} + +/* + ================================================================================ + + ovrEgl + + ================================================================================ + */ + +void ovrEgl_Clear(ovrEgl* egl) { + egl->MajorVersion = 0; + egl->MinorVersion = 0; + egl->Display = 0; + egl->Config = 0; + egl->TinySurface = EGL_NO_SURFACE; + egl->MainSurface = EGL_NO_SURFACE; + egl->Context = EGL_NO_CONTEXT; +} + +void ovrEgl_CreateContext(ovrEgl* egl, const ovrEgl* shareEgl) { + if (egl->Display != 0) { + return; + } + + egl->Display = eglGetDisplay(EGL_DEFAULT_DISPLAY); + ALOGV(" eglInitialize( Display, &MajorVersion, &MinorVersion )"); + eglInitialize(egl->Display, &egl->MajorVersion, &egl->MinorVersion); + // Do NOT use eglChooseConfig, because the Android EGL code pushes in multisample + // flags in eglChooseConfig if the user has selected the "force 4x MSAA" option in + // settings, and that is completely wasted for our warp target. + enum { MAX_CONFIGS = 1024 }; + EGLConfig configs[MAX_CONFIGS]; + EGLint numConfigs = 0; + if (eglGetConfigs(egl->Display, configs, MAX_CONFIGS, &numConfigs) == EGL_FALSE) { + ALOGE(" eglGetConfigs() failed: %s", EglErrorString(eglGetError())); + return; + } + const EGLint configAttribs[] = { + EGL_RED_SIZE, + 8, + EGL_GREEN_SIZE, + 8, + EGL_BLUE_SIZE, + 8, + EGL_ALPHA_SIZE, + 8, // need alpha for the multi-pass timewarp compositor + EGL_DEPTH_SIZE, + 0, + EGL_STENCIL_SIZE, + 0, + EGL_SAMPLES, + 0, + EGL_NONE}; + egl->Config = 0; + for (int i = 0; i < numConfigs; i++) { + EGLint value = 0; + + eglGetConfigAttrib(egl->Display, configs[i], EGL_RENDERABLE_TYPE, &value); + if ((value & EGL_OPENGL_ES3_BIT_KHR) != EGL_OPENGL_ES3_BIT_KHR) { + continue; + } + + // The pbuffer config also needs to be compatible with normal window rendering + // so it can share textures with the window context. + eglGetConfigAttrib(egl->Display, configs[i], EGL_SURFACE_TYPE, &value); + if ((value & (EGL_WINDOW_BIT | EGL_PBUFFER_BIT)) != (EGL_WINDOW_BIT | EGL_PBUFFER_BIT)) { + continue; + } + + int j = 0; + for (; configAttribs[j] != EGL_NONE; j += 2) { + eglGetConfigAttrib(egl->Display, configs[i], configAttribs[j], &value); + if (value != configAttribs[j + 1]) { + break; + } + } + if (configAttribs[j] == EGL_NONE) { + egl->Config = configs[i]; + break; + } + } + if (egl->Config == 0) { + ALOGE(" eglChooseConfig() failed: %s", EglErrorString(eglGetError())); + return; + } + EGLint contextAttribs[] = {EGL_CONTEXT_CLIENT_VERSION, 3, EGL_NONE}; + ALOGV(" Context = eglCreateContext( Display, Config, EGL_NO_CONTEXT, contextAttribs )"); + egl->Context = eglCreateContext( + egl->Display, + egl->Config, + (shareEgl != NULL) ? shareEgl->Context : EGL_NO_CONTEXT, + contextAttribs); + if (egl->Context == EGL_NO_CONTEXT) { + ALOGE(" eglCreateContext() failed: %s", EglErrorString(eglGetError())); + return; + } + const EGLint surfaceAttribs[] = {EGL_WIDTH, 16, EGL_HEIGHT, 16, EGL_NONE}; + ALOGV(" TinySurface = eglCreatePbufferSurface( Display, Config, surfaceAttribs )"); + egl->TinySurface = eglCreatePbufferSurface(egl->Display, egl->Config, surfaceAttribs); + if (egl->TinySurface == EGL_NO_SURFACE) { + ALOGE(" eglCreatePbufferSurface() failed: %s", EglErrorString(eglGetError())); + eglDestroyContext(egl->Display, egl->Context); + egl->Context = EGL_NO_CONTEXT; + return; + } + ALOGV(" eglMakeCurrent( Display, TinySurface, TinySurface, Context )"); + if (eglMakeCurrent(egl->Display, egl->TinySurface, egl->TinySurface, egl->Context) == + EGL_FALSE) { + ALOGE(" eglMakeCurrent() failed: %s", EglErrorString(eglGetError())); + eglDestroySurface(egl->Display, egl->TinySurface); + eglDestroyContext(egl->Display, egl->Context); + egl->Context = EGL_NO_CONTEXT; + return; + } +} + +void ovrEgl_DestroyContext(ovrEgl* egl) { + if (egl->Display != 0) { + ALOGE(" eglMakeCurrent( Display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT )"); + if (eglMakeCurrent(egl->Display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT) == + EGL_FALSE) { + ALOGE(" eglMakeCurrent() failed: %s", EglErrorString(eglGetError())); + } + } + if (egl->Context != EGL_NO_CONTEXT) { + ALOGE(" eglDestroyContext( Display, Context )"); + if (eglDestroyContext(egl->Display, egl->Context) == EGL_FALSE) { + ALOGE(" eglDestroyContext() failed: %s", EglErrorString(eglGetError())); + } + egl->Context = EGL_NO_CONTEXT; + } + if (egl->TinySurface != EGL_NO_SURFACE) { + ALOGE(" eglDestroySurface( Display, TinySurface )"); + if (eglDestroySurface(egl->Display, egl->TinySurface) == EGL_FALSE) { + ALOGE(" eglDestroySurface() failed: %s", EglErrorString(eglGetError())); + } + egl->TinySurface = EGL_NO_SURFACE; + } + if (egl->Display != 0) { + ALOGE(" eglTerminate( Display )"); + if (eglTerminate(egl->Display) == EGL_FALSE) { + ALOGE(" eglTerminate() failed: %s", EglErrorString(eglGetError())); + } + egl->Display = 0; + } +} + +#else + +void ovrEgl_CreateContext(ovrEgl* egl, const ovrEgl* shareEgl) { + ovrGl_CreateContext_Windows(&egl->hDC, &egl->hGLRC); +} + +void ovrEgl_DestroyContext(ovrEgl* egl) { + ovrGl_DestroyContext_Windows(); +} + +#endif // defined(ANDROID) diff --git a/Samples/SampleXrFramework/Src/Render/Egl.h b/Samples/SampleXrFramework/Src/Render/Egl.h new file mode 100755 index 0000000..0d3fb91 --- /dev/null +++ b/Samples/SampleXrFramework/Src/Render/Egl.h @@ -0,0 +1,201 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/************************************************************************************ + +Filename : Egl.h +Content : EGL utility functions. Originally part of the VrCubeWorld_NativeActivity + sample. +Created : March, 2015 +Authors : J.M.P. van Waveren +Language : C99 + + *************************************************************************************/ + +#pragma once + +#include +#include + +#if !defined(WIN32) +#include +#include +#include +#include +#else +#include "GlWrapperWin32.h" +#endif // !defined(WIN32) + +#if defined(__cplusplus) +extern "C" { +#endif + +#if !defined(EGL_OPENGL_ES3_BIT_KHR) +#define EGL_OPENGL_ES3_BIT_KHR 0x0040 +#endif + +// EXT_texture_border_clamp +#ifndef GL_CLAMP_TO_BORDER +#define GL_CLAMP_TO_BORDER 0x812D +#endif + +#ifndef GL_TEXTURE_BORDER_COLOR +#define GL_TEXTURE_BORDER_COLOR 0x1004 +#endif + +#if !defined(GL_EXT_multisampled_render_to_texture) +typedef void(GL_APIENTRY* PFNGLRENDERBUFFERSTORAGEMULTISAMPLEEXTPROC)( + GLenum target, + GLsizei samples, + GLenum internalformat, + GLsizei width, + GLsizei height); +typedef void(GL_APIENTRY* PFNGLFRAMEBUFFERTEXTURE2DMULTISAMPLEEXTPROC)( + GLenum target, + GLenum attachment, + GLenum textarget, + GLuint texture, + GLint level, + GLsizei samples); +#endif + +#if !defined(GL_OVR_multiview) +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_NUM_VIEWS_OVR 0x9630 +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_BASE_VIEW_INDEX_OVR 0x9632 +#define GL_MAX_VIEWS_OVR 0x9631 +typedef void(GL_APIENTRY* PFNGLFRAMEBUFFERTEXTUREMULTIVIEWOVRPROC)( + GLenum target, + GLenum attachment, + GLuint texture, + GLint level, + GLint baseViewIndex, + GLsizei numViews); +#endif + +#if !defined(GL_OVR_multiview_multisampled_render_to_texture) +typedef void(GL_APIENTRY* PFNGLFRAMEBUFFERTEXTUREMULTISAMPLEMULTIVIEWOVRPROC)( + GLenum target, + GLenum attachment, + GLuint texture, + GLint level, + GLsizei samples, + GLint baseViewIndex, + GLsizei numViews); +#endif + +// EXT_sRGB_write_control +#if !defined(GL_EXT_sRGB_write_control) +#define GL_FRAMEBUFFER_SRGB_EXT 0x8DB9 +#endif + +#ifndef GL_EXT_discard_framebuffer +#define GL_EXT_discard_framebuffer 1 +#define GL_COLOR_EXT 0x1800 +#define GL_DEPTH_EXT 0x1801 +#define GL_STENCIL_EXT 0x1802 +#endif /* GL_EXT_discard_framebuffer */ + +#ifndef GL_EXT_texture_filter_anisotropic +#define GL_EXT_texture_filter_anisotropic 1 +#define GL_TEXTURE_MAX_ANISOTROPY_EXT 0x84FE +#define GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT 0x84FF +#endif /* GL_EXT_texture_filter_anisotropic */ + +#ifndef GL_OES_EGL_image_external +#define GL_OES_EGL_image_external 1 +#define GL_TEXTURE_EXTERNAL_OES 0x8D65 +#define GL_TEXTURE_BINDING_EXTERNAL_OES 0x8D67 +#define GL_REQUIRED_TEXTURE_IMAGE_UNITS_OES 0x8D68 +#define GL_SAMPLER_EXTERNAL_OES 0x8D66 +#endif /* GL_OES_EGL_image_external */ + +typedef struct ovrEgl_s { +#if !defined(WIN32) + EGLint MajorVersion; + EGLint MinorVersion; + EGLDisplay Display; + EGLConfig Config; + EGLSurface TinySurface; + EGLSurface MainSurface; + EGLContext Context; +#else + HDC hDC; + HGLRC hGLRC; +#endif // defined(WIN32) +} ovrEgl; + +typedef struct { + bool multi_view; // GL_OVR_multiview, GL_OVR_multiview2 + bool EXT_texture_border_clamp; // GL_EXT_texture_border_clamp, GL_OES_texture_border_clamp + bool EXT_texture_filter_anisotropic; // GL_EXT_texture_filter_anisotropic +} OpenGLExtensions_t; + +extern OpenGLExtensions_t glExtensions; + +//============================================================================== +// forward declarations +//============================================================================== + +void* EglGetExtensionProc(const char* functionName); +void EglInitExtensions(); +void ovrEgl_Clear(ovrEgl* egl); +void ovrEgl_CreateContext(ovrEgl* egl, const ovrEgl* shareEgl); +void ovrEgl_DestroyContext(ovrEgl* egl); + +#if defined(ANDROID) +// EGL_KHR_reusable_sync +extern PFNEGLCREATESYNCKHRPROC eglCreateSyncKHR_; +extern PFNEGLDESTROYSYNCKHRPROC eglDestroySyncKHR_; +extern PFNEGLCLIENTWAITSYNCKHRPROC eglClientWaitSyncKHR_; +extern PFNEGLSIGNALSYNCKHRPROC eglSignalSyncKHR_; +extern PFNEGLGETSYNCATTRIBKHRPROC eglGetSyncAttribKHR_; +#endif // defined(ANDROID) + +typedef void(GL_APIENTRYP PFNGLINVALIDATEFRAMEBUFFER_)( + GLenum target, + GLsizei numAttachments, + const GLenum* attachments); +extern PFNGLINVALIDATEFRAMEBUFFER_ glInvalidateFramebuffer_; + +// These use a KHR_Sync object if available, so drivers can't "optimize" the finish/flush away. +void GL_Finish(); +void GL_Flush(); + +// Use EXT_discard_framebuffer or ES 3.0's glInvalidateFramebuffer as available +// This requires the isFBO parameter because GL ES 3.0's glInvalidateFramebuffer() uses +// different attachment values for FBO vs default framebuffer, unlike glDiscardFramebufferEXT() +enum invalidateTarget_t { INV_DEFAULT, INV_FBO }; +void GL_InvalidateFramebuffer( + const enum invalidateTarget_t isFBO, + const bool colorBuffer, + const bool depthBuffer); + +const char* GlFrameBufferStatusString(GLenum status); +#if defined(ANDROID) +const char* EglErrorString(const EGLint error); +#else +const char* EglErrorString(const GLint error); +#endif // defined(ANDROID) + +#ifdef OVR_BUILD_DEBUG +#define CHECK_GL_ERRORS 1 +#endif + +#ifdef CHECK_GL_ERRORS + +void GLCheckErrors(int line); + +#define GL(func) \ + func; \ + GLCheckErrors(__LINE__); + +#else // CHECK_GL_ERRORS + +#define GL(func) func; + +#endif // CHECK_GL_ERRORS + +bool GLCheckErrorsWithTitle(const char* logTitle); + +#if defined(__cplusplus) +} // extern "C" +#endif diff --git a/Samples/SampleXrFramework/Src/Render/Framebuffer.cpp b/Samples/SampleXrFramework/Src/Render/Framebuffer.cpp new file mode 100755 index 0000000..e1b3922 --- /dev/null +++ b/Samples/SampleXrFramework/Src/Render/Framebuffer.cpp @@ -0,0 +1,257 @@ +/************************************************************************************ + +Filename : Framebuffer.cpp +Content : Frame buffer utilities. Originally part of the VrCubeWorld_NativeActivity + sample. +Created : March, 2015 +Authors : J.M.P. van Waveren +Language : C99 + +Copyright : Copyright 2015 Oculus VR, LLC. All Rights reserved. + + *************************************************************************************/ + +#include "Framebuffer.h" +#include "Misc/Log.h" +#include + +#define OXR(func) \ + if (XR_FAILED(func)) { \ + ALOGV("OpenXR error on fuction: %s: \n", #func); \ + } + +void ovrFramebuffer_Clear(ovrFramebuffer* frameBuffer) { + frameBuffer->Width = 0; + frameBuffer->Height = 0; + frameBuffer->Multisamples = 0; + frameBuffer->TextureSwapChainLength = 0; + frameBuffer->TextureSwapChainIndex = 0; + frameBuffer->ColorSwapChain.Handle = XR_NULL_HANDLE; + frameBuffer->ColorSwapChain.Width = 0; + frameBuffer->ColorSwapChain.Height = 0; + frameBuffer->ColorSwapChainImage = NULL; + frameBuffer->DepthBuffers = NULL; + frameBuffer->FrameBuffers = NULL; +} + +bool ovrFramebuffer_Create( + XrSession session, + ovrFramebuffer* frameBuffer, + const GLenum colorFormat, + const int width, + const int height, + const int multisamples) { + PFNGLRENDERBUFFERSTORAGEMULTISAMPLEEXTPROC glRenderbufferStorageMultisampleEXT = + (PFNGLRENDERBUFFERSTORAGEMULTISAMPLEEXTPROC)EglGetExtensionProc( + "glRenderbufferStorageMultisampleEXT"); + PFNGLFRAMEBUFFERTEXTURE2DMULTISAMPLEEXTPROC glFramebufferTexture2DMultisampleEXT = + (PFNGLFRAMEBUFFERTEXTURE2DMULTISAMPLEEXTPROC)EglGetExtensionProc( + "glFramebufferTexture2DMultisampleEXT"); + + frameBuffer->Width = width; + frameBuffer->Height = height; + frameBuffer->Multisamples = multisamples; + + GLenum requestedGLFormat = colorFormat; + + // Get the number of supported formats. + uint32_t numInputFormats = 0; + uint32_t numOutputFormats = 0; + OXR(xrEnumerateSwapchainFormats(session, numInputFormats, &numOutputFormats, NULL)); + + // Allocate an array large enough to contain the supported formats. + numInputFormats = numOutputFormats; + + std::vector supportedFormats(numOutputFormats); + OXR(xrEnumerateSwapchainFormats( + session, numInputFormats, &numOutputFormats, supportedFormats.data())); + + // Verify the requested format is supported. + uint64_t selectedFormat = 0; + for (uint32_t i = 0; i < numOutputFormats; i++) { + if (supportedFormats[i] == requestedGLFormat) { + selectedFormat = supportedFormats[i]; + break; + } + } + + if (selectedFormat == 0) { + ALOGE("Format not supported"); + } + + XrSwapchainCreateInfo swapChainCreateInfo = {XR_TYPE_SWAPCHAIN_CREATE_INFO}; + swapChainCreateInfo.usageFlags = + XR_SWAPCHAIN_USAGE_SAMPLED_BIT | XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT; + swapChainCreateInfo.format = selectedFormat; + swapChainCreateInfo.sampleCount = 1; + swapChainCreateInfo.width = width; + swapChainCreateInfo.height = height; + swapChainCreateInfo.faceCount = 1; + swapChainCreateInfo.arraySize = 1; + swapChainCreateInfo.mipCount = 1; + + frameBuffer->ColorSwapChain.Width = swapChainCreateInfo.width; + frameBuffer->ColorSwapChain.Height = swapChainCreateInfo.height; + + // Create the swapchain. + OXR(xrCreateSwapchain(session, &swapChainCreateInfo, &frameBuffer->ColorSwapChain.Handle)); + // Get the number of swapchain images. + OXR(xrEnumerateSwapchainImages( + frameBuffer->ColorSwapChain.Handle, 0, &frameBuffer->TextureSwapChainLength, NULL)); + // Allocate the swapchain images array. +#if defined(XR_USE_GRAPHICS_API_OPENGL_ES) + frameBuffer->ColorSwapChainImage = (XrSwapchainImageOpenGLESKHR*)malloc( + frameBuffer->TextureSwapChainLength * sizeof(XrSwapchainImageOpenGLESKHR)); +#elif defined(XR_USE_GRAPHICS_API_OPENGL) + frameBuffer->ColorSwapChainImage = (XrSwapchainImageOpenGLKHR*)malloc( + frameBuffer->TextureSwapChainLength * sizeof(XrSwapchainImageOpenGLKHR)); +#endif // + + // Populate the swapchain image array. + for (uint32_t i = 0; i < frameBuffer->TextureSwapChainLength; i++) { +#if defined(XR_USE_GRAPHICS_API_OPENGL_ES) + frameBuffer->ColorSwapChainImage[i] = + XrSwapchainImageOpenGLESKHR{XR_TYPE_SWAPCHAIN_IMAGE_OPENGL_ES_KHR}; +#elif defined(XR_USE_GRAPHICS_API_OPENGL) + frameBuffer->ColorSwapChainImage[i] = + XrSwapchainImageOpenGLKHR{XR_TYPE_SWAPCHAIN_IMAGE_OPENGL_KHR}; +#endif // + } + OXR(xrEnumerateSwapchainImages( + frameBuffer->ColorSwapChain.Handle, + frameBuffer->TextureSwapChainLength, + &frameBuffer->TextureSwapChainLength, + (XrSwapchainImageBaseHeader*)frameBuffer->ColorSwapChainImage)); + + frameBuffer->DepthBuffers = + (GLuint*)malloc(frameBuffer->TextureSwapChainLength * sizeof(GLuint)); + frameBuffer->FrameBuffers = + (GLuint*)malloc(frameBuffer->TextureSwapChainLength * sizeof(GLuint)); + + for (uint32_t i = 0; i < frameBuffer->TextureSwapChainLength; i++) { + // Create the color buffer texture. + const GLuint colorTexture = frameBuffer->ColorSwapChainImage[i].image; + + GLenum colorTextureTarget = GL_TEXTURE_2D; + GL(glBindTexture(colorTextureTarget, colorTexture)); + GL(glTexParameteri(colorTextureTarget, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)); + GL(glTexParameteri(colorTextureTarget, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)); + GL(glTexParameteri(colorTextureTarget, GL_TEXTURE_MIN_FILTER, GL_LINEAR)); + GL(glTexParameteri(colorTextureTarget, GL_TEXTURE_MAG_FILTER, GL_LINEAR)); + GL(glBindTexture(colorTextureTarget, 0)); + + if (multisamples > 1 && glRenderbufferStorageMultisampleEXT != NULL && + glFramebufferTexture2DMultisampleEXT != NULL) { + // Create multisampled depth buffer. + GL(glGenRenderbuffers(1, &frameBuffer->DepthBuffers[i])); + GL(glBindRenderbuffer(GL_RENDERBUFFER, frameBuffer->DepthBuffers[i])); + GL(glRenderbufferStorageMultisampleEXT( + GL_RENDERBUFFER, multisamples, GL_DEPTH_COMPONENT24, width, height)); + GL(glBindRenderbuffer(GL_RENDERBUFFER, 0)); + + // Create the frame buffer. + // NOTE: glFramebufferTexture2DMultisampleEXT only works with GL_FRAMEBUFFER. + GL(glGenFramebuffers(1, &frameBuffer->FrameBuffers[i])); + GL(glBindFramebuffer(GL_FRAMEBUFFER, frameBuffer->FrameBuffers[i])); + GL(glFramebufferTexture2DMultisampleEXT( + GL_FRAMEBUFFER, + GL_COLOR_ATTACHMENT0, + GL_TEXTURE_2D, + colorTexture, + 0, + multisamples)); + GL(glFramebufferRenderbuffer( + GL_FRAMEBUFFER, + GL_DEPTH_ATTACHMENT, + GL_RENDERBUFFER, + frameBuffer->DepthBuffers[i])); + GL(GLenum renderFramebufferStatus = glCheckFramebufferStatus(GL_FRAMEBUFFER)); + GL(glBindFramebuffer(GL_FRAMEBUFFER, 0)); + if (renderFramebufferStatus != GL_FRAMEBUFFER_COMPLETE) { + ALOGE( + "Incomplete frame buffer object: %s", + GlFrameBufferStatusString(renderFramebufferStatus)); + return false; + } + } else { + // Create depth buffer. + GL(glGenRenderbuffers(1, &frameBuffer->DepthBuffers[i])); + GL(glBindRenderbuffer(GL_RENDERBUFFER, frameBuffer->DepthBuffers[i])); + GL(glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, width, height)); + GL(glBindRenderbuffer(GL_RENDERBUFFER, 0)); + + // Create the frame buffer. + GL(glGenFramebuffers(1, &frameBuffer->FrameBuffers[i])); + GL(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, frameBuffer->FrameBuffers[i])); + GL(glFramebufferRenderbuffer( + GL_DRAW_FRAMEBUFFER, + GL_DEPTH_ATTACHMENT, + GL_RENDERBUFFER, + frameBuffer->DepthBuffers[i])); + GL(glFramebufferTexture2D( + GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, colorTexture, 0)); + GL(GLenum renderFramebufferStatus = glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER)); + GL(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0)); + if (renderFramebufferStatus != GL_FRAMEBUFFER_COMPLETE) { + ALOGE( + "Incomplete frame buffer object: %s", + GlFrameBufferStatusString(renderFramebufferStatus)); + return false; + } + } + } + + return true; +} + +void ovrFramebuffer_Destroy(ovrFramebuffer* frameBuffer) { + GL(glDeleteFramebuffers(frameBuffer->TextureSwapChainLength, frameBuffer->FrameBuffers)); + GL(glDeleteRenderbuffers(frameBuffer->TextureSwapChainLength, frameBuffer->DepthBuffers)); + OXR(xrDestroySwapchain(frameBuffer->ColorSwapChain.Handle)); + free(frameBuffer->ColorSwapChainImage); + free(frameBuffer->DepthBuffers); + free(frameBuffer->FrameBuffers); + ovrFramebuffer_Clear(frameBuffer); +} + +void ovrFramebuffer_SetCurrent(ovrFramebuffer* frameBuffer) { + GL(glBindFramebuffer( + GL_DRAW_FRAMEBUFFER, frameBuffer->FrameBuffers[frameBuffer->TextureSwapChainIndex])); +} + +void ovrFramebuffer_SetNone() { + GL(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0)); +} + +void ovrFramebuffer_Resolve(ovrFramebuffer* frameBuffer) { + // Discard the depth buffer, so the tiler won't need to write it back out to memory. + const GLenum depthAttachment[1] = {GL_DEPTH_ATTACHMENT}; + glInvalidateFramebuffer(GL_DRAW_FRAMEBUFFER, 1, depthAttachment); + // We now let the resolve happen implicitly. +} + +void ovrFramebuffer_Acquire(ovrFramebuffer* frameBuffer) { + // Acquire the swapchain image + XrSwapchainImageAcquireInfo acquireInfo = {XR_TYPE_SWAPCHAIN_IMAGE_ACQUIRE_INFO}; + OXR(xrAcquireSwapchainImage( + frameBuffer->ColorSwapChain.Handle, &acquireInfo, &frameBuffer->TextureSwapChainIndex)); + + XrSwapchainImageWaitInfo waitInfo = {XR_TYPE_SWAPCHAIN_IMAGE_WAIT_INFO}; + waitInfo.timeout = 1000000000; /* timeout in nanoseconds */ + XrResult res; + OXR(res = xrWaitSwapchainImage(frameBuffer->ColorSwapChain.Handle, &waitInfo)); + int i = 0; + while (res == XR_TIMEOUT_EXPIRED) { + res = xrWaitSwapchainImage(frameBuffer->ColorSwapChain.Handle, &waitInfo); + i++; + ALOGV( + " Retry xrWaitSwapchainImage %d times due to XR_TIMEOUT_EXPIRED (duration %f seconds)", + i, + waitInfo.timeout * (1E-9)); + } +} + +void ovrFramebuffer_Release(ovrFramebuffer* frameBuffer) { + XrSwapchainImageReleaseInfo releaseInfo = {XR_TYPE_SWAPCHAIN_IMAGE_RELEASE_INFO}; + OXR(xrReleaseSwapchainImage(frameBuffer->ColorSwapChain.Handle, &releaseInfo)); +} diff --git a/Samples/SampleXrFramework/Src/Render/Framebuffer.h b/Samples/SampleXrFramework/Src/Render/Framebuffer.h new file mode 100755 index 0000000..2c284b7 --- /dev/null +++ b/Samples/SampleXrFramework/Src/Render/Framebuffer.h @@ -0,0 +1,116 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/************************************************************************************ + +Filename : Framebuffer.h +Content : Frame buffer utilities. Originally part of the VrCubeWorld_NativeActivity + sample. +Created : March, 2015 +Authors : J.M.P. van Waveren + + *************************************************************************************/ + +#pragma once + +#include "Render/Egl.h" + +#if !defined(EGL_OPENGL_ES3_BIT_KHR) +#define EGL_OPENGL_ES3_BIT_KHR 0x0040 +#endif + +// EXT_texture_border_clamp +#ifndef GL_CLAMP_TO_BORDER +#define GL_CLAMP_TO_BORDER 0x812D +#endif + +#ifndef GL_TEXTURE_BORDER_COLOR +#define GL_TEXTURE_BORDER_COLOR 0x1004 +#endif + +#if !defined(GL_EXT_multisampled_render_to_texture) +typedef void(GL_APIENTRY* PFNGLRENDERBUFFERSTORAGEMULTISAMPLEEXTPROC)( + GLenum target, + GLsizei samples, + GLenum internalformat, + GLsizei width, + GLsizei height); +typedef void(GL_APIENTRY* PFNGLFRAMEBUFFERTEXTURE2DMULTISAMPLEEXTPROC)( + GLenum target, + GLenum attachment, + GLenum textarget, + GLuint texture, + GLint level, + GLsizei samples); +#endif + +// GL_EXT_texture_cube_map_array +#if !defined(GL_TEXTURE_CUBE_MAP_ARRAY) +#define GL_TEXTURE_CUBE_MAP_ARRAY 0x9009 +#endif + +#if defined(ANDROID) +#include +#define XR_USE_GRAPHICS_API_OPENGL_ES 1 +#define XR_USE_PLATFORM_ANDROID 1 +#elif defined(WIN32) +#include +#define XR_USE_GRAPHICS_API_OPENGL 1 +#define XR_USE_PLATFORM_WIN32 1 +#endif // defined(ANDROID) + +#include +#include + +struct ovrSwapChain { + XrSwapchain Handle; + uint32_t Width; + uint32_t Height; +}; + +typedef struct ovrFramebuffer_s { + int Width; + int Height; + int Multisamples; + uint32_t TextureSwapChainLength; + uint32_t TextureSwapChainIndex; + struct ovrSwapChain ColorSwapChain; +#if defined(XR_USE_GRAPHICS_API_OPENGL_ES) + XrSwapchainImageOpenGLESKHR* ColorSwapChainImage; +#elif defined(XR_USE_GRAPHICS_API_OPENGL) + XrSwapchainImageOpenGLKHR* ColorSwapChainImage; +#endif // defined(XR_USE_GRAPHICS_API_OPENGL_ES) + GLuint* DepthBuffers; + GLuint* FrameBuffers; +} ovrFramebuffer; + +void ovrFramebuffer_Clear(ovrFramebuffer* frameBuffer); +bool ovrFramebuffer_Create( + XrSession session, + ovrFramebuffer* frameBuffer, + const GLenum colorFormat, + const int width, + const int height, + const int multisamples); +void ovrFramebuffer_Destroy(ovrFramebuffer* frameBuffer); +void ovrFramebuffer_SetCurrent(ovrFramebuffer* frameBuffer); +void ovrFramebuffer_SetNone(); +void ovrFramebuffer_Resolve(ovrFramebuffer* frameBuffer); +void ovrFramebuffer_Acquire(ovrFramebuffer* frameBuffer); +void ovrFramebuffer_Release(ovrFramebuffer* frameBuffer); + +/// convenience +class scope_frame_buffer { + public: + scope_frame_buffer(ovrFramebuffer* fb) : fb_(fb) { + ovrFramebuffer_Acquire(fb_); + ovrFramebuffer_SetCurrent(fb_); + } + ~scope_frame_buffer() { + ovrFramebuffer_Resolve(fb_); + ovrFramebuffer_Release(fb_); + ovrFramebuffer_SetNone(); + } + + private: + ovrFramebuffer* fb_; +}; diff --git a/Samples/SampleXrFramework/Src/Render/GeometryBuilder.cpp b/Samples/SampleXrFramework/Src/Render/GeometryBuilder.cpp new file mode 100755 index 0000000..b0783e4 --- /dev/null +++ b/Samples/SampleXrFramework/Src/Render/GeometryBuilder.cpp @@ -0,0 +1,135 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/************************************************************************************ + +Filename : GeometryBuilder.cpp +Content : OpenGL geometry setup. +Created : July 2020 +Authors : Federico Schliemann + +************************************************************************************/ + +#include "GeometryBuilder.h" + +namespace OVRFW { + +int GeometryBuilder::Add( + const OVRFW::GlGeometry::Descriptor& geometry, + int parentIndex, + const OVR::Vector4f& color, + const OVR::Matrix4f& transform) { + int parent = parentIndex; + OVR::Matrix4f t = transform; + + /// Ensure parent is valid + if (parentIndex >= 0 && parentIndex < int(nodes_.size())) { + /// Get world-space transform + t = nodes_[parentIndex].transform * geometry.transform * transform; + } else { + /// normalize bad entities to no-parent + parent = GeometryBuilder::kInvalidIndex; + } + + /// Add new node + nodes_.emplace_back(geometry, parent, color, t); + + /// Return the index of the newly added node + return int(nodes_.size()); +} + +OVRFW::GlGeometry::Descriptor GeometryBuilder::ToGeometryDescriptor( + const OVR::Matrix4f& rootTransform) const { + size_t vertexCount = 0u; + size_t indexCount = 0u; + + OVRFW::GlGeometry::Descriptor desc; + VertexAttribs& attribs = desc.attribs; + std::vector& indices = desc.indices; + + /// Find out the sizes + for (const auto& node : nodes_) { + vertexCount += node.geometry.attribs.position.size(); + indexCount += node.geometry.indices.size(); + } + + /// TODO - figure out if we need ALL attributes + + /// TODO - figure out whether we need int32 indices + + /// TODO - add a version where each sub-object has its own bone index + + /// Make room + attribs.position.resize(vertexCount); + attribs.normal.resize(vertexCount); + attribs.tangent.resize(vertexCount); + attribs.binormal.resize(vertexCount); + attribs.color.resize(vertexCount); + attribs.uv0.resize(vertexCount); + attribs.uv1.resize(vertexCount); + attribs.jointIndices.resize(vertexCount); + attribs.jointWeights.resize(vertexCount); + indices.resize(indexCount); + + /// Fill it + size_t currentVertex = 0u; + size_t currentIndex = 0u; + for (const auto& node : nodes_) { + /// Matrices + const OVR::Matrix4f t = rootTransform * node.transform; + const OVR::Matrix4f nt = t.Inverted().Transposed(); + + /// Vertices + for (size_t i = 0; i < node.geometry.attribs.position.size(); ++i) { + attribs.position[i + currentVertex] = t.Transform(node.geometry.attribs.position[i]); + } + for (size_t i = 0; i < node.geometry.attribs.normal.size(); ++i) { + attribs.normal[i + currentVertex] = + nt.Transform(node.geometry.attribs.normal[i]).Normalized(); + } + for (size_t i = 0; i < node.geometry.attribs.tangent.size(); ++i) { + attribs.tangent[i + currentVertex] = + nt.Transform(node.geometry.attribs.tangent[i]).Normalized(); + } + for (size_t i = 0; i < node.geometry.attribs.binormal.size(); ++i) { + attribs.binormal[i + currentVertex] = + nt.Transform(node.geometry.attribs.binormal[i]).Normalized(); + } + /// uv + for (size_t i = 0; i < node.geometry.attribs.uv0.size(); ++i) { + attribs.uv0[i + currentVertex] = node.geometry.attribs.uv0[i]; + } + for (size_t i = 0; i < node.geometry.attribs.uv1.size(); ++i) { + attribs.uv1[i + currentVertex] = node.geometry.attribs.uv1[i]; + } + + /// color - from here + for (size_t i = 0; i < node.geometry.attribs.position.size(); ++i) { + attribs.color[i + currentVertex] = node.color; + } + + /// bones + for (size_t i = 0; i < node.geometry.attribs.jointIndices.size(); ++i) { + attribs.jointIndices[i + currentVertex] = node.geometry.attribs.jointIndices[i]; + } + for (size_t i = 0; i < node.geometry.attribs.jointWeights.size(); ++i) { + attribs.jointWeights[i + currentVertex] = node.geometry.attribs.jointWeights[i]; + } + + /// Indices + for (size_t i = 0; i < node.geometry.indices.size(); ++i) { + indices[i + currentIndex] = node.geometry.indices[i] + currentVertex; + } + + /// Vertices + currentVertex += node.geometry.attribs.position.size(); + currentIndex += node.geometry.indices.size(); + } + + return desc; +} + +void GeometryBuilder::clear_nodes() { + nodes_.clear(); +} + +} // namespace OVRFW diff --git a/Samples/SampleXrFramework/Src/Render/GeometryBuilder.h b/Samples/SampleXrFramework/Src/Render/GeometryBuilder.h new file mode 100755 index 0000000..5c39d6d --- /dev/null +++ b/Samples/SampleXrFramework/Src/Render/GeometryBuilder.h @@ -0,0 +1,68 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/************************************************************************************ + +Filename : GeometryBuilder.h +Content : OpenGL geometry setup. +Created : July 2020 +Authors : Federico Schliemann + +************************************************************************************/ + +#pragma once + +#include +#include "OVR_Math.h" + +#include "GlProgram.h" +#include "GlGeometry.h" + +namespace OVRFW { + +class GeometryBuilder { + public: + GeometryBuilder() = default; + ~GeometryBuilder() = default; + + static constexpr int kInvalidIndex = -1; + + struct Node { + Node( + const OVRFW::GlGeometry::Descriptor& g, + int parent, + const OVR::Vector4f& c, + const OVR::Matrix4f& t) + : geometry(g), parentIndex(parent), color(c), transform(t) {} + + OVRFW::GlGeometry::Descriptor geometry; + int parentIndex = -1; + OVR::Vector4f color = OVR::Vector4f(0.5f, 0.5f, 0.5f, 1.0f); + OVR::Matrix4f transform = OVR::Matrix4f::Identity(); + }; + + int Add( + const OVRFW::GlGeometry::Descriptor& geometry, + int parentIndex = kInvalidIndex, + const OVR::Vector4f& color = OVR::Vector4f(0.5f, 0.5f, 0.5f, 1.0f), + const OVR::Matrix4f& transform = OVR::Matrix4f::Identity()); + + OVRFW::GlGeometry::Descriptor ToGeometryDescriptor( + const OVR::Matrix4f& rootTransform = OVR::Matrix4f::Identity()) const; + + OVRFW::GlGeometry ToGeometry( + const OVR::Matrix4f& rootTransform = OVR::Matrix4f::Identity()) const { + OVRFW::GlGeometry::Descriptor d = ToGeometryDescriptor(rootTransform); + return OVRFW::GlGeometry(d.attribs, d.indices); + } + + const std::vector& Nodes() const { + return nodes_; + } + + void clear_nodes(); + + private: + std::vector nodes_; +}; + +} // namespace OVRFW diff --git a/Samples/SampleXrFramework/Src/Render/GeometryRenderer.cpp b/Samples/SampleXrFramework/Src/Render/GeometryRenderer.cpp new file mode 100755 index 0000000..a31fbc8 --- /dev/null +++ b/Samples/SampleXrFramework/Src/Render/GeometryRenderer.cpp @@ -0,0 +1,180 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/******************************************************************************* + +Filename : GeometryRenderer.cpp +Content : Simple rendering for geometry-based types +Created : Mar 2021 +Authors : Federico Schliemann +Language : C++ + +*******************************************************************************/ + +#include "GeometryRenderer.h" +#include "Misc/Log.h" + +using OVR::Matrix4f; +using OVR::Posef; +using OVR::Quatf; +using OVR::Vector2f; +using OVR::Vector3f; +using OVR::Vector4f; + +namespace OVRFW { + +const char* GeometryVertexShaderSrc = R"glsl( + attribute highp vec4 Position; + attribute highp vec3 Normal; +#ifdef HAS_VERTEX_COLORS + attribute lowp vec4 VertexColor; + varying lowp vec4 oColor; +#endif /// HAS_VERTEX_COLORS + varying lowp vec3 oEye; + varying lowp vec3 oNormal; + + vec3 multiply( mat4 m, vec3 v ) + { + return vec3( + m[0].x * v.x + m[1].x * v.y + m[2].x * v.z, + m[0].y * v.x + m[1].y * v.y + m[2].y * v.z, + m[0].z * v.x + m[1].z * v.y + m[2].z * v.z ); + } + vec3 transposeMultiply( mat4 m, vec3 v ) + { + return vec3( + m[0].x * v.x + m[0].y * v.y + m[0].z * v.z, + m[1].x * v.x + m[1].y * v.y + m[1].z * v.z, + m[2].x * v.x + m[2].y * v.y + m[2].z * v.z ); + } + + void main() + { + gl_Position = TransformVertex( Position ); + +#ifdef HAS_VERTEX_COLORS + oColor = VertexColor; +#endif /// HAS_VERTEX_COLORS + lowp vec3 eye = transposeMultiply( sm.ViewMatrix[VIEW_ID], -vec3( sm.ViewMatrix[VIEW_ID][3] ) ); + oEye = eye - vec3( ModelMatrix * Position ); + // This matrix math should ideally not be done in the shader for perf reasons: + oNormal = multiply( transpose(inverse(ModelMatrix)), Normal ); + } +)glsl"; + +static const char* GeometryFragmentShaderSrc = R"glsl( + precision lowp float; + + uniform lowp vec4 ChannelControl; + uniform lowp vec4 DiffuseColor; + uniform lowp vec3 SpecularLightDirection; + uniform lowp vec3 SpecularLightColor; + uniform lowp vec3 AmbientLightColor; + +#ifdef HAS_VERTEX_COLORS + varying lowp vec4 oColor; +#endif /// HAS_VERTEX_COLORS + varying lowp vec3 oEye; + varying lowp vec3 oNormal; + + lowp float pow16( float x ) + { + float x2 = x * x; + float x4 = x2 * x2; + float x8 = x4 * x4; + float x16 = x8 * x8; + return x16; + } + + void main() + { + lowp vec3 eyeDir = normalize( oEye.xyz ); + lowp vec3 Normal = normalize( oNormal ); + + lowp vec4 diffuse = DiffuseColor; +#ifdef HAS_VERTEX_COLORS + diffuse = oColor; +#endif /// HAS_VERTEX_COLORS + lowp vec3 ambientValue = diffuse.xyz * AmbientLightColor; + + lowp float nDotL = max( dot( Normal, SpecularLightDirection ), 0.0 ); + lowp vec3 diffuseValue = diffuse.xyz * nDotL; + + lowp vec3 reflectDir = reflect( -SpecularLightDirection, Normal ); + lowp float specular = pow16(max(dot(eyeDir, reflectDir), 0.0)); + lowp float specularStrength = 1.0; + lowp vec3 specularValue = specular * specularStrength * SpecularLightColor; + + lowp vec3 color = diffuseValue * ChannelControl.x + + ambientValue * ChannelControl.y + + specularValue * ChannelControl.z + ; + gl_FragColor.xyz = color; + gl_FragColor.w = diffuse.w * ChannelControl.w; + } +)glsl"; + +void GeometryRenderer::Init(const GlGeometry::Descriptor& d) { + /// Program + static ovrProgramParm GeometryUniformParms[] = { + {"ChannelControl", ovrProgramParmType::FLOAT_VECTOR4}, + {"DiffuseColor", ovrProgramParmType::FLOAT_VECTOR4}, + {"SpecularLightDirection", ovrProgramParmType::FLOAT_VECTOR3}, + {"SpecularLightColor", ovrProgramParmType::FLOAT_VECTOR3}, + {"AmbientLightColor", ovrProgramParmType::FLOAT_VECTOR3}, + }; + + std::string programDefs = ""; + + /// Do we support vertex color in the goemetyr + const bool hasVertexColors = (d.attribs.color.size() > 0); + if (hasVertexColors) { + programDefs += "#define HAS_VERTEX_COLORS 1\n"; + } + const bool hasMultipleParts = (d.attribs.jointIndices.size() > 0); + if (hasMultipleParts) { + programDefs += "#define HAS_MULTIPLE_PARTS 1\n"; + } + + Program_ = GlProgram::Build( + programDefs.c_str(), + GeometryVertexShaderSrc, + programDefs.c_str(), + GeometryFragmentShaderSrc, + GeometryUniformParms, + sizeof(GeometryUniformParms) / sizeof(ovrProgramParm)); + + SurfaceDef_.geo = GlGeometry(d.attribs, d.indices); + + /// Hook the graphics command + ovrGraphicsCommand& gc = SurfaceDef_.graphicsCommand; + gc.Program = Program_; + gc.UniformData[0].Data = &ChannelControl; + gc.UniformData[1].Data = &DiffuseColor; + gc.UniformData[2].Data = &SpecularLightDirection; + gc.UniformData[3].Data = &SpecularLightColor; + gc.UniformData[4].Data = &AmbientLightColor; + + /// gpu state needs alpha blending + gc.GpuState.depthEnable = gc.GpuState.depthMaskEnable = true; + gc.GpuState.blendEnable = ovrGpuState::BLEND_ENABLE; +} + +void GeometryRenderer::Shutdown() { + OVRFW::GlProgram::Free(Program_); + SurfaceDef_.geo.Free(); +} + +void GeometryRenderer::Update() { + ModelPose_.Rotation.Normalize(); + ModelMatrix_ = OVR::Matrix4f(ModelPose_) * OVR::Matrix4f::Scaling(ModelScale_); +} + +void GeometryRenderer::Render(std::vector& surfaceList) { + ovrGraphicsCommand& gc = SurfaceDef_.graphicsCommand; + gc.GpuState.blendMode = BlendMode; + gc.GpuState.blendSrc = BlendSrc; + gc.GpuState.blendDst = BlendDst; + surfaceList.push_back(ovrDrawSurface(ModelMatrix_, &SurfaceDef_)); +} + +} // namespace OVRFW diff --git a/Samples/SampleXrFramework/Src/Render/GeometryRenderer.h b/Samples/SampleXrFramework/Src/Render/GeometryRenderer.h new file mode 100755 index 0000000..d025521 --- /dev/null +++ b/Samples/SampleXrFramework/Src/Render/GeometryRenderer.h @@ -0,0 +1,66 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/******************************************************************************* + +Filename : GeometryRenderer.h +Content : Simple rendering for geometry-based types +Created : Mar 2021 +Authors : Federico Schliemann +Language : C++ + +*******************************************************************************/ + +#pragma once +#include + +#include "OVR_Math.h" +#include "SurfaceRender.h" +#include "GlProgram.h" +#include "GeometryBuilder.h" + +namespace OVRFW { + +class GeometryRenderer { + public: + GeometryRenderer() = default; + virtual ~GeometryRenderer() = default; + + virtual void Init(const GlGeometry::Descriptor& d); + virtual void Shutdown(); + virtual void Update(); + virtual void Render(std::vector& surfaceList); + + void SetPose(const OVR::Posef& pose) { + ModelPose_ = pose; + } + OVR::Posef GetPose() { + return ModelPose_; + } + void SetScale(OVR::Vector3f v) { + ModelScale_ = v; + } + OVR::Vector3f GetScale() { + return ModelScale_; + } + + public: + OVR::Vector4f ChannelControl = {1, 1, 1, 1}; + OVR::Vector4f DiffuseColor = {0.4, 1.0, 0.2, 1.0}; + OVR::Vector3f SpecularLightDirection = OVR::Vector3f{1, 1, 1}.Normalized(); + OVR::Vector3f SpecularLightColor = {1, 1, 1}; + OVR::Vector3f AmbientLightColor = {.1, .1, .1}; + GLenum BlendSrc = GL_SRC_ALPHA; + GLenum BlendDst = GL_ONE_MINUS_SRC_ALPHA; + GLenum BlendMode = GL_FUNC_ADD; + + private: + ovrSurfaceDef SurfaceDef_; + GlProgram Program_; + OVR::Matrix4f ModelMatrix_ = OVR::Matrix4f::Identity(); + OVR::Vector3f ModelScale_ = {1, 1, 1}; + OVR::Posef ModelPose_ = OVR::Posef::Identity(); +}; + +} // namespace OVRFW + +//// -------------------------------- diff --git a/Samples/SampleXrFramework/Src/Render/GlBuffer.cpp b/Samples/SampleXrFramework/Src/Render/GlBuffer.cpp new file mode 100755 index 0000000..df9cd2a --- /dev/null +++ b/Samples/SampleXrFramework/Src/Render/GlBuffer.cpp @@ -0,0 +1,79 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/************************************************************************************ + +Filename : GlBuffer.cpp +Content : OpenGL texture loading. +Created : September 30, 2013 +Authors : John Carmack + +*************************************************************************************/ + +#include "GlBuffer.h" +#include "Misc/Log.h" +#include "CompilerUtils.h" + +namespace OVRFW { + +GlBuffer::GlBuffer() : target(0), buffer(0), size(0) {} + +bool GlBuffer::Create(const GlBufferType_t type, const size_t dataSize, const void* data) { + assert(buffer == 0); + + target = ((type == GLBUFFER_TYPE_UNIFORM) ? GL_UNIFORM_BUFFER : 0); + size = dataSize; + + glGenBuffers(1, &buffer); + glBindBuffer(target, buffer); + glBufferData(target, dataSize, data, GL_STATIC_DRAW); + glBindBuffer(target, 0); + + return true; +} + +void GlBuffer::Destroy() { + if (buffer != 0) { + glDeleteBuffers(1, &buffer); + buffer = 0; + } +} + +void GlBuffer::Update(const size_t updateDataSize, const void* data) const { + assert(buffer != 0); + + if (updateDataSize > size) { + ALOGE_FAIL( + "GlBuffer::Update: size overflow %zu specified, %zu allocated\n", updateDataSize, size); + } + + glBindBuffer(target, buffer); + glBufferSubData(target, 0, updateDataSize, data); + glBindBuffer(target, 0); +} + +void* GlBuffer::MapBuffer() const { + assert(buffer != 0); + + void* data = NULL; + glBindBuffer(target, buffer); + data = glMapBufferRange(target, 0, size, GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT); + glBindBuffer(target, 0); + + if (data == NULL) { + ALOGE_FAIL("GlBuffer::MapBuffer: Failed to map buffer"); + } + + return data; +} + +void GlBuffer::UnmapBuffer() const { + assert(buffer != 0); + + glBindBuffer(target, buffer); + if (!glUnmapBuffer(target)) { + ALOGW("GlBuffer::UnmapBuffer: Failed to unmap buffer."); + } + glBindBuffer(target, 0); +} + +} // namespace OVRFW diff --git a/Samples/SampleXrFramework/Src/Render/GlBuffer.h b/Samples/SampleXrFramework/Src/Render/GlBuffer.h new file mode 100755 index 0000000..b61e8ed --- /dev/null +++ b/Samples/SampleXrFramework/Src/Render/GlBuffer.h @@ -0,0 +1,47 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/************************************************************************************ + +Filename : GlBuffer.h +Content : OpenGL Buffer Management. +Created : +Authors : + +*************************************************************************************/ + +#pragma once + +#include "Egl.h" + +namespace OVRFW { + +enum GlBufferType_t { + GLBUFFER_TYPE_UNIFORM, +}; + +class GlBuffer { + public: + GlBuffer(); + + bool Create(const GlBufferType_t type, const size_t dataSize, const void* data); + void Destroy(); + + void Update(const size_t updateDataSize, const void* data) const; + + void* MapBuffer() const; + void UnmapBuffer() const; + + unsigned int GetBuffer() const { + return buffer; + } + size_t GetSize() const { + return size; + } + + private: + unsigned int target; + unsigned int buffer; + size_t size; +}; + +} // namespace OVRFW diff --git a/Samples/SampleXrFramework/Src/Render/GlGeometry.cpp b/Samples/SampleXrFramework/Src/Render/GlGeometry.cpp new file mode 100755 index 0000000..639e6ac --- /dev/null +++ b/Samples/SampleXrFramework/Src/Render/GlGeometry.cpp @@ -0,0 +1,1081 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/************************************************************************************ + +Filename : GlGeometry.cpp +Content : OpenGL geometry setup. +Created : October 8, 2013 +Authors : John Carmack, J.M.P. van Waveren + +*************************************************************************************/ + +#include "GlGeometry.h" + +#include "OVR_Math.h" +using OVR::Bounds3f; +using OVR::Vector2f; +using OVR::Vector3f; +using OVR::Vector4f; + +// #include "OVR_GlUtils.h" +#include "Misc/Log.h" + +/* + * These are all built inside VertexArrayObjects, so no GL state other + * than the VAO binding should be disturbed. + * + */ +namespace OVRFW { + +static bool enableGeometryTransfom = false; +static OVR::Matrix4f geometryTransfom = OVR::Matrix4f(); +GlGeometry::TransformScope::TransformScope(const OVR::Matrix4f m, bool enableTransfom) { + // store old + wasEnabled = enableGeometryTransfom; + previousTransform = geometryTransfom; + // set new + enableGeometryTransfom = enableTransfom; + geometryTransfom = m; +} +GlGeometry::TransformScope::~TransformScope() { + // restore + enableGeometryTransfom = wasEnabled; + geometryTransfom = previousTransform; +} + +unsigned GlGeometry::IndexType = (sizeof(TriangleIndex) == 2) ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT; + +template +void PackVertexAttribute( + std::vector& packed, + const std::vector<_attrib_type_>& attrib, + const int glLocation, + const int glType, + const int glComponents) { + if (attrib.size() > 0) { + const size_t offset = packed.size(); + const size_t size = attrib.size() * sizeof(attrib[0]); + + packed.resize(offset + size); + memcpy(&packed[offset], attrib.data(), size); + + glEnableVertexAttribArray(glLocation); + glVertexAttribPointer( + glLocation, glComponents, glType, false, sizeof(attrib[0]), (void*)(offset)); + } else { + glDisableVertexAttribArray(glLocation); + } +} + +void GlGeometry::Create(const VertexAttribs& attribs, const std::vector& indices) { + vertexCount = attribs.position.size(); + indexCount = indices.size(); + + const bool t = enableGeometryTransfom; + + std::vector position; + std::vector normal; + std::vector tangent; + std::vector binormal; + + /// we asked for incoming transfom + if (t) { + position.resize(attribs.position.size()); + normal.resize(attribs.normal.size()); + tangent.resize(attribs.position.size()); + binormal.resize(attribs.binormal.size()); + + /// poor man's 3x3 + OVR::Matrix4f nt = geometryTransfom.Transposed(); + + /// Positions use 4x4 + for (size_t i = 0; i < attribs.position.size(); ++i) { + position[i] = geometryTransfom.Transform(attribs.position[i]); + } + /// TBN use 3x3 + for (size_t i = 0; i < attribs.normal.size(); ++i) { + normal[i] = nt.Transform(attribs.normal[i]).Normalized(); + } + for (size_t i = 0; i < attribs.tangent.size(); ++i) { + tangent[i] = nt.Transform(attribs.tangent[i]).Normalized(); + } + for (size_t i = 0; i < attribs.binormal.size(); ++i) { + binormal[i] = nt.Transform(attribs.binormal[i]).Normalized(); + } + } + + glGenBuffers(1, &vertexBuffer); + glGenBuffers(1, &indexBuffer); + glGenVertexArrays(1, &vertexArrayObject); + glBindVertexArray(vertexArrayObject); + glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer); + + std::vector packed; + PackVertexAttribute( + packed, t ? position : attribs.position, VERTEX_ATTRIBUTE_LOCATION_POSITION, GL_FLOAT, 3); + PackVertexAttribute( + packed, t ? normal : attribs.normal, VERTEX_ATTRIBUTE_LOCATION_NORMAL, GL_FLOAT, 3); + PackVertexAttribute( + packed, t ? tangent : attribs.tangent, VERTEX_ATTRIBUTE_LOCATION_TANGENT, GL_FLOAT, 3); + PackVertexAttribute( + packed, t ? binormal : attribs.binormal, VERTEX_ATTRIBUTE_LOCATION_BINORMAL, GL_FLOAT, 3); + PackVertexAttribute(packed, attribs.color, VERTEX_ATTRIBUTE_LOCATION_COLOR, GL_FLOAT, 4); + PackVertexAttribute(packed, attribs.uv0, VERTEX_ATTRIBUTE_LOCATION_UV0, GL_FLOAT, 2); + PackVertexAttribute(packed, attribs.uv1, VERTEX_ATTRIBUTE_LOCATION_UV1, GL_FLOAT, 2); + PackVertexAttribute( + packed, attribs.jointIndices, VERTEX_ATTRIBUTE_LOCATION_JOINT_INDICES, GL_INT, 4); + PackVertexAttribute( + packed, attribs.jointWeights, VERTEX_ATTRIBUTE_LOCATION_JOINT_WEIGHTS, GL_FLOAT, 4); + // clang-format off + + glBufferData(GL_ARRAY_BUFFER, packed.size() * sizeof(packed[0]), packed.data(), GL_STATIC_DRAW); + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer); + glBufferData( + GL_ELEMENT_ARRAY_BUFFER, + indices.size() * sizeof(indices[0]), + indices.data(), + GL_STATIC_DRAW); + + glBindVertexArray(0); + + glDisableVertexAttribArray(VERTEX_ATTRIBUTE_LOCATION_POSITION); + glDisableVertexAttribArray(VERTEX_ATTRIBUTE_LOCATION_NORMAL); + glDisableVertexAttribArray(VERTEX_ATTRIBUTE_LOCATION_TANGENT); + glDisableVertexAttribArray(VERTEX_ATTRIBUTE_LOCATION_BINORMAL); + glDisableVertexAttribArray(VERTEX_ATTRIBUTE_LOCATION_COLOR); + glDisableVertexAttribArray(VERTEX_ATTRIBUTE_LOCATION_UV0); + glDisableVertexAttribArray(VERTEX_ATTRIBUTE_LOCATION_UV1); + glDisableVertexAttribArray(VERTEX_ATTRIBUTE_LOCATION_JOINT_INDICES); + glDisableVertexAttribArray(VERTEX_ATTRIBUTE_LOCATION_JOINT_WEIGHTS); + + localBounds.Clear(); + for (int i = 0; i < vertexCount; i++) { + localBounds.AddPoint(attribs.position[i]); + } +} + +void GlGeometry::Update(const VertexAttribs& attribs, const bool updateBounds) { + vertexCount = attribs.position.size(); + + glBindVertexArray(vertexArrayObject); + + glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer); + + std::vector packed; + PackVertexAttribute(packed, attribs.position, VERTEX_ATTRIBUTE_LOCATION_POSITION, GL_FLOAT, 3); + PackVertexAttribute(packed, attribs.normal, VERTEX_ATTRIBUTE_LOCATION_NORMAL, GL_FLOAT, 3); + PackVertexAttribute(packed, attribs.tangent, VERTEX_ATTRIBUTE_LOCATION_TANGENT, GL_FLOAT, 3); + PackVertexAttribute(packed, attribs.binormal, VERTEX_ATTRIBUTE_LOCATION_BINORMAL, GL_FLOAT, 3); + PackVertexAttribute(packed, attribs.color, VERTEX_ATTRIBUTE_LOCATION_COLOR, GL_FLOAT, 4); + PackVertexAttribute(packed, attribs.uv0, VERTEX_ATTRIBUTE_LOCATION_UV0, GL_FLOAT, 2); + PackVertexAttribute(packed, attribs.uv1, VERTEX_ATTRIBUTE_LOCATION_UV1, GL_FLOAT, 2); + PackVertexAttribute( + packed, attribs.jointIndices, VERTEX_ATTRIBUTE_LOCATION_JOINT_INDICES, GL_INT, 4); + PackVertexAttribute( + packed, attribs.jointWeights, VERTEX_ATTRIBUTE_LOCATION_JOINT_WEIGHTS, GL_FLOAT, 4); + + glBufferData(GL_ARRAY_BUFFER, packed.size() * sizeof(packed[0]), packed.data(), GL_STATIC_DRAW); + + if (updateBounds) { + localBounds.Clear(); + for (int i = 0; i < vertexCount; i++) { + localBounds.AddPoint(attribs.position[i]); + } + } +} + +void GlGeometry::Free() { + glDeleteVertexArrays(1, &vertexArrayObject); + glDeleteBuffers(1, &indexBuffer); + glDeleteBuffers(1, &vertexBuffer); + + indexBuffer = 0; + vertexBuffer = 0; + vertexArrayObject = 0; + vertexCount = 0; + indexCount = 0; + + localBounds.Clear(); +} + +GlGeometry::Descriptor BuildTesselatedQuadDescriptor( + const TriangleIndex horizontal, + const TriangleIndex vertical, + const bool twoSided) { + const int vertexCount = (horizontal + 1) * (vertical + 1); + + VertexAttribs attribs; + attribs.position.resize(vertexCount); + attribs.uv0.resize(vertexCount); + attribs.color.resize(vertexCount); + + for (int y = 0; y <= vertical; y++) { + const float yf = (float)y / (float)vertical; + for (int x = 0; x <= horizontal; x++) { + const float xf = (float)x / (float)horizontal; + const int index = y * (horizontal + 1) + x; + attribs.position[index].x = -1.0f + xf * 2.0f; + attribs.position[index].y = -1.0f + yf * 2.0f; + attribs.position[index].z = 0.0f; + attribs.uv0[index].x = xf; + attribs.uv0[index].y = 1.0f - yf; + for (int i = 0; i < 4; i++) { + attribs.color[index][i] = 1.0f; + } + // fade to transparent on the outside + if (x == 0 || x == horizontal || y == 0 || y == vertical) { + attribs.color[index][3] = 0.0f; + } + } + } + + std::vector indices; + indices.resize(horizontal * vertical * 6 * (twoSided ? 2 : 1)); + + // If this is to be used to draw a linear format texture, like + // a surface texture, it is better for cache performance that + // the triangles be drawn to follow the side to side linear order. + int index = 0; + for (TriangleIndex y = 0; y < vertical; y++) { + for (TriangleIndex x = 0; x < horizontal; x++) { + indices[index + 0] = y * (horizontal + 1) + x; + indices[index + 1] = y * (horizontal + 1) + x + 1; + indices[index + 2] = (y + 1) * (horizontal + 1) + x; + indices[index + 3] = (y + 1) * (horizontal + 1) + x; + indices[index + 4] = y * (horizontal + 1) + x + 1; + indices[index + 5] = (y + 1) * (horizontal + 1) + x + 1; + index += 6; + } + // fix the quads in the upper right and lower left corners so that the triangles in the + // quads share the edges going from the center of the tesselated quad to it's corners. + const int upperLeftIndexStart = 0; + indices[upperLeftIndexStart + 1] = indices[upperLeftIndexStart + 5]; + indices[upperLeftIndexStart + 3] = indices[upperLeftIndexStart + 0]; + + const int lowerRightIndexStart = (horizontal * (vertical - 1) * 6) + (horizontal - 1) * 6; + indices[lowerRightIndexStart + 1] = indices[lowerRightIndexStart + 5]; + indices[lowerRightIndexStart + 3] = indices[lowerRightIndexStart + 0]; + } + if (twoSided) { + for (TriangleIndex y = 0; y < vertical; y++) { + for (TriangleIndex x = 0; x < horizontal; x++) { + indices[index + 5] = y * (horizontal + 1) + x; + indices[index + 4] = y * (horizontal + 1) + x + 1; + indices[index + 3] = (y + 1) * (horizontal + 1) + x; + indices[index + 2] = (y + 1) * (horizontal + 1) + x; + indices[index + 1] = y * (horizontal + 1) + x + 1; + indices[index + 0] = (y + 1) * (horizontal + 1) + x + 1; + index += 6; + } + } + } + return GlGeometry::Descriptor(attribs, indices, geometryTransfom); +} + +GlGeometry::Descriptor BuildTesselatedCylinderDescriptor( + const float radius, + const float height, + const TriangleIndex horizontal, + const TriangleIndex vertical, + const float uScale, + const float vScale) { + const int vertexCount = (horizontal + 1) * (vertical + 1); + + VertexAttribs attribs; + attribs.position.resize(vertexCount); + attribs.normal.resize(vertexCount); + attribs.uv0.resize(vertexCount); + attribs.color.resize(vertexCount); + + for (int y = 0; y <= vertical; ++y) { + const float yf = (float)y / (float)vertical; + for (int x = 0; x <= horizontal; ++x) { + const float xf = (float)x / (float)horizontal; + const int index = y * (horizontal + 1) + x; + attribs.position[index].x = cosf(MATH_FLOAT_PI * 2 * xf) * radius; + attribs.position[index].y = sinf(MATH_FLOAT_PI * 2 * xf) * radius; + attribs.position[index].z = -height + yf * 2 * height; + attribs.normal[index] = + Vector3f(attribs.position[index].x, attribs.position[index].y, 0).Normalized(); + attribs.uv0[index].x = xf * uScale; + attribs.uv0[index].y = (1.0f - yf) * vScale; + for (int i = 0; i < 4; ++i) { + attribs.color[index][i] = 1.0f; + } + // fade to transparent on the outside + if (y == 0 || y == vertical) { + attribs.color[index][3] = 0.0f; + } + } + } + + std::vector indices; + indices.resize(horizontal * vertical * 6); + + // If this is to be used to draw a linear format texture, like + // a surface texture, it is better for cache performance that + // the triangles be drawn to follow the side to side linear order. + int index = 0; + for (TriangleIndex y = 0; y < vertical; y++) { + for (TriangleIndex x = 0; x < horizontal; x++) { + indices[index + 0] = y * (horizontal + 1) + x; + indices[index + 1] = y * (horizontal + 1) + x + 1; + indices[index + 2] = (y + 1) * (horizontal + 1) + x; + indices[index + 3] = (y + 1) * (horizontal + 1) + x; + indices[index + 4] = y * (horizontal + 1) + x + 1; + indices[index + 5] = (y + 1) * (horizontal + 1) + x + 1; + index += 6; + } + } + + return GlGeometry::Descriptor(attribs, indices, geometryTransfom); +} + +GlGeometry::Descriptor BuildTesselatedConeDescriptor( + const float radius, + const float height, + const TriangleIndex horizontal, + const TriangleIndex vertical, + const float uScale, + const float vScale) { + const int vertexCount = (horizontal + 1) * (vertical + 1); + + VertexAttribs attribs; + attribs.position.resize(vertexCount); + attribs.normal.resize(vertexCount); + attribs.uv0.resize(vertexCount); + attribs.color.resize(vertexCount); + + for (int y = 0; y <= vertical; ++y) { + const float yf = (float)y / (float)vertical; + for (int x = 0; x <= horizontal; ++x) { + const float xf = (float)x / (float)horizontal; + const int index = y * (horizontal + 1) + x; + attribs.position[index].x = cosf(MATH_FLOAT_PI * 2 * xf) * radius * yf; + attribs.position[index].y = sinf(MATH_FLOAT_PI * 2 * xf) * radius * yf; + attribs.position[index].z = -height + yf * 2 * height; + attribs.normal[index] = + Vector3f(attribs.position[index].x, attribs.position[index].y, 0).Normalized(); + attribs.uv0[index].x = xf * uScale; + attribs.uv0[index].y = (1.0f - yf) * vScale; + for (int i = 0; i < 4; ++i) { + attribs.color[index][i] = 1.0f; + } + // fade to transparent on the outside + if (y == 0 || y == vertical) { + attribs.color[index][3] = 0.0f; + } + } + } + + std::vector indices; + indices.resize(horizontal * vertical * 6); + + // If this is to be used to draw a linear format texture, like + // a surface texture, it is better for cache performance that + // the triangles be drawn to follow the side to side linear order. + int index = 0; + for (TriangleIndex y = 0; y < vertical; y++) { + for (TriangleIndex x = 0; x < horizontal; x++) { + indices[index + 0] = y * (horizontal + 1) + x; + indices[index + 1] = y * (horizontal + 1) + x + 1; + indices[index + 2] = (y + 1) * (horizontal + 1) + x; + indices[index + 3] = (y + 1) * (horizontal + 1) + x; + indices[index + 4] = y * (horizontal + 1) + x + 1; + indices[index + 5] = (y + 1) * (horizontal + 1) + x + 1; + index += 6; + } + } + + return GlGeometry::Descriptor(attribs, indices, geometryTransfom); +} + +GlGeometry::Descriptor BuildTesselatedCapsuleDescriptor( + const float radius, + const float height, + const TriangleIndex horizontal, + const TriangleIndex vertical) { + const int vertexCount = (horizontal + 1) * (vertical + 1); + const int indexCount = horizontal * vertical * 6; + const float latRads = MATH_FLOAT_PI * 0.5; + const float h = height * 0.5; + + VertexAttribs attribs; + attribs.position.resize(vertexCount * 3); + attribs.normal.resize(vertexCount * 3); + + std::vector indices; + indices.resize(indexCount * 3); + + int vertexIndexOffset = 0; + int triangleIndexOffset = 0; + + /// Cylinder + { + for (int y = 0; y <= vertical; ++y) { + const float yf = (float)y / (float)vertical; + for (int x = 0; x <= horizontal; ++x) { + const float xf = (float)x / (float)horizontal; + const int index = y * (horizontal + 1) + x + vertexIndexOffset; + attribs.position[index].x = cosf(MATH_FLOAT_PI * 2 * xf) * radius; + attribs.position[index].y = sinf(MATH_FLOAT_PI * 2 * xf) * radius; + attribs.position[index].z = -h + yf * 2 * h; + attribs.normal[index] = + Vector3f(attribs.position[index].x, attribs.position[index].y, 0).Normalized(); + } + } + + // If this is to be used to draw a linear format texture, like + // a surface texture, it is better for cache performance that + // the triangles be drawn to follow the side to side linear order. + int index = triangleIndexOffset; + for (TriangleIndex y = 0; y < vertical; y++) { + for (TriangleIndex x = 0; x < horizontal; x++) { + indices[index + 0] = y * (horizontal + 1) + x; + indices[index + 1] = y * (horizontal + 1) + x + 1; + indices[index + 2] = (y + 1) * (horizontal + 1) + x; + indices[index + 3] = (y + 1) * (horizontal + 1) + x; + indices[index + 4] = y * (horizontal + 1) + x + 1; + indices[index + 5] = (y + 1) * (horizontal + 1) + x + 1; + index += 6; + } + } + + vertexIndexOffset += vertexCount; + triangleIndexOffset += indexCount; + } + + /// Upper Dome + { + for (int y = 0; y <= vertical; y++) { + const float yf = (float)y / (float)vertical; + const float lat = MATH_FLOAT_PI - yf * latRads - 0.5f * MATH_FLOAT_PI; + const float cosLat = cosf(lat); + for (int x = 0; x <= horizontal; x++) { + const float xf = (float)x / (float)horizontal; + const float lon = (0.5f + xf) * MATH_FLOAT_PI * 2; + const int index = y * (horizontal + 1) + x + vertexIndexOffset; + attribs.position[index].x = radius * cosf(lon) * cosLat; + attribs.position[index].y = radius * sinf(lon) * cosLat; + attribs.position[index].z = h + (radius * sinf(lat)); + attribs.normal[index] = Vector3f( + attribs.position[index].x, + attribs.position[index].y, + attribs.position[index].z - h) + .Normalized(); + } + } + + int index = triangleIndexOffset; + for (TriangleIndex x = 0; x < horizontal; x++) { + for (TriangleIndex y = 0; y < vertical; y++) { + indices[index + 0] = vertexIndexOffset + y * (horizontal + 1) + x; + indices[index + 2] = vertexIndexOffset + y * (horizontal + 1) + x + 1; + indices[index + 1] = vertexIndexOffset + (y + 1) * (horizontal + 1) + x; + indices[index + 3] = vertexIndexOffset + (y + 1) * (horizontal + 1) + x; + indices[index + 5] = vertexIndexOffset + y * (horizontal + 1) + x + 1; + indices[index + 4] = vertexIndexOffset + (y + 1) * (horizontal + 1) + x + 1; + index += 6; + } + } + + vertexIndexOffset += vertexCount; + triangleIndexOffset += indexCount; + } + + /// Lower Dome + { + for (int y = 0; y <= vertical; y++) { + const float yf = (float)y / (float)vertical; + const float lat = MATH_FLOAT_PI - yf * latRads - 0.5f * MATH_FLOAT_PI; + const float cosLat = cosf(lat); + for (int x = 0; x <= horizontal; x++) { + const float xf = (float)x / (float)horizontal; + const float lon = (0.5f + xf) * MATH_FLOAT_PI * 2; + const int index = y * (horizontal + 1) + x + vertexIndexOffset; + attribs.position[index].x = radius * cosf(lon) * cosLat; + attribs.position[index].y = radius * sinf(lon) * cosLat; + attribs.position[index].z = -h + -(radius * sinf(lat)); + attribs.normal[index] = Vector3f( + attribs.position[index].x, + attribs.position[index].y, + attribs.position[index].z + h) + .Normalized(); + } + } + + int index = triangleIndexOffset; + for (TriangleIndex x = 0; x < horizontal; x++) { + for (TriangleIndex y = 0; y < vertical; y++) { + indices[index + 0] = vertexIndexOffset + y * (horizontal + 1) + x; + indices[index + 1] = vertexIndexOffset + y * (horizontal + 1) + x + 1; + indices[index + 2] = vertexIndexOffset + (y + 1) * (horizontal + 1) + x; + indices[index + 3] = vertexIndexOffset + (y + 1) * (horizontal + 1) + x; + indices[index + 4] = vertexIndexOffset + y * (horizontal + 1) + x + 1; + indices[index + 5] = vertexIndexOffset + (y + 1) * (horizontal + 1) + x + 1; + index += 6; + } + } + + vertexIndexOffset += vertexCount; + triangleIndexOffset += indexCount; + } + + return GlGeometry::Descriptor(attribs, indices, geometryTransfom); +} + +// To guarantee that the edge pixels are completely black, we need to +// have a band of solid 0. Just interpolating to 0 at the edges will +// leave some pixels with low color values. This stuck out as surprisingly +// visible smears from the distorted edges of the eye renderings in +// some cases. +GlGeometry::Descriptor BuildVignetteDescriptor(const float xFraction, const float yFraction) { + // Leave 25% of the vignette as solid black + const float posx[] = {-1.001f, + -1.0f + xFraction * 0.25f, + -1.0f + xFraction, + 1.0f - xFraction, + 1.0f - xFraction * 0.25f, + 1.001f}; + const float posy[] = {-1.001f, + -1.0f + yFraction * 0.25f, + -1.0f + yFraction, + 1.0f - yFraction, + 1.0f - yFraction * 0.25f, + 1.001f}; + + const int vertexCount = 6 * 6; + + VertexAttribs attribs; + attribs.position.resize(vertexCount); + attribs.uv0.resize(vertexCount); + attribs.color.resize(vertexCount); + + for (int y = 0; y < 6; y++) { + for (int x = 0; x < 6; x++) { + const int index = y * 6 + x; + attribs.position[index].x = posx[x]; + attribs.position[index].y = posy[y]; + attribs.position[index].z = 0.0f; + attribs.uv0[index].x = 0.0f; + attribs.uv0[index].y = 0.0f; + // the outer edges will have 0 color + const float c = (y <= 1 || y >= 4 || x <= 1 || x >= 4) ? 0.0f : 1.0f; + for (int i = 0; i < 3; i++) { + attribs.color[index][i] = c; + } + attribs.color[index][3] = 1.0f; // solid alpha + } + } + + std::vector indices; + indices.resize(24 * 6); + + int index = 0; + for (TriangleIndex x = 0; x < 5; x++) { + for (TriangleIndex y = 0; y < 5; y++) { + if (x == 2 && y == 2) { + continue; // the middle is open + } + // flip triangulation at corners + if (x == y) { + indices[index + 0] = y * 6 + x; + indices[index + 1] = (y + 1) * 6 + x + 1; + indices[index + 2] = (y + 1) * 6 + x; + indices[index + 3] = y * 6 + x; + indices[index + 4] = y * 6 + x + 1; + indices[index + 5] = (y + 1) * 6 + x + 1; + } else { + indices[index + 0] = y * 6 + x; + indices[index + 1] = y * 6 + x + 1; + indices[index + 2] = (y + 1) * 6 + x; + indices[index + 3] = (y + 1) * 6 + x; + indices[index + 4] = y * 6 + x + 1; + indices[index + 5] = (y + 1) * 6 + x + 1; + } + index += 6; + } + } + + return GlGeometry::Descriptor(attribs, indices, geometryTransfom); +} + +GlGeometry::Descriptor BuildDomeDescriptor(const float latRads, const float uScale, const float vScale) { + const int horizontal = 64; + const int vertical = 32; + const float radius = 100.0f; + + const int vertexCount = (horizontal + 1) * (vertical + 1); + + VertexAttribs attribs; + attribs.position.resize(vertexCount); + attribs.uv0.resize(vertexCount); + attribs.color.resize(vertexCount); + + for (int y = 0; y <= vertical; y++) { + const float yf = (float)y / (float)vertical; + const float lat = MATH_FLOAT_PI - yf * latRads - 0.5f * MATH_FLOAT_PI; + const float cosLat = cosf(lat); + for (int x = 0; x <= horizontal; x++) { + const float xf = (float)x / (float)horizontal; + const float lon = (0.5f + xf) * MATH_FLOAT_PI * 2; + const int index = y * (horizontal + 1) + x; + + if (x == horizontal) { + // Make sure that the wrap seam is EXACTLY the same + // xyz so there is no chance of pixel cracks. + attribs.position[index] = attribs.position[y * (horizontal + 1) + 0]; + } else { + attribs.position[index].x = radius * cosf(lon) * cosLat; + attribs.position[index].y = radius * sinf(lat); + attribs.position[index].z = radius * sinf(lon) * cosLat; + } + + attribs.uv0[index].x = xf * uScale; + attribs.uv0[index].y = (1.0f - yf) * vScale; + for (int i = 0; i < 4; i++) { + attribs.color[index][i] = 1.0f; + } + } + } + + std::vector indices; + indices.resize(horizontal * vertical * 6); + + int index = 0; + for (TriangleIndex x = 0; x < horizontal; x++) { + for (TriangleIndex y = 0; y < vertical; y++) { + indices[index + 0] = y * (horizontal + 1) + x; + indices[index + 1] = y * (horizontal + 1) + x + 1; + indices[index + 2] = (y + 1) * (horizontal + 1) + x; + indices[index + 3] = (y + 1) * (horizontal + 1) + x; + indices[index + 4] = y * (horizontal + 1) + x + 1; + indices[index + 5] = (y + 1) * (horizontal + 1) + x + 1; + index += 6; + } + } + + return GlGeometry::Descriptor(attribs, indices, geometryTransfom); +} + +// Build it with the equirect center down -Z +GlGeometry::Descriptor BuildGlobeDescriptor( + const float uScale /*= 1.0f*/, + const float vScale /*= 1.0f*/, + const float radius /*= 100.0f*/) { + // Make four rows at the polar caps in the place of one + // to diminish the degenerate triangle issue. + const int poleVertical = 3; + const int uniformVertical = 64; + const int horizontal = 128; + const int vertical = uniformVertical + poleVertical * 2; + + const int vertexCount = (horizontal + 1) * (vertical + 1); + + VertexAttribs attribs; + attribs.position.resize(vertexCount); + attribs.normal.resize(vertexCount); + attribs.uv0.resize(vertexCount); + attribs.color.resize(vertexCount); + + for (int y = 0; y <= vertical; y++) { + float yf; + if (y <= poleVertical) { + yf = (float)y / (poleVertical + 1) / uniformVertical; + } else if (y >= vertical - poleVertical) { + yf = + (float)(uniformVertical - 1 + ((float)(y - (vertical - poleVertical - 1)) / (poleVertical + 1))) / + uniformVertical; + } else { + yf = (float)(y - poleVertical) / uniformVertical; + } + const float lat = (yf - 0.5f) * MATH_FLOAT_PI; + const float cosLat = cosf(lat); + for (int x = 0; x <= horizontal; x++) { + const float xf = (float)x / (float)horizontal; + const float lon = (0.25f + xf) * MATH_FLOAT_PI * 2; + const int index = y * (horizontal + 1) + x; + + if (x == horizontal) { + // Make sure that the wrap seam is EXACTLY the same + // xyz so there is no chance of pixel cracks. + attribs.position[index] = attribs.position[y * (horizontal + 1) + 0]; + attribs.normal[index] = attribs.normal[y * (horizontal + 1) + 0]; + } else { + attribs.position[index].x = radius * cosf(lon) * cosLat; + attribs.position[index].y = radius * sinf(lat); + attribs.position[index].z = radius * sinf(lon) * cosLat; + attribs.normal[index] = attribs.position[index].Normalized(); + } + + // With a normal mapping, half the triangles degenerate at the poles, + // which causes seams between every triangle. It is better to make them + // a fan, and only get one seam. + if (y == 0 || y == vertical) { + attribs.uv0[index].x = 0.5f; + } else { + attribs.uv0[index].x = xf * uScale; + } + attribs.uv0[index].y = (1.0f - yf) * vScale; + for (int i = 0; i < 4; i++) { + attribs.color[index][i] = 1.0f; + } + } + } + + std::vector indices; + indices.resize(horizontal * vertical * 6); + + int index = 0; + for (TriangleIndex x = 0; x < horizontal; x++) { + for (TriangleIndex y = 0; y < vertical; y++) { + indices[index + 0] = y * (horizontal + 1) + x; + indices[index + 1] = y * (horizontal + 1) + x + 1; + indices[index + 2] = (y + 1) * (horizontal + 1) + x; + indices[index + 3] = (y + 1) * (horizontal + 1) + x; + indices[index + 4] = y * (horizontal + 1) + x + 1; + indices[index + 5] = (y + 1) * (horizontal + 1) + x + 1; + index += 6; + } + } + + return GlGeometry::Descriptor(attribs, indices, geometryTransfom); +} + +GlGeometry::Descriptor BuildSpherePatchDescriptor(const float fov) { + const int horizontal = 64; + const int vertical = 64; + const float radius = 100.0f; + + const int vertexCount = (horizontal + 1) * (vertical + 1); + + VertexAttribs attribs; + attribs.position.resize(vertexCount); + attribs.uv0.resize(vertexCount); + attribs.color.resize(vertexCount); + + for (int y = 0; y <= vertical; y++) { + const float yf = (float)y / (float)vertical; + const float lat = (yf - 0.5f) * fov; + const float cosLat = cosf(lat); + for (int x = 0; x <= horizontal; x++) { + const float xf = (float)x / (float)horizontal; + const float lon = (xf - 0.5f) * fov; + const int index = y * (horizontal + 1) + x; + + attribs.position[index].x = radius * cosf(lon) * cosLat; + attribs.position[index].y = radius * sinf(lat); + attribs.position[index].z = radius * sinf(lon) * cosLat; + + // center in the middle of the screen for roll rotation + attribs.uv0[index].x = xf - 0.5f; + attribs.uv0[index].y = (1.0f - yf) - 0.5f; + + for (int i = 0; i < 4; i++) { + attribs.color[index][i] = 1.0f; + } + } + } + + std::vector indices; + indices.resize(horizontal * vertical * 6); + + int index = 0; + for (TriangleIndex x = 0; x < horizontal; x++) { + for (TriangleIndex y = 0; y < vertical; y++) { + indices[index + 0] = y * (horizontal + 1) + x; + indices[index + 1] = y * (horizontal + 1) + x + 1; + indices[index + 2] = (y + 1) * (horizontal + 1) + x; + indices[index + 3] = (y + 1) * (horizontal + 1) + x; + indices[index + 4] = y * (horizontal + 1) + x + 1; + indices[index + 5] = (y + 1) * (horizontal + 1) + x + 1; + index += 6; + } + } + + return GlGeometry::Descriptor(attribs, indices, geometryTransfom); +} + +GlGeometry::Descriptor BuildUnitCubeLinesDescriptor() { + VertexAttribs attribs; + attribs.position.resize(8); + + for (int i = 0; i < 8; i++) { + attribs.position[i][0] = static_cast(i & 1); + attribs.position[i][1] = static_cast((i & 2) >> 1); + attribs.position[i][2] = static_cast((i & 4) >> 2); + } + + const TriangleIndex staticIndices[24] = {0, 1, 1, 3, 3, 2, 2, 0, 4, 5, 5, 7, + 7, 6, 6, 4, 0, 4, 1, 5, 3, 7, 2, 6}; + + std::vector indices; + indices.resize(24); + memcpy(&indices[0], staticIndices, 24 * sizeof(indices[0])); + + return GlGeometry::Descriptor(attribs, indices, geometryTransfom); +} + +// 2*side width cube, centered around the 0,0,0 point +GlGeometry::Descriptor BuildUnitCubeDescriptor(float side) { + VertexAttribs attribs; + + // positions + attribs.position = { + {-side, +side, -side}, {+side, +side, -side}, + {+side, +side, +side}, {-side, +side, +side}, // top + {-side, -side, -side}, {-side, -side, +side}, + {+side, -side, +side}, {+side, -side, -side}, // bottom + {+side, -side, -side}, {+side, +side, -side}, + {+side, +side, +side}, {+side, -side, +side}, // right + {-side, -side, -side}, {-side, -side, +side}, + {-side, +side, +side}, {-side, +side, -side}, // left + {-side, -side, +side}, {+side, -side, +side}, + {+side, +side, +side}, {-side, +side, +side}, // front + {-side, -side, -side}, {-side, +side, -side}, + {+side, +side, -side}, {+side, -side, -side}, // back + }; + attribs.normal = { + {0.0f, +1.0f, 0.0f}, {0.0f, +1.0f, 0.0f}, + {0.0f, +1.0f, 0.0f}, {0.0f, +1.0f, 0.0f}, // top + {0.0f, -1.0f, 0.0f}, {0.0f, -1.0f, 0.0f}, + {0.0f, -1.0f, 0.0f}, {0.0f, -1.0f, 0.0f}, // bottom + {+1.0f, 0.0f, 0.0f}, {+1.0f, 0.0f, 0.0f}, + {+1.0f, 0.0f, 0.0f}, {+1.0f, 0.0f, 0.0f}, // right + {-1.0f, 0.0f, 0.0f}, {-1.0f, 0.0f, 0.0f}, + {-1.0f, 0.0f, 0.0f}, {-1.0f, 0.0f, 0.0f}, // left + {0.0f, 0.0f, +1.0f}, {0.0f, 0.0f, +1.0f}, + {0.0f, 0.0f, +1.0f}, {0.0f, 0.0f, +1.0f}, // front + {0.0f, 0.0f, -1.0f}, {0.0f, 0.0f, -1.0f}, + {0.0f, 0.0f, -1.0f}, {0.0f, 0.0f, -1.0f}, // back + }; + + std::vector indices = { + 0, 2, 1, 2, 0, 3, // top + 4, 6, 5, 6, 4, 7, // bottom + 0 + 8, 1 + 8, 2 + 8, 2 + 8, 3 + 8, 0 + 8, // right + 4 + 8, 5 + 8, 6 + 8, 6 + 8, 7 + 8, 4 + 8, // left + 0 + 16, 1 + 16, 2 + 16, 2 + 16, 3 + 16, 0 + 16, // front + 4 + 16, 5 + 16, 6 + 16, 6 + 16, 7 + 16, 4 + 16, // back + }; + + return GlGeometry::Descriptor(attribs, indices, geometryTransfom); +} + +GlGeometry::Descriptor BuildAxisDescriptor(float sideLength, float sideRatio) { + VertexAttribs attribs; + // positions + attribs.position = { + {0.0f, 0.0f, 0.0f}, + {0.0f, 0.0f, 0.0f}, + {0.0f, 0.0f, 0.0f}, + {1.0f * sideLength, 0.0f, 0.0f}, + {0.0f, 1.0f * sideLength, 0.0f}, + {0.0f, 0.0f, 1.0f * sideLength}, + {0.0f, sideRatio * sideLength, 0.0f}, + {0.0f, 0.0f, sideRatio * sideLength}, + {sideRatio * sideLength, 0.0f, 0.0f}, + }; + + const Vector4f red{1.0f, 0.0f, 0.0f, 1.0f}; + const Vector4f green{0.0f, 1.0f, 0.0f, 1.0f}; + const Vector4f blue{0.0f, 0.0f, 1.0f, 1.0f}; + attribs.color = { + red, + green, + blue, + red, + green, + blue, + red, + green, + blue, + }; + + std::vector indices = { + 0, + 3, + 6, + 1, + 4, + 7, + 2, + 5, + 8, + 0, + 6, + 3, + 1, + 7, + 4, + 2, + 8, + 5, + }; + + return GlGeometry::Descriptor(attribs, indices, geometryTransfom); +} + +// pie-shaped wedge +GlGeometry::Descriptor BuildWedgeDescriptor( + const float radius, + const float height, + const float angleStart, // radians + const float angleStop, // radians + const OVR::Vector4f& color, + const TriangleIndex divisions, + const bool sides) { + + // 'divisions' represent the number of "pies" that compose (tesselate) the wedge + assert (divisions > 0); + assert (angleStop > angleStart); + + // each division is composed of 4 triangles + std::vector indices; + indices.resize(divisions * 3 * 4 + (sides ? 3 * 4 : 0)); + + // each division requires 1 (center) vertex + 2 outer vertices [face/cylinder] (on each side) + const int vertexCount = (1 + (divisions + 1) * 2) * 2; + const int vertexCountSides = sides ? 4 + 4 : 0; + ALOG ("BuildWedgeDescriptor() vertexCount=%d", vertexCount + vertexCountSides); + + VertexAttribs attribs; + attribs.position.resize(vertexCount + vertexCountSides); + attribs.normal.resize(vertexCount + vertexCountSides); + attribs.color.resize(vertexCount + vertexCountSides); + + // first two points are at the center of the disc, on front/back side + attribs.position[0].x = attribs.position[1].x = 0; + attribs.position[0].y = attribs.position[1].y = 0; + attribs.position[0].z = 0 + height / 2; + attribs.position[1].z = 0 - height / 2; + attribs.normal[0] = OVR::Vector3f(0, 0, +1).Normalized(); // front-facing + attribs.normal[1] = OVR::Vector3f(0, 0, -1).Normalized(); // rear-facing + + for (int channel = 0; channel < 4; ++channel) { + attribs.color[0][channel] = color[channel]; + attribs.color[1][channel] = color[channel]; + } + + for (int index = 0; index <= divisions; ++index) { + const int edge = 2 + index * 4; + const float angle = angleStart + (((angleStop - angleStart) / divisions) * index); + // build wedge in the X/Y plane + attribs.position[edge + 0].x = attribs.position[edge + 1].x = + attribs.position[edge + 2].x = attribs.position[edge + 3].x = + cosf(angle) * radius; + attribs.position[edge + 0].y = attribs.position[edge + 1].y = + attribs.position[edge + 2].y = attribs.position[edge + 3].y = + sinf(angle) * radius; + // "height" of wedge is represented by depth + attribs.position[edge + 0].z = attribs.position[edge + 1].z = 0 + height / 2; + attribs.position[edge + 2].z = attribs.position[edge + 3].z = 0 - height / 2; + + // front-facing (use same normal as front-center vertex) + attribs.normal[edge + 0] = attribs.normal[0]; + // outer (rounded) side-facing + attribs.normal[edge + 1] = attribs.normal[edge + 2] = + OVR::Vector3f(attribs.position[edge + 1].x, attribs.position[edge + 1].y, 0).Normalized(); + // rear-facing (use same normal as back-center vertex) + attribs.normal[edge + 3] = attribs.normal[1]; + + for (int channel = 0; channel < 4; ++channel) { + attribs.color[edge + 0][channel] = color[channel]; + attribs.color[edge + 1][channel] = color[channel]; + attribs.color[edge + 2][channel] = color[channel]; + attribs.color[edge + 3][channel] = color[channel]; + } + + // left or right sides of the wedge (if 'sides' flag is enabled) + if (sides && (index == 0 || index == divisions)) { + const int edgeSides = vertexCount + (index == divisions ? 4 : 0); + attribs.position[edgeSides + 0].x = attribs.position[edgeSides + 3].x = 0.0f; + attribs.position[edgeSides + 1].x = attribs.position[edgeSides + 2].x = attribs.position[edge + 0].x; + + attribs.position[edgeSides + 0].y = attribs.position[edgeSides + 3].y = 0.0f; + attribs.position[edgeSides + 1].y = attribs.position[edgeSides + 2].y = attribs.position[edge + 0].y; + + attribs.position[edgeSides + 0].z = attribs.position[edgeSides + 1].z = attribs.position[edge + 0].z; + attribs.position[edgeSides + 2].z = attribs.position[edgeSides + 3].z = attribs.position[edge + 2].z; + + OVR::Vector3f normal = OVR::Vector3f( + attribs.position[edgeSides + 3].x - attribs.position[edgeSides + 2].x, + attribs.position[edgeSides + 3].y - attribs.position[edgeSides + 2].y, + attribs.position[edgeSides + 3].z - attribs.position[edgeSides + 2].z).Cross( + OVR::Vector3f( + attribs.position[edgeSides + 2].x - attribs.position[edgeSides + 1].x, + attribs.position[edgeSides + 2].y - attribs.position[edgeSides + 1].y, + attribs.position[edgeSides + 2].z - attribs.position[edgeSides + 1].z) + ).Normalized(); + if (index == divisions) { + normal.x = 0 - normal.x; + normal.y = 0 - normal.y; + normal.z = 0 - normal.z; + } + + attribs.normal[edgeSides + 0] = attribs.normal[edgeSides + 1] = + attribs.normal[edgeSides + 2] = attribs.normal[edgeSides + 3] = normal; + + for (int channel = 0; channel < 4; ++channel) { + attribs.color[edgeSides + 0][channel] = color[channel]; + attribs.color[edgeSides + 1][channel] = color[channel]; + attribs.color[edgeSides + 2][channel] = color[channel]; + attribs.color[edgeSides + 3][channel] = color[channel]; + } + + // quad (2 triangles) for the side + const int indexSides = (divisions * 3 * 4) + (index == divisions ? 6 : 0); + if (index == divisions) { + indices[indexSides + 0] = edgeSides + 0; + indices[indexSides + 1] = edgeSides + 1; + indices[indexSides + 2] = edgeSides + 2; + + indices[indexSides + 3] = edgeSides + 0; + indices[indexSides + 4] = edgeSides + 2; + indices[indexSides + 5] = edgeSides + 3; + } else { + indices[indexSides + 0] = edgeSides + 0; + indices[indexSides + 1] = edgeSides + 2; + indices[indexSides + 2] = edgeSides + 1; + + indices[indexSides + 3] = edgeSides + 0; + indices[indexSides + 4] = edgeSides + 3; + indices[indexSides + 5] = edgeSides + 2; + } + } + + if (index < divisions) { + // front pie + indices[(index * 12) + 0] = 0; + indices[(index * 12) + 1] = edge + 0 + 0; + indices[(index * 12) + 2] = edge + 0 + 4; + + // height quad + indices[(index * 12) + 3] = edge + 1 + 4; + indices[(index * 12) + 4] = edge + 1 + 0; + indices[(index * 12) + 5] = edge + 2 + 0; + + indices[(index * 12) + 6] = edge + 2 + 0; + indices[(index * 12) + 7] = edge + 2 + 4; + indices[(index * 12) + 8] = edge + 1 + 4; + + // back pie + indices[(index * 12) + 9] = edge + 3 + 4; + indices[(index * 12) + 10] = edge + 3 + 0; + indices[(index * 12) + 11] = 1; + } + } + + return GlGeometry::Descriptor(attribs, indices, OVR::Matrix4f()); +} + +GlGeometry::Descriptor BuildDiscDescriptor( + const float radius, + const float height, + const OVR::Vector4f& color, + const TriangleIndex divisions) { + return BuildWedgeDescriptor(radius, height, 0.0f, MATH_FLOAT_PI * 2, color, divisions, false); +} + +} // namespace OVRFW diff --git a/Samples/SampleXrFramework/Src/Render/GlGeometry.h b/Samples/SampleXrFramework/Src/Render/GlGeometry.h new file mode 100755 index 0000000..1cac354 --- /dev/null +++ b/Samples/SampleXrFramework/Src/Render/GlGeometry.h @@ -0,0 +1,287 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/************************************************************************************ + +Filename : GlGeometry.h +Content : OpenGL geometry setup. +Created : October 8, 2013 +Authors : John Carmack + +************************************************************************************/ + +#pragma once + +#include +#include "OVR_Math.h" + +#include "GlProgram.h" + +namespace OVRFW { + +struct VertexAttribs { + std::vector position; + std::vector normal; + std::vector tangent; + std::vector binormal; + std::vector color; + std::vector uv0; + std::vector uv1; + std::vector jointIndices; + std::vector jointWeights; +}; + +typedef uint16_t TriangleIndex; + +class GlGeometry { + public: + GlGeometry() + : vertexBuffer(0), + indexBuffer(0), + vertexArrayObject(0), + primitiveType(0x0004 /* GL_TRIANGLES */), + vertexCount(0), + indexCount(0), + localBounds(OVR::Bounds3f::Init) {} + + GlGeometry(const VertexAttribs& attribs, const std::vector& indices) + : vertexBuffer(0), + indexBuffer(0), + vertexArrayObject(0), + primitiveType(0x0004 /* GL_TRIANGLES */), + vertexCount(0), + indexCount(0), + localBounds(OVR::Bounds3f::Init) { + Create(attribs, indices); + } + + // Create the VAO and vertex and index buffers from arrays of data. + void Create(const VertexAttribs& attribs, const std::vector& indices); + void Update(const VertexAttribs& attribs, const bool updateBounds = true); + + // Free the buffers and VAO, assuming that they are strictly for this geometry. + // We could save some overhead by packing an entire model into a single buffer, but + // it would add more coupling to the structures. + // This is not in the destructor to allow objects of this class to be passed by value. + void Free(); + + public: + static constexpr int32_t MAX_GEOMETRY_VERTICES = 1 << (sizeof(TriangleIndex) * 8); + static constexpr int32_t MAX_GEOMETRY_INDICES = 1024 * 1024 * 3; + + static constexpr inline int32_t GetMaxGeometryVertices() { + return MAX_GEOMETRY_VERTICES; + } + static constexpr inline int32_t GetMaxGeometryIndices() { + return MAX_GEOMETRY_INDICES; + } + + static unsigned IndexType; // GL_UNSIGNED_SHORT, GL_UNSIGNED_INT, etc. + + class TransformScope { + public: + TransformScope(const OVR::Matrix4f m, bool enableTransfom = true); + ~TransformScope(); + + private: + OVR::Matrix4f previousTransform; + bool wasEnabled; + }; + + struct Descriptor { + Descriptor( + const VertexAttribs& a, + const std::vector& i, + const OVR::Matrix4f& t) + : attribs(a), indices(i), transform(t) {} + Descriptor() : transform(OVR::Matrix4f::Identity()) {} + + VertexAttribs attribs; + std::vector indices; + OVR::Matrix4f transform; + }; + + public: + unsigned vertexBuffer; + unsigned indexBuffer; + unsigned vertexArrayObject; + unsigned primitiveType; // GL_TRIANGLES / GL_LINES / GL_POINTS / etc + int vertexCount; + int indexCount; + OVR::Bounds3f localBounds; +}; + +// Build it in a -1 to 1 range, which will be scaled to the appropriate +// aspect ratio for each usage. +// +// A horizontal and vertical value of 1 will give a single quad. +// +// Texcoords range from 0 to 1. +// +// Color is 1, fades alpha to 0 along the outer edge. +GlGeometry::Descriptor BuildTesselatedQuadDescriptor( + const TriangleIndex horizontal, + const TriangleIndex vertical, + bool twoSided = false); + +inline GlGeometry BuildTesselatedQuad( + const TriangleIndex horizontal, + const TriangleIndex vertical, + bool twoSided = false) { + const GlGeometry::Descriptor d = BuildTesselatedQuadDescriptor(horizontal, vertical, twoSided); + return GlGeometry(d.attribs, d.indices); +} + +// 8 quads making a thin border inside the -1 tp 1 square. +// The fractions are the total fraction that will be faded, +// half on one side, half on the other. +GlGeometry::Descriptor BuildVignetteDescriptor(const float xFraction, const float yFraction); + +inline GlGeometry BuildVignette(const float xFraction, const float yFraction) { + const GlGeometry::Descriptor d = BuildVignetteDescriptor(xFraction, yFraction); + return GlGeometry(d.attribs, d.indices); +} + +// Build it in a -1 to 1 range, which will be scaled to the appropriate +// aspect ratio for each usage. +// Fades alpha to 0 along the outer edge. +GlGeometry::Descriptor BuildTesselatedCylinderDescriptor( + const float radius, + const float height, + const TriangleIndex horizontal, + const TriangleIndex vertical, + const float uScale, + const float vScale); +inline GlGeometry BuildTesselatedCylinder( + const float radius, + const float height, + const TriangleIndex horizontal, + const TriangleIndex vertical, + const float uScale, + const float vScale) { + const GlGeometry::Descriptor d = + BuildTesselatedCylinderDescriptor(radius, height, horizontal, vertical, uScale, vScale); + return GlGeometry(d.attribs, d.indices); +} + +// Build it in a -1 to 1 range, which will be scaled to the appropriate +// aspect ratio for each usage. +// Fades alpha to 0 along the outer edge. +GlGeometry::Descriptor BuildTesselatedConeDescriptor( + const float radius, + const float height, + const TriangleIndex horizontal, + const TriangleIndex vertical, + const float uScale, + const float vScale); +inline GlGeometry BuildTesselatedCone( + const float radius, + const float height, + const TriangleIndex horizontal, + const TriangleIndex vertical, + const float uScale, + const float vScale) { + const GlGeometry::Descriptor d = + BuildTesselatedConeDescriptor(radius, height, horizontal, vertical, uScale, vScale); + return GlGeometry(d.attribs, d.indices); +} + +GlGeometry::Descriptor BuildTesselatedCapsuleDescriptor( + const float radius, + const float height, + const TriangleIndex horizontal, + const TriangleIndex vertical); +inline GlGeometry BuildTesselatedCapsule( + const float radius, + const float height, + const TriangleIndex horizontal, + const TriangleIndex vertical) { + const GlGeometry::Descriptor d = + BuildTesselatedCapsuleDescriptor(radius, height, horizontal, vertical); + return GlGeometry(d.attribs, d.indices); +} + +GlGeometry::Descriptor +BuildDomeDescriptor(const float latRads, const float uScale = 1.0f, const float vScale = 1.0f); +inline GlGeometry +BuildDome(const float latRads, const float uScale = 1.0f, const float vScale = 1.0f) { + const GlGeometry::Descriptor d = BuildDomeDescriptor(latRads, uScale, vScale); + return GlGeometry(d.attribs, d.indices); +} + +GlGeometry::Descriptor BuildGlobeDescriptor( + const float uScale = 1.0f, + const float vScale = 1.0f, + const float radius = 100.0f); +inline GlGeometry +BuildGlobe(const float uScale = 1.0f, const float vScale = 1.0f, const float radius = 100.0f) { + const GlGeometry::Descriptor d = BuildGlobeDescriptor(uScale, vScale, radius); + return GlGeometry(d.attribs, d.indices); +} + +// Make a square patch on a sphere that can rotate with the viewer +// so it always covers the screen. +GlGeometry::Descriptor BuildSpherePatchDescriptor(const float fov); +inline GlGeometry BuildSpherePatch(const float fov) { + const GlGeometry::Descriptor d = BuildSpherePatchDescriptor(fov); + return GlGeometry(d.attribs, d.indices); +} + +// 12 edges of a 0 to 1 unit cube. +GlGeometry::Descriptor BuildUnitCubeLinesDescriptor(); +inline GlGeometry BuildUnitCubeLines() { + const GlGeometry::Descriptor d = BuildUnitCubeLinesDescriptor(); + GlGeometry g(d.attribs, d.indices); + g.primitiveType = GL_LINES; + return g; +} + +// 1.0 width cube, centered around the 0,0,0 point +GlGeometry::Descriptor BuildUnitCubeDescriptor(float side = 0.5f); +inline GlGeometry BuildUnitCube(float side = 0.5f) { + const GlGeometry::Descriptor d = BuildUnitCubeDescriptor(side); + return GlGeometry(d.attribs, d.indices); +} + +GlGeometry::Descriptor BuildAxisDescriptor(float sideLength = 0.1f, float sideRatio = 0.25f); +inline GlGeometry BuildAxis(float sideLength = 0.1f, float sideRatio = 0.25f) { + const GlGeometry::Descriptor d = BuildAxisDescriptor(sideLength, sideRatio); + return GlGeometry(d.attribs, d.indices); +} + +GlGeometry::Descriptor BuildWedgeDescriptor( + const float radius, + const float height, + const float angleStart, // radians + const float angleStop, // radians + const OVR::Vector4f& color, + const TriangleIndex divisions, + const bool sides = true); // add sides to wedges (not needed for discs) + +inline GlGeometry BuildWedge( + const float radius, + const float height, + const float angleStart, + const float angleStop, + const OVR::Vector4f& color, + const TriangleIndex divisions) { + const GlGeometry::Descriptor d = + BuildWedgeDescriptor(radius, height, angleStart, angleStop, color, divisions); + return GlGeometry(d.attribs, d.indices); +} + +GlGeometry::Descriptor BuildDiscDescriptor( + const float radius, + const float height, + const OVR::Vector4f& color, + const TriangleIndex divisions); + +inline GlGeometry BuildDisc( + const float radius, + const float height, + const OVR::Vector4f& color, + const TriangleIndex divisions) { + const GlGeometry::Descriptor d = BuildDiscDescriptor(radius, height, color, divisions); + return GlGeometry(d.attribs, d.indices); +} + +} // namespace OVRFW diff --git a/Samples/SampleXrFramework/Src/Render/GlProgram.cpp b/Samples/SampleXrFramework/Src/Render/GlProgram.cpp new file mode 100755 index 0000000..db043fd --- /dev/null +++ b/Samples/SampleXrFramework/Src/Render/GlProgram.cpp @@ -0,0 +1,379 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/************************************************************************************ + +Filename : GlProgram.cpp +Content : Shader program compilation. +Created : October 11, 2013 +Authors : John Carmack + +*************************************************************************************/ + +#include "GlProgram.h" + +#include +#include +#include + +#include "Misc/Log.h" + +#include "OVR_Std.h" + +#include + +namespace OVRFW { +static bool UseMultiview = false; + +GlProgram::MultiViewScope::MultiViewScope(bool enableMultView) { + wasEnabled = UseMultiview; + GlProgram::SetUseMultiview(enableMultView); +} +GlProgram::MultiViewScope::~MultiViewScope() { + GlProgram::SetUseMultiview(wasEnabled); +} + +// All GlPrograms implicitly get the VertexHeader +static const char* VertexHeader = + R"glsl( +#ifndef DISABLE_MULTIVIEW + #define DISABLE_MULTIVIEW 0 +#endif +#define NUM_VIEWS 2 +#define attribute in +#define varying out +#if defined( GL_OVR_multiview2 ) && ! DISABLE_MULTIVIEW + #extension GL_OVR_multiview2 : require + layout(num_views=NUM_VIEWS) in; + #define VIEW_ID gl_ViewID_OVR +#else + uniform lowp int ViewID; + #define VIEW_ID ViewID +#endif + +uniform highp mat4 ModelMatrix; + +// Use a ubo in v300 path to workaround corruption issue on Adreno 420+v300 +// when uniform array of matrices used. +uniform SceneMatrices +{ + highp mat4 ViewMatrix[NUM_VIEWS]; + highp mat4 ProjectionMatrix[NUM_VIEWS]; +} sm; +// Use a function macro for TransformVertex to workaround an issue on exynos8890+Android-M driver: +// gl_ViewID_OVR outside of the vertex main block causes VIEW_ID to be 0 for every view. +// NOTE: Driver fix has landed with Android-N. +//highp vec4 TransformVertex( highp vec4 oPos ) +//{ +// highp vec4 hPos = sm.ProjectionMatrix[VIEW_ID] * ( sm.ViewMatrix[VIEW_ID] * ( ModelMatrix * oPos ) ); +// return hPos; +//} +#define TransformVertex(localPos) (sm.ProjectionMatrix[VIEW_ID] * ( sm.ViewMatrix[VIEW_ID] * ( ModelMatrix * localPos ))) +)glsl"; + +// All GlPrograms implicitly get the FragmentHeader +static const char* FragmentHeader = + R"glsl( + #define varying in + #define gl_FragColor fragColor + out mediump vec4 fragColor; + #define texture2D texture + #define textureCube texture +)glsl"; + +static const char* FindShaderVersionEnd(const char* src) { + if (src == nullptr || OVR::OVR_strncmp(src, "#version ", 9) != 0) { + return src; + } + while (*src != 0 && *src != '\n') { + src++; + } + if (*src == '\n') { + src++; + } + return src; +} + +static GLuint +CompileShader(GLenum shaderType, const char* directives, const char* src, GLint programVersion) { + assert(programVersion >= 300); + + const char* postVersion = FindShaderVersionEnd(src); + if (postVersion != src) { + ALOGW("GlProgram: #version in source is not supported. Specify at program build time."); + } + + std::string srcString; + srcString = std::string("#version ") + std::to_string(programVersion) + std::string(" es\n"); + + if (directives != NULL) { + srcString.append(directives); + srcString.append("\n"); + } + + // TODO: If a c string isn't passed here, the previous contents of srcString (ie version info) + // are corrupted. Determine why. + srcString += std::string("#define DISABLE_MULTIVIEW ") + std::to_string(UseMultiview ? 0 : 1) + + std::string("\n"); + + if (shaderType == GL_VERTEX_SHADER) { + srcString.append(VertexHeader); + } else if (shaderType == GL_FRAGMENT_SHADER) { + srcString.append(FragmentHeader); + } + + srcString.append(postVersion); + + src = srcString.c_str(); + + GLuint shader = glCreateShader(shaderType); + + const int numSources = 1; + const char* srcs[1]; + srcs[0] = src; + + glShaderSource(shader, numSources, srcs, 0); + glCompileShader(shader); + + GLint r; + glGetShaderiv(shader, GL_COMPILE_STATUS, &r); + if (r == GL_FALSE) { + ALOGW( + "Compiling %s shader: ****** failed ******\n", + shaderType == GL_VERTEX_SHADER ? "vertex" : "fragment"); + GLchar msg[1024]; + const char* sp = src; + int charCount = 0; + int line = 0; + do { + if (*sp != '\n') { + msg[charCount++] = *sp; + msg[charCount] = 0; + } + if (*sp == 0 || *sp == '\n' || charCount == 1023) { + charCount = 0; + line++; + ALOGW("%03d %s", line, msg); + msg[0] = 0; + if (*sp != '\n') { + line--; + } + } + sp++; + } while (*sp != 0); + if (charCount != 0) { + line++; + ALOGW("%03d %s", line, msg); + } + glGetShaderInfoLog(shader, sizeof(msg), 0, msg); + ALOGW("%s\n", msg); + glDeleteShader(shader); + return 0; + } + return shader; +} + +GlProgram GlProgram::Build( + const char* vertexSrc, + const char* fragmentSrc, + const ovrProgramParm* parms, + const int numParms, + const int requestedProgramVersion, + bool abortOnError) { + return Build( + NULL, vertexSrc, NULL, fragmentSrc, parms, numParms, requestedProgramVersion, abortOnError); +} + +GlProgram GlProgram::Build( + const char* vertexDirectives, + const char* vertexSrc, + const char* fragmentDirectives, + const char* fragmentSrc, + const ovrProgramParm* parms, + const int numParms, + const int requestedProgramVersion, + bool abortOnError) { + GlProgram p; + + //-------------------------- + // Compile and Create the Program + //-------------------------- + + int programVersion = requestedProgramVersion; + if (programVersion < GLSL_PROGRAM_VERSION) { + ALOGW( + "GlProgram: Program GLSL version requested %d, but does not meet required minimum %d", + requestedProgramVersion, + GLSL_PROGRAM_VERSION); + } + + p.VertexShader = CompileShader(GL_VERTEX_SHADER, vertexDirectives, vertexSrc, programVersion); + if (p.VertexShader == 0) { + Free(p); + ALOG( + "GlProgram: CompileShader GL_VERTEX_SHADER program failed: \n```%s\n```\n\n", + vertexSrc); + if (abortOnError) { + ALOGE_FAIL("Failed to compile vertex shader"); + } + return GlProgram(); + } + + p.FragmentShader = + CompileShader(GL_FRAGMENT_SHADER, fragmentDirectives, fragmentSrc, programVersion); + if (p.FragmentShader == 0) { + Free(p); + ALOG( + "GlProgram: CompileShader GL_FRAGMENT_SHADER program failed: \n```%s\n```\n\n", + fragmentSrc); + if (abortOnError) { + ALOGE_FAIL("Failed to compile fragment shader"); + } + return GlProgram(); + } + + p.Program = glCreateProgram(); + glAttachShader(p.Program, p.VertexShader); + glAttachShader(p.Program, p.FragmentShader); + + //-------------------------- + // Set attributes before linking + //-------------------------- + + glBindAttribLocation(p.Program, VERTEX_ATTRIBUTE_LOCATION_POSITION, "Position"); + glBindAttribLocation(p.Program, VERTEX_ATTRIBUTE_LOCATION_NORMAL, "Normal"); + glBindAttribLocation(p.Program, VERTEX_ATTRIBUTE_LOCATION_TANGENT, "Tangent"); + glBindAttribLocation(p.Program, VERTEX_ATTRIBUTE_LOCATION_BINORMAL, "Binormal"); + glBindAttribLocation(p.Program, VERTEX_ATTRIBUTE_LOCATION_COLOR, "VertexColor"); + glBindAttribLocation(p.Program, VERTEX_ATTRIBUTE_LOCATION_UV0, "TexCoord"); + glBindAttribLocation(p.Program, VERTEX_ATTRIBUTE_LOCATION_UV1, "TexCoord1"); + glBindAttribLocation(p.Program, VERTEX_ATTRIBUTE_LOCATION_JOINT_INDICES, "JointIndices"); + glBindAttribLocation(p.Program, VERTEX_ATTRIBUTE_LOCATION_JOINT_WEIGHTS, "JointWeights"); + glBindAttribLocation(p.Program, VERTEX_ATTRIBUTE_LOCATION_FONT_PARMS, "FontParms"); + + //-------------------------- + // Link Program + //-------------------------- + + glLinkProgram(p.Program); + + GLint linkStatus; + glGetProgramiv(p.Program, GL_LINK_STATUS, &linkStatus); + if (linkStatus == GL_FALSE) { + GLchar msg[1024]; + glGetProgramInfoLog(p.Program, sizeof(msg), 0, msg); + Free(p); + ALOG("GlProgram: Linking program failed: %s\n", msg); + if (abortOnError) { + ALOGE_FAIL("Failed to link program"); + } + return GlProgram(); + } + + //-------------------------- + // Determine Uniform Parm Location and Binding. + //-------------------------- + + p.numTextureBindings = 0; + p.numUniformBufferBindings = 0; + + // Globally-defined system level uniforms. + { + p.ViewID.Type = ovrProgramParmType::INT; + p.ViewID.Location = glGetUniformLocation(p.Program, "ViewID"); + p.ViewID.Binding = p.ViewID.Location; + + p.SceneMatrices.Type = ovrProgramParmType::BUFFER_UNIFORM; + p.SceneMatrices.Location = glGetUniformBlockIndex(p.Program, "SceneMatrices"); + if (p.SceneMatrices.Location >= 0) // this won't be present for v100 shaders. + { + p.SceneMatrices.Binding = p.numUniformBufferBindings++; + glUniformBlockBinding(p.Program, p.SceneMatrices.Location, p.SceneMatrices.Binding); + } + + p.ModelMatrix.Type = ovrProgramParmType::FLOAT_MATRIX4; + p.ModelMatrix.Location = glGetUniformLocation(p.Program, "ModelMatrix"); + p.ModelMatrix.Binding = p.ModelMatrix.Location; + } + + glUseProgram(p.Program); + + for (int i = 0; i < numParms; ++i) { + OVR_ASSERT(parms[i].Type != ovrProgramParmType::MAX); + + /// Trace the names of the uniform for this progam +#if OVR_USE_UNIFORM_NAMES + p.UniformNames[i] = std::string(parms[i].Name); + ALOG( + " GlProgram[ %d ]: Uniforms[%d] = `%s` %s", + p.Program, + i, + parms[i].Name, + parms[i].Type == ovrProgramParmType::TEXTURE_SAMPLED ? "Texture" : ""); +#endif /// OVR_USE_UNIFORM_NAMES + + p.Uniforms[i].Type = parms[i].Type; + if (parms[i].Type == ovrProgramParmType::TEXTURE_SAMPLED) { + p.Uniforms[i].Location = + static_cast(glGetUniformLocation(p.Program, parms[i].Name)); + p.Uniforms[i].Binding = p.numTextureBindings++; + glUniform1i(p.Uniforms[i].Location, p.Uniforms[i].Binding); + } else if (parms[i].Type == ovrProgramParmType::BUFFER_UNIFORM) { + p.Uniforms[i].Location = glGetUniformBlockIndex(p.Program, parms[i].Name); + p.Uniforms[i].Binding = p.numUniformBufferBindings++; + glUniformBlockBinding(p.Program, p.Uniforms[i].Location, p.Uniforms[i].Binding); + } else { + p.Uniforms[i].Location = + static_cast(glGetUniformLocation(p.Program, parms[i].Name)); + p.Uniforms[i].Binding = p.Uniforms[i].Location; + } + if (false == (p.Uniforms[i].Location >= 0 && p.Uniforms[i].Binding >= 0)) { +#if OVR_USE_UNIFORM_NAMES + p.UniformNames[i] = std::string(parms[i].Name); + ALOGW( + " GlProgram[ %d ]: Uniforms[%d] = `%s` %s NOT BOUND / USED", + p.Program, + i, + parms[i].Name, + parms[i].Type == ovrProgramParmType::TEXTURE_SAMPLED ? "Texture" : ""); +#else + ALOGW(" GlProgram[ %d ]: Uniforms[%d] NOT BOUND / USED", p.Program, i); +#endif /// OVR_USE_UNIFORM_NAMES + } + } + + glUseProgram(0); + + return p; +} + +void GlProgram::Free(GlProgram& prog) { + glUseProgram(0); + if (prog.Program != 0) { + glDeleteProgram(prog.Program); + } + if (prog.VertexShader != 0) { + glDeleteShader(prog.VertexShader); + } + if (prog.FragmentShader != 0) { + glDeleteShader(prog.FragmentShader); + } + prog.Program = 0; + prog.VertexShader = 0; + prog.FragmentShader = 0; +} + +void GlProgram::SetUseMultiview(const bool useMultiview_) { + UseMultiview = useMultiview_; +} + +void ovrGraphicsCommand::BindUniformTextures() { + /// Late bind Textures to the right texture objects + for (int i = 0; i < ovrUniform::MAX_UNIFORMS; ++i) { + const ovrUniform& uniform = Program.Uniforms[i]; + if (uniform.Type == ovrProgramParmType::TEXTURE_SAMPLED) { + UniformData[i].Data = &Textures[uniform.Binding]; + } + } +} + +} // namespace OVRFW diff --git a/Samples/SampleXrFramework/Src/Render/GlProgram.h b/Samples/SampleXrFramework/Src/Render/GlProgram.h new file mode 100755 index 0000000..cb9ede7 --- /dev/null +++ b/Samples/SampleXrFramework/Src/Render/GlProgram.h @@ -0,0 +1,188 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/************************************************************************************ + +Filename : GlProgram.h +Content : Shader program compilation. +Created : October 11, 2013 +Authors : John Carmack + +*************************************************************************************/ + +#pragma once + +#include "Egl.h" + +#include "GpuState.h" +#include "GlTexture.h" +#include "GlBuffer.h" + +#include "OVR_Math.h" + +#include + +namespace OVRFW { + +class GlTexture; +class GlBuffer; + +#ifdef OVR_BUILD_DEBUG +#define OVR_USE_UNIFORM_NAMES 1 +#else +#define OVR_USE_UNIFORM_NAMES 0 +#endif + +// STRINGIZE is used so program text strings can include lines like: +// "uniform highp mat4 Joints["MAX_JOINTS_STRING"];\n" + +#define STRINGIZE(x) #x +#define STRINGIZE_VALUE(x) STRINGIZE(x) + +#define MAX_JOINTS 64 +#define MAX_JOINTS_STRING STRINGIZE_VALUE(MAX_JOINTS) + +// No attempt is made to support sharing shaders between programs, +// it isn't worth avoiding the duplication. + +// Shader uniforms Texture0 - Texture7 are bound to texture units 0 - 7 + +enum VertexAttributeLocation { + VERTEX_ATTRIBUTE_LOCATION_POSITION = 0, + VERTEX_ATTRIBUTE_LOCATION_NORMAL = 1, + VERTEX_ATTRIBUTE_LOCATION_TANGENT = 2, + VERTEX_ATTRIBUTE_LOCATION_BINORMAL = 3, + VERTEX_ATTRIBUTE_LOCATION_COLOR = 4, + VERTEX_ATTRIBUTE_LOCATION_UV0 = 5, + VERTEX_ATTRIBUTE_LOCATION_UV1 = 6, + VERTEX_ATTRIBUTE_LOCATION_JOINT_INDICES = 7, + VERTEX_ATTRIBUTE_LOCATION_JOINT_WEIGHTS = 8, + VERTEX_ATTRIBUTE_LOCATION_FONT_PARMS = 9 +}; + +enum class ovrProgramParmType : char { + INT, // int + INT_VECTOR2, // Vector2i + INT_VECTOR3, // Vector3i + INT_VECTOR4, // Vector4i + FLOAT, // float + FLOAT_VECTOR2, // Vector2f + FLOAT_VECTOR3, // Vector3f + FLOAT_VECTOR4, // Vector4f + FLOAT_MATRIX4, // Matrix4f (always inverted for GL) + TEXTURE_SAMPLED, // GlTexture + BUFFER_UNIFORM, // read-only uniform buffer (GLSL: uniform) + MAX +}; + +struct ovrProgramParm { + const char* Name; + ovrProgramParmType Type; +}; + +struct ovrUniform { + // can be made as high as 16 + static const int MAX_UNIFORMS = 16; + + ovrUniform() : Location(-1), Binding(-1), Type(ovrProgramParmType::MAX) {} + + int Location; // the index of the uniform in the render program + int Binding; // the resource binding (eg. texture image unit or uniform block) + ovrProgramParmType Type; // the type of the data +}; + +struct ovrUniformData { + ovrUniformData() : Data(nullptr), Count(1) {} + + void* Data; // pointer to data + int Count; // number of items of ovrProgramParmType in the Data buffer +}; + +//============================================================== +// GlProgram +// Freely copyable. In general, the compilation unit that calls Build() should +// be the compilation unit that calls Free(). Other copies of the object should +// never Free(). +struct GlProgram { + GlProgram() + : Program(0), + VertexShader(0), + FragmentShader(0), + Uniforms(), + numTextureBindings(0), + numUniformBufferBindings(0) {} + + static const int GLSL_PROGRAM_VERSION = 300; // Minimum requirement for multiview support. + + static GlProgram Build( + const char* vertexSrc, + const char* fragmentSrc, + const ovrProgramParm* parms, + const int numParms, + const int programVersion = GLSL_PROGRAM_VERSION, // minimum requirement + bool abortOnError = true); + + // Use this build variant for specifying extensions or other program directives. + static GlProgram Build( + const char* vertexDirectives, + const char* vertexSrc, + const char* fragmentDirectives, + const char* fragmentSrc, + const ovrProgramParm* parms, + const int numParms, + const int programVersion = GLSL_PROGRAM_VERSION, // minimum requirement + bool abortOnError = true); + + static void Free(GlProgram& program); + + static void SetUseMultiview(const bool useMultiview_); + + bool IsValid() const { + return Program != 0; + } + + static const int MAX_VIEWS = 2; + static const int SCENE_MATRICES_UBO_SIZE = 2 * sizeof(OVR::Matrix4f) * MAX_VIEWS; + + unsigned int Program; + unsigned int VertexShader; + unsigned int FragmentShader; + + // Globally-defined system level uniforms. + ovrUniform ViewID; // uniform for ViewID; is -1 if OVR_multiview unavailable or disabled + ovrUniform ModelMatrix; // uniform for "uniform mat4 ModelMatrix;" + ovrUniform SceneMatrices; // uniform for "SceneMatrices" ubo : + // uniform SceneMatrices { + // mat4 ViewMatrix[NUM_VIEWS]; + // mat4 ProjectionMatrix[NUM_VIEWS]; + // } sm; + + ovrUniform Uniforms[ovrUniform::MAX_UNIFORMS]; + int numTextureBindings; + int numUniformBufferBindings; +#if OVR_USE_UNIFORM_NAMES + std::string UniformNames[ovrUniform::MAX_UNIFORMS]; +#endif /// OVR_USE_UNIFORM_NAMES + + class MultiViewScope { + public: + MultiViewScope(bool enableMultView); + ~MultiViewScope(); + + private: + bool wasEnabled; + }; +}; + +struct ovrGraphicsCommand { + static const int MAX_TEXTURES = 8; + + GlProgram Program; + ovrGpuState GpuState; + ovrUniformData + UniformData[ovrUniform::MAX_UNIFORMS]; // data matching the types in Program.Uniforms[] + GlTexture Textures[ovrGraphicsCommand::MAX_TEXTURES]; + + void BindUniformTextures(); +}; + +} // namespace OVRFW diff --git a/Samples/SampleXrFramework/Src/Render/GlTexture.cpp b/Samples/SampleXrFramework/Src/Render/GlTexture.cpp new file mode 100755 index 0000000..2cd97b4 --- /dev/null +++ b/Samples/SampleXrFramework/Src/Render/GlTexture.cpp @@ -0,0 +1,1767 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/************************************************************************************ + +Filename : GlTexture.cpp +Content : OpenGL texture loading. +Created : September 30, 2013 +Authors : John Carmack + +*************************************************************************************/ + +// Make sure we get PRIu64 +#define __STDC_FORMAT_MACROS 1 + +#include "GlTexture.h" + +#include "Egl.h" +#include "GL/gl_format.h" +#include "Misc/Log.h" +#include "CompilerUtils.h" +#include "PackageFiles.h" +#include "stb_image.h" + +// #define OVR_USE_PERF_TIMER +#if defined(OVR_USE_PERF_TIMER) +#include "OVR_PerfTimer.h" +#else +#define OVR_PERF_TIMER(x) \ + { ; } +#endif + +#include +#include +#include +#include + +#include + +#define GL_COMPRESSED_RGBA_ASTC_4x4_KHR 0x93B0 +#define GL_COMPRESSED_RGBA_ASTC_5x4_KHR 0x93B1 +#define GL_COMPRESSED_RGBA_ASTC_5x5_KHR 0x93B2 +#define GL_COMPRESSED_RGBA_ASTC_6x5_KHR 0x93B3 +#define GL_COMPRESSED_RGBA_ASTC_6x6_KHR 0x93B4 +#define GL_COMPRESSED_RGBA_ASTC_8x5_KHR 0x93B5 +#define GL_COMPRESSED_RGBA_ASTC_8x6_KHR 0x93B6 +#define GL_COMPRESSED_RGBA_ASTC_8x8_KHR 0x93B7 +#define GL_COMPRESSED_RGBA_ASTC_10x5_KHR 0x93B8 +#define GL_COMPRESSED_RGBA_ASTC_10x6_KHR 0x93B9 +#define GL_COMPRESSED_RGBA_ASTC_10x8_KHR 0x93BA +#define GL_COMPRESSED_RGBA_ASTC_10x10_KHR 0x93BB +#define GL_COMPRESSED_RGBA_ASTC_12x10_KHR 0x93BC +#define GL_COMPRESSED_RGBA_ASTC_12x12_KHR 0x93BD +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR 0x93D0 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR 0x93D1 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR 0x93D2 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR 0x93D3 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR 0x93D4 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR 0x93D5 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR 0x93D6 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR 0x93D7 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR 0x93D8 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR 0x93D9 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR 0x93DA +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR 0x93DB +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR 0x93DC +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR 0x93DD + +uint32_t DecodeNextChar_Advance0(const char** putf8Buffer) { + uint32_t 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_t)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; + } + +#undef INVALID_CHAR +#undef FIRST_BYTE +#undef NEXT_BYTE +} + +// Safer version of DecodeNextChar, which doesn't advance pointer if +// null character is hit. +inline uint32_t DecodeNextChar(const char** putf8Buffer) { + uint32_t ch = DecodeNextChar_Advance0(putf8Buffer); + if (ch == 0) + (*putf8Buffer)--; + return ch; +} + +static void ScanFilePath(const char* url, const char** pfilename, const char** pext) { + const char* filename = url; + const char* lastDot = nullptr; + + uint32_t charVal = DecodeNextChar(&url); + + while (charVal != 0) { + if ((charVal == '/') || (charVal == '\\')) { + filename = url; + lastDot = nullptr; + } else if (charVal == '.') { + lastDot = url - 1; + } + + charVal = DecodeNextChar(&url); + } + + if (pfilename) { + *pfilename = filename; + } + + if (pext) { + *pext = lastDot; + } +} + +static std::string GetExtension(const std::string& s) { + const char* ext = nullptr; + ScanFilePath(s.c_str(), nullptr, &ext); + return ext != nullptr ? std::string(ext) : ""; +} + +namespace OVRFW { + +// Not declared inline in the header to avoid having to use GL_TEXTURE_2D +GlTexture::GlTexture(const unsigned texture_, const int w, const int h) + : texture(texture_), target(GL_TEXTURE_2D), Width(w), Height(h) {} + +static int RoundUpToPow2(int i) { + if (i == 0) { + return 0; + } + return static_cast(pow(2, ceil(log(double(i)) / log(2)))); +} + +static int IntegerLog2(int i) { + if (i == 0) { + return 0; + } + return static_cast(log(double(i)) / log(2.0)); +} + +int ComputeFullMipChainNumLevels(const int width, const int height) { + return IntegerLog2(RoundUpToPow2(std::max(width, height))); +} + +static bool IsCompressedFormat(const eTextureFormat format) { + switch (format) { + case Texture_None: + case Texture_R: + case Texture_RGB: + case Texture_RGBA: + return false; + case Texture_DXT1: + case Texture_DXT3: + case Texture_DXT5: + case Texture_PVR4bRGB: + case Texture_PVR4bRGBA: + case Texture_ATC_RGB: + case Texture_ATC_RGBA: + case Texture_ETC1: + case Texture_ETC2_RGB: + case Texture_ETC2_RGBA: + case Texture_ASTC_4x4: + case Texture_ASTC_5x4: + case Texture_ASTC_5x5: + case Texture_ASTC_6x5: + case Texture_ASTC_6x6: + case Texture_ASTC_8x5: + case Texture_ASTC_8x6: + case Texture_ASTC_8x8: + case Texture_ASTC_10x5: + case Texture_ASTC_10x6: + case Texture_ASTC_10x8: + case Texture_ASTC_10x10: + case Texture_ASTC_12x10: + case Texture_ASTC_12x12: + case Texture_ASTC_SRGB_4x4: + case Texture_ASTC_SRGB_5x4: + case Texture_ASTC_SRGB_5x5: + case Texture_ASTC_SRGB_6x5: + case Texture_ASTC_SRGB_6x6: + case Texture_ASTC_SRGB_8x5: + case Texture_ASTC_SRGB_8x6: + case Texture_ASTC_SRGB_8x8: + case Texture_ASTC_SRGB_10x5: + case Texture_ASTC_SRGB_10x6: + case Texture_ASTC_SRGB_10x8: + case Texture_ASTC_SRGB_10x10: + case Texture_ASTC_SRGB_12x10: + case Texture_ASTC_SRGB_12x12: + return true; + default: + assert(false); + return false; + } +} + +static int GetASTCIndex(const eTextureFormat format) { + int const formatType = format & Texture_TypeMask; + int const index = (formatType - Texture_ASTC_Start) >> 8; + return index; +} + +GLenum GetASTCInternalFormat(eTextureFormat const format) { + int const NUM_ASTC_FORMATS = (Texture_ASTC_End - Texture_ASTC_Start) >> 8; + + int const index = GetASTCIndex(format); + + GLenum internalFormats[NUM_ASTC_FORMATS] = { + GL_COMPRESSED_RGBA_ASTC_4x4_KHR, GL_COMPRESSED_RGBA_ASTC_5x4_KHR, + GL_COMPRESSED_RGBA_ASTC_5x5_KHR, GL_COMPRESSED_RGBA_ASTC_6x5_KHR, + GL_COMPRESSED_RGBA_ASTC_6x6_KHR, GL_COMPRESSED_RGBA_ASTC_8x5_KHR, + GL_COMPRESSED_RGBA_ASTC_8x6_KHR, GL_COMPRESSED_RGBA_ASTC_8x8_KHR, + GL_COMPRESSED_RGBA_ASTC_10x5_KHR, GL_COMPRESSED_RGBA_ASTC_10x6_KHR, + GL_COMPRESSED_RGBA_ASTC_10x8_KHR, GL_COMPRESSED_RGBA_ASTC_10x10_KHR, + GL_COMPRESSED_RGBA_ASTC_12x10_KHR, GL_COMPRESSED_RGBA_ASTC_12x12_KHR, + GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR, + GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR, + GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR, + GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR, + GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR, + GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR, + GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR}; + return internalFormats[index]; +} + +static int +GetASTCTextureSize(const eTextureFormat format, const int w, const int h, const int depth) { + struct blockDims_t { + int x; + int y; + int z; + }; + + int const NUM_ASTC_FORMATS = (Texture_ASTC_End - Texture_ASTC_Start) >> 8; + blockDims_t const blockDims[NUM_ASTC_FORMATS] = { + {4, 4, 1}, {5, 4, 1}, {5, 5, 1}, {6, 5, 1}, {6, 6, 1}, {8, 5, 1}, {8, 6, 1}, + {8, 8, 1}, {10, 5, 1}, {10, 6, 1}, {10, 8, 1}, {10, 10, 1}, {12, 10, 1}, {12, 12, 1}, + {4, 4, 1}, {5, 4, 1}, {5, 5, 1}, {6, 5, 1}, {6, 6, 1}, {8, 5, 1}, {8, 6, 1}, + {8, 8, 1}, {10, 5, 1}, {10, 6, 1}, {10, 8, 1}, {10, 10, 1}, {12, 10, 1}, {12, 12, 1}}; + + int const index = GetASTCIndex(format); + + blockDims_t const& dims = blockDims[index]; + + // Compute number of blocks in each direction + int const xblocks = (w + dims.x - 1) / dims.x; + int const yblocks = (h + dims.y - 1) / dims.y; + int const zblocks = (depth + dims.z - 1) / dims.z; + + // Each block is encoded on 16 bytes, so calculate total compressed image data size. + int const numBytes = xblocks * yblocks * zblocks * 16; + return numBytes; +} + +static int32_t GetOvrTextureSize(const eTextureFormat format, const int w, const int h) { + switch (format & Texture_TypeMask) { + case Texture_R: + return w * h; + case Texture_RGB: + return w * h * 3; + case Texture_RGBA: + return w * h * 4; + case Texture_ATC_RGB: + case Texture_ETC1: + case Texture_ETC2_RGB: + case Texture_DXT1: { + int bw = (w + 3) / 4, bh = (h + 3) / 4; + return bw * bh * 8; + } + case Texture_ATC_RGBA: + case Texture_ETC2_RGBA: + case Texture_DXT3: + case Texture_DXT5: { + int bw = (w + 3) / 4, bh = (h + 3) / 4; + return bw * bh * 16; + } + case Texture_PVR4bRGB: + case Texture_PVR4bRGBA: { + unsigned int width = (unsigned int)w; + unsigned int height = (unsigned int)h; + unsigned int min_width = 8; + unsigned int min_height = 8; + + // pad the dimensions + width = width + ((-1 * width) % min_width); + height = height + ((-1 * height) % min_height); + unsigned int depth = 1; + + unsigned int bpp = 4; + unsigned int bits = bpp * width * height * depth; + return (int)(bits / 8); + } + case Texture_ASTC_4x4: + case Texture_ASTC_5x4: + case Texture_ASTC_5x5: + case Texture_ASTC_6x5: + case Texture_ASTC_6x6: + case Texture_ASTC_8x5: + case Texture_ASTC_8x6: + case Texture_ASTC_8x8: + case Texture_ASTC_10x5: + case Texture_ASTC_10x6: + case Texture_ASTC_10x8: + case Texture_ASTC_10x10: + case Texture_ASTC_12x10: + case Texture_ASTC_12x12: + case Texture_ASTC_SRGB_4x4: + case Texture_ASTC_SRGB_5x4: + case Texture_ASTC_SRGB_5x5: + case Texture_ASTC_SRGB_6x5: + case Texture_ASTC_SRGB_6x6: + case Texture_ASTC_SRGB_8x5: + case Texture_ASTC_SRGB_8x6: + case Texture_ASTC_SRGB_8x8: + case Texture_ASTC_SRGB_10x5: + case Texture_ASTC_SRGB_10x6: + case Texture_ASTC_SRGB_10x8: + case Texture_ASTC_SRGB_10x10: + case Texture_ASTC_SRGB_12x10: + case Texture_ASTC_SRGB_12x12: { + return GetASTCTextureSize(format, w, h, 1); + } + default: { + assert(false); + break; + } + } + return 0; +} + +bool TextureFormatToGlFormat( + const eTextureFormat format, + const bool useSrgbFormat, + GLenum& glFormat, + GLenum& glInternalFormat) { + switch (format & Texture_TypeMask) { + case Texture_RGB: { + glFormat = GL_RGB; + if (useSrgbFormat) { + glInternalFormat = GL_SRGB8; + // LOG( "GL texture format is GL_RGB / GL_SRGB8" ); + } else { + glInternalFormat = GL_RGB; + // LOG( "GL texture format is GL_RGB / GL_RGB" ); + } + return true; + } + case Texture_RGBA: { + glFormat = GL_RGBA; + if (useSrgbFormat) { + glInternalFormat = GL_SRGB8_ALPHA8; + // LOG( "GL texture format is GL_RGBA / GL_SRGB8_ALPHA8" ); + } else { + glInternalFormat = GL_RGBA; + // LOG( "GL texture format is GL_RGBA / GL_RGBA" ); + } + return true; + } + case Texture_R: { + glInternalFormat = GL_R8; + glFormat = GL_RED; + // LOG( "GL texture format is GL_R8" ); + return true; + } + case Texture_DXT1: { + glFormat = glInternalFormat = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT; + // LOG( "GL texture format is GL_COMPRESSED_RGBA_S3TC_DXT1_EXT" ); + return true; + } + // unsupported on OpenGL ES: + // case Texture_DXT3: glFormat = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT; break; + // case Texture_DXT5: glFormat = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; break; + case Texture_PVR4bRGB: { + glFormat = GL_RGB; + glInternalFormat = GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG; + // LOG( "GL texture format is GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG" ); + return true; + } + case Texture_PVR4bRGBA: { + glFormat = GL_RGBA; + glInternalFormat = GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG; + // LOG( "GL texture format is GL_RGBA / GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG" ); + return true; + } + case Texture_ETC1: { + glFormat = GL_RGB; + if (useSrgbFormat) { + // Note that ETC2 is backwards compatible with ETC1. + glInternalFormat = GL_COMPRESSED_SRGB8_ETC2; + // LOG( "GL texture format is GL_RGB / GL_COMPRESSED_SRGB8_ETC2 " ); + } else { + glInternalFormat = GL_ETC1_RGB8_OES; + // LOG( "GL texture format is GL_RGB / GL_ETC1_RGB8_OES" ); + } + return true; + } + case Texture_ETC2_RGB: { + glFormat = GL_RGB; + if (useSrgbFormat) { + glInternalFormat = GL_COMPRESSED_SRGB8_ETC2; + // LOG( "GL texture format is GL_RGB / GL_COMPRESSED_SRGB8_ETC2 " ); + } else { + glInternalFormat = GL_COMPRESSED_RGB8_ETC2; + // LOG( "GL texture format is GL_RGB / GL_COMPRESSED_RGB8_ETC2 " ); + } + return true; + } + case Texture_ETC2_RGBA: { + glFormat = GL_RGBA; + if (useSrgbFormat) { + glInternalFormat = GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC; + // LOG( "GL texture format is GL_RGBA / + // GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC + //" + //); + } else { + glInternalFormat = GL_COMPRESSED_RGBA8_ETC2_EAC; + // LOG( "GL texture format is GL_RGBA / GL_COMPRESSED_RGBA8_ETC2_EAC " + //); + } + return true; + } + case Texture_ASTC_4x4: + case Texture_ASTC_5x4: + case Texture_ASTC_5x5: + case Texture_ASTC_6x5: + case Texture_ASTC_6x6: + case Texture_ASTC_8x5: + case Texture_ASTC_8x6: + case Texture_ASTC_8x8: + case Texture_ASTC_10x5: + case Texture_ASTC_10x6: + case Texture_ASTC_10x8: + case Texture_ASTC_10x10: + case Texture_ASTC_12x10: + case Texture_ASTC_12x12: + case Texture_ASTC_SRGB_4x4: + case Texture_ASTC_SRGB_5x4: + case Texture_ASTC_SRGB_5x5: + case Texture_ASTC_SRGB_6x5: + case Texture_ASTC_SRGB_6x6: + case Texture_ASTC_SRGB_8x5: + case Texture_ASTC_SRGB_8x6: + case Texture_ASTC_SRGB_8x8: + case Texture_ASTC_SRGB_10x5: + case Texture_ASTC_SRGB_10x6: + case Texture_ASTC_SRGB_10x8: + case Texture_ASTC_SRGB_10x10: + case Texture_ASTC_SRGB_12x10: + case Texture_ASTC_SRGB_12x12: { + glFormat = GL_RGBA; + glInternalFormat = GetASTCInternalFormat(format); + + // Force the format to be correct for the given useSrgbFormat state + if (useSrgbFormat && glInternalFormat >= GL_COMPRESSED_RGBA_ASTC_4x4_KHR && + glInternalFormat <= GL_COMPRESSED_RGBA_ASTC_12x12_KHR) { + glInternalFormat += + (GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR - GL_COMPRESSED_RGBA_ASTC_4x4_KHR); + } + if (!useSrgbFormat && glInternalFormat >= GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR && + glInternalFormat <= GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR) { + glInternalFormat -= + (GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR - GL_COMPRESSED_RGBA_ASTC_4x4_KHR); + } + return true; + } + case Texture_ATC_RGB: { + glFormat = GL_RGB; + glInternalFormat = GL_ATC_RGB_AMD; + // LOG( "GL texture format is GL_RGB / GL_ATC_RGB_AMD" ); + return true; + } + case Texture_ATC_RGBA: { + glFormat = GL_RGBA; + glInternalFormat = GL_ATC_RGBA_EXPLICIT_ALPHA_AMD; + // LOG( "GL texture format is GL_RGBA / GL_ATC_RGBA_EXPLICIT_ALPHA_AMD" ); + return true; + } + } + return false; +} + +bool GlFormatToTextureFormat( + eTextureFormat& format, + const GLenum glFormat, + const GLenum glInternalFormat) { + if (glFormat == GL_RED && glInternalFormat == GL_R8) { + format = Texture_R; + return true; + } + if (glFormat == GL_RGB && (glInternalFormat == GL_RGB || glInternalFormat == GL_SRGB8)) { + format = Texture_RGB; + return true; + } + if (glFormat == GL_RGBA && + (glInternalFormat == GL_RGBA || glInternalFormat == GL_RGBA8 || + glInternalFormat == GL_SRGB8_ALPHA8)) { + format = Texture_RGBA; + return true; + } + if ((glFormat == 0 || glFormat == GL_COMPRESSED_RGBA_S3TC_DXT1_EXT) && + glInternalFormat == GL_COMPRESSED_RGBA_S3TC_DXT1_EXT) { + format = Texture_DXT1; + return true; + } + if ((glFormat == 0 || glFormat == GL_RGB) && + glInternalFormat == GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG) { + format = Texture_PVR4bRGB; + return true; + } + if ((glFormat == 0 || glFormat == GL_RGBA) && + glInternalFormat == GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG) { + format = Texture_PVR4bRGBA; + return true; + } + if ((glFormat == 0 || glFormat == GL_RGB) && + (glInternalFormat == GL_ETC1_RGB8_OES || glInternalFormat == GL_COMPRESSED_SRGB8_ETC2)) { + format = Texture_ETC1; + return true; + } + if ((glFormat == 0 || glFormat == GL_RGB) && + (glInternalFormat == GL_COMPRESSED_RGB8_ETC2 || + glInternalFormat == GL_COMPRESSED_SRGB8_ETC2)) { + format = Texture_ETC2_RGB; + return true; + } + if ((glFormat == 0 || glFormat == GL_RGBA) && + (glInternalFormat == GL_COMPRESSED_RGBA8_ETC2_EAC || + glInternalFormat == GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC)) { + format = Texture_ETC2_RGBA; + return true; + } + if ((glFormat == 0 || glFormat == GL_RGB) && glInternalFormat == GL_ATC_RGB_AMD) { + format = Texture_ATC_RGB; + return true; + } + if ((glFormat == 0 || glFormat == GL_RGBA) && + glInternalFormat == GL_ATC_RGBA_EXPLICIT_ALPHA_AMD) { + format = Texture_ATC_RGBA; + return true; + } + if (glFormat == 0 || glFormat == GL_RGBA) { + if (glInternalFormat >= GL_COMPRESSED_RGBA_ASTC_4x4_KHR && + glInternalFormat <= GL_COMPRESSED_RGBA_ASTC_12x12_KHR) { + format = (eTextureFormat)(Texture_ASTC_4x4 + + ((glInternalFormat - GL_COMPRESSED_RGBA_ASTC_4x4_KHR) << 8)); + return true; + } + if (glInternalFormat >= GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR && + glInternalFormat <= GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR) { + format = (eTextureFormat)(Texture_ASTC_SRGB_4x4 + + ((glInternalFormat - GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR) + << 8)); + return true; + } + } + + return false; +} + +static GlTexture CreateGlTexture( + const char* fileName, + const eTextureFormat format, + const int width, + const int height, + const void* data, + const size_t dataSize, + const int mipcount, + const bool useSrgbFormat, + const bool imageSizeStored) { +#if defined(OVR_USE_PERF_TIMER) + ALOG("Loading '%s', w = %i, h = %i, mipcount = %i", fileName, width, height, mipcount); +#endif + + GLCheckErrorsWithTitle("pre-CreateGlTexture"); + + OVR_PERF_TIMER(CreateGlTexture); + + // LOG( "CreateGLTexture(): format %s", NameForTextureFormat( static_cast< TextureFormat >( + // format ) ) ); + + GLenum glFormat; + GLenum glInternalFormat; + if (!TextureFormatToGlFormat(format, useSrgbFormat, glFormat, glInternalFormat)) { + return GlTexture(0, 0, 0); + } + + if (mipcount <= 0) { + ALOG("%s: Invalid mip count %d", fileName, mipcount); + return GlTexture(0, 0, 0); + } + + // larger than this would require mipSize below to be a larger type + if (width <= 0 || width > 32768 || height <= 0 || height > 32768) { + ALOG("%s: Invalid texture size (%dx%d)", fileName, width, height); + return GlTexture(0, 0, 0); + } + + GLuint texId; + glGenTextures(1, &texId); + glBindTexture(GL_TEXTURE_2D, texId); + + const unsigned char* level = (const unsigned char*)data; + const unsigned char* endOfBuffer = level + dataSize; + + int w = width; + int h = height; + for (int i = 0; i < mipcount; i++) { + int32_t mipSize = GetOvrTextureSize(format, w, h); + if (imageSizeStored) { + mipSize = static_cast(*(const size_t*)level); + + level += 4; + if (level > endOfBuffer) { + ALOG("%s: Image data exceeds buffer size", fileName); + glBindTexture(GL_TEXTURE_2D, 0); + return GlTexture(texId, GL_TEXTURE_2D, width, height); + } + } + + if (mipSize <= 0 || mipSize > endOfBuffer - level) { + ALOG( + "%s: Mip level %d exceeds buffer size (%d > %td)", + fileName, + i, + mipSize, + ptrdiff_t(endOfBuffer - level)); + glBindTexture(GL_TEXTURE_2D, 0); + return GlTexture(texId, GL_TEXTURE_2D, width, height); + } + + if (IsCompressedFormat(format)) { + OVR_PERF_TIMER(CreateGlTexture_CompressedTexImage2D); + glCompressedTexImage2D(GL_TEXTURE_2D, i, glInternalFormat, w, h, 0, mipSize, level); + GLCheckErrorsWithTitle("Texture_Compressed"); + } else { + OVR_PERF_TIMER(CreateGlTexture_TexImage2D); + glTexImage2D( + GL_TEXTURE_2D, i, glInternalFormat, w, h, 0, glFormat, GL_UNSIGNED_BYTE, level); + } + + level += mipSize; + if (imageSizeStored) { + level += 3 - ((mipSize + 3) % 4); + if (level > endOfBuffer) { + ALOG("%s: Image data exceeds buffer size", fileName); + glBindTexture(GL_TEXTURE_2D, 0); + return GlTexture(texId, GL_TEXTURE_2D, width, height); + } + } + + w >>= 1; + h >>= 1; + if (w < 1) { + w = 1; + } + if (h < 1) { + h = 1; + } + } + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + // Surfaces look pretty terrible without trilinear filtering + if (mipcount <= 1) { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + } else { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + } + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + GLCheckErrorsWithTitle("Texture load"); + + glBindTexture(GL_TEXTURE_2D, 0); + + return GlTexture(texId, GL_TEXTURE_2D, width, height); +} + +static GlTexture CreateGlCubeTexture( + const char* fileName, + const eTextureFormat format, + const int width, + const int height, + const void* data, + const size_t dataSize, + const int mipcount, + const bool useSrgbFormat, + const bool imageSizeStored) { + assert(width == height); + + GLCheckErrorsWithTitle("Pre Cube Texture load"); + + if (mipcount <= 0) { + ALOG("%s: Invalid mip count %d", fileName, mipcount); + return GlTexture(0, 0, 0); + } + + // larger than this would require mipSize below to be a larger type + if (width <= 0 || width > 32768 || height <= 0 || height > 32768) { + ALOG("%s: Invalid texture size (%dx%d)", fileName, width, height); + return GlTexture(0, 0, 0); + } + + GLenum glFormat; + GLenum glInternalFormat; + if (!TextureFormatToGlFormat(format, useSrgbFormat, glFormat, glInternalFormat)) { + ALOG( + "%s: TextureFormatToGlFormat 0x%x %s failed", + fileName, + (int)format, + useSrgbFormat ? "true" : "false"); + return GlTexture(0, 0, 0); + } + + GLuint texId; + glGenTextures(1, &texId); + glBindTexture(GL_TEXTURE_CUBE_MAP, texId); + + const unsigned char* level = (const unsigned char*)data; + const unsigned char* endOfBuffer = level + dataSize; + + for (int i = 0; i < mipcount; i++) { + const int w = width >> i; + int32_t mipSize = GetOvrTextureSize(format, w, w); + if (imageSizeStored) { + mipSize = static_cast(*(const size_t*)level); + level += 4; + if (level > endOfBuffer) { + ALOG("%s: Image data exceeds buffer size: %p > %p", fileName, level, endOfBuffer); + glBindTexture(GL_TEXTURE_CUBE_MAP, 0); + return GlTexture(texId, GL_TEXTURE_CUBE_MAP, width, height); + } + } + + for (int side = 0; side < 6; side++) { + if (mipSize <= 0 || mipSize > endOfBuffer - level) { + ALOG( + "%s: Mip level %d exceeds buffer size (%u > %td)", + fileName, + i, + mipSize, + ptrdiff_t(endOfBuffer - level)); + glBindTexture(GL_TEXTURE_CUBE_MAP, 0); + return GlTexture(texId, GL_TEXTURE_CUBE_MAP, width, height); + } + + if (IsCompressedFormat(format)) { + glCompressedTexImage2D( + GL_TEXTURE_CUBE_MAP_POSITIVE_X + side, + i, + glInternalFormat, + w, + w, + 0, + mipSize, + level); + } else { + glTexImage2D( + GL_TEXTURE_CUBE_MAP_POSITIVE_X + side, + i, + glInternalFormat, + w, + w, + 0, + glFormat, + GL_UNSIGNED_BYTE, + level); + } + + level += mipSize; + if (imageSizeStored) { + level += 3 - ((mipSize + 3) % 4); + if (level > endOfBuffer) { + ALOG("%s: Image data exceeds buffer size", fileName); + glBindTexture(GL_TEXTURE_CUBE_MAP, 0); + return GlTexture(texId, GL_TEXTURE_CUBE_MAP, width, height); + } + } + } + } + + // Surfaces look pretty terrible without trilinear filtering + if (mipcount <= 1) { + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + } else { + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + } + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + GLCheckErrorsWithTitle("Cube Texture load"); + + glBindTexture(GL_TEXTURE_CUBE_MAP, 0); + + return GlTexture(texId, GL_TEXTURE_CUBE_MAP, width, height); +} + +GlTexture LoadRGBATextureFromMemory( + const uint8_t* texture, + const int width, + const int height, + const bool useSrgbFormat) { + const size_t dataSize = GetOvrTextureSize(Texture_RGBA, width, height); + return CreateGlTexture( + "memory-RGBA", Texture_RGBA, width, height, texture, dataSize, 1, useSrgbFormat, false); +} + +GlTexture +LoadRGBACubeTextureFromMemory(const uint8_t* texture, const int dim, const bool useSrgbFormat) { + const size_t dataSize = GetOvrTextureSize(Texture_RGBA, dim, dim) * 6; + return CreateGlCubeTexture( + "memory-CubeRGBA", Texture_RGBA, dim, dim, texture, dataSize, 1, useSrgbFormat, false); +} + +GlTexture LoadRGBTextureFromMemory( + const uint8_t* texture, + const int width, + const int height, + const bool useSrgbFormat) { + const size_t dataSize = GetOvrTextureSize(Texture_RGB, width, height); + return CreateGlTexture( + "memory-RGB", Texture_RGB, width, height, texture, dataSize, 1, useSrgbFormat, false); +} + +GlTexture LoadRTextureFromMemory(const uint8_t* texture, const int width, const int height) { + const size_t dataSize = GetOvrTextureSize(Texture_R, width, height); + return CreateGlTexture( + "memory-R", Texture_R, width, height, texture, dataSize, 1, false, false); +} + +// .astc files are created by the reference Mali compression tool. +// As of 9/17/2016, it appears to automatically flip y, which is a good +// reason to avoid it. +struct astcHeader { + unsigned char magic[4]; + unsigned char blockDim_x; + unsigned char blockDim_y; + unsigned char blockDim_z; + unsigned char xsize[3]; + unsigned char ysize[3]; + unsigned char zsize[3]; +}; + +GlTexture LoadASTCTextureFromMemory( + uint8_t const* buffer, + const size_t bufferSize, + const int numPlanes, + const bool useSrgbFormat) { + astcHeader const* header = reinterpret_cast(buffer); + + int const w = + ((int)header->xsize[2] << 16) | ((int)header->xsize[1] << 8) | ((int)header->xsize[0]); + int const h = + ((int)header->ysize[2] << 16) | ((int)header->ysize[1] << 8) | ((int)header->ysize[0]); + + assert(numPlanes == 1 || numPlanes == 4); + OVR_UNUSED(numPlanes); + if (header->blockDim_z != 1) { + assert(header->blockDim_z == 1); + ALOG("Only 2D ASTC textures are supported"); + return GlTexture(); + } + + eTextureFormat format = Texture_None; + if (header->blockDim_x == 4) { + if (header->blockDim_y == 4) { + format = Texture_ASTC_4x4; + } + } else if (header->blockDim_x == 5) { + if (header->blockDim_y == 4) { + format = Texture_ASTC_5x4; + } else if (header->blockDim_y == 5) { + format = Texture_ASTC_5x5; + } + } else if (header->blockDim_x == 6) { + if (header->blockDim_y == 5) { + format = Texture_ASTC_6x5; + } else if (header->blockDim_y == 6) { + format = Texture_ASTC_6x6; + } + } else if (header->blockDim_x == 8) { + if (header->blockDim_y == 5) { + format = Texture_ASTC_8x5; + } else if (header->blockDim_y == 6) { + format = Texture_ASTC_8x6; + } else if (header->blockDim_y == 8) { + format = Texture_ASTC_8x8; + } + } else if (header->blockDim_x == 10) { + if (header->blockDim_y == 5) { + format = Texture_ASTC_10x5; + } else if (header->blockDim_y == 6) { + format = Texture_ASTC_10x6; + } else if (header->blockDim_y == 8) { + format = Texture_ASTC_10x8; + } else if (header->blockDim_y == 10) { + format = Texture_ASTC_10x10; + } + } else if (header->blockDim_x == 12) { + if (header->blockDim_y == 10) { + format = Texture_ASTC_12x10; + } else if (header->blockDim_y == 12) { + format = Texture_ASTC_12x12; + } + } + + if (format == Texture_None) { + assert(format != Texture_None); + ALOG("Unhandled ASTC block size: %i x %i", header->blockDim_x, header->blockDim_y); + return GlTexture(); + } + return CreateGlTexture( + "memory-ASTC", + format, + w, + h, + buffer + sizeof(struct astcHeader), + bufferSize - sizeof(struct astcHeader), + 1, + useSrgbFormat, + false); +} +/* + +PVR Container Format + +Offset Size Name Description +0x0000 4 [DWORD] Version 0x03525650 +0x0004 4 [DWORD] Flags 0x0000 if no flags set + 0x0002 if colors within the texture +0x0008 8 [Union] Pixel Format This can either be one of several predetermined enumerated + values (a DWORD) or a 4-character array and a 4-byte array (8 +bytes). If the most significant 4 bytes of the 64-bit (8-byte) value are all zero, then it indicates +that it is the enumeration with the following values: Value Pixel Type 0 PVRTC 2bpp RGB 1 +PVRTC 2bpp RGBA 2 PVRTC 4bpp RGB 3 PVRTC 4bpp RGBA 4 PVRTC-II 2bpp 5 PVRTC-II +4bpp 6 ETC1 7 DXT1 / BC1 8 DXT2 9 DXT3 / BC2 10 DXT4 11 DXT5 / BC3 12 +BC4 13 BC5 14 BC6 15 BC7 16 UYVY 17 YUY2 18 BW1bpp 19 R9G9B9E5 Shared +Exponent 20 RGBG8888 21 GRGB8888 22 ETC2 RGB 23 ETC2 RGBA 24 ETC2 RGB A1 25 EAC +R11 Unsigned 26 EAC R11 Signed 27 EAC RG11 Unsigned 28 EAC RG11 Signed If the most +significant 4 bytes are not zero then the 8-byte character array indicates the pixel format as +follows: The least significant 4 bytes indicate channel order, such as: { 'b', 'g', 'r', 'a' } or { +'b', 'g', 'r', '\0' } The most significant 4 bytes indicate the width of each channel in bits, as +follows: { 4, 4, 4, 4 } or { 2, 2, 2, 2 }, or {5, 5, 5, 0 } 0x0010 4 [DWORD] Color Space This +is an enumerated field, currently two values: Value Color Space 0 Linear RGB 1 Standard RGB +0x0014 4 [DWORD] Channel Type This is another enumerated field: + Value Data Type + 0 Unsigned Byte Normalized + 1 Signed Byte Normalized + 2 Unsigned Byte + 3 Signed Byte + 4 Unsigned Short Normalized + 5 Signed Short Normalized + 6 Unsigned Short + 7 Signed Short + 8 Unsigned Integer Normalized + 9 Signed Integer Normalized + 10 Unsigned Integer + 11 Signed Integer + 12 Float (no size specified) +0x0018 4 [DWORD] Height Height of the image. +0x001C 4 [DWORD] Width Width of the image. +0x0020 4 [DWORD] Depth Depth of the image, in pixels. +0x0024 4 [DWORD] Surface Count The number of surfaces to this texture, used for texture arrays. +0x0028 4 [DWORD] Face Count The number of faces to this texture, used for cube maps. +0x002C 4 [DWORD] MIP-Map Count The number of MIP-Map levels, including a top level. +0x0030 4 [DWORD] Metadata Size The size, in bytes, of meta data that immediately follows this +header. + +*/ + +#pragma pack(1) +struct OVR_PVR_HEADER { + std::uint32_t Version; + std::uint32_t Flags; + std::uint64_t PixelFormat; + std::uint32_t ColorSpace; + std::uint32_t ChannelType; + std::uint32_t Height; + std::uint32_t Width; + std::uint32_t Depth; + std::uint32_t NumSurfaces; + std::uint32_t NumFaces; + std::uint32_t MipMapCount; + std::uint32_t MetaDataSize; +}; +#pragma pack() + +GlTexture LoadTexturePVR( + const char* fileName, + const unsigned char* buffer, + const int bufferLength, + bool useSrgbFormat, + bool noMipMaps, + int& width, + int& height) { + width = 0; + height = 0; + + if (bufferLength < (int)(sizeof(OVR_PVR_HEADER))) { + ALOG("%s: Invalid PVR file", fileName); + return GlTexture(0, 0, 0); + } + + const OVR_PVR_HEADER& header = *(OVR_PVR_HEADER*)buffer; + if (header.Version != 0x03525650) { + ALOG("%s: Invalid PVR file version", fileName); + return GlTexture(0, 0, 0); + } + + eTextureFormat format = Texture_None; + switch (header.PixelFormat) { + case 2: + format = Texture_PVR4bRGB; + break; + case 3: + format = Texture_PVR4bRGBA; + break; + case 6: + format = Texture_ETC1; + break; + case 22: + format = Texture_ETC2_RGB; + break; + case 23: + format = Texture_ETC2_RGBA; + break; + case 578721384203708274llu: + format = Texture_RGBA; + break; + default: + ALOG( + "%s: Unknown PVR texture format %u, size %ix%i", + fileName, + static_cast(header.PixelFormat), + width, + height); + return GlTexture(0, 0, 0); + } + + // skip the metadata + const std::uint32_t startTex = sizeof(OVR_PVR_HEADER) + header.MetaDataSize; + if ((startTex < sizeof(OVR_PVR_HEADER)) || (startTex >= static_cast(bufferLength))) { + ALOG("%s: Invalid PVR header sizes", fileName); + return GlTexture(0, 0, 0); + } + + const std::uint32_t mipCount = (noMipMaps) + ? 1 + : std::max(static_cast(1u), header.MipMapCount); + + width = header.Width; + height = header.Height; + + if (header.NumFaces == 1) { + return CreateGlTexture( + fileName, + format, + width, + height, + buffer + startTex, + bufferLength - startTex, + mipCount, + useSrgbFormat, + false); + } else if (header.NumFaces == 6) { + return CreateGlCubeTexture( + fileName, + format, + width, + height, + buffer + startTex, + bufferLength - startTex, + mipCount, + useSrgbFormat, + false); + } else { + ALOG("%s: PVR file has unsupported number of faces %d", fileName, header.NumFaces); + } + + width = 0; + height = 0; + return GlTexture(0, 0, 0); +} + +unsigned char* LoadPVRBuffer(const char* fileName, int& width, int& height) { + width = 0; + height = 0; + + std::vector buffer; + std::ifstream is; + is.open(fileName, std::ios::binary | std::ios::in); + if (!is.is_open()) { + return nullptr; + } + // get size + is.seekg(0, is.end); + size_t file_size = (size_t)is.tellg(); + is.seekg(0, is.beg); + // allocate buffer + buffer.resize(file_size); + // read all file + if (!is.read(reinterpret_cast(buffer.data()), file_size)) { + return nullptr; + } + // close + is.close(); + + if (buffer.size() < sizeof(OVR_PVR_HEADER)) { + ALOG("Invalid PVR file"); + return nullptr; + } + + const OVR_PVR_HEADER& header = *(OVR_PVR_HEADER*)buffer.data(); + if (header.Version != 0x03525650) { + ALOG("Invalid PVR file version"); + return nullptr; + } + + eTextureFormat format = Texture_None; + switch (header.PixelFormat) { + case 578721384203708274llu: + format = Texture_RGBA; + break; + default: + ALOG( + "Unknown PVR texture format %u, size %ix%i", + static_cast(header.PixelFormat), + width, + height); + return nullptr; + } + + // skip the metadata + size_t startTex = sizeof(OVR_PVR_HEADER) + header.MetaDataSize; + if ((startTex < sizeof(OVR_PVR_HEADER)) || (startTex >= buffer.size())) { + ALOG("Invalid PVR header sizes"); + return nullptr; + } + + size_t mipSize = GetOvrTextureSize(format, header.Width, header.Height); + + // NOTE: cast to int before subtracting!!!! + const int outBufferSizeBytes = static_cast(buffer.size()) - static_cast(startTex); + if (outBufferSizeBytes < 0 || mipSize > static_cast(outBufferSizeBytes)) { + return nullptr; + } + + width = header.Width; + height = header.Height; + + // skip the metadata + unsigned char* outBuffer = (unsigned char*)malloc(outBufferSizeBytes); + memcpy(outBuffer, (unsigned char*)buffer.data() + startTex, outBufferSizeBytes); + return outBuffer; +} + +/* + +KTX Container Format + +KTX is a format for storing textures for OpenGL and OpenGL ES applications. +It is distinguished by the simplicity of the loader required to instantiate +a GL texture object from the file contents. + +Byte[12] identifier +std::uint32_t endianness +std::uint32_t glType +std::uint32_t glTypeSize +std::uint32_t glFormat +Uint32 glInternalFormat +Uint32 glBaseInternalFormat +std::uint32_t pixelWidth +std::uint32_t pixelHeight +std::uint32_t pixelDepth +std::uint32_t numberOfArrayElements +std::uint32_t numberOfFaces +std::uint32_t numberOfMipmapLevels +std::uint32_t bytesOfKeyValueData + +for each keyValuePair that fits in bytesOfKeyValueData + std::uint32_t keyAndValueByteSize + Byte keyAndValue[keyAndValueByteSize] + Byte valuePadding[3 - ((keyAndValueByteSize + 3) % 4)] +end + +for each mipmap_level in numberOfMipmapLevels* + std::uint32_t imageSize; + for each array_element in numberOfArrayElements* + for each face in numberOfFaces + for each z_slice in pixelDepth* + for each row or row_of_blocks in pixelHeight* + for each pixel or block_of_pixels in pixelWidth + Byte data[format-specific-number-of-bytes]** + end + end + end + Byte cubePadding[0-3] + end + end + Byte mipPadding[3 - ((imageSize + 3) % 4)] +end + +*/ + +#pragma pack(1) +struct OVR_KTX_HEADER { + std::uint8_t identifier[12]; + std::uint32_t endianness; + std::uint32_t glType; + std::uint32_t glTypeSize; + std::uint32_t glFormat; + std::uint32_t glInternalFormat; + std::uint32_t glBaseInternalFormat; + std::uint32_t pixelWidth; + std::uint32_t pixelHeight; + std::uint32_t pixelDepth; + std::uint32_t numberOfArrayElements; + std::uint32_t numberOfFaces; + std::uint32_t numberOfMipmapLevels; + std::uint32_t bytesOfKeyValueData; +}; +#pragma pack() + +GlTexture LoadTextureKTX( + const char* fileName, + const unsigned char* buffer, + const int bufferLength, + bool useSrgbFormat, + bool noMipMaps, + int& width, + int& height) { + width = 0; + height = 0; + + if (bufferLength < (int)(sizeof(OVR_KTX_HEADER))) { + ALOG("%s: Invalid KTX file", fileName); + return GlTexture(0, 0, 0); + } + + const char fileIdentifier[12] = { + '\xAB', 'K', 'T', 'X', ' ', '1', '1', '\xBB', '\r', '\n', '\x1A', '\n'}; + + const OVR_KTX_HEADER& header = *(OVR_KTX_HEADER*)buffer; + if (memcmp(header.identifier, fileIdentifier, sizeof(fileIdentifier)) != 0) { + ALOG("%s: Invalid KTX file", fileName); + return GlTexture(0, 0, 0); + } + // only support little endian + if (header.endianness != 0x04030201) { + ALOG("%s: KTX file has wrong endianess", fileName); + return GlTexture(0, 0, 0); + } + // only support compressed or unsigned byte + if (header.glType != 0 && header.glType != GL_UNSIGNED_BYTE) { + ALOG("%s: KTX file has unsupported glType %d", fileName, header.glType); + return GlTexture(0, 0, 0); + } + // no support for texture arrays + if (header.numberOfArrayElements != 0) { + ALOG( + "%s: KTX file has unsupported number of array elements %d", + fileName, + header.numberOfArrayElements); + return GlTexture(0, 0, 0); + } + + // derive the texture format from the GL format + eTextureFormat format = Texture_None; + if (!GlFormatToTextureFormat(format, header.glFormat, header.glInternalFormat)) { + ALOG( + "%s: KTX file has unsupported glFormat %d, glInternalFormat %d", + fileName, + header.glFormat, + header.glInternalFormat); + return GlTexture(0, 0, 0); + } + + // skip the key value data + const uintptr_t startTex = sizeof(OVR_KTX_HEADER) + header.bytesOfKeyValueData; + if ((startTex < sizeof(OVR_KTX_HEADER)) || (startTex >= static_cast(bufferLength))) { + ALOG("%s: Invalid KTX header sizes", fileName); + return GlTexture(0, 0, 0); + } + + width = header.pixelWidth; + height = header.pixelHeight; + + const std::uint32_t mipCount = (noMipMaps) + ? 1 + : std::max(static_cast(1u), header.numberOfMipmapLevels); + + if (header.numberOfFaces == 1) { + return CreateGlTexture( + fileName, + format, + width, + height, + buffer + startTex, + bufferLength - startTex, + mipCount, + useSrgbFormat, + true); + } else if (header.numberOfFaces == 6) { + return CreateGlCubeTexture( + fileName, + format, + width, + height, + buffer + startTex, + bufferLength - startTex, + mipCount, + useSrgbFormat, + true); + } else { + ALOG("%s: KTX file has unsupported number of faces %d", fileName, header.numberOfFaces); + } + + width = 0; + height = 0; + return GlTexture(0, 0, 0); +} + +struct OVR_KTX2_HEADER { + std::uint8_t identifier[12]; + std::uint32_t glFormat; + std::uint32_t glTypeSize; + std::uint32_t pixelWidth; + std::uint32_t pixelHeight; + std::uint32_t pixelDepth; + std::uint32_t numberOfArrayElements; + std::uint32_t numberOfFaces; + std::uint32_t numberOfMipmapLevels; + std::uint32_t supercompressionScheme; +}; + +GlTexture LoadTextureKTX2( + const char* fileName, + const unsigned char* buffer, + const int bufferLength, + bool useSrgbFormat, + bool noMipMaps, + int& width, + int& height) { + width = 0; + height = 0; + + if (bufferLength < (int)(sizeof(OVR_KTX2_HEADER))) { + ALOG("%s: Invalid KTX2 file", fileName); + return GlTexture(0, 0, 0); + } + + const char fileIdentifier[12] = { + '\xAB', 'K', 'T', 'X', ' ', '2', '0', '\xBB', '\r', '\n', '\x1A', '\n'}; + + const OVR_KTX2_HEADER& header = *(OVR_KTX2_HEADER*)buffer; + if (memcmp(header.identifier, fileIdentifier, sizeof(fileIdentifier)) != 0) { + ALOG("%s: Invalid KTX2 file", fileName); + return GlTexture(0, 0, 0); + } + // no support for texture arrays + if (header.numberOfArrayElements != 0) { + ALOG( + "%s: KTX2 file has unsupported number of array elements %d", + fileName, + header.numberOfArrayElements); + return GlTexture(0, 0, 0); + } + + width = header.pixelWidth; + height = header.pixelHeight; + + // read ktx2 and transcode if necessary + ktxTexture* kTexture; + KTX_error_code result = ktxTexture_CreateFromMemory( + (const uint8_t*)buffer, bufferLength, KTX_TEXTURE_CREATE_LOAD_IMAGE_DATA_BIT, &kTexture); + if (result != KTX_SUCCESS) { + ALOG("%s: KTX2 CreateFromMemory failed. result is %d", fileName, result); + return GlTexture(0, 0, 0); + } + + if (ktxTexture_NeedsTranscoding(kTexture)) { + result = ktxTexture2_TranscodeBasis( + (ktxTexture2*)kTexture, ktx_transcode_fmt_e::KTX_TTF_ASTC_4x4_RGBA, 0); + if (result != KTX_SUCCESS) { + ALOG("%s: Coudln't transcode ktx2 file to ASTC, ETC files not supported", fileName); + return GlTexture(0, 0, 0); + } + } + + GLuint texid = 0; + GLenum target, glerror; + result = ktxTexture_GLUpload(kTexture, &texid, &target, &glerror); + if (result != KTX_SUCCESS) { + ALOG("%s: GLUpload result failed. result is %d", fileName, result); + return GlTexture(0, 0, 0); + } else { + return GlTexture(texid, target, width, height); + } +} + +unsigned char* LoadImageToRGBABuffer( + const char* fileName, + const unsigned char* inBuffer, + const size_t inBufferLen, + int& width, + int& height) { + std::string ext = GetExtension(fileName); + auto& loc = std::use_facet>(std::locale()); + loc.tolower(&ext[0], &ext[0] + ext.length()); + + width = 0; + height = 0; + + if (ext == ".jpg" || ext == ".tga" || ext == ".png" || ext == ".bmp" || ext == ".psd" || + ext == ".gif" || ext == ".hdr" || ext == ".pic") { + // Uncompressed files loaded by stb_image + int comp; + stbi_uc* image = stbi_load_from_memory( + (unsigned char*)inBuffer, (int)inBufferLen, &width, &height, &comp, 4); + return image; + } + return nullptr; +} + +void FreeRGBABuffer(const unsigned char* buffer) { + stbi_image_free((void*)buffer); +} + +static int MipLevelsForSize(int width, int height) { + int levels = 1; + + while (width > 1 || height > 1) { + levels++; + width >>= 1; + height >>= 1; + } + return levels; +} + +GlTexture LoadTextureFromBuffer( + const char* fileName, + const uint8_t* buffer, + size_t bufferSize, + const TextureFlags_t& flags, + int& width, + int& height) { + std::string ext = GetExtension(fileName); + auto& loc = std::use_facet>(std::locale()); + loc.tolower(&ext[0], &ext[0] + ext.length()); + + // LOG( "Loading texture buffer %s (%s), length %i", fileName, ext.c_str(), buffer.Length ); + + GlTexture texId; + width = 0; + height = 0; + + if (fileName == nullptr || buffer == nullptr || bufferSize < 1) { + // can't load anything from an empty buffer +#if defined(OVR_BUILD_DEBUG) + ALOG( + "LoadTextureFromBuffer - can't load from empties: fileName = %s buffer = %p bufferSize = %d", + fileName == nullptr ? "" : fileName, + buffer == nullptr ? 0 : buffer, + static_cast(bufferSize)); +#endif + } else if ( + ext == ".jpg" || ext == ".tga" || ext == ".png" || ext == ".bmp" || ext == ".psd" || + ext == ".gif" || ext == ".hdr" || ext == ".pic") { + // Uncompressed files loaded by stb_image + int comp; + stbi_uc* image = stbi_load_from_memory(buffer, bufferSize, &width, &height, &comp, 4); + if (image != NULL) { + // Optionally outline the border alpha. + if (flags & TEXTUREFLAG_ALPHA_BORDER) { + for (int i = 0; i < width; i++) { + image[i * 4 + 3] = 0; + image[((height - 1) * width + i) * 4 + 3] = 0; + } + for (int i = 0; i < height; i++) { + image[i * width * 4 + 3] = 0; + image[(i * width + width - 1) * 4 + 3] = 0; + } + } + + const size_t dataSize = GetOvrTextureSize(Texture_RGBA, width, height); + texId = CreateGlTexture( + fileName, + Texture_RGBA, + width, + height, + image, + dataSize, + (flags & TEXTUREFLAG_NO_MIPMAPS) ? 1 : MipLevelsForSize(width, height), + flags & TEXTUREFLAG_USE_SRGB, + false); + free(image); + if (!(flags & TEXTUREFLAG_NO_MIPMAPS)) { + glBindTexture(texId.target, texId.texture); + glGenerateMipmap(texId.target); + glTexParameteri(texId.target, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + } + } else { + ALOG("stbi_load_from_memory() failed!"); + } + } else if (ext == ".pvr") { + texId = LoadTexturePVR( + fileName, + buffer, + bufferSize, + (flags & TEXTUREFLAG_USE_SRGB), + (flags & TEXTUREFLAG_NO_MIPMAPS), + width, + height); + } else if (ext == ".ktx") { + texId = LoadTextureKTX( + fileName, + buffer, + bufferSize, + (flags & TEXTUREFLAG_USE_SRGB), + (flags & TEXTUREFLAG_NO_MIPMAPS), + width, + height); + } else if (ext == ".ktx2") { + texId = LoadTextureKTX2( + fileName, + buffer, + bufferSize, + (flags & TEXTUREFLAG_USE_SRGB), + (flags & TEXTUREFLAG_NO_MIPMAPS), + width, + height); + } else if (ext == ".astc") { + texId = LoadASTCTextureFromMemory(buffer, bufferSize, 4, flags & TEXTUREFLAG_USE_SRGB); + } else if (ext == ".pkm") { + ALOG("PKM format not supported"); + } else { + ALOG("unsupported file extension '%s', for file '%s'", ext.c_str(), fileName); + } + + // Create a default texture if the load failed + if (texId.texture == 0) { + ALOGW("Failed to load %s", fileName); + if ((flags & TEXTUREFLAG_NO_DEFAULT) == 0) { + static uint8_t defaultTexture[8 * 8 * 3] = { + 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, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 255, 255, 255, + 255, 255, 255, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 255, 255, 255, 255, 255, 255, 64, 64, 64, 64, 64, + 64, 255, 255, 255, 255, 255, 255, 64, 64, 64, 64, 64, 64, 255, 255, 255, + 255, 255, 255, 64, 64, 64, 64, 64, 64, 255, 255, 255, 255, 255, 255, 64, + 64, 64, 64, 64, 64, 255, 255, 255, 255, 255, 255, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 255, 255, 255, + 255, 255, 255, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 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}; + texId = LoadRGBTextureFromMemory(defaultTexture, 8, 8, flags & TEXTUREFLAG_USE_SRGB); +#if defined(OVR_BUILD_DEBUG) + ALOG("FAILD to load '%s' -> using default via LoadRGBTextureFromMemory", fileName); +#endif + } + } +#if defined(OVR_BUILD_DEBUG) + else { + ALOG("Finish loading '%s' -> SUCCESS", fileName); + } +#endif + + return texId; +} + +GlTexture LoadTextureFromOtherApplicationPackage( + void* zipFile, + const char* nameInZip, + const TextureFlags_t& flags, + int& width, + int& height) { + width = 0; + height = 0; + if (zipFile == 0) { + return GlTexture(0, 0, 0); + } + + std::vector buffer; + ovr_ReadFileFromOtherApplicationPackage(zipFile, nameInZip, buffer); + if (buffer.size() == 0) { + return GlTexture(0, 0, 0); + } + + return LoadTextureFromBuffer(nameInZip, buffer, flags, width, height); +} + +GlTexture LoadTextureFromApplicationPackage( + const char* nameInZip, + const TextureFlags_t& flags, + int& width, + int& height) { + return LoadTextureFromOtherApplicationPackage( + ovr_GetApplicationPackageFile(), nameInZip, flags, width, height); +} + +GlTexture LoadTextureFromUri( + class ovrFileSys& fileSys, + const char* uri, + const TextureFlags_t& flags, + int& width, + int& height) { + std::vector buffer; + if (!fileSys.ReadFile(uri, buffer)) { + return GlTexture(); + } + + return LoadTextureFromBuffer(uri, buffer, flags, width, height); +} + +void FreeTexture(GlTexture texId) { + if (texId.texture) { + glDeleteTextures(1, &texId.texture); + } +} + +void DeleteTexture(GlTexture& texture) { + if (texture.texture != 0) { + glDeleteTextures(1, &texture.texture); + texture.texture = 0; + texture.target = 0; + texture.Width = 0; + texture.Height = 0; + } +} + +void MakeTextureClamped(GlTexture texId) { + glBindTexture(texId.target, texId.texture); + glTexParameteri(texId.target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(texId.target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glBindTexture(texId.target, 0); +} + +void MakeTextureLodClamped(GlTexture texId, int maxLod) { + glBindTexture(texId.target, texId.texture); + glTexParameteri(texId.target, GL_TEXTURE_MAX_LEVEL, maxLod); + glBindTexture(texId.target, 0); +} + +void MakeTextureTrilinear(GlTexture texId) { + glBindTexture(texId.target, texId.texture); + glTexParameteri(texId.target, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + glTexParameteri(texId.target, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glBindTexture(texId.target, 0); +} + +void MakeTextureLinearNearest(GlTexture texId) { + glBindTexture(texId.target, texId.texture); + glTexParameteri(texId.target, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST); + glTexParameteri(texId.target, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glBindTexture(texId.target, 0); +} + +void MakeTextureLinear(GlTexture texId) { + glBindTexture(texId.target, texId.texture); + glTexParameteri(texId.target, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(texId.target, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glBindTexture(texId.target, 0); +} + +void MakeTextureAniso(GlTexture texId, float maxAniso) { + glBindTexture(texId.target, texId.texture); + glTexParameterf(texId.target, GL_TEXTURE_MAX_ANISOTROPY_EXT, maxAniso); + glBindTexture(texId.target, 0); +} + +void BuildTextureMipmaps(GlTexture texId) { + glBindTexture(texId.target, texId.texture); + glGenerateMipmap(texId.target); + glBindTexture(texId.target, 0); +} + +} // namespace OVRFW diff --git a/Samples/SampleXrFramework/Src/Render/GlTexture.h b/Samples/SampleXrFramework/Src/Render/GlTexture.h new file mode 100755 index 0000000..8c21a5e --- /dev/null +++ b/Samples/SampleXrFramework/Src/Render/GlTexture.h @@ -0,0 +1,266 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/************************************************************************************ + +Filename : GlTexture.h +Content : OpenGL texture loading. +Created : September 30, 2013 +Authors : John Carmack + +*************************************************************************************/ + +#pragma once + +#include +#include +#include "OVR_BitFlags.h" +#include "Egl.h" +#include "OVR_FileSys.h" + +#include + +// Explicitly using unsigned instead of GLUint / GLenum to avoid including GL headers + +namespace OVRFW { + +enum eTextureFlags { + // Normally, a failure to load will create an 8x8 default texture, but + // if you want to take explicit action, setting this flag will cause + // it to return 0 for the texId. + TEXTUREFLAG_NO_DEFAULT, + + // Use GL_SRGB8 / GL_SRGB8_ALPHA8 / GL_COMPRESSED_SRGB8_ETC2 formats instead + // of GL_RGB / GL_RGBA / GL_ETC1_RGB8_OES + TEXTUREFLAG_USE_SRGB, + + // No mip maps are loaded or generated when this flag is specified. + TEXTUREFLAG_NO_MIPMAPS, + + // Forces a one pixel border around the texture to have + // zero alpha, so a blended quad will be perfectly anti-aliased. + // Will only work for uncompressed textures. + // TODO: this only does the top mip level, since we use genMipmaps + // to create the rest. Consider manually building the mip levels. + TEXTUREFLAG_ALPHA_BORDER +}; + +typedef OVR::BitFlagsT TextureFlags_t; + +enum eTextureFormat { + Texture_None = 0x00000, + Texture_R = 0x00100, + Texture_RGB = 0x00200, + Texture_RGBA = 0x00300, + Texture_DXT1 = 0x01100, + Texture_DXT3 = 0x01200, + Texture_DXT5 = 0x01300, + Texture_PVR4bRGB = 0x01400, + Texture_PVR4bRGBA = 0x01500, + Texture_ATC_RGB = 0x01600, + Texture_ATC_RGBA = 0x01700, + Texture_ETC1 = 0x01800, + Texture_ETC2_RGB = 0x01900, + Texture_ETC2_RGBA = 0x01A00, + + // ASTC values must be sequential between Texture_ASTC_Start and Texture_ASTC_End + Texture_ASTC_Start = 0x01B00, + Texture_ASTC_4x4 = Texture_ASTC_Start, + + Texture_ASTC_5x4 = 0x01C00, + Texture_ASTC_5x5 = 0x01D00, + Texture_ASTC_6x5 = 0x01E00, + Texture_ASTC_6x6 = 0x01F00, + Texture_ASTC_8x5 = 0x02000, + Texture_ASTC_8x6 = 0x02100, + Texture_ASTC_8x8 = 0x02200, + Texture_ASTC_10x5 = 0x02300, + Texture_ASTC_10x6 = 0x02400, + Texture_ASTC_10x8 = 0x02500, + Texture_ASTC_10x10 = 0x02600, + Texture_ASTC_12x10 = 0x02700, + Texture_ASTC_12x12 = 0x02800, + + Texture_ASTC_SRGB_4x4 = 0x02900, + Texture_ASTC_SRGB_5x4 = 0x02A00, + Texture_ASTC_SRGB_5x5 = 0x02B00, + Texture_ASTC_SRGB_6x5 = 0x02C00, + Texture_ASTC_SRGB_6x6 = 0x02D00, + Texture_ASTC_SRGB_8x5 = 0x02E00, + Texture_ASTC_SRGB_8x6 = 0x02F00, + Texture_ASTC_SRGB_8x8 = 0x03000, + Texture_ASTC_SRGB_10x5 = 0x03100, + Texture_ASTC_SRGB_10x6 = 0x03200, + Texture_ASTC_SRGB_10x8 = 0x03300, + Texture_ASTC_SRGB_10x10 = 0x03400, + Texture_ASTC_SRGB_12x10 = 0x03500, + Texture_ASTC_SRGB_12x12 = 0x03600, + + Texture_ASTC_End = 0x03700, + + Texture_Depth = 0x08000, + + Texture_TypeMask = 0x0ff00, + Texture_SamplesMask = 0x000ff, + Texture_RenderTarget = 0x10000, + Texture_GenMipmaps = 0x20000 +}; + +// texture id/target pair +// the auto-casting should be removed but allows the target to be ignored by the code that does not +// care +class GlTexture { + public: + GlTexture() : texture(0), target(0), Width(0), Height(0) {} + + GlTexture(const unsigned texture_, const int w, const int h); + + GlTexture(unsigned texture_, unsigned target_, const int w, const int h) + : texture(texture_), target(target_), Width(w), Height(h) {} + operator unsigned() const { + return texture; + } + + bool IsValid() const { + return texture != 0; + } + + unsigned texture; + unsigned target; + int Width; + int Height; +}; + +bool TextureFormatToGlFormat( + const eTextureFormat format, + const bool useSrgbFormat, + GLenum& glFormat, + GLenum& glInternalFormat); +bool GlFormatToTextureFormat( + eTextureFormat& format, + const GLenum glFormat, + const GLenum glInternalFormat); + +// Calculate the full mip chain levels based on width and height. +int ComputeFullMipChainNumLevels(const int width, const int height); + +// Allocates a GPU texture and uploads the raw data. +GlTexture LoadRGBATextureFromMemory( + const uint8_t* texture, + const int width, + const int height, + const bool useSrgbFormat); +GlTexture +LoadRGBACubeTextureFromMemory(const uint8_t* texture, const int dim, const bool useSrgbFormat); +GlTexture LoadRGBTextureFromMemory( + const uint8_t* texture, + const int width, + const int height, + const bool useSrgbFormat); +GlTexture LoadRTextureFromMemory(const uint8_t* texture, const int width, const int height); +GlTexture LoadASTCTextureFromMemory( + const uint8_t* buffer, + const size_t bufferSize, + const int numPlanes, + const bool useSrgbFormat); + +void MakeTextureClamped(GlTexture texid); +void MakeTextureLodClamped(GlTexture texId, int maxLod); +void MakeTextureTrilinear(GlTexture texid); +void MakeTextureLinear(GlTexture texId); +void MakeTextureLinearNearest(GlTexture texId); +void MakeTextureAniso(GlTexture texId, float maxAniso); +void BuildTextureMipmaps(GlTexture texid); + +// Loads an image file to an RGBA buffer. +// Supported formats are: +// .jpg .tga .png .bmp .psd .gif .hdr and .pic +unsigned char* LoadImageToRGBABuffer( + const char* fileName, + const unsigned char* inBuffer, + const size_t inBufferLen, + int& width, + int& height); + +// Free image data allocated by LoadImageToRGBABuffer +void FreeRGBABuffer(const unsigned char* buffer); + +// FileName's extension determines the file type, but the data is taken from an +// already loaded buffer. +// +// The stb_image file formats are supported: +// .jpg .tga .png .bmp .psd .gif .hdr .pic +// +// Limited support for the PVR and KTX container formats. +// +// If TEXTUREFLAG_NO_DEFAULT, no default texture will be created. +// Otherwise a default square texture will be created on any failure. +// +// Uncompressed image formats will have mipmaps generated and trilinear filtering set. +GlTexture LoadTextureFromBuffer( + const char* fileName, + const uint8_t* buffer, + size_t bufferSize, + const TextureFlags_t& flags, + int& width, + int& height); + +inline GlTexture LoadTextureFromBuffer( + const char* fileName, + const std::vector& buffer, + const TextureFlags_t& flags, + int& width, + int& height) { + return LoadTextureFromBuffer(fileName, buffer.data(), buffer.size(), flags, width, height); +} + +// Returns 0 if the file is not found. +// For a file placed in the project assets folder, nameInZip would be +// something like "assets/cube.pvr". +// See GlTexture.h for supported formats. +/// DEPRECATED! Use LoadTextureFromUri instead or your asset will not be loadable in Windows ports! +GlTexture LoadTextureFromOtherApplicationPackage( + void* zipFile, + const char* nameInZip, + const TextureFlags_t& flags, + int& width, + int& height); +/// DEPRECATED! Use the version that takes a fileSys or your asset will not be loadable in Windows +/// ports! +GlTexture LoadTextureFromApplicationPackage( + const char* nameInZip, + const TextureFlags_t& flags, + int& width, + int& height); + +// takes a ovrFileSys compatible URI specifying a texture resource. To load from the application's +// own apk use the apk scheme with the form: +// apk:///res/raw/texture_name.tga +// +// You can also specify an explicit host: +// +// localhost will load from the application's own apk: +// apk://localhost/res/raw/texture_name.tga +// +// Other apk's (assuming they were added to the apk scheme in ovrFileSys::Init() can be specified +// by package name: +// apk://com.oculus/systemactivities/res/raw/texture_name.tga +// +// Finally, fonts have their own host because their actual location may change in the future: +// apk://font/res/raw/efigs.tga +// +// See ovrFileSys comments for more information. +GlTexture LoadTextureFromUri( + class ovrFileSys& fileSys, + const char* uri, + const TextureFlags_t& flags, + int& width, + int& height); + +unsigned char* LoadPVRBuffer(const char* fileName, int& width, int& height); + +// glDeleteTextures() +// Can be safely called on a 0 texture without checking. +void FreeTexture(GlTexture texId); +void DeleteTexture(GlTexture& texture); + +} // namespace OVRFW diff --git a/Samples/SampleXrFramework/Src/Render/GlWrapperWin32.c b/Samples/SampleXrFramework/Src/Render/GlWrapperWin32.c new file mode 100755 index 0000000..6e8f6c4 --- /dev/null +++ b/Samples/SampleXrFramework/Src/Render/GlWrapperWin32.c @@ -0,0 +1,805 @@ +/* +Copyright (c) 2016 Oculus VR, LLC. +*/ + +// This file is a cut down version of gfxwrapper_opengl.c from the OpenXR SDK. +// It is originally based on the Oculus multiplatform GL wrapper but all of the +// code for platforms other than WIN32 has been removed. + +#include "GlWrapperWin32.h" + +#if defined(WIN32) + +#include +#include +#include +#include +#include +#include +#include +#include // for memset +#include // for EBUSY, ETIMEDOUT etc. +#include // for isspace, isdigit + +#define APPLICATION_NAME "OpenGL SI" +#define WINDOW_TITLE "OpenGL SI" + +#include "GL/Glu.h" +#pragma comment(lib, "glu32.lib") + +static void Error(const char* format, ...) { + char buffer[4096]; + va_list args; + va_start(args, format); + vsnprintf_s(buffer, 4096, _TRUNCATE, format, args); + va_end(args); + + OutputDebugStringA(buffer); + + // MessageBoxA(NULL, buffer, "ERROR", MB_OK | MB_ICONINFORMATION); + // Without exiting, the application will likely crash. + if (format != NULL) { + exit(1); + } +} + +/* +================================================================================================================================ + +OpenGL error checking. + +================================================================================================================================ +*/ + +#if !defined(NDEBUG) +#define GL(func) \ + func; \ + GlCheckErrors(#func); +#else +#define GL(func) func; +#endif + +#if !defined(NDEBUG) +#define EGL(func) \ + if (func == EGL_FALSE) { \ + Error(#func " failed: %s", EglErrorString(eglGetError())); \ + } +#else +#define EGL(func) \ + if (func == EGL_FALSE) { \ + Error(#func " failed: %s", EglErrorString(eglGetError())); \ + } +#endif + +/* +================================ +Get proc address / extensions +================================ +*/ + +PROC GetExtension(const char* functionName) { + return wglGetProcAddress(functionName); +} + +PFNGLGENFRAMEBUFFERSPROC glGenFramebuffers; +PFNGLDELETEFRAMEBUFFERSPROC glDeleteFramebuffers; +PFNGLBINDFRAMEBUFFERPROC glBindFramebuffer; +PFNGLBLITFRAMEBUFFERPROC glBlitFramebuffer; +PFNGLGENRENDERBUFFERSPROC glGenRenderbuffers; +PFNGLDELETERENDERBUFFERSPROC glDeleteRenderbuffers; +PFNGLBINDRENDERBUFFERPROC glBindRenderbuffer; +PFNGLISRENDERBUFFERPROC glIsRenderbuffer; +PFNGLRENDERBUFFERSTORAGEPROC glRenderbufferStorage; +PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC glRenderbufferStorageMultisample; +PFNGLFRAMEBUFFERRENDERBUFFERPROC glFramebufferRenderbuffer; +PFNGLFRAMEBUFFERTEXTURE2DPROC glFramebufferTexture2D; +PFNGLFRAMEBUFFERTEXTURELAYERPROC glFramebufferTextureLayer; +PFNGLCHECKFRAMEBUFFERSTATUSPROC glCheckFramebufferStatus; +PFNGLCHECKNAMEDFRAMEBUFFERSTATUSPROC glCheckNamedFramebufferStatus; + +PFNGLGENBUFFERSPROC glGenBuffers; +PFNGLDELETEBUFFERSPROC glDeleteBuffers; +PFNGLBINDBUFFERPROC glBindBuffer; +PFNGLBINDBUFFERBASEPROC glBindBufferBase; +PFNGLBUFFERDATAPROC glBufferData; +PFNGLBUFFERSUBDATAPROC glBufferSubData; +PFNGLBUFFERSTORAGEPROC glBufferStorage; +PFNGLMAPBUFFERPROC glMapBuffer; +PFNGLMAPBUFFERRANGEPROC glMapBufferRange; +PFNGLUNMAPBUFFERPROC glUnmapBuffer; + +PFNGLGENVERTEXARRAYSPROC glGenVertexArrays; +PFNGLDELETEVERTEXARRAYSPROC glDeleteVertexArrays; +PFNGLBINDVERTEXARRAYPROC glBindVertexArray; +PFNGLVERTEXATTRIBPOINTERPROC glVertexAttribPointer; +PFNGLVERTEXATTRIBDIVISORPROC glVertexAttribDivisor; +PFNGLDISABLEVERTEXATTRIBARRAYPROC glDisableVertexAttribArray; +PFNGLENABLEVERTEXATTRIBARRAYPROC glEnableVertexAttribArray; + +PFNGLACTIVETEXTUREPROC glActiveTexture; +PFNGLTEXIMAGE3DPROC glTexImage3D; +PFNGLCOMPRESSEDTEXIMAGE2DPROC glCompressedTexImage2D; +PFNGLCOMPRESSEDTEXIMAGE3DPROC glCompressedTexImage3D; +PFNGLTEXSUBIMAGE3DPROC glTexSubImage3D; +PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC glCompressedTexSubImage2D; +PFNGLCOMPRESSEDTEXSUBIMAGE3DPROC glCompressedTexSubImage3D; +PFNGLTEXSTORAGE2DPROC glTexStorage2D; +PFNGLTEXSTORAGE3DPROC glTexStorage3D; +PFNGLTEXIMAGE2DMULTISAMPLEPROC glTexImage2DMultisample; +PFNGLTEXIMAGE3DMULTISAMPLEPROC glTexImage3DMultisample; +PFNGLTEXSTORAGE2DMULTISAMPLEPROC glTexStorage2DMultisample; +PFNGLTEXSTORAGE3DMULTISAMPLEPROC glTexStorage3DMultisample; +PFNGLGENERATEMIPMAPPROC glGenerateMipmap; +PFNGLBINDIMAGETEXTUREPROC glBindImageTexture; + +PFNGLCREATEPROGRAMPROC glCreateProgram; +PFNGLDELETEPROGRAMPROC glDeleteProgram; +PFNGLCREATESHADERPROC glCreateShader; +PFNGLDELETESHADERPROC glDeleteShader; +PFNGLSHADERSOURCEPROC glShaderSource; +PFNGLCOMPILESHADERPROC glCompileShader; +PFNGLGETSHADERIVPROC glGetShaderiv; +PFNGLGETSHADERINFOLOGPROC glGetShaderInfoLog; +PFNGLUSEPROGRAMPROC glUseProgram; +PFNGLATTACHSHADERPROC glAttachShader; +PFNGLLINKPROGRAMPROC glLinkProgram; +PFNGLGETPROGRAMIVPROC glGetProgramiv; +PFNGLGETPROGRAMINFOLOGPROC glGetProgramInfoLog; +PFNGLGETATTRIBLOCATIONPROC glGetAttribLocation; +PFNGLBINDATTRIBLOCATIONPROC glBindAttribLocation; +PFNGLGETUNIFORMLOCATIONPROC glGetUniformLocation; +PFNGLGETUNIFORMBLOCKINDEXPROC glGetUniformBlockIndex; +PFNGLGETPROGRAMRESOURCEINDEXPROC glGetProgramResourceIndex; +PFNGLUNIFORMBLOCKBINDINGPROC glUniformBlockBinding; +PFNGLSHADERSTORAGEBLOCKBINDINGPROC glShaderStorageBlockBinding; +PFNGLPROGRAMUNIFORM1IPROC glProgramUniform1i; +PFNGLUNIFORM1IPROC glUniform1i; +PFNGLUNIFORM1IVPROC glUniform1iv; +PFNGLUNIFORM2IVPROC glUniform2iv; +PFNGLUNIFORM3IVPROC glUniform3iv; +PFNGLUNIFORM4IVPROC glUniform4iv; +PFNGLUNIFORM1FPROC glUniform1f; +PFNGLUNIFORM3FPROC glUniform3f; +PFNGLUNIFORM1FVPROC glUniform1fv; +PFNGLUNIFORM2FVPROC glUniform2fv; +PFNGLUNIFORM3FVPROC glUniform3fv; +PFNGLUNIFORM4FVPROC glUniform4fv; +PFNGLUNIFORMMATRIX2FVPROC glUniformMatrix2fv; +PFNGLUNIFORMMATRIX2X3FVPROC glUniformMatrix2x3fv; +PFNGLUNIFORMMATRIX2X4FVPROC glUniformMatrix2x4fv; +PFNGLUNIFORMMATRIX3X2FVPROC glUniformMatrix3x2fv; +PFNGLUNIFORMMATRIX3FVPROC glUniformMatrix3fv; +PFNGLUNIFORMMATRIX3X4FVPROC glUniformMatrix3x4fv; +PFNGLUNIFORMMATRIX4X2FVPROC glUniformMatrix4x2fv; +PFNGLUNIFORMMATRIX4X3FVPROC glUniformMatrix4x3fv; +PFNGLUNIFORMMATRIX4FVPROC glUniformMatrix4fv; + +PFNGLDRAWELEMENTSINSTANCEDPROC glDrawElementsInstanced; +PFNGLDISPATCHCOMPUTEPROC glDispatchCompute; +PFNGLMEMORYBARRIERPROC glMemoryBarrier; + +PFNGLGENQUERIESPROC glGenQueries; +PFNGLDELETEQUERIESPROC glDeleteQueries; +PFNGLISQUERYPROC glIsQuery; +PFNGLBEGINQUERYPROC glBeginQuery; +PFNGLENDQUERYPROC glEndQuery; +PFNGLQUERYCOUNTERPROC glQueryCounter; +PFNGLGETQUERYIVPROC glGetQueryiv; +PFNGLGETQUERYOBJECTIVPROC glGetQueryObjectiv; +PFNGLGETQUERYOBJECTUIVPROC glGetQueryObjectuiv; +PFNGLGETQUERYOBJECTI64VPROC glGetQueryObjecti64v; +PFNGLGETQUERYOBJECTUI64VPROC glGetQueryObjectui64v; + +PFNGLFENCESYNCPROC glFenceSync; +PFNGLCLIENTWAITSYNCPROC glClientWaitSync; +PFNGLDELETESYNCPROC glDeleteSync; +PFNGLISSYNCPROC glIsSync; + +PFNGLBLENDFUNCSEPARATEPROC glBlendFuncSeparate; +PFNGLBLENDEQUATIONSEPARATEPROC glBlendEquationSeparate; + +PFNGLDEBUGMESSAGECONTROLPROC glDebugMessageControl; +PFNGLDEBUGMESSAGECALLBACKPROC glDebugMessageCallback; + +PFNGLBLENDCOLORPROC glBlendColor; +PFNWGLCHOOSEPIXELFORMATARBPROC wglChoosePixelFormatARB; +PFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribsARB; +PFNWGLSWAPINTERVALEXTPROC wglSwapIntervalEXT; +PFNWGLDELAYBEFORESWAPNVPROC wglDelayBeforeSwapNV; + +PFNGLDEPTHRANGEFPROC glDepthRangef; +PFNGLBLENDEQUATIONPROC glBlendEquation; +PFNGLINVALIDATEFRAMEBUFFERPROC glInvalidateFramebuffer; + +void GlBootstrapExtensions() { + wglChoosePixelFormatARB = + (PFNWGLCHOOSEPIXELFORMATARBPROC)GetExtension("wglChoosePixelFormatARB"); + wglCreateContextAttribsARB = + (PFNWGLCREATECONTEXTATTRIBSARBPROC)GetExtension("wglCreateContextAttribsARB"); + wglSwapIntervalEXT = (PFNWGLSWAPINTERVALEXTPROC)GetExtension("wglSwapIntervalEXT"); + wglDelayBeforeSwapNV = (PFNWGLDELAYBEFORESWAPNVPROC)GetExtension("wglDelayBeforeSwapNV"); +} + +void GlInitExtensions() { + glGenFramebuffers = (PFNGLGENFRAMEBUFFERSPROC)GetExtension("glGenFramebuffers"); + glDeleteFramebuffers = (PFNGLDELETEFRAMEBUFFERSPROC)GetExtension("glDeleteFramebuffers"); + glBindFramebuffer = (PFNGLBINDFRAMEBUFFERPROC)GetExtension("glBindFramebuffer"); + glBlitFramebuffer = (PFNGLBLITFRAMEBUFFERPROC)GetExtension("glBlitFramebuffer"); + glGenRenderbuffers = (PFNGLGENRENDERBUFFERSPROC)GetExtension("glGenRenderbuffers"); + glDeleteRenderbuffers = (PFNGLDELETERENDERBUFFERSPROC)GetExtension("glDeleteRenderbuffers"); + glBindRenderbuffer = (PFNGLBINDRENDERBUFFERPROC)GetExtension("glBindRenderbuffer"); + glIsRenderbuffer = (PFNGLISRENDERBUFFERPROC)GetExtension("glIsRenderbuffer"); + glRenderbufferStorage = (PFNGLRENDERBUFFERSTORAGEPROC)GetExtension("glRenderbufferStorage"); + glRenderbufferStorageMultisample = + (PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC)GetExtension("glRenderbufferStorageMultisample"); + glFramebufferRenderbuffer = + (PFNGLFRAMEBUFFERRENDERBUFFERPROC)GetExtension("glFramebufferRenderbuffer"); + glFramebufferTexture2D = (PFNGLFRAMEBUFFERTEXTURE2DPROC)GetExtension("glFramebufferTexture2D"); + glFramebufferTextureLayer = + (PFNGLFRAMEBUFFERTEXTURELAYERPROC)GetExtension("glFramebufferTextureLayer"); + glCheckFramebufferStatus = + (PFNGLCHECKFRAMEBUFFERSTATUSPROC)GetExtension("glCheckFramebufferStatus"); + glCheckNamedFramebufferStatus = + (PFNGLCHECKNAMEDFRAMEBUFFERSTATUSPROC)GetExtension("glCheckNamedFramebufferStatus"); + + glGenBuffers = (PFNGLGENBUFFERSPROC)GetExtension("glGenBuffers"); + glDeleteBuffers = (PFNGLDELETEBUFFERSPROC)GetExtension("glDeleteBuffers"); + glBindBuffer = (PFNGLBINDBUFFERPROC)GetExtension("glBindBuffer"); + glBindBufferBase = (PFNGLBINDBUFFERBASEPROC)GetExtension("glBindBufferBase"); + glBufferData = (PFNGLBUFFERDATAPROC)GetExtension("glBufferData"); + glBufferSubData = (PFNGLBUFFERSUBDATAPROC)GetExtension("glBufferSubData"); + glBufferStorage = (PFNGLBUFFERSTORAGEPROC)GetExtension("glBufferStorage"); + glMapBuffer = (PFNGLMAPBUFFERPROC)GetExtension("glMapBuffer"); + glMapBufferRange = (PFNGLMAPBUFFERRANGEPROC)GetExtension("glMapBufferRange"); + glUnmapBuffer = (PFNGLUNMAPBUFFERPROC)GetExtension("glUnmapBuffer"); + + glGenVertexArrays = (PFNGLGENVERTEXARRAYSPROC)GetExtension("glGenVertexArrays"); + glDeleteVertexArrays = (PFNGLDELETEVERTEXARRAYSPROC)GetExtension("glDeleteVertexArrays"); + glBindVertexArray = (PFNGLBINDVERTEXARRAYPROC)GetExtension("glBindVertexArray"); + glVertexAttribPointer = (PFNGLVERTEXATTRIBPOINTERPROC)GetExtension("glVertexAttribPointer"); + glVertexAttribDivisor = (PFNGLVERTEXATTRIBDIVISORPROC)GetExtension("glVertexAttribDivisor"); + glDisableVertexAttribArray = + (PFNGLDISABLEVERTEXATTRIBARRAYPROC)GetExtension("glDisableVertexAttribArray"); + glEnableVertexAttribArray = + (PFNGLENABLEVERTEXATTRIBARRAYPROC)GetExtension("glEnableVertexAttribArray"); + + glActiveTexture = (PFNGLACTIVETEXTUREPROC)GetExtension("glActiveTexture"); + glTexImage3D = (PFNGLTEXIMAGE3DPROC)GetExtension("glTexImage3D"); + glCompressedTexImage2D = (PFNGLCOMPRESSEDTEXIMAGE2DPROC)GetExtension("glCompressedTexImage2D"); + glCompressedTexImage3D = (PFNGLCOMPRESSEDTEXIMAGE3DPROC)GetExtension("glCompressedTexImage3D"); + glTexSubImage3D = (PFNGLTEXSUBIMAGE3DPROC)GetExtension("glTexSubImage3D"); + glCompressedTexSubImage2D = + (PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC)GetExtension("glCompressedTexSubImage2D"); + glCompressedTexSubImage3D = + (PFNGLCOMPRESSEDTEXSUBIMAGE3DPROC)GetExtension("glCompressedTexSubImage3D"); + glTexStorage2D = (PFNGLTEXSTORAGE2DPROC)GetExtension("glTexStorage2D"); + glTexStorage3D = (PFNGLTEXSTORAGE3DPROC)GetExtension("glTexStorage3D"); + glTexImage2DMultisample = + (PFNGLTEXIMAGE2DMULTISAMPLEPROC)GetExtension("glTexImage2DMultisample"); + glTexImage3DMultisample = + (PFNGLTEXIMAGE3DMULTISAMPLEPROC)GetExtension("glTexImage3DMultisample"); + glTexStorage2DMultisample = + (PFNGLTEXSTORAGE2DMULTISAMPLEPROC)GetExtension("glTexStorage2DMultisample"); + glTexStorage3DMultisample = + (PFNGLTEXSTORAGE3DMULTISAMPLEPROC)GetExtension("glTexStorage3DMultisample"); + glGenerateMipmap = (PFNGLGENERATEMIPMAPPROC)GetExtension("glGenerateMipmap"); + glBindImageTexture = (PFNGLBINDIMAGETEXTUREPROC)GetExtension("glBindImageTexture"); + + glCreateProgram = (PFNGLCREATEPROGRAMPROC)GetExtension("glCreateProgram"); + glDeleteProgram = (PFNGLDELETEPROGRAMPROC)GetExtension("glDeleteProgram"); + glCreateShader = (PFNGLCREATESHADERPROC)GetExtension("glCreateShader"); + glDeleteShader = (PFNGLDELETESHADERPROC)GetExtension("glDeleteShader"); + glShaderSource = (PFNGLSHADERSOURCEPROC)GetExtension("glShaderSource"); + glCompileShader = (PFNGLCOMPILESHADERPROC)GetExtension("glCompileShader"); + glGetShaderiv = (PFNGLGETSHADERIVPROC)GetExtension("glGetShaderiv"); + glGetShaderInfoLog = (PFNGLGETSHADERINFOLOGPROC)GetExtension("glGetShaderInfoLog"); + glUseProgram = (PFNGLUSEPROGRAMPROC)GetExtension("glUseProgram"); + glAttachShader = (PFNGLATTACHSHADERPROC)GetExtension("glAttachShader"); + glLinkProgram = (PFNGLLINKPROGRAMPROC)GetExtension("glLinkProgram"); + glGetProgramiv = (PFNGLGETPROGRAMIVPROC)GetExtension("glGetProgramiv"); + glGetProgramInfoLog = (PFNGLGETPROGRAMINFOLOGPROC)GetExtension("glGetProgramInfoLog"); + glGetAttribLocation = (PFNGLGETATTRIBLOCATIONPROC)GetExtension("glGetAttribLocation"); + glBindAttribLocation = (PFNGLBINDATTRIBLOCATIONPROC)GetExtension("glBindAttribLocation"); + glGetUniformLocation = (PFNGLGETUNIFORMLOCATIONPROC)GetExtension("glGetUniformLocation"); + glGetUniformBlockIndex = (PFNGLGETUNIFORMBLOCKINDEXPROC)GetExtension("glGetUniformBlockIndex"); + glProgramUniform1i = (PFNGLPROGRAMUNIFORM1IPROC)GetExtension("glProgramUniform1i"); + glUniform1i = (PFNGLUNIFORM1IPROC)GetExtension("glUniform1i"); + glUniform1iv = (PFNGLUNIFORM1IVPROC)GetExtension("glUniform1iv"); + glUniform2iv = (PFNGLUNIFORM2IVPROC)GetExtension("glUniform2iv"); + glUniform3iv = (PFNGLUNIFORM3IVPROC)GetExtension("glUniform3iv"); + glUniform4iv = (PFNGLUNIFORM4IVPROC)GetExtension("glUniform4iv"); + glUniform1f = (PFNGLUNIFORM1FPROC)GetExtension("glUniform1f"); + glUniform3f = (PFNGLUNIFORM3FPROC)GetExtension("glUniform3f"); + glUniform1fv = (PFNGLUNIFORM1FVPROC)GetExtension("glUniform1fv"); + glUniform2fv = (PFNGLUNIFORM2FVPROC)GetExtension("glUniform2fv"); + glUniform3fv = (PFNGLUNIFORM3FVPROC)GetExtension("glUniform3fv"); + glUniform4fv = (PFNGLUNIFORM4FVPROC)GetExtension("glUniform4fv"); + glUniformMatrix2fv = (PFNGLUNIFORMMATRIX2FVPROC)GetExtension("glUniformMatrix2fv"); + glUniformMatrix2x3fv = (PFNGLUNIFORMMATRIX2X3FVPROC)GetExtension("glUniformMatrix2x3fv"); + glUniformMatrix2x4fv = (PFNGLUNIFORMMATRIX2X4FVPROC)GetExtension("glUniformMatrix2x4fv"); + glUniformMatrix3x2fv = (PFNGLUNIFORMMATRIX3X2FVPROC)GetExtension("glUniformMatrix3x2fv"); + glUniformMatrix3fv = (PFNGLUNIFORMMATRIX3FVPROC)GetExtension("glUniformMatrix3fv"); + glUniformMatrix3x4fv = (PFNGLUNIFORMMATRIX3X4FVPROC)GetExtension("glUniformMatrix3x4fv"); + glUniformMatrix4x2fv = (PFNGLUNIFORMMATRIX4X2FVPROC)GetExtension("glUniformMatrix4x2fv"); + glUniformMatrix4x3fv = (PFNGLUNIFORMMATRIX4X3FVPROC)GetExtension("glUniformMatrix4x3fv"); + glUniformMatrix4fv = (PFNGLUNIFORMMATRIX4FVPROC)GetExtension("glUniformMatrix4fv"); + glGetProgramResourceIndex = + (PFNGLGETPROGRAMRESOURCEINDEXPROC)GetExtension("glGetProgramResourceIndex"); + glUniformBlockBinding = (PFNGLUNIFORMBLOCKBINDINGPROC)GetExtension("glUniformBlockBinding"); + glShaderStorageBlockBinding = + (PFNGLSHADERSTORAGEBLOCKBINDINGPROC)GetExtension("glShaderStorageBlockBinding"); + + glDrawElementsInstanced = + (PFNGLDRAWELEMENTSINSTANCEDPROC)GetExtension("glDrawElementsInstanced"); + glDispatchCompute = (PFNGLDISPATCHCOMPUTEPROC)GetExtension("glDispatchCompute"); + glMemoryBarrier = (PFNGLMEMORYBARRIERPROC)GetExtension("glMemoryBarrier"); + + glGenQueries = (PFNGLGENQUERIESPROC)GetExtension("glGenQueries"); + glDeleteQueries = (PFNGLDELETEQUERIESPROC)GetExtension("glDeleteQueries"); + glIsQuery = (PFNGLISQUERYPROC)GetExtension("glIsQuery"); + glBeginQuery = (PFNGLBEGINQUERYPROC)GetExtension("glBeginQuery"); + glEndQuery = (PFNGLENDQUERYPROC)GetExtension("glEndQuery"); + glQueryCounter = (PFNGLQUERYCOUNTERPROC)GetExtension("glQueryCounter"); + glGetQueryiv = (PFNGLGETQUERYIVPROC)GetExtension("glGetQueryiv"); + glGetQueryObjectiv = (PFNGLGETQUERYOBJECTIVPROC)GetExtension("glGetQueryObjectiv"); + glGetQueryObjectuiv = (PFNGLGETQUERYOBJECTUIVPROC)GetExtension("glGetQueryObjectuiv"); + glGetQueryObjecti64v = (PFNGLGETQUERYOBJECTI64VPROC)GetExtension("glGetQueryObjecti64v"); + glGetQueryObjectui64v = (PFNGLGETQUERYOBJECTUI64VPROC)GetExtension("glGetQueryObjectui64v"); + + glFenceSync = (PFNGLFENCESYNCPROC)GetExtension("glFenceSync"); + glClientWaitSync = (PFNGLCLIENTWAITSYNCPROC)GetExtension("glClientWaitSync"); + glDeleteSync = (PFNGLDELETESYNCPROC)GetExtension("glDeleteSync"); + glIsSync = (PFNGLISSYNCPROC)GetExtension("glIsSync"); + + glBlendFuncSeparate = (PFNGLBLENDFUNCSEPARATEPROC)GetExtension("glBlendFuncSeparate"); + glBlendEquationSeparate = + (PFNGLBLENDEQUATIONSEPARATEPROC)GetExtension("glBlendEquationSeparate"); + + glBlendColor = (PFNGLBLENDCOLORPROC)GetExtension("glBlendColor"); + + glDebugMessageControl = (PFNGLDEBUGMESSAGECONTROLPROC)GetExtension("glDebugMessageControl"); + glDebugMessageCallback = (PFNGLDEBUGMESSAGECALLBACKPROC)GetExtension("glDebugMessageCallback"); + + glDepthRangef = (PFNGLDEPTHRANGEFPROC)GetExtension("glDepthRangef"); + glBlendEquation = (PFNGLBLENDEQUATIONPROC)GetExtension("glBlendEquation"); + glInvalidateFramebuffer = + (PFNGLINVALIDATEFRAMEBUFFERPROC)GetExtension("glInvalidateFramebuffer"); +} + +typedef enum { + KS_GPU_SURFACE_COLOR_FORMAT_R5G6B5, + KS_GPU_SURFACE_COLOR_FORMAT_B5G6R5, + KS_GPU_SURFACE_COLOR_FORMAT_R8G8B8A8, + KS_GPU_SURFACE_COLOR_FORMAT_B8G8R8A8, + KS_GPU_SURFACE_COLOR_FORMAT_MAX +} ksGpuSurfaceColorFormat; + +typedef enum { + KS_GPU_SURFACE_DEPTH_FORMAT_NONE, + KS_GPU_SURFACE_DEPTH_FORMAT_D16, + KS_GPU_SURFACE_DEPTH_FORMAT_D24, + KS_GPU_SURFACE_DEPTH_FORMAT_MAX +} ksGpuSurfaceDepthFormat; + +typedef enum { + KS_GPU_SAMPLE_COUNT_1 = 1, + KS_GPU_SAMPLE_COUNT_2 = 2, + KS_GPU_SAMPLE_COUNT_4 = 4, + KS_GPU_SAMPLE_COUNT_8 = 8, + KS_GPU_SAMPLE_COUNT_16 = 16, + KS_GPU_SAMPLE_COUNT_32 = 32, + KS_GPU_SAMPLE_COUNT_64 = 64, +} ksGpuSampleCount; + +typedef struct { + HDC hDC; + HGLRC hGLRC; +} ksGpuContext; + +typedef struct { + unsigned char redBits; + unsigned char greenBits; + unsigned char blueBits; + unsigned char alphaBits; + unsigned char colorBits; + unsigned char depthBits; +} ksGpuSurfaceBits; + +typedef struct { + ksGpuContext context; + ksGpuSurfaceColorFormat colorFormat; + ksGpuSurfaceDepthFormat depthFormat; + ksGpuSampleCount sampleCount; + int windowWidth; + int windowHeight; + + HINSTANCE hInstance; + HDC hDC; + HWND hWnd; +} ksGpuWindow; + +/* +================================================================================================================================ + +GPU Context. + +================================================================================================================================ +*/ + +ksGpuSurfaceBits ksGpuContext_BitsForSurfaceFormat( + const ksGpuSurfaceColorFormat colorFormat, + const ksGpuSurfaceDepthFormat depthFormat) { + ksGpuSurfaceBits bits; + bits.redBits = + ((colorFormat == KS_GPU_SURFACE_COLOR_FORMAT_R8G8B8A8) + ? 8 + : ((colorFormat == KS_GPU_SURFACE_COLOR_FORMAT_B8G8R8A8) + ? 8 + : ((colorFormat == KS_GPU_SURFACE_COLOR_FORMAT_R5G6B5) + ? 5 + : ((colorFormat == KS_GPU_SURFACE_COLOR_FORMAT_B5G6R5) ? 5 : 8)))); + bits.greenBits = + ((colorFormat == KS_GPU_SURFACE_COLOR_FORMAT_R8G8B8A8) + ? 8 + : ((colorFormat == KS_GPU_SURFACE_COLOR_FORMAT_B8G8R8A8) + ? 8 + : ((colorFormat == KS_GPU_SURFACE_COLOR_FORMAT_R5G6B5) + ? 6 + : ((colorFormat == KS_GPU_SURFACE_COLOR_FORMAT_B5G6R5) ? 6 : 8)))); + bits.blueBits = + ((colorFormat == KS_GPU_SURFACE_COLOR_FORMAT_R8G8B8A8) + ? 8 + : ((colorFormat == KS_GPU_SURFACE_COLOR_FORMAT_B8G8R8A8) + ? 8 + : ((colorFormat == KS_GPU_SURFACE_COLOR_FORMAT_R5G6B5) + ? 5 + : ((colorFormat == KS_GPU_SURFACE_COLOR_FORMAT_B5G6R5) ? 5 : 8)))); + bits.alphaBits = + ((colorFormat == KS_GPU_SURFACE_COLOR_FORMAT_R8G8B8A8) + ? 8 + : ((colorFormat == KS_GPU_SURFACE_COLOR_FORMAT_B8G8R8A8) + ? 8 + : ((colorFormat == KS_GPU_SURFACE_COLOR_FORMAT_R5G6B5) + ? 0 + : ((colorFormat == KS_GPU_SURFACE_COLOR_FORMAT_B5G6R5) ? 0 : 8)))); + bits.colorBits = bits.redBits + bits.greenBits + bits.blueBits + bits.alphaBits; + bits.depthBits = + ((depthFormat == KS_GPU_SURFACE_DEPTH_FORMAT_D16) + ? 16 + : ((depthFormat == KS_GPU_SURFACE_DEPTH_FORMAT_D24) ? 24 : 0)); + return bits; +} + +static bool ksGpuContext_CreateForSurface( + ksGpuContext* context, + const ksGpuSurfaceColorFormat colorFormat, + const ksGpuSurfaceDepthFormat depthFormat, + const ksGpuSampleCount sampleCount, + HINSTANCE hInstance, + HDC hDC) { + const ksGpuSurfaceBits bits = ksGpuContext_BitsForSurfaceFormat(colorFormat, depthFormat); + + PIXELFORMATDESCRIPTOR pfd = { + sizeof(PIXELFORMATDESCRIPTOR), + 1, // version + PFD_DRAW_TO_WINDOW | // must support windowed + PFD_SUPPORT_OPENGL | // must support OpenGL + PFD_DOUBLEBUFFER, // must support double buffering + PFD_TYPE_RGBA, // iPixelType + bits.colorBits, // cColorBits + 0, + 0, // cRedBits, cRedShift + 0, + 0, // cGreenBits, cGreenShift + 0, + 0, // cBlueBits, cBlueShift + 0, + 0, // cAlphaBits, cAlphaShift + 0, // cAccumBits + 0, // cAccumRedBits + 0, // cAccumGreenBits + 0, // cAccumBlueBits + 0, // cAccumAlphaBits + bits.depthBits, // cDepthBits + 0, // cStencilBits + 0, // cAuxBuffers + PFD_MAIN_PLANE, // iLayerType + 0, // bReserved + 0, // dwLayerMask + 0, // dwVisibleMask + 0 // dwDamageMask + }; + + HWND localWnd = NULL; + HDC localDC = hDC; + + if (sampleCount > KS_GPU_SAMPLE_COUNT_1) { + // A valid OpenGL context is needed to get OpenGL extensions including + // wglChoosePixelFormatARB and wglCreateContextAttribsARB. A device context with a valid + // pixel format is needed to create an OpenGL context. However, once a pixel format is set + // on a device context it is final. Therefore a pixel format is set on the device context of + // a temporary window to create a context to get the extensions for multi-sampling. + localWnd = + CreateWindowA(APPLICATION_NAME, "temp", 0, 0, 0, 0, 0, NULL, NULL, hInstance, NULL); + localDC = GetDC(localWnd); + } + + int pixelFormat = ChoosePixelFormat(localDC, &pfd); + if (pixelFormat == 0) { + Error("Failed to find a suitable pixel format."); + return false; + } + + if (!SetPixelFormat(localDC, pixelFormat, &pfd)) { + Error("Failed to set the pixel format."); + return false; + } + + // Now that the pixel format is set, create a temporary context to get the extensions. + { + HGLRC hGLRC = wglCreateContext(localDC); + wglMakeCurrent(localDC, hGLRC); + + GlBootstrapExtensions(); + + wglMakeCurrent(NULL, NULL); + wglDeleteContext(hGLRC); + } + + if (sampleCount > KS_GPU_SAMPLE_COUNT_1) { + // Release the device context and destroy the window that were created to get extensions. + ReleaseDC(localWnd, localDC); + DestroyWindow(localWnd); + + int pixelFormatAttribs[] = { + WGL_DRAW_TO_WINDOW_ARB, + GL_TRUE, + WGL_SUPPORT_OPENGL_ARB, + GL_TRUE, + WGL_DOUBLE_BUFFER_ARB, + GL_TRUE, + WGL_PIXEL_TYPE_ARB, + WGL_TYPE_RGBA_ARB, + WGL_COLOR_BITS_ARB, + bits.colorBits, + WGL_DEPTH_BITS_ARB, + bits.depthBits, + WGL_SAMPLE_BUFFERS_ARB, + 1, + WGL_SAMPLES_ARB, + sampleCount, + 0}; + + unsigned int numPixelFormats = 0; + + if (!wglChoosePixelFormatARB( + hDC, pixelFormatAttribs, NULL, 1, &pixelFormat, &numPixelFormats) || + numPixelFormats == 0) { + Error("Failed to find MSAA pixel format."); + return false; + } + + memset(&pfd, 0, sizeof(pfd)); + + if (!DescribePixelFormat(hDC, pixelFormat, sizeof(PIXELFORMATDESCRIPTOR), &pfd)) { + Error("Failed to describe the pixel format."); + return false; + } + + if (!SetPixelFormat(hDC, pixelFormat, &pfd)) { + Error("Failed to set the pixel format."); + return false; + } + } + + int contextAttribs[] = { + WGL_CONTEXT_MAJOR_VERSION_ARB, + OPENGL_VERSION_MAJOR, + WGL_CONTEXT_MINOR_VERSION_ARB, + OPENGL_VERSION_MINOR, + WGL_CONTEXT_PROFILE_MASK_ARB, + WGL_CONTEXT_CORE_PROFILE_BIT_ARB, + WGL_CONTEXT_FLAGS_ARB, + WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB | WGL_CONTEXT_DEBUG_BIT_ARB, + 0}; + + context->hDC = hDC; + context->hGLRC = wglCreateContextAttribsARB(hDC, NULL, contextAttribs); + if (!context->hGLRC) { + Error("Failed to create GL context."); + return false; + } + + wglMakeCurrent(hDC, context->hGLRC); + + GlInitExtensions(); + + return true; +} + +void ksGpuContext_Destroy(ksGpuContext* context) { + if (context->hGLRC) { + if (!wglMakeCurrent(NULL, NULL)) { + DWORD error = GetLastError(); + Error("Failed to release context error code (%d).", error); + } + + if (!wglDeleteContext(context->hGLRC)) { + DWORD error = GetLastError(); + Error("Failed to delete context error code (%d).", error); + } + context->hGLRC = NULL; + } + context->hDC = NULL; +} + +/* +================================================================================================================================ + +GPU Window. + +================================================================================================================================ +*/ + +void ksGpuWindow_Destroy(ksGpuWindow* window) { + ksGpuContext_Destroy(&window->context); + + if (window->hDC) { + if (!ReleaseDC(window->hWnd, window->hDC)) { + Error("Failed to release device context."); + } + window->hDC = NULL; + } + + if (window->hWnd) { + if (!DestroyWindow(window->hWnd)) { + Error("Failed to destroy the window."); + } + window->hWnd = NULL; + } + + if (window->hInstance) { + if (!UnregisterClassA(APPLICATION_NAME, window->hInstance)) { + Error("Failed to unregister window class."); + } + window->hInstance = NULL; + } +} + +static LRESULT APIENTRY WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { + switch (message) { + case WM_CLOSE: { + PostQuitMessage(0); + return 0; + } + } + return DefWindowProcA(hWnd, message, wParam, lParam); +} + +bool ksGpuWindow_Create( + ksGpuWindow* window, + ksGpuSurfaceColorFormat colorFormat, + ksGpuSurfaceDepthFormat depthFormat, + ksGpuSampleCount sampleCount, + int width, + int height) { + memset(window, 0, sizeof(ksGpuWindow)); + + window->colorFormat = colorFormat; + window->depthFormat = depthFormat; + window->sampleCount = sampleCount; + window->windowWidth = width; + window->windowHeight = height; + + DEVMODEA lpDevMode; + memset(&lpDevMode, 0, sizeof(DEVMODEA)); + lpDevMode.dmSize = sizeof(DEVMODEA); + lpDevMode.dmDriverExtra = 0; + + window->hInstance = GetModuleHandleA(NULL); + + WNDCLASSA wc; + wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; + wc.lpfnWndProc = (WNDPROC)WndProc; + wc.cbClsExtra = 0; + wc.cbWndExtra = 0; + wc.hInstance = window->hInstance; + wc.hIcon = LoadIcon(NULL, IDI_WINLOGO); + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + wc.hbrBackground = NULL; + wc.lpszMenuName = NULL; + wc.lpszClassName = APPLICATION_NAME; + + if (!RegisterClassA(&wc)) { + Error("Failed to register window class."); + return false; + } + + // Fixed size window. + DWORD dwExStyle = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE; + DWORD dwStyle = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX; + + RECT windowRect; + windowRect.left = (long)0; + windowRect.right = (long)width; + windowRect.top = (long)0; + windowRect.bottom = (long)height; + + AdjustWindowRectEx(&windowRect, dwStyle, FALSE, dwExStyle); + + RECT desktopRect; + GetWindowRect(GetDesktopWindow(), &desktopRect); + + const int offsetX = (desktopRect.right - (windowRect.right - windowRect.left)) / 2; + const int offsetY = (desktopRect.bottom - (windowRect.bottom - windowRect.top)) / 2; + + windowRect.left += offsetX; + windowRect.right += offsetX; + windowRect.top += offsetY; + windowRect.bottom += offsetY; + + window->hWnd = CreateWindowExA( + dwExStyle, // Extended style for the window + APPLICATION_NAME, // Class name + WINDOW_TITLE, // Window title + dwStyle | // Defined window style + WS_CLIPSIBLINGS | // Required window style + WS_CLIPCHILDREN, // Required window style + windowRect.left, // Window X position + windowRect.top, // Window Y position + windowRect.right - windowRect.left, // Window width + windowRect.bottom - windowRect.top, // Window height + NULL, // No parent window + NULL, // No menu + window->hInstance, // Instance + NULL); // No WM_CREATE parameter + if (!window->hWnd) { + ksGpuWindow_Destroy(window); + Error("Failed to create window."); + return false; + } + + SetWindowLongPtrA(window->hWnd, GWLP_USERDATA, (LONG_PTR)window); + + window->hDC = GetDC(window->hWnd); + if (!window->hDC) { + ksGpuWindow_Destroy(window); + Error("Failed to acquire device context."); + return false; + } + + ksGpuContext_CreateForSurface( + &window->context, colorFormat, depthFormat, sampleCount, window->hInstance, window->hDC); + + wglMakeCurrent(window->context.hDC, window->context.hGLRC); + + ShowWindow(window->hWnd, SW_SHOW); + SetForegroundWindow(window->hWnd); + SetFocus(window->hWnd); + + return true; +} + +ksGpuWindow window; + +void ovrGl_CreateContext_Windows(HDC* hDC, HGLRC* hGLRC) { + ksGpuSurfaceColorFormat colorFormat = KS_GPU_SURFACE_COLOR_FORMAT_B8G8R8A8; + ksGpuSurfaceDepthFormat depthFormat = KS_GPU_SURFACE_DEPTH_FORMAT_D24; + ksGpuSampleCount sampleCount = KS_GPU_SAMPLE_COUNT_1; + if (!ksGpuWindow_Create(&window, colorFormat, depthFormat, sampleCount, 640, 480)) { + Error("Unable to create GL context"); + } + + *hDC = window.context.hDC; + *hGLRC = window.context.hGLRC; +} + +void ovrGl_DestroyContext_Windows(HDC* hDC, HGLRC* hGLRC) { + ksGpuWindow_Destroy(&window); +} + +const char* ovrGl_ErrorString_Windows(const GLint err) { + return (const char*)gluErrorString(err); +} + +#endif // defined(OS_WINDOWS) diff --git a/Samples/SampleXrFramework/Src/Render/GlWrapperWin32.h b/Samples/SampleXrFramework/Src/Render/GlWrapperWin32.h new file mode 100755 index 0000000..102eec6 --- /dev/null +++ b/Samples/SampleXrFramework/Src/Render/GlWrapperWin32.h @@ -0,0 +1,175 @@ +/* +Copyright (c) 2016 Oculus VR, LLC. +*/ + +// This file is a cut down version of gfxwrapper_opengl.h from the OpenXR SDK. +// It is originally based on the Oculus multiplatform GL wrapper but all of the +// code for platforms other than WIN32 has been removed. + +#pragma once + +#if defined(WIN32) + +#ifdef __cplusplus +extern "C" { +#endif + +#define OPENGL_VERSION_MAJOR 4 +#define OPENGL_VERSION_MINOR 3 +#define GLSL_VERSION "430" +#define SPIRV_VERSION "99" + +#define GL_APIENTRY APIENTRY +#define GL_APIENTRYP APIENTRYP + +#include +#include +#define GL_EXT_color_subtable +#include +#include +#include + +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_NUM_VIEWS_OVR 0x9630 +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_BASE_VIEW_INDEX_OVR 0x9632 +#define GL_MAX_VIEWS_OVR 0x9631 + +extern PFNGLGENFRAMEBUFFERSPROC glGenFramebuffers; +extern PFNGLDELETEFRAMEBUFFERSPROC glDeleteFramebuffers; +extern PFNGLBINDFRAMEBUFFERPROC glBindFramebuffer; +extern PFNGLBLITFRAMEBUFFERPROC glBlitFramebuffer; +extern PFNGLGENRENDERBUFFERSPROC glGenRenderbuffers; +extern PFNGLDELETERENDERBUFFERSPROC glDeleteRenderbuffers; +extern PFNGLBINDRENDERBUFFERPROC glBindRenderbuffer; +extern PFNGLISRENDERBUFFERPROC glIsRenderbuffer; +extern PFNGLRENDERBUFFERSTORAGEPROC glRenderbufferStorage; +extern PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC glRenderbufferStorageMultisample; +extern PFNGLFRAMEBUFFERRENDERBUFFERPROC glFramebufferRenderbuffer; +extern PFNGLFRAMEBUFFERTEXTURE2DPROC glFramebufferTexture2D; +extern PFNGLFRAMEBUFFERTEXTURELAYERPROC glFramebufferTextureLayer; +extern PFNGLCHECKFRAMEBUFFERSTATUSPROC glCheckFramebufferStatus; +extern PFNGLCHECKNAMEDFRAMEBUFFERSTATUSPROC glCheckNamedFramebufferStatus; + +extern PFNGLGENBUFFERSPROC glGenBuffers; +extern PFNGLDELETEBUFFERSPROC glDeleteBuffers; +extern PFNGLBINDBUFFERPROC glBindBuffer; +extern PFNGLBINDBUFFERBASEPROC glBindBufferBase; +extern PFNGLBUFFERDATAPROC glBufferData; +extern PFNGLBUFFERSUBDATAPROC glBufferSubData; +extern PFNGLBUFFERSTORAGEPROC glBufferStorage; +extern PFNGLMAPBUFFERPROC glMapBuffer; +extern PFNGLMAPBUFFERRANGEPROC glMapBufferRange; +extern PFNGLUNMAPBUFFERPROC glUnmapBuffer; + +extern PFNGLGENVERTEXARRAYSPROC glGenVertexArrays; +extern PFNGLDELETEVERTEXARRAYSPROC glDeleteVertexArrays; +extern PFNGLBINDVERTEXARRAYPROC glBindVertexArray; +extern PFNGLVERTEXATTRIBPOINTERPROC glVertexAttribPointer; +extern PFNGLVERTEXATTRIBDIVISORPROC glVertexAttribDivisor; +extern PFNGLDISABLEVERTEXATTRIBARRAYPROC glDisableVertexAttribArray; +extern PFNGLENABLEVERTEXATTRIBARRAYPROC glEnableVertexAttribArray; + +extern PFNGLACTIVETEXTUREPROC glActiveTexture; +extern PFNGLTEXIMAGE3DPROC glTexImage3D; +extern PFNGLCOMPRESSEDTEXIMAGE2DPROC glCompressedTexImage2D; +extern PFNGLCOMPRESSEDTEXIMAGE3DPROC glCompressedTexImage3D; +extern PFNGLTEXSUBIMAGE3DPROC glTexSubImage3D; +extern PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC glCompressedTexSubImage2D; +extern PFNGLCOMPRESSEDTEXSUBIMAGE3DPROC glCompressedTexSubImage3D; + +extern PFNGLTEXSTORAGE2DPROC glTexStorage2D; +extern PFNGLTEXSTORAGE3DPROC glTexStorage3D; +extern PFNGLTEXIMAGE2DMULTISAMPLEPROC glTexImage2DMultisample; +extern PFNGLTEXIMAGE3DMULTISAMPLEPROC glTexImage3DMultisample; +extern PFNGLTEXSTORAGE2DMULTISAMPLEPROC glTexStorage2DMultisample; +extern PFNGLTEXSTORAGE3DMULTISAMPLEPROC glTexStorage3DMultisample; +extern PFNGLGENERATEMIPMAPPROC glGenerateMipmap; +extern PFNGLBINDIMAGETEXTUREPROC glBindImageTexture; + +extern PFNGLCREATEPROGRAMPROC glCreateProgram; +extern PFNGLDELETEPROGRAMPROC glDeleteProgram; +extern PFNGLCREATESHADERPROC glCreateShader; +extern PFNGLDELETESHADERPROC glDeleteShader; +extern PFNGLSHADERSOURCEPROC glShaderSource; +extern PFNGLCOMPILESHADERPROC glCompileShader; +extern PFNGLGETSHADERIVPROC glGetShaderiv; +extern PFNGLGETSHADERINFOLOGPROC glGetShaderInfoLog; +extern PFNGLUSEPROGRAMPROC glUseProgram; +extern PFNGLATTACHSHADERPROC glAttachShader; +extern PFNGLLINKPROGRAMPROC glLinkProgram; +extern PFNGLGETPROGRAMIVPROC glGetProgramiv; +extern PFNGLGETPROGRAMINFOLOGPROC glGetProgramInfoLog; +extern PFNGLGETATTRIBLOCATIONPROC glGetAttribLocation; +extern PFNGLBINDATTRIBLOCATIONPROC glBindAttribLocation; +extern PFNGLGETUNIFORMLOCATIONPROC glGetUniformLocation; +extern PFNGLGETUNIFORMBLOCKINDEXPROC glGetUniformBlockIndex; +extern PFNGLGETPROGRAMRESOURCEINDEXPROC glGetProgramResourceIndex; +extern PFNGLUNIFORMBLOCKBINDINGPROC glUniformBlockBinding; +extern PFNGLSHADERSTORAGEBLOCKBINDINGPROC glShaderStorageBlockBinding; +extern PFNGLPROGRAMUNIFORM1IPROC glProgramUniform1i; +extern PFNGLUNIFORM1IPROC glUniform1i; +extern PFNGLUNIFORM1IVPROC glUniform1iv; +extern PFNGLUNIFORM2IVPROC glUniform2iv; +extern PFNGLUNIFORM3IVPROC glUniform3iv; +extern PFNGLUNIFORM4IVPROC glUniform4iv; +extern PFNGLUNIFORM1FPROC glUniform1f; +extern PFNGLUNIFORM3FPROC glUniform3f; +extern PFNGLUNIFORM1FVPROC glUniform1fv; +extern PFNGLUNIFORM2FVPROC glUniform2fv; +extern PFNGLUNIFORM3FVPROC glUniform3fv; +extern PFNGLUNIFORM4FVPROC glUniform4fv; +extern PFNGLUNIFORMMATRIX2FVPROC glUniformMatrix2fv; +extern PFNGLUNIFORMMATRIX2X3FVPROC glUniformMatrix2x3fv; +extern PFNGLUNIFORMMATRIX2X4FVPROC glUniformMatrix2x4fv; +extern PFNGLUNIFORMMATRIX3X2FVPROC glUniformMatrix3x2fv; +extern PFNGLUNIFORMMATRIX3FVPROC glUniformMatrix3fv; +extern PFNGLUNIFORMMATRIX3X4FVPROC glUniformMatrix3x4fv; +extern PFNGLUNIFORMMATRIX4X2FVPROC glUniformMatrix4x2fv; +extern PFNGLUNIFORMMATRIX4X3FVPROC glUniformMatrix4x3fv; +extern PFNGLUNIFORMMATRIX4FVPROC glUniformMatrix4fv; + +extern PFNGLDRAWELEMENTSINSTANCEDPROC glDrawElementsInstanced; +extern PFNGLDISPATCHCOMPUTEPROC glDispatchCompute; +extern PFNGLMEMORYBARRIERPROC glMemoryBarrier; + +extern PFNGLGENQUERIESPROC glGenQueries; +extern PFNGLDELETEQUERIESPROC glDeleteQueries; +extern PFNGLISQUERYPROC glIsQuery; +extern PFNGLBEGINQUERYPROC glBeginQuery; +extern PFNGLENDQUERYPROC glEndQuery; +extern PFNGLQUERYCOUNTERPROC glQueryCounter; +extern PFNGLGETQUERYIVPROC glGetQueryiv; +extern PFNGLGETQUERYOBJECTIVPROC glGetQueryObjectiv; +extern PFNGLGETQUERYOBJECTUIVPROC glGetQueryObjectuiv; +extern PFNGLGETQUERYOBJECTI64VPROC glGetQueryObjecti64v; +extern PFNGLGETQUERYOBJECTUI64VPROC glGetQueryObjectui64v; + +extern PFNGLFENCESYNCPROC glFenceSync; +extern PFNGLCLIENTWAITSYNCPROC glClientWaitSync; +extern PFNGLDELETESYNCPROC glDeleteSync; +extern PFNGLISSYNCPROC glIsSync; + +extern PFNGLBLENDFUNCSEPARATEPROC glBlendFuncSeparate; +extern PFNGLBLENDEQUATIONSEPARATEPROC glBlendEquationSeparate; + +extern PFNGLDEBUGMESSAGECONTROLPROC glDebugMessageControl; +extern PFNGLDEBUGMESSAGECALLBACKPROC glDebugMessageCallback; + +extern PFNGLBLENDCOLORPROC glBlendColor; +extern PFNWGLCHOOSEPIXELFORMATARBPROC wglChoosePixelFormatARB; +extern PFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribsARB; +extern PFNWGLSWAPINTERVALEXTPROC wglSwapIntervalEXT; +extern PFNWGLDELAYBEFORESWAPNVPROC wglDelayBeforeSwapNV; + +extern PFNGLDEPTHRANGEFPROC glDepthRangef; +extern PFNGLBLENDEQUATIONPROC glBlendEquation; +extern PFNGLINVALIDATEFRAMEBUFFERPROC glInvalidateFramebuffer; + +void ovrGl_CreateContext_Windows(HDC* hDC, HGLRC* hGLRC); +void ovrGl_DestroyContext_Windows(); +const char* ovrGl_ErrorString_Windows(const GLint error); + +#ifdef __cplusplus +} +#endif + +#endif // defined(WIN32) diff --git a/Samples/SampleXrFramework/Src/Render/GpuState.h b/Samples/SampleXrFramework/Src/Render/GpuState.h new file mode 100755 index 0000000..fc6be73 --- /dev/null +++ b/Samples/SampleXrFramework/Src/Render/GpuState.h @@ -0,0 +1,67 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/************************************************************************************ + +Filename : GpuState.h +Content : Gpu state management. +Created : August 9. 2013 +Authors : John Carmack + +*************************************************************************************/ + +#pragma once + +#include "Egl.h" + +#if !defined(GL_FILL) +#define GL_FILL 0x00 // just a placeholder -- this define doesn't exist in GL ES +#endif + +namespace OVRFW { + +struct ovrGpuState { + enum ovrBlendEnable { BLEND_DISABLE, BLEND_ENABLE, BLEND_ENABLE_SEPARATE }; + + ovrGpuState() + : blendMode(GL_FUNC_ADD), + blendSrc(GL_ONE), + blendDst(GL_ZERO), + blendSrcAlpha(GL_ONE), + blendDstAlpha(GL_ZERO), + blendModeAlpha(GL_FUNC_ADD), + depthFunc(GL_LEQUAL), + frontFace(GL_CCW), + polygonMode(GL_FILL), + blendEnable(BLEND_DISABLE), + depthEnable(true), + depthMaskEnable(true), + polygonOffsetEnable(false), + cullEnable(true), + lineWidth(1.0f) { + colorMaskEnable[0] = colorMaskEnable[1] = colorMaskEnable[2] = colorMaskEnable[3] = true; + depthRange[0] = 0.0f; + depthRange[1] = 1.0f; + } + + GLenum blendMode; // GL_FUNC_ADD, GL_FUNC_SUBTRACT, GL_FUNC_REVERSE_SUBTRACT, GL_MIN, GL_MAX + GLenum blendSrc; + GLenum blendDst; + GLenum blendSrcAlpha; + GLenum blendDstAlpha; + GLenum blendModeAlpha; + GLenum depthFunc; + GLenum frontFace; // GL_CW, GL_CCW + GLenum polygonMode; + + ovrBlendEnable blendEnable; // off, normal, separate + + bool depthEnable; + bool depthMaskEnable; + bool colorMaskEnable[4]; + bool polygonOffsetEnable; + bool cullEnable; + GLfloat lineWidth; + GLfloat depthRange[2]; // nearVal, farVal +}; + +} // namespace OVRFW diff --git a/Samples/SampleXrFramework/Src/Render/PanelRenderer.cpp b/Samples/SampleXrFramework/Src/Render/PanelRenderer.cpp new file mode 100755 index 0000000..c001724 --- /dev/null +++ b/Samples/SampleXrFramework/Src/Render/PanelRenderer.cpp @@ -0,0 +1,139 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/************************************************************************************ + +Filename : PanelRenderer.cpp +Content : Class that manages and renders quad-based panels with custom shaders. +Created : September 19, 2019 +Authors : Federico Schliemann + +*************************************************************************************/ + +#include "PanelRenderer.h" + +#include "Misc/Log.h" +#include "GlGeometry.h" + +using OVR::Matrix4f; +using OVR::Posef; +using OVR::Quatf; +using OVR::Vector2f; +using OVR::Vector3f; +using OVR::Vector4f; + +namespace OVRFW { + +static const char* VertexSrc = R"glsl( +attribute vec4 Position; +attribute vec2 TexCoord; + +varying highp vec2 oTexCoord; + +void main() +{ + gl_Position = TransformVertex( Position ); + oTexCoord = TexCoord; +} +)glsl"; + +static const char* FragmentSrc = R"glsl( +uniform highp vec4 ChannelEnable; +uniform highp vec2 GraphOffset; +uniform highp vec4 ChannelColor0; +uniform highp vec4 ChannelColor1; +uniform highp vec4 ChannelColor2; +uniform highp vec4 ChannelColor3; +uniform ChannelData +{ + highp vec4 dataSample[256]; +} cd; + +varying highp vec2 oTexCoord; + +void main() +{ + vec2 pixelPos = vec2( (oTexCoord.x + GraphOffset.x) * 256.0, oTexCoord.y * 256.0); + vec4 dataS = cd.dataSample[ int(pixelPos.x) & 0x00FF ]; + + vec4 noData = vec4(0); + float invCoord = 1.0 - oTexCoord.y; + + // first channel + vec4 color0 = dataS.x > invCoord ? ChannelColor0 : noData; + vec4 color1 = dataS.y > invCoord ? ChannelColor1 : noData; + vec4 color2 = dataS.z > invCoord ? ChannelColor2 : noData; + vec4 color4 = dataS.w > invCoord ? ChannelColor3 : noData; + + vec4 aggregate = + color0 * ChannelEnable.x + + color1 * ChannelEnable.y + + color2 * ChannelEnable.z + + color4 * ChannelEnable.w; + + gl_FragColor = min( aggregate, vec4(1) ); +} +)glsl"; + +static ovrProgramParm UniformParms[] = { + {"ChannelEnable", ovrProgramParmType::FLOAT_VECTOR4}, + {"GraphOffset", ovrProgramParmType::FLOAT_VECTOR2}, + {"ChannelData", ovrProgramParmType::BUFFER_UNIFORM}, + {"ChannelColor0", ovrProgramParmType::FLOAT_VECTOR4}, + {"ChannelColor1", ovrProgramParmType::FLOAT_VECTOR4}, + {"ChannelColor2", ovrProgramParmType::FLOAT_VECTOR4}, + {"ChannelColor3", ovrProgramParmType::FLOAT_VECTOR4}, +}; + +void ovrPanelRenderer::Init() { + Program = GlProgram::Build( + VertexSrc, FragmentSrc, UniformParms, sizeof(UniformParms) / sizeof(UniformParms[0])); + + SurfaceDef.geo = BuildTesselatedQuad(1, 1, false); + + ChannelDataBuffer.Create( + GLBUFFER_TYPE_UNIFORM, NUM_DATA_POINTS * sizeof(Vector4f), UniformChannelData.data()); + + /// Hook the graphics command + ovrGraphicsCommand& gc = SurfaceDef.graphicsCommand; + gc.Program = Program; + gc.UniformData[0].Data = &UniformChannelEnable; + gc.UniformData[1].Data = &UniformGraphOffset; + gc.UniformData[2].Count = NUM_DATA_POINTS; + gc.UniformData[2].Data = &ChannelDataBuffer; + gc.UniformData[3].Data = &UniformChannelColor[0]; + gc.UniformData[4].Data = &UniformChannelColor[1]; + gc.UniformData[5].Data = &UniformChannelColor[2]; + gc.UniformData[6].Data = &UniformChannelColor[3]; + + /// gpu state needs alpha blending + gc.GpuState.depthEnable = gc.GpuState.depthMaskEnable = true; + gc.GpuState.blendEnable = ovrGpuState::BLEND_ENABLE; + gc.GpuState.blendSrc = GL_SRC_ALPHA; + gc.GpuState.blendDst = GL_ONE_MINUS_SRC_ALPHA; +} + +void ovrPanelRenderer::Shutdown() {} + +void ovrPanelRenderer::Update(const OVR::Vector4f& dataPoint) { + // Update data + UniformChannelData[WritePosition] = dataPoint; + + // Update rendering offset + const float dataOffsetSize = static_cast(NUM_DATA_POINTS - 1); + UniformGraphOffset.x = static_cast(WritePosition) / dataOffsetSize; + + // Move circular buffer + WritePosition = (WritePosition + 1) % NUM_DATA_POINTS; + + /// Update the shader uniform parameters + ChannelDataBuffer.Update( + UniformChannelData.size() * sizeof(Vector4f), UniformChannelData.data()); +} + +void ovrPanelRenderer::Render(std::vector& surfaceList) { + if (SurfaceDef.geo.indexCount > 0) { + surfaceList.push_back(ovrDrawSurface(ModelMatrix, &SurfaceDef)); + } +} + +} // namespace OVRFW diff --git a/Samples/SampleXrFramework/Src/Render/PanelRenderer.h b/Samples/SampleXrFramework/Src/Render/PanelRenderer.h new file mode 100755 index 0000000..7ad1a15 --- /dev/null +++ b/Samples/SampleXrFramework/Src/Render/PanelRenderer.h @@ -0,0 +1,75 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/************************************************************************************ + +Filename : PanelRenderer.h +Content : Class that manages and renders quad-based panels with custom shaders. +Created : September 19, 2019 +Authors : Federico Schliemann + +*************************************************************************************/ + +#pragma once + +#include + +#include "OVR_Math.h" + +#include "SurfaceRender.h" +#include "GlProgram.h" + +#include "FrameParams.h" + +namespace OVRFW { + +//============================================================== +// PanelRenderer +class ovrPanelRenderer { + public: + static const int NUM_DATA_POINTS = 256; + + ovrPanelRenderer() + : ModelScale(1.0f), + UniformChannelEnable(1.0f, 1.0f, 1.0f, 1.0f), + UniformGraphOffset(1.0f, 0.0f), + WritePosition(0) { + UniformChannelData.resize(NUM_DATA_POINTS, OVR::Vector4f(0.0f)); + UniformChannelColor[0] = OVR::Vector4f(0.75f, 0.0f, 0.0f, 0.5f); + UniformChannelColor[1] = OVR::Vector4f(0.0f, 0.75f, 0.0f, 0.5f); + UniformChannelColor[2] = OVR::Vector4f(0.0f, 0.0f, 0.75f, 0.5f); + UniformChannelColor[3] = OVR::Vector4f(0.5f, 0.0f, 0.5f, 0.5f); + } + virtual ~ovrPanelRenderer() = default; + + virtual void Init(); + virtual void Shutdown(); + + virtual void Update(const OVR::Vector4f& dataPoint); + virtual void Render(std::vector& surfaceList); + + void SetPose(const OVR::Posef& pose) { + ModelMatrix = OVR::Matrix4f(pose) * OVR::Matrix4f::Scaling(ModelScale); + } + void SetScale(OVR::Vector3f v) { + ModelScale = v; + } + void SetChannelColor(int channel, OVR::Vector4f c) { + UniformChannelColor[channel % 4] = c; + } + + private: + ovrSurfaceDef SurfaceDef; + GlProgram Program; + OVR::Matrix4f ModelMatrix; + OVR::Vector3f ModelScale; + + OVR::Vector4f UniformChannelEnable; + OVR::Vector2f UniformGraphOffset; + std::vector UniformChannelData; + OVR::Vector4f UniformChannelColor[4]; + + GlBuffer ChannelDataBuffer; + int WritePosition; +}; + +} // namespace OVRFW diff --git a/Samples/SampleXrFramework/Src/Render/ParticleSystem.cpp b/Samples/SampleXrFramework/Src/Render/ParticleSystem.cpp new file mode 100755 index 0000000..52b742f --- /dev/null +++ b/Samples/SampleXrFramework/Src/Render/ParticleSystem.cpp @@ -0,0 +1,465 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/************************************************************************************ + +Filename : ParticleSystem.h +Content : A simple particle system for System Activities. +Created : October 12, 2015 +Authors : Jonathan E. Wright + +************************************************************************************/ + +#include "ParticleSystem.h" + +#include "TextureAtlas.h" +#include "Render/GeometryBuilder.h" +#include "Render/GlGeometry.h" + +using OVR::Matrix4f; +using OVR::Posef; +using OVR::Quatf; +using OVR::Vector2f; +using OVR::Vector3f; +using OVR::Vector4f; + +inline Vector3f GetViewMatrixForward(Matrix4f const& m) { + return Vector3f(-m.M[2][0], -m.M[2][1], -m.M[2][2]).Normalized(); +} + +namespace OVRFW { + +static const char* particleVertexSrc = R"glsl( +attribute vec4 Position; +attribute vec2 TexCoord; +attribute vec4 VertexColor; +varying highp vec2 oTexCoord; +varying lowp vec4 oColor; +void main() +{ + gl_Position = TransformVertex( Position ); + oTexCoord = TexCoord; + oColor = VertexColor; +} +)glsl"; + +static const char* particleFragmentSrc = R"glsl( +uniform sampler2D Texture0; +varying highp vec2 oTexCoord; +varying lowp vec4 oColor; +void main() +{ + gl_FragColor = oColor * texture2D( Texture0, oTexCoord ); +} +)glsl"; + +static const char* particleGeoFragmentSrc = R"glsl( +precision highp float; + +varying highp vec2 oTexCoord; +varying lowp vec4 oColor; +void main() +{ + float dist = distance(oTexCoord, vec2(0.0f)); + float alpha = smoothstep(0.6f, 0.35f, dist); + gl_FragColor = mix(vec4(0.0f), oColor, alpha); +} +)glsl"; + +static Vector3f quadVertPos[4] = { + {-0.5f, 0.5f, 0.0f}, + {0.5f, 0.5f, 0.0f}, + {0.5f, -0.5f, 0.0f}, + {-0.5f, -0.5f, 0.0f}}; + +static Vector2f quadUVs[4] = {{0.0f, 0.0f}, {1.0f, 0.0f}, {1.0f, 1.0f}, {0.0f, 1.0f}}; + +ovrParticleSystem::ovrParticleSystem() : MaxParticles(0) {} + +ovrParticleSystem::~ovrParticleSystem() { + Shutdown(); +} + +void ovrParticleSystem::Init( + const int maxParticles, + const ovrTextureAtlas* atlas, + const ovrGpuState& gpuState, + bool const sortParticles) { + // this can be called multiple times + Shutdown(); + + MaxParticles = maxParticles; + + // free any existing particles + Particles.Reserve(maxParticles); + FreeParticles.Reserve(maxParticles); + ActiveParticles.Reserve(maxParticles); + + // create the geometry + CreateGeometry(maxParticles); + + { + OVRFW::ovrProgramParm uniformParms[] = { + /// Vertex + /// Fragment + {"Texture0", OVRFW::ovrProgramParmType::TEXTURE_SAMPLED}, + }; + const int uniformCount = sizeof(uniformParms) / sizeof(OVRFW::ovrProgramParm); + if (atlas != nullptr) { + Program = OVRFW::GlProgram::Build( + particleVertexSrc, particleFragmentSrc, uniformParms, uniformCount); + SurfaceDef.surfaceName = std::string("particles_") + atlas->GetTextureName(); + SurfaceDef.graphicsCommand.Textures[0] = atlas->GetTexture(); + } else { + Program = OVRFW::GlProgram::Build( + particleVertexSrc, particleGeoFragmentSrc, uniformParms, uniformCount); + } + } + + SurfaceDef.graphicsCommand.Program = Program; + SurfaceDef.graphicsCommand.BindUniformTextures(); + + SurfaceDef.graphicsCommand.GpuState = gpuState; + + SortParticles = sortParticles; + + Derived.Reserve(maxParticles); + SortIndices.Reserve(maxParticles); + Attr.position.Reserve(maxParticles * 4); + Attr.color.Reserve(maxParticles * 4); + Attr.uv0.Reserve(maxParticles * 4); + PackedAttr.Reserve(maxParticles * 12 * 16 * 8); +} + +ovrGpuState ovrParticleSystem::GetDefaultGpuState() { + ovrGpuState s; + s.blendEnable = ovrGpuState::BLEND_ENABLE; + s.blendSrc = GL_SRC_ALPHA; + s.blendDst = GL_ONE; + s.depthEnable = true; + s.depthMaskEnable = false; + s.cullEnable = true; + return s; +} + +int ParticleSortFn(void const* a, void const* b) { + if (static_cast(b)->DistanceSq < + static_cast(a)->DistanceSq) { + return -1; + } + return 1; +} + +template +void PackVertexAttribute2( + ovrSimpleArray& packed, + const ovrSimpleArray<_attrib_type_>& attrib, + const int glLocation, + const int glType, + const int glComponents) { + if (attrib.GetSize() > 0) { + const int offset = packed.GetSizeI(); + const int size = attrib.GetSizeI() * sizeof(attrib[0]); + + packed.Resize(offset + size); + memcpy(&packed[offset], attrib.GetDataPtr(), size); + + glEnableVertexAttribArray(glLocation); + glVertexAttribPointer( + glLocation, glComponents, glType, false, sizeof(attrib[0]), (void*)((size_t)offset)); + } else { + glDisableVertexAttribArray(glLocation); + } +} + +void UpdateGeometry( + GlGeometry& geo, + ovrSimpleArray& packed, + const ovrVertexAttribs& attribs) { + geo.vertexCount = attribs.position.GetSizeI(); + + glBindVertexArray(geo.vertexArrayObject); + + glBindBuffer(GL_ARRAY_BUFFER, geo.vertexBuffer); + + PackVertexAttribute2(packed, attribs.position, VERTEX_ATTRIBUTE_LOCATION_POSITION, GL_FLOAT, 3); + PackVertexAttribute2(packed, attribs.normal, VERTEX_ATTRIBUTE_LOCATION_NORMAL, GL_FLOAT, 3); + PackVertexAttribute2(packed, attribs.tangent, VERTEX_ATTRIBUTE_LOCATION_TANGENT, GL_FLOAT, 3); + PackVertexAttribute2(packed, attribs.binormal, VERTEX_ATTRIBUTE_LOCATION_BINORMAL, GL_FLOAT, 3); + PackVertexAttribute2(packed, attribs.color, VERTEX_ATTRIBUTE_LOCATION_COLOR, GL_FLOAT, 4); + PackVertexAttribute2(packed, attribs.uv0, VERTEX_ATTRIBUTE_LOCATION_UV0, GL_FLOAT, 2); + PackVertexAttribute2(packed, attribs.uv1, VERTEX_ATTRIBUTE_LOCATION_UV1, GL_FLOAT, 2); + PackVertexAttribute2( + packed, attribs.jointIndices, VERTEX_ATTRIBUTE_LOCATION_JOINT_INDICES, GL_INT, 4); + PackVertexAttribute2( + packed, attribs.jointWeights, VERTEX_ATTRIBUTE_LOCATION_JOINT_WEIGHTS, GL_FLOAT, 4); + + size_t s = packed.GetSize() * sizeof(packed[0]); + glBufferData( + GL_ARRAY_BUFFER, + s, + packed.GetDataPtr(), + GL_STATIC_DRAW); // shouldn't this be GL_DYNAMIC_DRAW? +} + +void ovrParticleSystem::Frame( + const OVRFW::ovrApplFrameIn& frame, + const ovrTextureAtlas* atlas, + const Matrix4f& centerEyeViewMatrix) { + // OVR_PERF_TIMER( ovrParticleSystem_Frame ); + + SurfaceDef.geo.indexCount = 0; + if (ActiveParticles.GetSizeI() <= 0) { + return; + } + + // update particles + Matrix4f invViewMatrix = centerEyeViewMatrix.Inverted(); + Vector3f viewPos = invViewMatrix.GetTranslation(); + + int activeCount = 0; + + // update existing particles and its vertices, deriving the current state of each particle based + // on it's current age store the derived state of each particle in the derived array. Also, make + // another array that is just the index of each particle and its distance to the view position. + // This array will be sorted by distance and then used to index into the derived array the + // vertices are transformed. + + ovrSimpleArray& derived = Derived; + ovrSimpleArray& sortIndices = SortIndices; + derived.Resize(ActiveParticles.GetSizeI()); + sortIndices.Resize(ActiveParticles.GetSizeI()); + + for (int i = 0; i < ActiveParticles.GetSizeI(); ++i) { + const handle_t handle = ActiveParticles[i]; + ovrParticle& p = Particles[handle.Get()]; + + if (frame.PredictedDisplayTime - p.StartTime > p.LifeTime) { + // free expired particle + p.StartTime = -1.0; // mark as unused + FreeParticles.PushBack(handle); + ActiveParticles.RemoveAtUnordered(i); + i--; // last particle was moved into current slot, so don't skip it + continue; + } + + float t = static_cast(frame.PredictedDisplayTime - p.StartTime); + float tSq = t * t; + + particleDerived_t& d = derived[activeCount]; + // x = x0 + v0 * t + 0.5f * a * t^2 + d.Pos = p.InitialPosition + p.InitialVelocity * t + p.HalfAcceleration * tSq; + d.Orientation = (p.RotationRate * t) + p.InitialOrientation; + + d.Color = EaseFunctions[p.EaseFunc](p.InitialColor, t / p.LifeTime); + + d.Scale = p.InitialScale; + d.SpriteIndex = p.SpriteIndex; + + particleSort_t& ps = sortIndices[activeCount]; + ps.ActiveIndex = activeCount; + ps.DistanceSq = + (d.Pos - viewPos).LengthSq(); // Dot( centerEyeViewMatrix.GetZBasis() );//.LengthSq(); + + activeCount++; + } + + assert(ActiveParticles.GetSizeI() == activeCount); + + if (activeCount > 0) { + // sort by distance to view pos + if (SortParticles) { + qsort(&sortIndices[0], activeCount, sizeof(sortIndices[0]), ParticleSortFn); + } + + Attr.position.Resize(activeCount * 4); + Attr.color.Resize(activeCount * 4); + Attr.uv0.Resize(activeCount * 4); + + // transform vertices for each particle quad + for (int i = 0; i < activeCount; ++i) { + particleSort_t const& si = sortIndices[i]; + particleDerived_t const& p = derived[si.ActiveIndex]; + + // transform vertices on the CPU + Matrix4f rotMatrix = Matrix4f::RotationZ(p.Orientation); + // This always aligns the particle to the direction of the particle to the view + // position. This looks a little better but is more expensive and only really makes a + // difference for large particles. + Vector3f normal = (viewPos - p.Pos).Normalized(); + if (normal.LengthSq() < 0.999f) { + normal = GetViewMatrixForward(centerEyeViewMatrix); + } + Matrix4f particleTransform = + Matrix4f::CreateFromBasisVectors(normal, Vector3f(0.0f, 1.0f, 0.0f)); + particleTransform.SetTranslation(p.Pos); + + for (int v = 0; v < 4; ++v) { + Attr.position[i * 4 + v] = + particleTransform.Transform(rotMatrix.Transform(quadVertPos[v] * p.Scale)); + Attr.color[i * 4 + v] = p.Color; + } + + if (atlas != nullptr) { + // set UVs of this sprite in the atlas + const ovrTextureAtlas::ovrSpriteDef& sd = atlas->GetSpriteDef(p.SpriteIndex); + Attr.uv0[i * 4 + 0] = Vector2f(sd.uvMins.x, sd.uvMins.y); + Attr.uv0[i * 4 + 1] = Vector2f(sd.uvMaxs.x, sd.uvMins.y); + Attr.uv0[i * 4 + 2] = Vector2f(sd.uvMaxs.x, sd.uvMaxs.y); + Attr.uv0[i * 4 + 3] = Vector2f(sd.uvMins.x, sd.uvMaxs.y); + } else { + Attr.uv0[i * 4 + 0] = Vector2f(-1, -1); + Attr.uv0[i * 4 + 1] = Vector2f(1, -1); + Attr.uv0[i * 4 + 2] = Vector2f(1, 1); + Attr.uv0[i * 4 + 3] = Vector2f(-1, 1); + } + } + } + + // update the geometry with new vertex attributes + SurfaceDef.geo.indexCount = activeCount * 6; + PackedAttr.Resize(0); + UpdateGeometry(SurfaceDef.geo, PackedAttr, Attr); +} + +void ovrParticleSystem::Shutdown() { + SurfaceDef.geo.Free(); + OVRFW::GlProgram::Free(Program); +} + +void ovrParticleSystem::RenderEyeView( + Matrix4f const& viewMatrix, + Matrix4f const& projectionMatrix, + std::vector& surfaceList) const { + // OVR_UNUSED( viewMatrix ); + // OVR_UNUSED( projectionMatrix ); + + // add a surface + ovrDrawSurface surf; + surf.modelMatrix = ModelMatrix; + surf.surface = &SurfaceDef; + surfaceList.push_back(surf); +} + +ovrParticleSystem::handle_t ovrParticleSystem::AddParticle( + const OVRFW::ovrApplFrameIn& frame, + const Vector3f& initialPosition, + const float initialOrientation, + const Vector3f& initialVelocity, + const Vector3f& acceleration, + const Vector4f& initialColor, + const ovrEaseFunc easeFunc, + const float rotationRate, + const float scale, + const float lifeTime, + const uint16_t spriteIndex) { + ovrParticle* p; + + handle_t particleHandle; + if (FreeParticles.GetSizeI() > 0) { + particleHandle = handle_t(FreeParticles[FreeParticles.GetSizeI() - 1]); + FreeParticles.PopBack(); + ActiveParticles.PushBack(particleHandle); + assert(particleHandle.IsValid()); + assert(particleHandle.Get() < Particles.GetSizeI()); + p = &Particles[particleHandle.Get()]; + } else { + if (Particles.GetSizeI() >= MaxParticles) { + return handle_t(); // adding more would overflow the VAO + } + particleHandle = handle_t(Particles.GetSizeI()); + Particles.PushBack(ovrParticle()); + ActiveParticles.PushBack(particleHandle); + p = &Particles[Particles.GetSizeI() - 1]; + } + + p->StartTime = frame.PredictedDisplayTime; + p->LifeTime = lifeTime; + p->InitialPosition = initialPosition; + p->InitialOrientation = initialOrientation; + p->InitialVelocity = initialVelocity; + p->HalfAcceleration = acceleration * 0.5f; + p->InitialColor = initialColor; + p->EaseFunc = easeFunc; + p->RotationRate = rotationRate; + p->InitialScale = scale; + p->SpriteIndex = spriteIndex; + + return particleHandle; +} + +void ovrParticleSystem::UpdateParticle( + const OVRFW::ovrApplFrameIn& frame, + const handle_t handle, + const Vector3f& position, + const float orientation, + const Vector3f& velocity, + const Vector3f& acceleration, + const Vector4f& color, + const ovrEaseFunc easeFunc, + const float rotationRate, + const float scale, + const float lifeTime, + const uint16_t spriteIndex) { + if (!handle.IsValid() || handle.Get() >= Particles.GetSizeI()) { + assert(handle.IsValid() && handle.Get() < Particles.GetSizeI()); + return; + } + ovrParticle& p = Particles[handle.Get()]; + p.InitialPosition = position; + p.InitialOrientation = orientation; + p.InitialVelocity = velocity; + p.HalfAcceleration = acceleration * 0.5f; + p.InitialColor = color; + p.EaseFunc = easeFunc; + p.RotationRate = rotationRate; + p.InitialScale = scale; + p.SpriteIndex = spriteIndex; + p.StartTime = frame.PredictedDisplayTime; + p.LifeTime = lifeTime; +} + +void ovrParticleSystem::RemoveParticle(const handle_t handle) { + if (!handle.IsValid() || handle.Get() >= Particles.GetSizeI()) { + return; + } + // particle will get removed in the next update + Particles[handle.Get()].StartTime = -1.0; // mark as unused + Particles[handle.Get()].LifeTime = 0.0; +} + +void ovrParticleSystem::CreateGeometry(const int maxParticles) { + SurfaceDef.geo.Free(); + + VertexAttribs attr; + const int numVerts = maxParticles * 4; + + attr.position.resize(numVerts); + attr.normal.resize(numVerts); + attr.color.resize(numVerts); + attr.uv0.resize(numVerts); + + std::vector indices; + const int numIndices = maxParticles * 6; + indices.resize(numIndices); + + for (int i = 0; i < maxParticles; ++i) { + for (int v = 0; v < 4; v++) { + attr.position[i * 4 + v] = quadVertPos[v]; + attr.normal[i * 4 + v] = {0.0f, 0.0f, 1.0f}; + attr.color[i * 4 + v] = {1.0f, 0.0f, 1.0f, 1.0f}; + attr.uv0[i * 4 + v] = quadUVs[v]; + } + + indices[i * 6 + 0] = static_cast(i * 4 + 0); + indices[i * 6 + 1] = static_cast(i * 4 + 3); + indices[i * 6 + 2] = static_cast(i * 4 + 1); + indices[i * 6 + 3] = static_cast(i * 4 + 1); + indices[i * 6 + 4] = static_cast(i * 4 + 3); + indices[i * 6 + 5] = static_cast(i * 4 + 2); + } + + SurfaceDef.geo.Create(attr, indices); + SurfaceDef.geo.indexCount = 0; // nothing to render until particles are added +} + +} // namespace OVRFW diff --git a/Samples/SampleXrFramework/Src/Render/ParticleSystem.h b/Samples/SampleXrFramework/Src/Render/ParticleSystem.h new file mode 100755 index 0000000..b93848e --- /dev/null +++ b/Samples/SampleXrFramework/Src/Render/ParticleSystem.h @@ -0,0 +1,212 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/************************************************************************************ + +Filename : ParticleSystem.h +Content : A simple particle system for System Activities. +Created : October 12, 2015 +Authors : Jonathan E. Wright + +************************************************************************************/ + +#pragma once + +#include + +#include "OVR_Math.h" +#include "OVR_TypesafeNumber.h" + +#include "FrameParams.h" +#include "Render/SurfaceRender.h" +#include "Render/GlProgram.h" +#include "OVR_FileSys.h" + +#include "EaseFunctions.h" + +#include +#include + +namespace OVRFW { + +template +class ovrSimpleArray { + public: + ovrSimpleArray() {} + + void PushBack(T const& item) { + Vector.push_back(item); + } + + void PopBack() { + Vector.pop_back(); + } + + void Reserve(int const newCapacity) { + Vector.reserve(newCapacity); + } + + void Resize(int const newCount) { + Vector.resize(newCount); + } + + void RemoveAtUnordered(int const index) { + Vector[index] = Vector[Vector.size() - 1]; + Vector.pop_back(); + } + + int GetSizeI() const { + return static_cast(Vector.size()); + } + size_t GetSize() const { + return Vector.size(); + } + int GetCapacity() const { + return Vector.capacity(); + } + + T const* GetDataPtr() const { + return &Vector[0]; + } + T* GetDataPtr() { + return &Vector[0]; + } + + T const& operator[](int const index) const { + return Vector[index]; + } + T& operator[](int const index) { + return Vector[index]; + } + + private: + std::vector Vector; +}; + +struct ovrVertexAttribs { + ovrSimpleArray position; + ovrSimpleArray normal; + ovrSimpleArray tangent; + ovrSimpleArray binormal; + ovrSimpleArray color; + ovrSimpleArray uv0; + ovrSimpleArray uv1; + ovrSimpleArray jointIndices; + ovrSimpleArray jointWeights; +}; + +class ovrTextureAtlas; + +struct particleDerived_t { + OVR::Vector3f Pos; + OVR::Vector4f Color; + float Orientation; // roll angle in radians + float Scale; + uint16_t SpriteIndex; +}; + +struct particleSort_t { + int ActiveIndex; + float DistanceSq; +}; + +//============================================================== +// ovrParticleSystem +class ovrParticleSystem { + public: + enum ovrParticleIndex { INVALID_PARTICLE_INDEX = -1 }; + + typedef OVR::TypesafeNumberT handle_t; + + ovrParticleSystem(); + virtual ~ovrParticleSystem(); + + // specify sprite locations as a regular grid + void Init( + const int maxParticles, + const ovrTextureAtlas* atlas, + const ovrGpuState& gpuState, + bool const sortParticles); + + void Frame( + const OVRFW::ovrApplFrameIn& frame, + const ovrTextureAtlas* textureAtlas, + const OVR::Matrix4f& centerEyeViewMatrix); + + void Shutdown(); + + void RenderEyeView( + OVR::Matrix4f const& viewMatrix, + OVR::Matrix4f const& projectionMatrix, + std::vector& surfaceList) const; + + handle_t AddParticle( + const OVRFW::ovrApplFrameIn& frame, + const OVR::Vector3f& initialPosition, + const float initialOrientation, + const OVR::Vector3f& initialVelocity, + const OVR::Vector3f& acceleration, + const OVR::Vector4f& initialColor, + const ovrEaseFunc easeFunc, + const float rotationRate, + const float scale, + const float lifeTime, + const uint16_t spriteIndex); + + void UpdateParticle( + const OVRFW::ovrApplFrameIn& frame, + const handle_t handle, + const OVR::Vector3f& position, + const float orientation, + const OVR::Vector3f& velocity, + const OVR::Vector3f& acceleration, + const OVR::Vector4f& color, + const ovrEaseFunc easeFunc, + const float rotationRate, + const float scale, + const float lifeTime, + const uint16_t spriteIndex); + + void RemoveParticle(const handle_t handle); + + static ovrGpuState GetDefaultGpuState(); + + private: + void CreateGeometry(const int maxParticles); + + int GetMaxParticles() const { + return SurfaceDef.geo.vertexCount / 4; + } + + class ovrParticle { + public: + // empty constructor so we don't pay the price for double initialization + ovrParticle() {} + + double StartTime; // time particle was created, negative means this particle is invalid + float LifeTime; // time particle should die + OVR::Vector3f InitialPosition; // initial position of the particle + float InitialOrientation; // initial orientation of the particle + OVR::Vector3f InitialVelocity; // initial velocity of the particle + OVR::Vector3f HalfAcceleration; // 1/2 the initial acceleration of the particle + OVR::Vector4f InitialColor; // initial color of the particle + float RotationRate; // rotation of the particle + float InitialScale; // initial scale of the particle + uint16_t SpriteIndex; // index of the sprite for this particle + ovrEaseFunc EaseFunc; // parametric function used to compute alpha + }; + + int MaxParticles; // maximum allowd particles + ovrSimpleArray Particles; // all active particles + ovrSimpleArray FreeParticles; // indices of free particles + ovrSimpleArray ActiveParticles; // indices of active particles + ovrSimpleArray Derived; + ovrSimpleArray SortIndices; + ovrSimpleArray PackedAttr; + ovrVertexAttribs Attr; + GlProgram Program; + ovrSurfaceDef SurfaceDef; + OVR::Matrix4f ModelMatrix; + bool SortParticles; +}; + +} // namespace OVRFW diff --git a/Samples/SampleXrFramework/Src/Render/PointList.cpp b/Samples/SampleXrFramework/Src/Render/PointList.cpp new file mode 100755 index 0000000..61777e9 --- /dev/null +++ b/Samples/SampleXrFramework/Src/Render/PointList.cpp @@ -0,0 +1,99 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/************************************************************************************ + +Filename : PointList.cpp +Content : Abstract base class for a list of points. +Created : 6/16/2017 +Authors : Jonathan E. Wright + +************************************************************************************/ + +#include "PointList.h" +#include + +using OVR::Vector3f; + +namespace OVRFW { + +//============================================================================================== +// ovrPointList_Vector + +ovrPointList_Vector::ovrPointList_Vector(const int maxPoints) : MaxPoints(maxPoints) {} + +int ovrPointList_Vector::GetNext(const int cur) const { + int next = cur + 1; + int arraySize = (int)Points.size(); + if (next >= arraySize) { + return -1; + } + return next; +} + +void ovrPointList_Vector::RemoveHead() { + for (int i = 1; i < (int)Points.size(); ++i) { + Points[i - 1] = Points[i]; + } + Points.resize(Points.size() - 1); +} + +//============================================================================================== +// ovrPointList_Circular + +ovrPointList_Circular::ovrPointList_Circular(const int bufferSize) + : BufferSize(bufferSize), CurPoints(0), HeadIndex(0), TailIndex(0) { + Points.resize(BufferSize); +} + +int ovrPointList_Circular::GetFirst() const { + if (IsEmpty()) { + assert(!IsEmpty()); + return -1; + } + return HeadIndex; +} + +int ovrPointList_Circular::GetNext(const int cur) const { + if (IsEmpty()) { + assert(!IsEmpty()); + return -1; + } + int next = IncIndex(cur); + if (next == TailIndex) { + return -1; + } + return next; +} + +int ovrPointList_Circular::GetLast() const { + if (IsEmpty()) { + return -1; + } + const int lastIndex = DecIndex(TailIndex); + return lastIndex; +} + +void ovrPointList_Circular::AddToTail(const Vector3f& p) { + if (!IsFull()) { + CurPoints++; + Points[TailIndex] = p; + TailIndex = IncIndex(TailIndex); + } +} +void ovrPointList_Circular::RemoveHead() { + if (HeadIndex == TailIndex) { + return; + } + HeadIndex = IncIndex(HeadIndex); + CurPoints--; +} + +int ovrPointList_Circular::DecIndex(const int in) const { + int d = in - 1; + if (d < 0) { + d += BufferSize; + } + return d; +} + +} // namespace OVRFW diff --git a/Samples/SampleXrFramework/Src/Render/PointList.h b/Samples/SampleXrFramework/Src/Render/PointList.h new file mode 100755 index 0000000..de935c8 --- /dev/null +++ b/Samples/SampleXrFramework/Src/Render/PointList.h @@ -0,0 +1,131 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/************************************************************************************ + +Filename : PointList.h +Content : Abstract base class for a list of points. +Created : 6/16/2017 +Authors : Jonathan E. Wright + +************************************************************************************/ + +#pragma once + +#include +#include +#include "OVR_Math.h" + +namespace OVRFW { + +//============================================================== +// ovrPointList +class ovrPointList { + public: + virtual ~ovrPointList() {} + + virtual bool IsEmpty() const = 0; + virtual bool IsFull() const = 0; + virtual int GetFirst() const = 0; + virtual int GetNext(const int cur) const = 0; + virtual int GetLast() const = 0; + virtual int GetCurPoints() const = 0; + virtual int GetMaxPoints() const = 0; + + virtual const OVR::Vector3f& Get(const int index) const = 0; + virtual OVR::Vector3f& Get(const int index) = 0; + + virtual void AddToTail(const OVR::Vector3f& p) = 0; + virtual void RemoveHead() = 0; +}; + +//============================================================== +// ovrPointList_Vector +class ovrPointList_Vector : public ovrPointList { + public: + ovrPointList_Vector(const int maxPoints); + + virtual bool IsEmpty() const override { + return Points.size() == 0; + } + virtual bool IsFull() const override { + return false; + } + virtual int GetFirst() const override { + return 0; + } + virtual int GetNext(const int cur) const override; + virtual int GetLast() const override { + return (int)Points.size() - 1; + } + virtual int GetCurPoints() const override { + return Points.size(); + } + virtual int GetMaxPoints() const override { + return MaxPoints; + } + + virtual const OVR::Vector3f& Get(const int index) const override { + return Points[index]; + } + virtual OVR::Vector3f& Get(const int index) override { + return Points[index]; + } + + virtual void AddToTail(const OVR::Vector3f& p) override { + Points.push_back(p); + } + virtual void RemoveHead() override; + + private: + std::vector Points; + int MaxPoints; +}; + +//============================================================== +// ovrPointList_Circular +class ovrPointList_Circular : public ovrPointList { + public: + ovrPointList_Circular(const int bufferSize); + + virtual bool IsEmpty() const override { + return HeadIndex == TailIndex; + } + virtual bool IsFull() const override { + return IncIndex(TailIndex) == HeadIndex; + } + virtual int GetFirst() const override; + virtual int GetNext(const int cur) const override; + virtual int GetLast() const override; + virtual int GetCurPoints() const override { + return CurPoints; + } + virtual int GetMaxPoints() const override { + return BufferSize - 1; + } + + virtual const OVR::Vector3f& Get(const int index) const override { + return Points[index]; + } + virtual OVR::Vector3f& Get(const int index) override { + return Points[index]; + } + + virtual void AddToTail(const OVR::Vector3f& p) override; + virtual void RemoveHead() override; + + private: + std::vector Points; + + int BufferSize; + int CurPoints; + int HeadIndex; + int TailIndex; + + private: + int IncIndex(const int in) const { + return (in + 1) % BufferSize; + } + int DecIndex(const int in) const; +}; + +} // namespace OVRFW diff --git a/Samples/SampleXrFramework/Src/Render/Ribbon.cpp b/Samples/SampleXrFramework/Src/Render/Ribbon.cpp new file mode 100755 index 0000000..f485e0f --- /dev/null +++ b/Samples/SampleXrFramework/Src/Render/Ribbon.cpp @@ -0,0 +1,276 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/************************************************************************************ + +Filename : Ribbon.h +Content : Class that renders connected polygon strips from a list of points +Created : 6/16/2017 +Authors : Jonathan E. Wright + +************************************************************************************/ + +#include "Ribbon.h" +#include "OVR_Math.h" +#include "Render/GlTexture.h" +#include "Misc/Log.h" + +using OVR::Matrix4f; +using OVR::Vector3f; +using OVR::Vector4f; + +inline Vector3f GetViewMatrixForward(Matrix4f const& m) { + return Vector3f(-m.M[2][0], -m.M[2][1], -m.M[2][2]).Normalized(); +} + +namespace OVRFW { + +static const char* ribbonVertexShader = R"glsl( + attribute vec4 Position; + attribute vec4 VertexColor; + attribute vec2 TexCoord; + varying lowp vec4 outColor; + varying highp vec2 oTexCoord; + void main() + { + gl_Position = TransformVertex( Position ); + oTexCoord = TexCoord; + outColor = VertexColor; + } +)glsl"; + +static const char* ribbonFragmentShader = R"glsl( + uniform sampler2D Texture0; + varying lowp vec4 outColor; + varying highp vec2 oTexCoord; + void main() + { + gl_FragColor = outColor * texture2D( Texture0, oTexCoord ); + //gl_FragColor = outColor; + } +)glsl"; +//============================================================================================== +// ovrRibbon + +static GlTexture CreateRibbonTexture() { + const int TEX_WIDTH = 64; + const int TEX_HEIGHT = 64; + const int TEX_SIZE = TEX_WIDTH * TEX_HEIGHT; + uint32_t* tex = new uint32_t[TEX_SIZE]; + for (int y = 0; y < TEX_HEIGHT; ++y) { + const uint32_t alpha = (y < 16) ? y * 16 : (y > 48) ? (TEX_HEIGHT - y) * 16 : 0xff; + const uint32_t color = (alpha << 24) | 0xffffff; + for (int x = 0; x < TEX_WIDTH; ++x) { + tex[(y * TEX_WIDTH) + x] = color; + } + } + GlTexture glTexture = + LoadRGBATextureFromMemory(reinterpret_cast(tex), TEX_WIDTH, TEX_HEIGHT, false); + delete[] tex; + return glTexture; +} + +ovrRibbon::ovrRibbon(const ovrPointList& pointList, const float width, const Vector4f& color) + : HalfWidth(width), Color(color) { + // initialize the surface geometry + const int maxPoints = pointList.GetMaxPoints(); + const int maxQuads = (maxPoints - 1); + const int numVerts = maxQuads * 4; + + VertexAttribs attr; + attr.position.resize(numVerts); + attr.color.resize(numVerts); + attr.uv0.resize(numVerts); + + // the indices will never change + const int numIndices = maxQuads * 6; + std::vector indices; + indices.resize(numIndices); + // so we can just set them up at initialization time + TriangleIndex v = 0; + for (int i = 0; i < maxQuads; ++i) { + indices[i * 6 + 0] = v + 0; + indices[i * 6 + 1] = v + 1; + indices[i * 6 + 2] = v + 2; + indices[i * 6 + 3] = v + 2; + indices[i * 6 + 4] = v + 1; + indices[i * 6 + 5] = v + 3; + v += 4; + } + + Surface.geo.Create(attr, indices); + Surface.geo.primitiveType = GL_TRIANGLES; + Surface.geo.indexCount = 0; + + // initialize the rest of the surface + Surface.surfaceName = "ribbon"; + Surface.numInstances = 1; + + ovrGraphicsCommand& gc = Surface.graphicsCommand; + + Texture = CreateRibbonTexture(); +#if 1 + gc.UniformData[0].Data = &Texture; + + ovrProgramParm parms[] = { + {"Texture0", ovrProgramParmType::TEXTURE_SAMPLED}, + }; + + gc.Program = GlProgram::Build( + ribbonVertexShader, + ribbonFragmentShader, + &parms[0], + sizeof(parms) / sizeof(ovrProgramParm)); +#else + gc.Program = GlProgram::Build(ribbonVertexShader, ribbonFragmentShader, nullptr, 0); +#endif + + if (!Surface.graphicsCommand.Program.IsValid()) { + ALOG("Error building ribbon gpu program"); + } + + ovrGpuState& gpu = gc.GpuState; + gpu.depthEnable = true; + gpu.depthMaskEnable = false; + gpu.blendEnable = ovrGpuState::BLEND_ENABLE; + gpu.blendSrc = GL_SRC_ALPHA; + gpu.blendDst = GL_ONE_MINUS_SRC_ALPHA; + gpu.blendSrcAlpha = GL_SRC_ALPHA; + gpu.blendDstAlpha = GL_ONE_MINUS_SRC_ALPHA; + gpu.cullEnable = true; +} + +ovrRibbon::~ovrRibbon() { + DeleteTexture(Texture); + GlProgram::Free(Surface.graphicsCommand.Program); + Surface.geo.Free(); +} + +void ovrRibbon::AddPoint(ovrPointList& pointList, const OVR::Vector3f& point) { + if (pointList.IsFull()) { + return; + } + + // don't add points really close together + const int lastIndex = pointList.GetLast(); + const Vector3f& curTail = pointList.Get(lastIndex); + + float d = (curTail - point).LengthSq(); + if (d < 0.0001f) { + return; + } + pointList.AddToTail(point); +} + +void ovrRibbon::SetColor(const OVR::Vector4f& color) { + Color = color; +} + +void ovrRibbon::SetWidth(const float width) { + HalfWidth = width; +} + +void ovrRibbon::Update( + const ovrPointList& pointList, + const OVR::Matrix4f& centerViewMatrix, + const bool invertAlpha) { + if (pointList.GetCurPoints() <= 1) { + return; + } + + VertexAttribs attr; + const int curPoints = pointList.GetCurPoints(); + const int numVerts = (curPoints - 1) * 4; + attr.position.resize(numVerts); + attr.color.resize(numVerts); + attr.uv0.resize(numVerts); + + Vector3f eyeFwd(GetViewMatrixForward(centerViewMatrix)); + int numQuads = 0; + int curEdge = 1; + int curIdx = pointList.GetFirst(); + int nextIdx = pointList.GetNext(curIdx); + + auto getEdgeDir2 = [](const Vector3f& eyeFwd, const Vector3f& cur, const Vector3f& next) { + Vector3f dir = next - cur; + Vector3f proj = (dir - (eyeFwd * dir.Dot(eyeFwd))).Normalized(); + Vector3f cross = proj.Cross(eyeFwd); + return cross; + }; + + auto calcAlpha = [](const int curEdge, const int curPoints, const bool invertAlpha) { + if (invertAlpha) { + return 1.0f - std::clamp((float)(curEdge >> 1) / (float)(curPoints), 0.0f, 1.0f); + } else { + return std::clamp((float)curEdge / (float)(curPoints >> 1), 0.0f, 1.0f); + } + }; + + const Vector3f* curPoint = &pointList.Get(curIdx); + const Vector3f* nextPoint = &pointList.Get(nextIdx); + + Vector3f edgeDir = getEdgeDir2(eyeFwd, *curPoint, *nextPoint); + float alpha = calcAlpha(curEdge, pointList.GetCurPoints(), invertAlpha); + + // cur edge + attr.position[(numQuads * 4) + 0] = *curPoint + (edgeDir * HalfWidth); + attr.color[(numQuads * 4) + 0] = Vector4f(Color.x, Color.y, Color.z, alpha); + attr.position[(numQuads * 4) + 1] = *curPoint - (edgeDir * HalfWidth); + attr.color[(numQuads * 4) + 1] = Vector4f(Color.x, Color.y, Color.z, alpha); + attr.uv0[(numQuads * 4) + 0] = OVR::Vector2f(0.0f, 0.0f); + attr.uv0[(numQuads * 4) + 1] = OVR::Vector2f(0.0f, 1.0f); + + for (;;) { + curPoint = &pointList.Get(curIdx); + nextPoint = &pointList.Get(nextIdx); + + edgeDir = getEdgeDir2(eyeFwd, *curPoint, *nextPoint); + curEdge++; + alpha = calcAlpha(curEdge, pointList.GetCurPoints(), invertAlpha); + + // current quad next edge + attr.position[(numQuads * 4) + 2] = *nextPoint + (edgeDir * HalfWidth * alpha); + attr.color[(numQuads * 4) + 2] = Vector4f(Color.x, Color.y, Color.z, alpha); + attr.position[(numQuads * 4) + 3] = *nextPoint - (edgeDir * HalfWidth * alpha); + attr.color[(numQuads * 4) + 3] = Vector4f(Color.x, Color.y, Color.z, alpha); + + attr.uv0[(numQuads * 4) + 2] = OVR::Vector2f(1.0f, 0.0f); + attr.uv0[(numQuads * 4) + 3] = OVR::Vector2f(1.0f, 1.0f); + + curIdx = nextIdx; + nextIdx = pointList.GetNext(nextIdx); + if (nextIdx < 0) { + break; + } + + numQuads++; + + alpha = calcAlpha(curEdge, pointList.GetCurPoints(), invertAlpha); + + // next quad first edge + attr.position[(numQuads * 4) + 0] = *nextPoint + (edgeDir * HalfWidth * alpha); + attr.color[(numQuads * 4) + 0] = Vector4f(Color.x, Color.y, Color.z, alpha); + attr.position[(numQuads * 4) + 1] = *nextPoint - (edgeDir * HalfWidth * alpha); + attr.color[(numQuads * 4) + 1] = Vector4f(Color.x, Color.y, Color.z, alpha); + attr.uv0[(numQuads * 4) + 0] = OVR::Vector2f(0.0f, 0.0f); + attr.uv0[(numQuads * 4) + 1] = OVR::Vector2f(0.0f, 1.0f); + } + + // ALOG( "Ribbon: %i points, %i edges, %i quads", pointList.GetCurPoints(), curEdge, numQuads ); + // update the vertices + Surface.geo.Update(attr, false); + Surface.geo.indexCount = numQuads * 6; +} + +void ovrRibbon::GenerateSurfaceList(std::vector& surfaceList) const { + if (Surface.geo.indexCount == 0) { + return; + } + + ovrDrawSurface drawSurf; + drawSurf.modelMatrix = Matrix4f::Identity(); + drawSurf.surface = &Surface; + + surfaceList.push_back(drawSurf); +} + +} // namespace OVRFW diff --git a/Samples/SampleXrFramework/Src/Render/Ribbon.h b/Samples/SampleXrFramework/Src/Render/Ribbon.h new file mode 100755 index 0000000..2df5909 --- /dev/null +++ b/Samples/SampleXrFramework/Src/Render/Ribbon.h @@ -0,0 +1,45 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/************************************************************************************ + +Filename : Ribbon.h +Content : Class that renders connected polygon strips from a list of points +Created : 6/16/2017 +Authors : Jonathan E. Wright + +************************************************************************************/ + +#pragma once + +#include + +#include "OVR_Math.h" +#include "PointList.h" +#include "SurfaceRender.h" + +namespace OVRFW { + +//============================================================== +// ovrRibbon +class ovrRibbon { + public: + ovrRibbon(const ovrPointList& pointList, const float width, const OVR::Vector4f& color); + ~ovrRibbon(); + + void AddPoint(ovrPointList& pointList, const OVR::Vector3f& point); + void Update( + const ovrPointList& pointList, + const OVR::Matrix4f& centerViewMatrix, + const bool invertAlpha); + void SetColor(const OVR::Vector4f& color); + void SetWidth(const float width); + void GenerateSurfaceList(std::vector& surfaceList) const; + + private: + float HalfWidth; + OVR::Vector4f Color; + ovrSurfaceDef Surface; + GlTexture Texture; +}; + +} // namespace OVRFW diff --git a/Samples/SampleXrFramework/Src/Render/SimpleBeamRenderer.h b/Samples/SampleXrFramework/Src/Render/SimpleBeamRenderer.h new file mode 100755 index 0000000..a045757 --- /dev/null +++ b/Samples/SampleXrFramework/Src/Render/SimpleBeamRenderer.h @@ -0,0 +1,113 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/************************************************************************************ + +Filename : SimpleBeamRenderer.h +Content : Helper for beam and particle rendering +Created : July 2020 +Authors : Matthew Langille + +************************************************************************************/ + +#pragma once + +#include "OVR_Math.h" +#include "BeamRenderer.h" +#include "ParticleSystem.h" +#include "Input/TinyUI.h" + +namespace OVRFW { + +class SimpleBeamRenderer { + public: + SimpleBeamRenderer() = default; + ~SimpleBeamRenderer() { + delete spriteAtlas_; + } + + void Init( + OVRFW::ovrFileSys* fileSys, + const char* particleTexture, + OVR::Vector4f particleColor, + float scale = 1.0f) { + PointerParticleColor = particleColor; + Scale = scale; + beamRenderer_.Init(256, true); + + if (particleTexture != nullptr) { + spriteAtlas_ = new OVRFW::ovrTextureAtlas(); + spriteAtlas_->Init(*fileSys, particleTexture); + spriteAtlas_->BuildSpritesFromGrid(4, 2, 8); + particleSystem_.Init( + 1024, spriteAtlas_, ovrParticleSystem::GetDefaultGpuState(), false); + } else { + particleSystem_.Init(1024, nullptr, ovrParticleSystem::GetDefaultGpuState(), false); + } + } + + void Shutdown() { + beamRenderer_.Shutdown(); + particleSystem_.Shutdown(); + } + + void Update( + const OVRFW::ovrApplFrameIn& in, + const std::vector& hitTestDevices) { + // Clear old beams and particles + for (auto h : beams_) { + beamRenderer_.RemoveBeam(h); + } + for (auto h : particles_) { + particleSystem_.RemoveParticle(h); + } + + // Add UI pointers to render + for (auto& device : hitTestDevices) { + constexpr float beamLength = 0.5f; // 0.5 meter beam length + const OVR::Vector3f beamDir = + ((device.pointerEnd - device.pointerStart) * 0.5f).Normalized(); + const OVR::Vector3f beamEnd = device.pointerStart + beamDir * beamLength; + const auto& beam = + beamRenderer_.AddBeam(in, 0.015f, device.pointerStart, beamEnd, BeamColor); + beams_.push_back(beam); + + if (device.hitObject) { + const auto& particle = particleSystem_.AddParticle( + in, + device.pointerEnd, + 0.0f, + OVR::Vector3f(0.0f), + OVR::Vector3f(0.0f), + PointerParticleColor, + ovrEaseFunc::NONE, + 0.0f, + 0.05f * Scale, + 0.1f, + 0); + particles_.push_back(particle); + } + } + } + void Render(const OVRFW::ovrApplFrameIn& in, OVRFW::ovrRendererOutput& out) { + /// Render beams + particleSystem_.Frame(in, spriteAtlas_, out.FrameMatrices.CenterView); + particleSystem_.RenderEyeView( + out.FrameMatrices.CenterView, out.FrameMatrices.EyeProjection[0], out.Surfaces); + beamRenderer_.Frame(in, out.FrameMatrices.CenterView); + beamRenderer_.Render(out.Surfaces); + } + + public: + OVR::Vector4f PointerParticleColor = {0.5f, 0.8f, 1.0f, 1.0f}; + OVR::Vector4f BeamColor = {0.5f, 0.8f, 1.0f, 1.0f}; + + private: + OVRFW::ovrBeamRenderer beamRenderer_; + OVRFW::ovrParticleSystem particleSystem_; + OVRFW::ovrTextureAtlas* spriteAtlas_ = nullptr; + std::vector beams_; + std::vector particles_; + float Scale; +}; + +} // namespace OVRFW diff --git a/Samples/SampleXrFramework/Src/Render/SimpleCollisionRenderer.h b/Samples/SampleXrFramework/Src/Render/SimpleCollisionRenderer.h new file mode 100755 index 0000000..c7a6a65 --- /dev/null +++ b/Samples/SampleXrFramework/Src/Render/SimpleCollisionRenderer.h @@ -0,0 +1,159 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/************************************************************************************ + +Filename : SimpleCollisionRenderer.h +Content : Based on SimpleBeamRenderer, this renderer takes a subset of beam ids + for which it only draws particles and can treat differently in other + ways--coloring the particles individually, etc. +Created : October 2022 +Authors : Isaac Greenbride + +************************************************************************************/ + +#pragma once + +#include "OVR_Math.h" +#include "BeamRenderer.h" +#include "ParticleSystem.h" +#include "Input/TinyUI.h" +#include + +namespace OVRFW { + +static float get_random_to_one_float() { + static std::default_random_engine e; + static std::uniform_real_distribution<> dis(0, 1.0f); // range 0 - 1.0f + return dis(e); +} +static int get_random_to_zero_two() { + static std::default_random_engine e; + static std::uniform_real_distribution<> dis(0, 2); // range 0 - 3 + return dis(e); +} + +class SimpleCollisionRenderer { + public: + SimpleCollisionRenderer() = default; + ~SimpleCollisionRenderer() { + delete spriteAtlas_; + } + + void Init( + OVRFW::ovrFileSys* fileSys, + const char* particleTexture, + OVR::Vector4f particleColor, + float scale = 1.0f, + std::vector noBeamIds = {}, + bool randomizeNoBeamParticleColor = false, + bool showNoBeamParticles = true) { + PointerParticleColor = particleColor; + Scale = scale; + RandomizeNoBeamParticleColor = randomizeNoBeamParticleColor; + ShowNoBeamParticles = showNoBeamParticles; + beamRenderer_.Init(256, true); + UpdateNoBeamIds(noBeamIds); + + if (particleTexture != nullptr) { + spriteAtlas_ = new OVRFW::ovrTextureAtlas(); + spriteAtlas_->Init(*fileSys, particleTexture); + spriteAtlas_->BuildSpritesFromGrid(4, 2, 8); + particleSystem_.Init( + 1024, spriteAtlas_, ovrParticleSystem::GetDefaultGpuState(), false); + } else { + particleSystem_.Init(1024, nullptr, ovrParticleSystem::GetDefaultGpuState(), false); + } + } + + void Shutdown() { + beamRenderer_.Shutdown(); + particleSystem_.Shutdown(); + } + + void ShowRandomParticleColor(bool makeRandom) { + RandomizeNoBeamParticleColor = makeRandom; + } + + void ShowParticlesForSpecifiedIds(bool show) { + ShowNoBeamParticles = show; + } + + void UpdateNoBeamIds(const std::vector& noBeamIds) { + for (int id : noBeamIds) { + std::vector rgb = {0.0f, 0.0f, 0.0f}; + rgb[get_random_to_zero_two()] = + get_random_to_one_float(); // just changing one channel at a time + beamlessDeviceIds_.insert({id, OVR::Vector4f(rgb[0], rgb[1], rgb[2], 1.0f)}); + } + } + + void Update( + const OVRFW::ovrApplFrameIn& in, + const std::vector& hitTestDevices) { + // Clear old beams and particles + for (auto h : beams_) { + beamRenderer_.RemoveBeam(h); + } + for (auto h : particles_) { + particleSystem_.RemoveParticle(h); + } + + // Add UI pointers to render + for (auto& device : hitTestDevices) { + bool showBeam = beamlessDeviceIds_.count(device.deviceNum) == 0; + if (showBeam) { + constexpr float beamLength = 0.5f; // 0.5 meter beam length + const OVR::Vector3f beamDir = + ((device.pointerEnd - device.pointerStart) * 0.5f).Normalized(); + const OVR::Vector3f beamEnd = device.pointerStart + beamDir * beamLength; + const auto& beam = + beamRenderer_.AddBeam(in, 0.015f, device.pointerStart, beamEnd, BeamColor); + beams_.push_back(beam); + } + if (device.hitObject && (showBeam || ShowNoBeamParticles)) { + OVR::Vector4f currentPointerParticleColor = + showBeam || !RandomizeNoBeamParticleColor + ? PointerParticleColor + : beamlessDeviceIds_[device.deviceNum]; + const auto& particle = particleSystem_.AddParticle( + in, + device.pointerEnd, + 0.0f, + OVR::Vector3f(0.0f), + OVR::Vector3f(0.0f), + currentPointerParticleColor, + ovrEaseFunc::NONE, + 0.0f, + 0.05f * Scale, + 0.1f, + 0); + particles_.push_back(particle); + } + } + } + void Render(const OVRFW::ovrApplFrameIn& in, OVRFW::ovrRendererOutput& out) { + /// Render beams + particleSystem_.Frame(in, spriteAtlas_, out.FrameMatrices.CenterView); + particleSystem_.RenderEyeView( + out.FrameMatrices.CenterView, out.FrameMatrices.EyeProjection[0], out.Surfaces); + beamRenderer_.Frame(in, out.FrameMatrices.CenterView); + beamRenderer_.Render(out.Surfaces); + } + + public: + OVR::Vector4f PointerParticleColor = {0.5f, 0.8f, 1.0f, 1.0f}; + OVR::Vector4f BeamColor = {0.5f, 0.8f, 1.0f, 1.0f}; + + private: + OVRFW::ovrBeamRenderer beamRenderer_; + OVRFW::ovrParticleSystem particleSystem_; + OVRFW::ovrTextureAtlas* spriteAtlas_ = nullptr; + std::unordered_map beamlessDeviceIds_; + std::vector beams_; + std::vector particles_; + float Scale; + bool RandomizeNoBeamParticleColor; + bool ShowNoBeamParticles; +}; + +} // namespace OVRFW diff --git a/Samples/SampleXrFramework/Src/Render/SimpleGlbRenderer.cpp b/Samples/SampleXrFramework/Src/Render/SimpleGlbRenderer.cpp new file mode 100755 index 0000000..154c933 --- /dev/null +++ b/Samples/SampleXrFramework/Src/Render/SimpleGlbRenderer.cpp @@ -0,0 +1,266 @@ +/************************************************************************************************ +Filename : SimpleGlbRenderer.cpp +Content : A one stop for glb models from the render model OpenXR extension +Created : April 2021 +Authors : Federico Schliemann +Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved. +************************************************************************************************/ +#include "SimpleGlbRenderer.h" +#include "Model/ModelFile.h" +#include "Model/ModelFileLoading.h" +#include "Model/ModelDef.h" +#include + +using OVR::Matrix4f; +using OVR::Posef; +using OVR::Quatf; +using OVR::Vector3f; +using OVR::Vector4f; + +namespace OVRFW { +namespace SimpleGlbShaders { + +/// clang-format off +static const char* VertexShaderSrc = R"glsl( +attribute highp vec4 Position; +attribute highp vec3 Normal; +attribute highp vec2 TexCoord; +attribute highp vec4 JointIndices; + +varying lowp vec3 oEye; +varying lowp vec3 oNormal; +varying lowp vec2 oTexCoord; + +uniform JointMatrices +{ + highp mat4 Joints[16]; +} jb; + +vec3 multiply( mat4 m, vec3 v ) +{ + return vec3( + m[0].x * v.x + m[1].x * v.y + m[2].x * v.z, + m[0].y * v.x + m[1].y * v.y + m[2].y * v.z, + m[0].z * v.x + m[1].z * v.y + m[2].z * v.z ); +} + +vec3 transposeMultiply( mat4 m, vec3 v ) +{ + return vec3( + m[0].x * v.x + m[0].y * v.y + m[0].z * v.z, + m[1].x * v.x + m[1].y * v.y + m[1].z * v.z, + m[2].x * v.x + m[2].y * v.y + m[2].z * v.z ); +} + +void main() +{ + highp vec4 localPos = jb.Joints[int(JointIndices.x)] * Position; + gl_Position = TransformVertex( localPos ); + highp vec3 eye = transposeMultiply(sm.ViewMatrix[VIEW_ID], + -vec3(sm.ViewMatrix[VIEW_ID][3])); + oEye = normalize(eye - vec3( ModelMatrix * localPos )); + highp vec3 iNormal = multiply(jb.Joints[int(JointIndices.x)], Normal); + oNormal = normalize(multiply(ModelMatrix, iNormal)); + + oTexCoord = TexCoord; +} +)glsl"; + +static const char* FragmentShaderSrc = R"glsl( +precision lowp float; + +uniform sampler2D Texture0; +uniform lowp vec3 SpecularLightDirection; +uniform lowp vec3 SpecularLightColor; +uniform lowp vec3 AmbientLightColor; +uniform float Opacity; +uniform float AlphaBlend; + +varying lowp vec3 oEye; +varying lowp vec3 oNormal; +varying lowp vec2 oTexCoord; + +lowp vec3 multiply( lowp mat3 m, lowp vec3 v ) +{ + return vec3( + m[0].x * v.x + m[1].x * v.y + m[2].x * v.z, + m[0].y * v.x + m[1].y * v.y + m[2].y * v.z, + m[0].z * v.x + m[1].z * v.y + m[2].z * v.z ); +} + +void main() +{ + lowp vec3 eyeDir = normalize( oEye.xyz ); + lowp vec3 Normal = normalize( oNormal ); + + lowp vec3 reflectionDir = dot( eyeDir, Normal ) * 2.0 * Normal - eyeDir; + lowp vec4 diffuse = texture2D( Texture0, oTexCoord ); + lowp vec3 ambientValue = diffuse.xyz * AmbientLightColor; + + lowp float nDotL = max( dot( Normal , SpecularLightDirection ), 0.0 ); + lowp vec3 diffuseValue = diffuse.xyz * SpecularLightColor * nDotL; + + lowp float specularPower = 1.0f - diffuse.a; + specularPower = specularPower * specularPower; + + lowp vec3 H = normalize( SpecularLightDirection + eyeDir ); + lowp float nDotH = max( dot( Normal, H ), 0.0 ); + lowp float specularIntensity = pow( nDotH, 64.0f * ( specularPower ) ) * specularPower; + lowp vec3 specularValue = specularIntensity * SpecularLightColor; + + lowp vec3 controllerColor = diffuseValue + ambientValue + specularValue; + + float alphaBlendFactor = max(diffuse.w, AlphaBlend) * Opacity; + + // apply alpha + gl_FragColor.w = alphaBlendFactor; + // premult + gl_FragColor.xyz = controllerColor * gl_FragColor.w; +} +)glsl"; + +/// clang-format on + +} // namespace SimpleGlbShaders + +SimpleGlbRenderer::~SimpleGlbRenderer() { + if (jointsBuffer) { + jointsBuffer->Destroy(); + } +} + +std::vector SimpleGlbRenderer::GetDefaultPoseTransforms() const { + std::vector outTransforms; + + if (!RenderModel->Skins.empty()) { + outTransforms.resize(RenderModel->Skins[0].jointIndexes.size()); + for (size_t joint = 0; joint < RenderModel->Skins[0].jointIndexes.size(); ++joint) { + outTransforms[joint] = (RenderModel->Nodes[RenderModel->Skins[0].jointIndexes[joint]] + .GetGlobalTransform() * + RenderModel->Skins[0].inverseBindMatrices[joint]) + .Transposed(); + } + } else { + Matrix4f transform; // identity + for (size_t nodeIndex = 0; nodeIndex < RenderModel->Nodes.size(); ++nodeIndex) { + const ModelNode& node = RenderModel->Nodes[nodeIndex]; + + if (node.parentIndex < 0) { + transform = node.GetLocalTransform(); + break; + } + } + + outTransforms.resize(kMaxJoints); + for (size_t joint = 0; joint < kMaxJoints; ++joint) { + outTransforms[joint] = transform; + } + } + + return outTransforms; +} + +bool SimpleGlbRenderer::Init(std::vector& modelBuffer) { + /// Shader + ovrProgramParm UniformParms[] = { + {"Texture0", ovrProgramParmType::TEXTURE_SAMPLED}, + {"SpecularLightDirection", ovrProgramParmType::FLOAT_VECTOR3}, + {"SpecularLightColor", ovrProgramParmType::FLOAT_VECTOR3}, + {"AmbientLightColor", ovrProgramParmType::FLOAT_VECTOR3}, + {"Opacity", ovrProgramParmType::FLOAT}, + {"AlphaBlend", ovrProgramParmType::FLOAT}, + {"JointMatrices", ovrProgramParmType::BUFFER_UNIFORM}, + }; + ProgRenderModel = GlProgram::Build( + "", + SimpleGlbShaders::VertexShaderSrc, + "", + SimpleGlbShaders::FragmentShaderSrc, + UniformParms, + sizeof(UniformParms) / sizeof(ovrProgramParm)); + + MaterialParms materials = {}; + ModelGlPrograms programs = {}; + programs.ProgSingleTexture = &ProgRenderModel; + programs.ProgBaseColorPBR = &ProgRenderModel; + programs.ProgSkinnedBaseColorPBR = &ProgRenderModel; + programs.ProgLightMapped = &ProgRenderModel; + programs.ProgBaseColorEmissivePBR = &ProgRenderModel; + programs.ProgSkinnedBaseColorEmissivePBR = &ProgRenderModel; + programs.ProgSimplePBR = &ProgRenderModel; + programs.ProgSkinnedSimplePBR = &ProgRenderModel; + + RenderModel = std::unique_ptr(LoadModelFile_glB( + "modelrenderer", (const char*)modelBuffer.data(), modelBuffer.size(), programs, materials)); + + if (RenderModel == nullptr || static_cast(RenderModel->Models.size()) < 1) { + ALOGE("Couldn't load modelrenderer model!"); + return false; + } + + jointsBuffer.reset(new OVRFW::GlBuffer()); + jointsBuffer->Create( + OVRFW::GlBufferType_t::GLBUFFER_TYPE_UNIFORM, kMaxJoints * sizeof(Matrix4f), nullptr); + + std::vector poseTransforms = GetDefaultPoseTransforms(); + Matrix4f* pose = (Matrix4f*)jointsBuffer->MapBuffer(); + for (size_t joint = 0; joint < poseTransforms.size(); ++joint) { + pose[joint] = poseTransforms[joint]; + } + jointsBuffer->UnmapBuffer(); + + for (auto& model : RenderModel->Models) { + auto& gc = model.surfaces[0].surfaceDef.graphicsCommand; + gc.UniformData[0].Data = &gc.Textures[0]; + gc.UniformData[1].Data = &SpecularLightDirection; + gc.UniformData[2].Data = &SpecularLightColor; + gc.UniformData[3].Data = &AmbientLightColor; + gc.UniformData[4].Data = &Opacity; + gc.UniformData[5].Data = &AlphaBlendFactor; + gc.UniformData[6].Data = jointsBuffer.get(); + gc.GpuState.depthEnable = gc.GpuState.depthMaskEnable = true; + gc.GpuState.blendEnable = ovrGpuState::BLEND_ENABLE; + gc.GpuState.blendMode = GL_FUNC_ADD; + gc.GpuState.blendSrc = GL_ONE; + gc.GpuState.blendDst = GL_ONE_MINUS_SRC_ALPHA; + } + + /// Set defaults + SpecularLightDirection = Vector3f(1.0f, 1.0f, 0.0f); + SpecularLightColor = Vector3f(1.0f, 0.95f, 0.8f) * 0.75f; + AmbientLightColor = Vector3f(1.0f, 1.0f, 1.0f) * 0.15f; + + /// all good + Initialized = true; + return true; +} + +void SimpleGlbRenderer::Shutdown() { + OVRFW::GlProgram::Free(ProgRenderModel); +} + +void SimpleGlbRenderer::Update(const OVR::Posef& pose) { + /// Compute transform for the root + + OVR::Posef offsetPose = pose; + + Transform = Matrix4f(offsetPose); +} + +void SimpleGlbRenderer::Render(std::vector& surfaceList) { + /// toggle alpha override + AlphaBlendFactor = UseSolidTexture ? 1.0f : 0.0f; + if (RenderModel != nullptr) { + for (int i = 0; i < static_cast(RenderModel->Models.size()); i++) { + auto& model = RenderModel->Models[i]; + ovrDrawSurface controllerSurface; + for (int j = 0; j < static_cast(model.surfaces.size()); j++) { + controllerSurface.surface = &(model.surfaces[j].surfaceDef); + controllerSurface.modelMatrix = Transform; + surfaceList.push_back(controllerSurface); + } + } + } +} + +} // namespace OVRFW diff --git a/Samples/SampleXrFramework/Src/Render/SimpleGlbRenderer.h b/Samples/SampleXrFramework/Src/Render/SimpleGlbRenderer.h new file mode 100755 index 0000000..786dcbd --- /dev/null +++ b/Samples/SampleXrFramework/Src/Render/SimpleGlbRenderer.h @@ -0,0 +1,58 @@ +/************************************************************************************************ +Filename : SimpleGlbRenderer.h +Content : A one stop for models from the render model extension +Created : April 2021 +Authors : Federico Schliemann +Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved. +************************************************************************************************/ +#pragma once + +#include +#include +#include + +/// Sample Framework +#include "Misc/Log.h" +#include "Model/SceneView.h" +#include "Render/GlProgram.h" +#include "Render/GlBuffer.h" +#include "Render/SurfaceRender.h" +#include "OVR_FileSys.h" +#include "OVR_Math.h" + +namespace OVRFW { + +class SimpleGlbRenderer { + public: + SimpleGlbRenderer() = default; + ~SimpleGlbRenderer(); + + bool Init(std::vector& modelBuffer); + void Shutdown(); + void Update(const OVR::Posef& pose); + void Render(std::vector& surfaceList); + bool IsInitialized() const { + return Initialized; + } + std::vector GetDefaultPoseTransforms() const; + + public: + OVR::Vector3f SpecularLightDirection; + OVR::Vector3f SpecularLightColor; + OVR::Vector3f AmbientLightColor; + std::unique_ptr jointsBuffer; + bool UseSolidTexture = true; + float Opacity = 1.0f; + + private: + bool Initialized = false; + float AlphaBlendFactor = 1.0f; + GlProgram ProgRenderModel; + std::unique_ptr RenderModel{}; + GlTexture RenderModelTextureSolid; + OVR::Matrix4f Transform; + OVR::Posef GripPose = OVR::Posef::Identity(); + size_t kMaxJoints = 16; +}; + +} // namespace OVRFW diff --git a/Samples/SampleXrFramework/Src/Render/SurfaceRender.cpp b/Samples/SampleXrFramework/Src/Render/SurfaceRender.cpp new file mode 100755 index 0000000..2776956 --- /dev/null +++ b/Samples/SampleXrFramework/Src/Render/SurfaceRender.cpp @@ -0,0 +1,443 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/************************************************************************************ + +Filename : SurfaceRender.cpp +Content : Optimized OpenGL rendering path +Created : August 9, 2013 +Authors : John Carmack + +************************************************************************************/ + +#include "SurfaceRender.h" + +#include + +#include "Misc/Log.h" + +#include "Egl.h" +#include "GlTexture.h" +#include "GlProgram.h" +#include "GlBuffer.h" + +#include + +using OVR::Bounds3f; +using OVR::Matrix4f; +using OVR::Vector2f; +using OVR::Vector3f; +using OVR::Vector4f; + +namespace OVRFW { + +bool LogRenderSurfaces = false; // Do not check in set to true! + +static void +ChangeGpuState(const ovrGpuState oldState, const ovrGpuState newState, bool force = false) { + if (force || newState.blendEnable != oldState.blendEnable) { + if (newState.blendEnable) { + GL(glEnable(GL_BLEND)); + } else { + GL(glDisable(GL_BLEND)); + } + } + if (force || newState.blendEnable != oldState.blendEnable || + newState.blendSrc != oldState.blendSrc || newState.blendDst != oldState.blendDst || + newState.blendSrcAlpha != oldState.blendSrcAlpha || + newState.blendDstAlpha != oldState.blendDstAlpha || + newState.blendMode != oldState.blendMode || + newState.blendModeAlpha != oldState.blendModeAlpha) { + if (newState.blendEnable == ovrGpuState::BLEND_ENABLE_SEPARATE) { + GL(glBlendFuncSeparate( + newState.blendSrc, + newState.blendDst, + newState.blendSrcAlpha, + newState.blendDstAlpha)); + GL(glBlendEquationSeparate(newState.blendMode, newState.blendModeAlpha)); + } else { + GL(glBlendFunc(newState.blendSrc, newState.blendDst)); + GL(glBlendEquation(newState.blendMode)); + } + } + + if (force || newState.depthFunc != oldState.depthFunc) { + GL(glDepthFunc(newState.depthFunc)); + } + if (force || newState.frontFace != oldState.frontFace) { + GL(glFrontFace(newState.frontFace)); + } + if (force || newState.depthEnable != oldState.depthEnable) { + if (newState.depthEnable) { + GL(glEnable(GL_DEPTH_TEST)); + } else { + GL(glDisable(GL_DEPTH_TEST)); + } + } + if (force || newState.depthMaskEnable != oldState.depthMaskEnable) { + if (newState.depthMaskEnable) { + GL(glDepthMask(GL_TRUE)); + } else { + GL(glDepthMask(GL_FALSE)); + } + } + if (force || newState.colorMaskEnable[0] != oldState.colorMaskEnable[0] || + newState.colorMaskEnable[1] != oldState.colorMaskEnable[1] || + newState.colorMaskEnable[2] != oldState.colorMaskEnable[2] || + newState.colorMaskEnable[3] != oldState.colorMaskEnable[3]) { + GL(glColorMask( + newState.colorMaskEnable[0] ? GL_TRUE : GL_FALSE, + newState.colorMaskEnable[1] ? GL_TRUE : GL_FALSE, + newState.colorMaskEnable[2] ? GL_TRUE : GL_FALSE, + newState.colorMaskEnable[3] ? GL_TRUE : GL_FALSE)); + } + if (force || newState.polygonOffsetEnable != oldState.polygonOffsetEnable) { + if (newState.polygonOffsetEnable) { + GL(glEnable(GL_POLYGON_OFFSET_FILL)); + GL(glPolygonOffset(1.0f, 1.0f)); + } else { + GL(glDisable(GL_POLYGON_OFFSET_FILL)); + } + } + if (force || newState.cullEnable != oldState.cullEnable) { + if (newState.cullEnable) { + GL(glEnable(GL_CULL_FACE)); + } else { + GL(glDisable(GL_CULL_FACE)); + } + } + if (force || newState.lineWidth != oldState.lineWidth) { + GL(glLineWidth(newState.lineWidth)); + } + if (force || (newState.depthRange[0] != oldState.depthRange[0]) || + (newState.depthRange[1] != oldState.depthRange[1])) { + GL(glDepthRangef(newState.depthRange[0], newState.depthRange[1])); + } +#if !defined(GL_ES_VERSION_2_0) || GL_ES_VERSION_2_0 == 0 + if (force || newState.polygonMode != oldState.polygonMode) { + GL(glPolygonMode(GL_FRONT_AND_BACK, newState.polygonMode)); + } +#endif + // extend as needed +} + +ovrSurfaceRender::ovrSurfaceRender() : CurrentSceneMatricesIdx(0) {} + +ovrSurfaceRender::~ovrSurfaceRender() {} + +void ovrSurfaceRender::Init() { + for (int i = 0; i < MAX_SCENEMATRICES_UBOS; i++) { + SceneMatrices[i].Create(GLBUFFER_TYPE_UNIFORM, GlProgram::SCENE_MATRICES_UBO_SIZE, NULL); + } + + CurrentSceneMatricesIdx = 0; +} + +void ovrSurfaceRender::Shutdown() { + for (int i = 0; i < MAX_SCENEMATRICES_UBOS; i++) { + SceneMatrices[i].Destroy(); + } +} + +int ovrSurfaceRender::UpdateSceneMatrices( + const Matrix4f* viewMatrix, + const Matrix4f* projectionMatrix, + const int numViews) { + assert(numViews >= 0 && numViews <= GlProgram::MAX_VIEWS); + + // ----DEPRECATED_DRAWEYEVIEW + // NOTE: Apps which still use DrawEyeView (or that are in process of moving away from it) will + // call RenderSurfaceList multiple times per frame outside of AppRender. This can cause a + // rendering hazard in that we may be updating the scene matrices ubo while it is still in use. + // The typical DEV case is 2x a frame, but have seen it top 8x a frame. Since the matrices + // in the DEV path are typically the same, test for that condition and don't update the ubo. + // This check can be removed once the DEV path is removed. + bool requiresUpdate = false; +#if 1 + for (int i = 0; i < numViews; i++) { + requiresUpdate |= !(CachedViewMatrix[i] == viewMatrix[i]); + requiresUpdate |= !(CachedProjectionMatrix[i] == projectionMatrix[i]); + } +#else + requiresUpdate = true; +#endif + // ----DEPRECATED_DRAWEYEVIEW + + if (requiresUpdate) { + // Advance to the next available scene matrix ubo. + CurrentSceneMatricesIdx = (CurrentSceneMatricesIdx + 1) % MAX_SCENEMATRICES_UBOS; + + // Cache and transpose the matrices before passing to GL. + Matrix4f viewMatrixTransposed[GlProgram::MAX_VIEWS]; + Matrix4f projectionMatrixTransposed[GlProgram::MAX_VIEWS]; + for (int i = 0; i < numViews; i++) { + CachedViewMatrix[i] = viewMatrix[i]; + CachedProjectionMatrix[i] = projectionMatrix[i]; + + viewMatrixTransposed[i] = CachedViewMatrix[i].Transposed(); + projectionMatrixTransposed[i] = CachedProjectionMatrix[i].Transposed(); + } + + void* matricesBuffer = SceneMatrices[CurrentSceneMatricesIdx].MapBuffer(); + if (matricesBuffer != NULL) { + memcpy( + (char*)matricesBuffer + 0 * GlProgram::MAX_VIEWS * sizeof(Matrix4f), + viewMatrixTransposed, + GlProgram::MAX_VIEWS * sizeof(Matrix4f)); + memcpy( + (char*)matricesBuffer + 1 * GlProgram::MAX_VIEWS * sizeof(Matrix4f), + projectionMatrixTransposed, + GlProgram::MAX_VIEWS * sizeof(Matrix4f)); + SceneMatrices[CurrentSceneMatricesIdx].UnmapBuffer(); + } + } + + // ALOG( "UpdateSceneMatrices: RequiresUpdate %d, CurrIdx %d", requiresUpdate, + // CurrentSceneMatricesIdx ); + + return CurrentSceneMatricesIdx; +} + +// Renders a list of pointers to models in order. +ovrDrawCounters ovrSurfaceRender::RenderSurfaceList( + const std::vector& surfaceList, + const Matrix4f& viewMatrix, + const Matrix4f& projectionMatrix, + const int eye) { + assert(eye >= 0 && eye < GlProgram::MAX_VIEWS); + + // Force the GPU state to a known value, then only set on changes + ovrGpuState currentGpuState; + ChangeGpuState(currentGpuState, currentGpuState, true /* force */); + + // TODO: These should be range checked containers. + GLuint currentBuffers[ovrUniform::MAX_UNIFORMS] = {}; + GLuint currentTextures[ovrUniform::MAX_UNIFORMS] = {}; + GLuint currentProgramObject = 0; + + const int sceneMatricesIdx = + UpdateSceneMatrices(&viewMatrix, &projectionMatrix, GlProgram::MAX_VIEWS /* num eyes */); + + // counters + ovrDrawCounters counters; + + // Loop through all the surfaces + for (const ovrDrawSurface& drawSurface : surfaceList) { + const ovrSurfaceDef& surfaceDef = *drawSurface.surface; + const ovrGraphicsCommand& cmd = surfaceDef.graphicsCommand; + + if (cmd.Program.IsValid()) { + ChangeGpuState(currentGpuState, cmd.GpuState); + currentGpuState = cmd.GpuState; + GLCheckErrorsWithTitle(surfaceDef.surfaceName.c_str()); + + // update the program object + if (cmd.Program.Program != currentProgramObject) { + counters.numProgramBinds++; + + currentProgramObject = cmd.Program.Program; + GL(glUseProgram(cmd.Program.Program)); + } + + // Update globally defined system level uniforms. + { + if (cmd.Program.ViewID.Location >= 0) // not defined when multiview enabled + { + GL(glUniform1i(cmd.Program.ViewID.Location, eye)); + } + GL(glUniformMatrix4fv( + cmd.Program.ModelMatrix.Location, 1, GL_TRUE, drawSurface.modelMatrix.M[0])); + + if (cmd.Program.SceneMatrices.Location >= 0) { + GL(glBindBufferBase( + GL_UNIFORM_BUFFER, + cmd.Program.SceneMatrices.Binding, + SceneMatrices[sceneMatricesIdx].GetBuffer())); + } + } + + // update texture bindings and uniform values + bool uniformsDone = false; + { + for (int i = 0; i < ovrUniform::MAX_UNIFORMS && !uniformsDone; ++i) { + counters.numParameterUpdates++; + const int parmLocation = cmd.Program.Uniforms[i].Location; + + switch (cmd.Program.Uniforms[i].Type) { + case ovrProgramParmType::INT: { + if (parmLocation >= 0 && cmd.UniformData[i].Data != NULL) { + GL(glUniform1iv( + parmLocation, + 1, + static_cast(cmd.UniformData[i].Data))); + } + } break; + case ovrProgramParmType::INT_VECTOR2: { + if (parmLocation >= 0 && cmd.UniformData[i].Data != NULL) { + GL(glUniform2iv( + parmLocation, + 1, + static_cast(cmd.UniformData[i].Data))); + } + } break; + case ovrProgramParmType::INT_VECTOR3: { + if (parmLocation >= 0 && cmd.UniformData[i].Data != NULL) { + GL(glUniform3iv( + parmLocation, + 1, + static_cast(cmd.UniformData[i].Data))); + } + } break; + case ovrProgramParmType::INT_VECTOR4: { + if (parmLocation >= 0 && cmd.UniformData[i].Data != NULL) { + GL(glUniform4iv( + parmLocation, + 1, + static_cast(cmd.UniformData[i].Data))); + } + } break; + case ovrProgramParmType::FLOAT: { + if (parmLocation >= 0 && cmd.UniformData[i].Data != NULL) { + GL(glUniform1f( + parmLocation, + *static_cast(cmd.UniformData[i].Data))); + } + } break; + case ovrProgramParmType::FLOAT_VECTOR2: { + if (parmLocation >= 0 && cmd.UniformData[i].Data != NULL) { + GL(glUniform2fv( + parmLocation, + 1, + static_cast(cmd.UniformData[i].Data))); + } + } break; + case ovrProgramParmType::FLOAT_VECTOR3: { + if (parmLocation >= 0 && cmd.UniformData[i].Data != NULL) { + GL(glUniform3fv( + parmLocation, + 1, + static_cast(cmd.UniformData[i].Data))); + } + } break; + case ovrProgramParmType::FLOAT_VECTOR4: { + if (parmLocation >= 0 && cmd.UniformData[i].Data != NULL) { + GL(glUniform4fv( + parmLocation, + 1, + static_cast(cmd.UniformData[i].Data))); + } + } break; + case ovrProgramParmType::FLOAT_MATRIX4: { + if (parmLocation >= 0 && cmd.UniformData[i].Data != NULL) { + if (cmd.UniformData[i].Count > 1) { + /// FIXME: setting glUniformMatrix4fv transpose to GL_TRUE for + /// an array of matrices produces garbage using the Adreno 420 + /// OpenGL ES 3.0 driver. + static Matrix4f transposedJoints[MAX_JOINTS]; + const int numJoints = + std::min(cmd.UniformData[i].Count, MAX_JOINTS); + for (int j = 0; j < numJoints; j++) { + transposedJoints[j] = + static_cast(cmd.UniformData[i].Data)[j] + .Transposed(); + } + GL(glUniformMatrix4fv( + parmLocation, + numJoints, + GL_FALSE, + static_cast(&transposedJoints[0].M[0][0]))); + } else { + GL(glUniformMatrix4fv( + parmLocation, + cmd.UniformData[i].Count, + GL_TRUE, + static_cast(cmd.UniformData[i].Data))); + } + } + } break; + case ovrProgramParmType::TEXTURE_SAMPLED: { + const int parmBinding = cmd.Program.Uniforms[i].Binding; + if (parmBinding >= 0 && cmd.UniformData[i].Data != NULL) { + const GlTexture& texture = + *static_cast(cmd.UniformData[i].Data); + if (currentTextures[parmBinding] != texture.texture) { + counters.numTextureBinds++; + currentTextures[parmBinding] = texture.texture; + GL(glActiveTexture(GL_TEXTURE0 + parmBinding)); + GL(glBindTexture( + texture.target ? texture.target : GL_TEXTURE_2D, + texture.texture)); + } + } + } break; + case ovrProgramParmType::BUFFER_UNIFORM: { + const int parmBinding = cmd.Program.Uniforms[i].Binding; + if (parmBinding >= 0 && cmd.UniformData[i].Data != NULL) { + const GlBuffer& buffer = + *static_cast(cmd.UniformData[i].Data); + if (currentBuffers[parmBinding] != buffer.GetBuffer()) { + counters.numBufferBinds++; + currentBuffers[parmBinding] = buffer.GetBuffer(); + GL(glBindBufferBase( + GL_UNIFORM_BUFFER, parmBinding, buffer.GetBuffer())); + } + } + } break; + case ovrProgramParmType::MAX: + uniformsDone = true; + break; // done + default: + assert(false); + uniformsDone = true; + break; + } + } + } + } + + counters.numDrawCalls++; + + if (LogRenderSurfaces) { + ALOG( + "Drawing %s vao=%d vb=%d primitive=0x%04x indexCount=%d IndexType=0x%04x ", + surfaceDef.surfaceName.c_str(), + surfaceDef.geo.vertexArrayObject, + surfaceDef.geo.vertexBuffer, + surfaceDef.geo.primitiveType, + surfaceDef.geo.indexCount, + surfaceDef.geo.IndexType); + } + + // Bind all the vertex and element arrays + { + GL(glBindVertexArray(surfaceDef.geo.vertexArrayObject)); + + if (surfaceDef.numInstances > 1) { + GL(glDrawElementsInstanced( + surfaceDef.geo.primitiveType, + surfaceDef.geo.indexCount, + surfaceDef.geo.IndexType, + NULL, + surfaceDef.numInstances)); + } else { + GL(glDrawElements( + surfaceDef.geo.primitiveType, + surfaceDef.geo.indexCount, + surfaceDef.geo.IndexType, + NULL)); + } + } + + GLCheckErrorsWithTitle(surfaceDef.surfaceName.c_str()); + } + + // set the gpu state back to the default + ChangeGpuState(currentGpuState, ovrGpuState()); + GL(glActiveTexture(GL_TEXTURE0)); + GL(glBindTexture(GL_TEXTURE_2D, 0)); + GL(glUseProgram(0)); + GL(glBindVertexArray(0)); + + return counters; +} + +} // namespace OVRFW diff --git a/Samples/SampleXrFramework/Src/Render/SurfaceRender.h b/Samples/SampleXrFramework/Src/Render/SurfaceRender.h new file mode 100755 index 0000000..0d076cc --- /dev/null +++ b/Samples/SampleXrFramework/Src/Render/SurfaceRender.h @@ -0,0 +1,122 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/************************************************************************************ + +Filename : SurfaceRender.h +Content : Optimized OpenGL rendering path +Created : August 9, 2013 +Authors : John Carmack + +************************************************************************************/ + +#pragma once + +#include +#include + +#include "OVR_Math.h" + +#include "GlGeometry.h" +#include "GpuState.h" +#include "GlProgram.h" +#include "GlBuffer.h" +#include "GlTexture.h" + +namespace OVRFW { + +struct ovrSurfaceDef { + ovrSurfaceDef() : numInstances(1) {} + + // Name from the model file, can be used to control surfaces with code. + // May be multiple semi-colon separated names if multiple source meshes + // were merged into one surface. + std::string surfaceName; + + // There is a space savings to be had with triangle strips + // if primitive restart is supported, but it is a net speed + // loss on most architectures. Adreno docs still recommend, + // so it might be worth trying. + GlGeometry geo; + + // state + program + uniforms + ovrGraphicsCommand graphicsCommand; + + // Number of instances to be rendered (0 or 1 denotes no instancing) + int numInstances; +}; + +struct ovrDrawCounters { + ovrDrawCounters() + : numElements(0), + numDrawCalls(0), + numProgramBinds(0), + numParameterUpdates(0), + numTextureBinds(0), + numBufferBinds(0) {} + + int numElements; + int numDrawCalls; + int numProgramBinds; + int numParameterUpdates; // MVP, etc + int numTextureBinds; + int numBufferBinds; +}; + +struct ovrDrawSurface { + ovrDrawSurface() : surface(NULL) {} + + ovrDrawSurface(const OVR::Matrix4f& modelMatrix_, const ovrSurfaceDef* surface_) + : modelMatrix(modelMatrix_), surface(surface_) {} + + ovrDrawSurface(const ovrSurfaceDef* surface_) : surface(surface_) {} + + void Clear() { + modelMatrix = OVR::Matrix4f(); + surface = NULL; + } + + OVR::Matrix4f modelMatrix; + const ovrSurfaceDef* surface; +}; + +class ovrSurfaceRender { + public: + ovrSurfaceRender(); + ~ovrSurfaceRender(); + + // Requires an active GL context. + void Init(); + void Shutdown(); + + // Draws a list of surfaces in order. + // Any sorting or culling should be performed before calling. + ovrDrawCounters RenderSurfaceList( + const std::vector& surfaceList, + const OVR::Matrix4f& viewMatrix, + const OVR::Matrix4f& projectionMatrix, + const int eye); + + private: + // Returns the index of the updated SceneMatrices UBO. + int UpdateSceneMatrices( + const OVR::Matrix4f* viewMatrix, + const OVR::Matrix4f* projectionMatrix, + const int numMatrices); + + private: + // Use a ring-buffer to avoid rendering hazards with potential update + // of the SceneMatrices UBO multiple times per frame. + static const int MAX_SCENEMATRICES_UBOS = 8; + int CurrentSceneMatricesIdx; + GlBuffer SceneMatrices[MAX_SCENEMATRICES_UBOS]; // ubo for storing per-view scene matrices which + // are common to the framework render programs + // and do not change frequently. + + OVR::Matrix4f CachedViewMatrix[GlProgram::MAX_VIEWS]; + OVR::Matrix4f CachedProjectionMatrix[GlProgram::MAX_VIEWS]; +}; + +// Set this true for log spew from BuildDrawSurfaceList and RenderSurfaceList. +extern bool LogRenderSurfaces; + +} // namespace OVRFW diff --git a/Samples/SampleXrFramework/Src/Render/SurfaceTexture.cpp b/Samples/SampleXrFramework/Src/Render/SurfaceTexture.cpp new file mode 100755 index 0000000..83ce80d --- /dev/null +++ b/Samples/SampleXrFramework/Src/Render/SurfaceTexture.cpp @@ -0,0 +1,129 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/************************************************************************************ + +Filename : SurfaceTexture.cpp +Content : Interface to Android SurfaceTexture objects +Created : September 17, 2013 +Authors : John Carmack + +*************************************************************************************/ + +#include "SurfaceTexture.h" + +#include + +#include "Misc/Log.h" +#include "Egl.h" +#include "GlTexture.h" + +namespace OVRFW { + +SurfaceTexture::SurfaceTexture(JNIEnv* jni_) + : textureId(0), + javaObject(NULL), + jni(NULL), + nanoTimeStamp(0) +#if defined(OVR_OS_ANDROID) + , + updateTexImageMethodId(NULL), + getTimestampMethodId(NULL), + setDefaultBufferSizeMethodId(NULL) +#endif // defined(OVR_OS_ANDROID) +{ + jni = jni_; + + glGenTextures(1, &textureId); + glBindTexture(GL_TEXTURE_EXTERNAL_OES, GetTextureId()); + glTexParameterf(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameterf(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameterf(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameterf(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glBindTexture(GL_TEXTURE_EXTERNAL_OES, 0); + +#if defined(OVR_OS_ANDROID) + static const char* className = "android/graphics/SurfaceTexture"; + JavaClass surfaceTextureClass(jni, jni->FindClass(className)); + if (0 == surfaceTextureClass.GetJClass()) { + ALOGE_FAIL("FindClass( %s ) failed", className); + } + + // find the constructor that takes an int + const jmethodID constructor = + jni->GetMethodID(surfaceTextureClass.GetJClass(), "", "(I)V"); + if (constructor == 0) { + ALOGE_FAIL("GetMethodID( ) failed"); + } + updateTexImageMethodId = + jni->GetMethodID(surfaceTextureClass.GetJClass(), "updateTexImage", "()V"); + if (updateTexImageMethodId == 0) { + ALOGE_FAIL("couldn't get updateTexImageMethodId"); + } + getTimestampMethodId = jni->GetMethodID(surfaceTextureClass.GetJClass(), "getTimestamp", "()J"); + if (getTimestampMethodId == 0) { + ALOGE_FAIL("couldn't get getTimestampMethodId"); + } + setDefaultBufferSizeMethodId = + jni->GetMethodID(surfaceTextureClass.GetJClass(), "setDefaultBufferSize", "(II)V"); + if (setDefaultBufferSizeMethodId == 0) { + ALOGE_FAIL("couldn't get setDefaultBufferSize"); + } + + JavaObject obj( + jni, jni->NewObject(surfaceTextureClass.GetJClass(), constructor, GetTextureId())); + if (obj.GetJObject() == 0) { + ALOGE_FAIL("NewObject() failed"); + } + + /// Keep globar ref around + javaObject = jni->NewGlobalRef(obj.GetJObject()); + if (javaObject == 0) { + ALOGE_FAIL("NewGlobalRef() failed"); + } +#endif // defined(OVR_OS_ANDROID) +} + +SurfaceTexture::~SurfaceTexture() { + if (textureId != 0) { + glDeleteTextures(1, &textureId); + textureId = 0; + } +#if defined(OVR_OS_ANDROID) + if (javaObject) { + jni->DeleteGlobalRef(javaObject); + javaObject = 0; + } +#endif // defined(OVR_OS_ANDROID) +} + +void SurfaceTexture::SetDefaultBufferSize(const int width, const int height) { +#if defined(OVR_OS_ANDROID) + jni->CallVoidMethod(javaObject, setDefaultBufferSizeMethodId, width, height); +#endif // defined(OVR_OS_ANDROID) +} + +void SurfaceTexture::Update() { +#if defined(OVR_OS_ANDROID) + // latch the latest movie frame to the texture + if (!javaObject) { + return; + } + + jni->CallVoidMethod(javaObject, updateTexImageMethodId); + nanoTimeStamp = jni->CallLongMethod(javaObject, getTimestampMethodId); +#endif // defined(OVR_OS_ANDROID) +} + +unsigned int SurfaceTexture::GetTextureId() { + return textureId; +} + +jobject SurfaceTexture::GetJavaObject() { + return javaObject; +} + +long long SurfaceTexture::GetNanoTimeStamp() { + return nanoTimeStamp; +} + +} // namespace OVRFW diff --git a/Samples/SampleXrFramework/Src/Render/SurfaceTexture.h b/Samples/SampleXrFramework/Src/Render/SurfaceTexture.h new file mode 100755 index 0000000..e220718 --- /dev/null +++ b/Samples/SampleXrFramework/Src/Render/SurfaceTexture.h @@ -0,0 +1,67 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/************************************************************************************ + +Filename : SurfaceTexture.h +Content : Interface to Android SurfaceTexture objects +Created : September 17, 2013 +Authors : John Carmack + +*************************************************************************************/ + +#pragma once + +#include "JniUtils.h" + +namespace OVRFW { + +// SurfaceTextures are used to get movie frames, Camera +// previews, and Android canvas views. +// +// Note that these never have mipmaps, so you will often +// want to render them to another texture and generate mipmaps +// to avoid aliasing when drawing, unless you know it will +// always be magnified. +// +// Note that we do not get and use the TransformMatrix +// from java. Presumably this was only necessary before +// non-power-of-two textures became ubiquitous. +class SurfaceTexture { + public: + SurfaceTexture(JNIEnv* jni_); + ~SurfaceTexture(); + + // For some java-side uses, you can set the size + // of the buffer before it is used to control how + // large it is. Video decompression and camera preview + // always override the size automatically. + void SetDefaultBufferSize(const int width, const int height); + + // This can only be called with an active GL context. + // As a side effect, the textureId will be bound to the + // GL_TEXTURE_EXTERNAL_OES target of the currently active + // texture unit. + void Update(); + + unsigned int GetTextureId(); + jobject GetJavaObject(); + long long GetNanoTimeStamp(); + + private: + unsigned int textureId; + jobject javaObject; + JNIEnv* jni; + + // Updated when Update() is called, can be used to + // check if a new frame is available and ready + // to be processed / mipmapped by other code. + long long nanoTimeStamp; + +#if defined(OVR_OS_ANDROID) + jmethodID updateTexImageMethodId; + jmethodID getTimestampMethodId; + jmethodID setDefaultBufferSizeMethodId; +#endif // defined(OVR_OS_ANDROID) +}; + +} // namespace OVRFW diff --git a/Samples/SampleXrFramework/Src/Render/TextureAtlas.cpp b/Samples/SampleXrFramework/Src/Render/TextureAtlas.cpp new file mode 100755 index 0000000..f9c6761 --- /dev/null +++ b/Samples/SampleXrFramework/Src/Render/TextureAtlas.cpp @@ -0,0 +1,150 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/************************************************************************************ + +Filename : TextureAtlas.h +Content : A simple particle system for System Activities. +Created : October 23, 2015 +Authors : Jonathan E. Wright + +************************************************************************************/ + +#include "TextureAtlas.h" + +#include "OVR_Std.h" +#include "Render/Egl.h" + +using OVR::Bounds3f; +using OVR::Matrix4f; +using OVR::Quatf; +using OVR::Vector2f; +using OVR::Vector3f; +using OVR::Vector4f; + +namespace OVRFW { + +ovrTextureAtlas::ovrTextureAtlas() : TextureWidth(0), TextureHeight(0) {} + +ovrTextureAtlas::~ovrTextureAtlas() { + Shutdown(); +} + +bool ovrTextureAtlas::BuildSpritesFromGrid( + const int numSpriteColumns, + const int numSpriteRows, + const int numSprites) { + // this should be called only after loading the texture because the width and height need to be + // known + assert(TextureWidth > 0 && TextureHeight > 0); + + // dimensions must be evenly divisible by grid to avoid sub-pixel uv boundaries + assert(TextureWidth % numSpriteColumns == 0); + assert(TextureHeight % numSpriteRows == 0); + + Sprites.resize(0); + + bool done = false; + for (int y = 0; y < numSpriteRows; ++y) { + for (int x = 0; x < numSpriteColumns; ++x) { + if (y * numSpriteColumns + x >= numSprites) { + done = true; + break; + } + char name[128]; + OVR::OVR_sprintf(name, sizeof(name), "%ix%i", x, y); + + Vector2f uvMins; + Vector2f uvMaxs; + GetUVsForGridCell( + x, + y, + numSpriteColumns, + numSpriteRows, + TextureWidth, + TextureHeight, + 8, + 8, + uvMins, + uvMaxs); + Sprites.push_back(ovrSpriteDef(name, uvMins, uvMaxs)); + } + if (done) { + break; + } + } + + return true; +} + +// specify sprite locations as a custom list +bool ovrTextureAtlas::Init(ovrFileSys& fileSys, const char* atlasTextureName) { + std::vector buffer; + if (fileSys.ReadFile(atlasTextureName, buffer)) { + GLCheckErrorsWithTitle("Pre atlas texture load"); + + AtlasTexture = LoadTextureFromBuffer( + atlasTextureName, + buffer.data(), + buffer.size(), + TextureFlags_t(TEXTUREFLAG_NO_DEFAULT), + TextureWidth, + TextureHeight); + + GLCheckErrorsWithTitle("Post atlas texture load"); + + if (AtlasTexture == 0) { + return false; + } + } + + TextureName = atlasTextureName; + + return true; +} + +bool ovrTextureAtlas::SetSpriteDefs(const std::vector& sprites) { + Sprites = sprites; + return true; +} + +void ovrTextureAtlas::SetSpriteName(const int index, const char* name) { + Sprites[index].Name = name; +} + +void ovrTextureAtlas::Shutdown() { + FreeTexture(AtlasTexture); +} + +const ovrTextureAtlas::ovrSpriteDef& ovrTextureAtlas::GetSpriteDef(const char* spriteName) const { + for (int i = 0; i < static_cast(Sprites.size()); ++i) { + if (OVR::OVR_stricmp(Sprites[i].Name.c_str(), spriteName) == 0) { + return Sprites[i]; + } + } + static ovrSpriteDef sd; + return sd; +} + +void ovrTextureAtlas::GetUVsForGridCell( + const int x, + const int y, + const int numColumns, + const int numRows, + const int textureWidth, + const int textureHeight, + const int borderX, + const int borderY, + Vector2f& uvMins, + Vector2f& uvMaxs) { + int px = (x * textureWidth) / numColumns + borderX; + int py = (y * textureHeight) / numRows + borderY; + int npx = ((x + 1) * textureWidth) / numColumns - borderX; + int npy = ((y + 1) * textureHeight) / numRows - borderY; + + uvMins.x = px / (float)textureWidth; + uvMins.y = py / (float)textureHeight; + uvMaxs.x = npx / (float)textureWidth; + uvMaxs.y = npy / (float)textureHeight; +} + +} // namespace OVRFW diff --git a/Samples/SampleXrFramework/Src/Render/TextureAtlas.h b/Samples/SampleXrFramework/Src/Render/TextureAtlas.h new file mode 100755 index 0000000..54889bb --- /dev/null +++ b/Samples/SampleXrFramework/Src/Render/TextureAtlas.h @@ -0,0 +1,109 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/************************************************************************************ + +Filename : TextureAtlas.h +Content : A simple particle system for System Activities. +Created : October 23, 2015 +Authors : Jonathan E. Wright + +************************************************************************************/ + +#pragma once + +#include +#include + +#include "OVR_Math.h" + +#include "OVR_FileSys.h" +#include "SurfaceRender.h" +#include "GlTexture.h" + +namespace OVRFW { + +// typedef float (*ovrAlphaFunc_t)( const double t ); + +class ovrTextureAtlas { + public: + // class describing each sprite in the atlas + class ovrSpriteDef { + public: + ovrSpriteDef() : uvMins(0.0f, 0.0f), uvMaxs(1.0f, 1.0f) {} + + ovrSpriteDef(const char* name, const OVR::Vector2f& uvMins, const OVR::Vector2f& uvMaxs) + : Name(name), uvMins(uvMins), uvMaxs(uvMaxs) {} + + ovrSpriteDef( + const char* name, + const float u0, + const float v0, + const float u1, + const float v1) + : Name(name), uvMins(u0, v0), uvMaxs(u1, v1) {} + + std::string Name; // name of the sprite texture + OVR::Vector2f uvMins; // bounds in texture space + OVR::Vector2f uvMaxs; + }; + + ovrTextureAtlas(); + ~ovrTextureAtlas(); + + // Specify the texture to load for this atlas. + bool Init(ovrFileSys& fileSys, const char* atlasTextureName); + + bool SetSpriteDefs(const std::vector& sprites); + void SetSpriteName(const int index, const char* name); + + void Shutdown(); + + // Divides the atlas texture evenly into columns * rows sprites. If numSprites is 0 + // then the function assumes all grid blocks are occupied by a valid sprite. If + // numSprites != 0 then only the first numSprites slots are presumed filled. + // The texture width and height should be evenly divisiable by the colums and rows to + // avoid sub-pixel UV boundaries. + bool + BuildSpritesFromGrid(const int numSpriteColumns, const int numSpriteRows, const int numSprites); + + int GetNumSprites() const { + return static_cast(Sprites.size()); + } + const GlTexture& GetTexture() const { + return AtlasTexture; + } + const ovrSpriteDef& GetSpriteDef(const int index) const { + return Sprites[index]; + } + const ovrSpriteDef& GetSpriteDef(const char* spriteName) const; + const std::string& GetTextureName() const { + return TextureName; + } + int GetTextureWidth() const { + return TextureWidth; + } + int GetTextureHeight() const { + return TextureHeight; + } + + static void GetUVsForGridCell( + const int x, + const int y, + const int numColumns, + const int numRows, + const int textureWidth, + const int textureHeight, + const int borderX, + const int borderY, + OVR::Vector2f& uvMins, + OVR::Vector2f& uvMaxs); + + private: + std::vector Sprites; + GlTexture AtlasTexture; + int TextureWidth; + int TextureHeight; + std::string TextureName; +}; + +} // namespace OVRFW diff --git a/Samples/SampleXrFramework/Src/Render/TextureManager.cpp b/Samples/SampleXrFramework/Src/Render/TextureManager.cpp new file mode 100755 index 0000000..6463456 --- /dev/null +++ b/Samples/SampleXrFramework/Src/Render/TextureManager.cpp @@ -0,0 +1,569 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/************************************************************************************ + +Filename : OVR_TextureManager.cpp +Content : Keeps track of textures so they don't need to be loaded more than once. +Created : 1/22/2016 +Authors : Jonathan E. Wright + +*************************************************************************************/ + +// Make sure we get PRIu64 +#define __STDC_FORMAT_MACROS 1 + +#include "TextureManager.h" + +#include "Misc/Log.h" + +#include +#include + +#include "OVR_FileSys.h" +#include "PackageFiles.h" + +namespace OVRFW { + +//============================================================================================== +// ovrManagedTexture +//============================================================================================== + +//============================== +// ovrManagedTexture::Free +void ovrManagedTexture::Free() { + FreeTexture(Texture); + Source = TEXTURE_SOURCE_MAX; + Uri = ""; + IconId = -1; + Handle = textureHandle_t(); +} + +//============================================================================================== +// ovrTextureManagerImpl +//============================================================================================== + +//============================================================== +// ovrTextureManagerImpl +class ovrTextureManagerImpl : public ovrTextureManager { + public: + friend class ovrTextureManager; + + virtual void Init() OVR_OVERRIDE; + virtual void Shutdown() OVR_OVERRIDE; + + virtual textureHandle_t LoadTexture( + ovrFileSys& fileSys, + char const* uri, + ovrTextureFilter const filterType = FILTER_DEFAULT, + ovrTextureWrap const wrapType = WRAP_DEFAULT) OVR_OVERRIDE; + virtual textureHandle_t LoadTexture( + char const* uri, + void const* buffer, + size_t const bufferSize, + ovrTextureFilter const filterType = FILTER_DEFAULT, + ovrTextureWrap const wrapType = WRAP_DEFAULT) OVR_OVERRIDE; + virtual textureHandle_t LoadRGBATexture( + char const* uri, + void const* imageData, + int const imageWidth, + int const imageHeight, + ovrTextureFilter const filterType = FILTER_DEFAULT, + ovrTextureWrap const wrapType = WRAP_DEFAULT) OVR_OVERRIDE; + virtual textureHandle_t LoadRGBATexture( + int const iconId, + void const* imageData, + int const imageWidth, + int const imageHeight, + ovrTextureFilter const filterType = FILTER_DEFAULT, + ovrTextureWrap const wrapType = WRAP_DEFAULT) OVR_OVERRIDE; + + virtual void FreeTexture(textureHandle_t const handle) OVR_OVERRIDE; + + virtual ovrManagedTexture GetTexture(textureHandle_t const handle) const OVR_OVERRIDE; + virtual GlTexture GetGlTexture(textureHandle_t const handle) const OVR_OVERRIDE; + + virtual textureHandle_t GetTextureHandle(char const* uri) const OVR_OVERRIDE; + virtual textureHandle_t GetTextureHandle(int const iconId) const OVR_OVERRIDE; + + virtual void PrintStats() const OVR_OVERRIDE; + + private: + std::vector Textures; + std::vector FreeTextures; + bool Initialized; + std::unordered_map UriHash; + + mutable int NumUriLoads; + mutable int NumActualUriLoads; + mutable int NumBufferLoads; + mutable int NumActualBufferLoads; + mutable int NumStringSearches; + mutable int NumStringCompares; + mutable int NumSearches; + mutable int NumCompares; + + private: + ovrTextureManagerImpl(); + ~ovrTextureManagerImpl() override; + + int FindTextureIndex(char const* uri) const; + int FindTextureIndex(int const iconId) const; + int IndexForHandle(textureHandle_t const handle) const; + textureHandle_t AllocTexture(); + + static void SetTextureWrapping(GlTexture& tex, ovrTextureWrap const wrapType); + static void SetTextureFiltering(GlTexture& tex, ovrTextureFilter const filterType); +}; + +//============================== +// ovrTextureManagerImpl:: +ovrTextureManagerImpl::ovrTextureManagerImpl() + : Initialized(false), + NumUriLoads(0), + NumActualUriLoads(0), + NumBufferLoads(0), + NumActualBufferLoads(0), + NumStringSearches(0), + NumStringCompares(0), + NumSearches(0), + NumCompares(0) {} + +//============================== +// ovrTextureManagerImpl:: +ovrTextureManagerImpl::~ovrTextureManagerImpl() { + assert(!Initialized); // call Shutdown() explicitly +} + +//============================== +// ovrTextureManagerImpl:: +void ovrTextureManagerImpl::Init() { + UriHash.reserve(512); + Initialized = true; +} + +//============================== +// ovrTextureManagerImpl:: +void ovrTextureManagerImpl::Shutdown() { + for (auto& texture : Textures) { + if (texture.IsValid()) { + texture.Free(); + } + } + + Textures.resize(0); + FreeTextures.resize(0); + UriHash.clear(); + + Initialized = false; +} + +//============================== +// ovrTextureManagerImpl::SetTextureWrapping +void ovrTextureManagerImpl::SetTextureWrapping(GlTexture& tex, ovrTextureWrap const wrapType) { + /// OVR_PERF_TIMER( SetTextureWrapping ); + switch (wrapType) { + case WRAP_DEFAULT: + return; + case WRAP_CLAMP: + MakeTextureClamped(tex); + break; + case WRAP_REPEAT: + glBindTexture(tex.target, tex.texture); + glTexParameteri(tex.target, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(tex.target, GL_TEXTURE_WRAP_T, GL_REPEAT); + glBindTexture(tex.target, 0); + break; + default: + assert(false); + break; + } +} + +//============================== +// ovrTextureManagerImpl::SetTextureFiltering +void ovrTextureManagerImpl::SetTextureFiltering(GlTexture& tex, ovrTextureFilter const filterType) { + /// OVR_PERF_TIMER( SetTextureFiltering ); + switch (filterType) { + case FILTER_DEFAULT: + return; + case FILTER_LINEAR: + MakeTextureLinear(tex); + break; + case FILTER_MIPMAP_LINEAR: + MakeTextureLinearNearest(tex); + break; + case FILTER_MIPMAP_TRILINEAR: + MakeTextureTrilinear(tex); + break; + case FILTER_MIPMAP_ANISOTROPIC2: + MakeTextureAniso(tex, 2.0f); + break; + case FILTER_MIPMAP_ANISOTROPIC4: + MakeTextureAniso(tex, 4.0f); + break; + case FILTER_MIPMAP_ANISOTROPIC8: + MakeTextureAniso(tex, 8.0f); + break; + case FILTER_MIPMAP_ANISOTROPIC16: + MakeTextureAniso(tex, 16.0f); + break; + default: + assert(false); + } +} + +//============================== +// ovrTextureManagerImpl::LoadTexture +textureHandle_t ovrTextureManagerImpl::LoadTexture( + ovrFileSys& fileSys, + char const* uri, + ovrTextureFilter const filterType, + ovrTextureWrap const wrapType) { + /// OVR_PERF_TIMER( LoadTexture_FromFile ); + + NumUriLoads++; + + int idx = FindTextureIndex(uri); + if (idx >= 0) { + return Textures[idx].GetHandle(); + } + + int w; + int h; + GlTexture tex = LoadTextureFromUri(fileSys, uri, TextureFlags_t(TEXTUREFLAG_NO_DEFAULT), w, h); + if (!tex.IsValid()) { + ALOG("LoadTextureFromUri( '%s' ) failed!", uri); + return textureHandle_t(); + } + + textureHandle_t handle = AllocTexture(); + if (handle.IsValid()) { + SetTextureWrapping(tex, wrapType); + SetTextureFiltering(tex, filterType); + + idx = IndexForHandle(handle); + Textures[idx] = ovrManagedTexture(handle, uri, tex); + UriHash[std::string(uri)] = idx; + + NumActualUriLoads++; + } + + return handle; +} + +//============================== +// ovrTextureManagerImpl::LoadTexture +textureHandle_t ovrTextureManagerImpl::LoadTexture( + char const* uri, + void const* buffer, + size_t const bufferSize, + ovrTextureFilter const filterType, + ovrTextureWrap const wrapType) { + /// OVR_PERF_TIMER( LoadTexture_FromBuffer ); + + NumBufferLoads++; + + int idx = FindTextureIndex(uri); + if (idx >= 0) { + return Textures[idx].GetHandle(); + } + + int width = 0; + int height = 0; + // NOTE: buffer ownership handled by caller + GlTexture tex = LoadTextureFromBuffer( + uri, + static_cast(buffer), + bufferSize, + TextureFlags_t(TEXTUREFLAG_NO_DEFAULT), + width, + height); + + if (!tex.IsValid()) { + ALOG( + "LoadTextureFromBuffer( '%s', %p, %u ) failed!", + uri, + buffer, + static_cast(bufferSize)); + return textureHandle_t(); + } + + textureHandle_t handle = AllocTexture(); + if (handle.IsValid()) { + /// OVR_PERF_TIMER( LoadTexture_FromBuffer_IsValid ); + SetTextureWrapping(tex, wrapType); + SetTextureFiltering(tex, filterType); + + idx = IndexForHandle(handle); + Textures[idx] = ovrManagedTexture(handle, uri, tex); + { + /// OVR_PERF_TIMER( LoadTexture_FromBuffer_Hash ); + UriHash[std::string(uri)] = idx; + } + + NumActualBufferLoads++; + } + + return handle; +} + +//============================== +// ovrTextureManagerImpl::LoadRGBATexture +textureHandle_t ovrTextureManagerImpl::LoadRGBATexture( + char const* uri, + void const* imageData, + int const imageWidth, + int const imageHeight, + ovrTextureFilter const filterType, + ovrTextureWrap const wrapType) { + /// OVR_PERF_TIMER( LoadRGBATexture_uri ); + + ALOG("LoadRGBATexture: uri = '%s' ", uri); + + NumBufferLoads++; + if (imageData == nullptr || imageWidth <= 0 || imageHeight <= 0) { + return textureHandle_t(); + } + + int idx = FindTextureIndex(uri); + if (idx >= 0) { + return Textures[idx].GetHandle(); + } + + GlTexture tex; + { + /// OVR_PERF_TIMER( LoadRGBATexture_uri_LoadRGBATextureFromMemory ); + tex = LoadRGBATextureFromMemory( + static_cast(imageData), imageWidth, imageHeight, false); + if (!tex.IsValid()) { + // ALOG( "LoadRGBATextureFromMemory( '%s', %p, %i, %i ) failed!", uri, imageData, + // imageWidth, imageHeight ); + return textureHandle_t(); + } + } + + textureHandle_t handle = AllocTexture(); + if (handle.IsValid()) { + /// OVR_PERF_TIMER( LoadRGBATexture_uri_IsValid ); + SetTextureWrapping(tex, wrapType); + SetTextureFiltering(tex, filterType); + + idx = IndexForHandle(handle); + Textures[idx] = ovrManagedTexture(handle, uri, tex); + { + /// OVR_PERF_TIMER( LoadRGBATexture_uri_Hash ); + UriHash[std::string(uri)] = idx; + } + NumActualBufferLoads++; + } + return handle; +} + +//============================== +// ovrTextureManagerImpl::LoadRGBATexture +textureHandle_t ovrTextureManagerImpl::LoadRGBATexture( + int const iconId, + void const* imageData, + int const imageWidth, + int const imageHeight, + ovrTextureFilter const filterType, + ovrTextureWrap const wrapType) { + /// OVR_PERF_TIMER( LoadRGBATexture_icon ); + + NumBufferLoads++; + if (imageData == nullptr || imageWidth <= 0 || imageHeight <= 0) { + return textureHandle_t(); + } + + int idx = FindTextureIndex(iconId); + if (idx >= 0) { + return Textures[idx].GetHandle(); + } + + GlTexture tex; + { + /// OVR_PERF_TIMER( LoadRGBATexture_icon_LoadRGBATextureFromMemory ); + tex = LoadRGBATextureFromMemory( + static_cast(imageData), imageWidth, imageHeight, false); + if (!tex.IsValid()) { + // ALOG( "LoadRGBATextureFromMemory( %d, %p, %i, %i ) failed!", iconId, imageData, + // imageWidth, imageHeight ); + return textureHandle_t(); + } + } + + textureHandle_t handle = AllocTexture(); + if (handle.IsValid()) { + SetTextureWrapping(tex, wrapType); + SetTextureFiltering(tex, filterType); + + idx = IndexForHandle(handle); + Textures[idx] = ovrManagedTexture(handle, iconId, tex); + + NumActualBufferLoads++; + } + return handle; +} + +//============================== +// ovrTextureManagerImpl::GetTexture +ovrManagedTexture ovrTextureManagerImpl::GetTexture(textureHandle_t const handle) const { + int idx = IndexForHandle(handle); + if (idx < 0) { + return ovrManagedTexture(); + } + return Textures[idx]; +} + +//============================== +// ovrTextureManagerImpl::GetGlTexture +GlTexture ovrTextureManagerImpl::GetGlTexture(textureHandle_t const handle) const { + int idx = IndexForHandle(handle); + if (idx < 0) { + return GlTexture(); + } + return Textures[idx].GetTexture(); +} + +//============================== +// ovrTextureManagerImpl::FreeTexture +void ovrTextureManagerImpl::FreeTexture(textureHandle_t const handle) { + int idx = IndexForHandle(handle); + if (idx >= 0) { + if (Textures[idx].GetUri().empty()) { + UriHash.erase(Textures[idx].GetUri()); + } + Textures[idx].Free(); + FreeTextures.push_back(idx); + } +} + +//============================== +// ovrTextureManagerImpl::FindTextureIndex +int ovrTextureManagerImpl::FindTextureIndex(char const* uri) const { + /// OVR_PERF_TIMER( FindTextureIndex_uri ); + + NumStringSearches++; + + // enable this to always return the first texture. This basically allows + // texture file loads to be eliminated during perf testing for purposes + // of comparison +#if 0 + if ( Textures.size() > 0 ) + { + return 0; + } +#endif + + auto it = UriHash.find(std::string(uri)); + if (it != UriHash.end()) { + return it->second; + } + + return -1; +} + +//============================== +// ovrTextureManagerImpl::FindTextureIndex +int ovrTextureManagerImpl::FindTextureIndex(int const iconId) const { + /// OVR_PERF_TIMER( FindTextureIndex_iconId ); + + NumSearches++; + if (Textures.size() > 0) { + return 0; + } + for (int i = 0; i < static_cast(Textures.size()); ++i) { + if (Textures[i].IsValid() && Textures[i].GetIconId() == iconId) { + NumCompares += i; + return i; + } + } + NumCompares += static_cast(Textures.size()); + return -1; +} + +//============================== +// ovrTextureManagerImpl::IndexForHandle +int ovrTextureManagerImpl::IndexForHandle(textureHandle_t const handle) const { + if (!handle.IsValid()) { + return -1; + } + return handle.Get(); +} + +//============================== +// ovrTextureManagerImpl::AllocTexture +textureHandle_t ovrTextureManagerImpl::AllocTexture() { + /// OVR_PERF_TIMER( AllocTexture ); + + if (FreeTextures.size() > 0) { + int idx = FreeTextures[static_cast(FreeTextures.size()) - 1]; + FreeTextures.pop_back(); + Textures[idx] = ovrManagedTexture(); + return textureHandle_t(idx); + } + + int idx = static_cast(Textures.size()); + Textures.push_back(ovrManagedTexture()); + + return textureHandle_t(idx); +} + +//============================== +// ovrTextureManagerImpl::GetTextureHandle +textureHandle_t ovrTextureManagerImpl::GetTextureHandle(char const* uri) const { + int idx = FindTextureIndex(uri); + if (idx < 0) { + return textureHandle_t(); + } + return Textures[idx].GetHandle(); +} + +//============================== +// ovrTextureManagerImpl::GetTextureHandle +textureHandle_t ovrTextureManagerImpl::GetTextureHandle(int const iconId) const { + int idx = FindTextureIndex(iconId); + if (idx < 0) { + return textureHandle_t(); + } + return Textures[idx].GetHandle(); +} + +//============================== +// ovrTextureManagerImpl::PrintStats +void ovrTextureManagerImpl::PrintStats() const { + ALOG("NumUriLoads: %i", NumUriLoads); + ALOG("NumBufferLoads: %i", NumBufferLoads); + ALOG("NumActualUriLoads: %i", NumActualUriLoads); + ALOG("NumActualBufferLoads: %i", NumActualBufferLoads); + + ALOG("NumStringSearches: %i", NumStringSearches); + ALOG("NumStringCompares: %i", NumStringCompares); + + ALOG("NumSearches: %i", NumSearches); + ALOG("NumCompares: %i", NumCompares); +} + +//============================================================================================== +// ovrTextureManager +//============================================================================================== + +//============================== +// ovrTextureManager::Create +ovrTextureManager* ovrTextureManager::Create() { + ovrTextureManagerImpl* m = new ovrTextureManagerImpl(); + m->Init(); + return m; +} + +//============================== +// ovrTextureManager::Destroy +void ovrTextureManager::Destroy(ovrTextureManager*& m) { + if (m != nullptr) { + m->Shutdown(); + delete m; + m = nullptr; + } +} + +} // namespace OVRFW diff --git a/Samples/SampleXrFramework/Src/Render/TextureManager.h b/Samples/SampleXrFramework/Src/Render/TextureManager.h new file mode 100755 index 0000000..e0dcc05 --- /dev/null +++ b/Samples/SampleXrFramework/Src/Render/TextureManager.h @@ -0,0 +1,125 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/************************************************************************************ + +Filename : OVR_TextureManager.cpp +Content : Keeps track of textures so they don't need to be loaded more than once. +Created : 1/22/2016 +Authors : Jonathan E. Wright + +*************************************************************************************/ + +#pragma once + +#include "OVR_TypesafeNumber.h" +#include "GlTexture.h" + +#include + +namespace OVRFW { + +enum ovrTextureHandle { INVALID_TEXTURE_HANDLE = -1 }; + +typedef OVR::TypesafeNumberT textureHandle_t; + +class ovrManagedTexture { + public: + enum ovrTextureSource { TEXTURE_SOURCE_URI, TEXTURE_SOURCE_ICON, TEXTURE_SOURCE_MAX }; + + ovrManagedTexture() : Source(TEXTURE_SOURCE_MAX), IconId(-1) {} + ovrManagedTexture(textureHandle_t const handle, char const* uri, GlTexture const& texture) + : Handle(handle), Texture(texture), Source(TEXTURE_SOURCE_URI), Uri(uri) {} + + ovrManagedTexture(textureHandle_t const handle, int const iconId, GlTexture const& texture) + : Handle(handle), Texture(texture), Source(TEXTURE_SOURCE_ICON), IconId(iconId) {} + + void Free(); + + textureHandle_t GetHandle() const { + return Handle; + } + GlTexture const& GetTexture() const { + return Texture; + } + ovrTextureSource GetSource() const { + return Source; + } + std::string const& GetUri() const { + return Uri; + } + int GetIconId() const { + return IconId; + } + bool IsValid() const { + return Texture.IsValid(); + } + + private: + textureHandle_t Handle; // handle of the texture + GlTexture Texture; // the GL texture handle + ovrTextureSource Source; // where this texture came from + std::string Uri; // name of the uri, if the texture was loaded from a uri + int IconId; // id of the icon, if loaded from an icon +}; + +class ovrTextureManager { + public: + enum ovrTextureWrap { WRAP_DEFAULT, WRAP_CLAMP, WRAP_REPEAT }; + enum ovrTextureFilter { + FILTER_DEFAULT, // decided by load time based on image properties (i.e. mipmaps or not) + FILTER_LINEAR, + FILTER_MIPMAP_LINEAR, + FILTER_MIPMAP_TRILINEAR, + FILTER_MIPMAP_ANISOTROPIC2, + FILTER_MIPMAP_ANISOTROPIC4, + FILTER_MIPMAP_ANISOTROPIC8, + FILTER_MIPMAP_ANISOTROPIC16 + + }; + + virtual ~ovrTextureManager() {} + + static ovrTextureManager* Create(); + static void Destroy(ovrTextureManager*& m); + + virtual void Init() = 0; + virtual void Shutdown() = 0; + + virtual textureHandle_t LoadTexture( + class ovrFileSys& fileSys, + char const* uri, + ovrTextureFilter const filterType = FILTER_DEFAULT, + ovrTextureWrap const wrapType = WRAP_DEFAULT) = 0; + virtual textureHandle_t LoadTexture( + char const* uri, + void const* buffer, + size_t const bufferSize, + ovrTextureFilter const filterType = FILTER_DEFAULT, + ovrTextureWrap const wrapType = WRAP_DEFAULT) = 0; + virtual textureHandle_t LoadRGBATexture( + char const* uri, + void const* imageData, + int const imageWidth, + int const imageHeight, + ovrTextureFilter const filterType = FILTER_DEFAULT, + ovrTextureWrap const wrapType = WRAP_DEFAULT) = 0; + virtual textureHandle_t LoadRGBATexture( + int const iconId, + void const* imageData, + int const imageWidth, + int const imageHeight, + ovrTextureFilter const filterType = FILTER_DEFAULT, + ovrTextureWrap const wrapType = WRAP_DEFAULT) = 0; + + virtual void FreeTexture(textureHandle_t const handle) = 0; + + virtual ovrManagedTexture GetTexture(textureHandle_t const handle) const = 0; + virtual GlTexture GetGlTexture(textureHandle_t const handle) const = 0; + + virtual textureHandle_t GetTextureHandle(char const* uri) const = 0; + virtual textureHandle_t GetTextureHandle(int const iconId) const = 0; + + virtual void PrintStats() const = 0; +}; + +} // namespace OVRFW diff --git a/Samples/SampleXrFramework/Src/System.cpp b/Samples/SampleXrFramework/Src/System.cpp new file mode 100755 index 0000000..969a47f --- /dev/null +++ b/Samples/SampleXrFramework/Src/System.cpp @@ -0,0 +1,31 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/******************************************************************************* + +Filename : System.cpp +Content : Global system functions. +Created : February 21, 2018 +Authors : J.M.P. van Waveren, Jonathan Wright +Language : C++ + +*******************************************************************************/ + +#include "System.h" + +#include +#include "time.h" +#include + +namespace OVRFW { + +double GetTimeInSeconds() { + struct timespec now; +#if !defined(WIN32) + clock_gettime(CLOCK_MONOTONIC, &now); +#else + timespec_get(&now, TIME_UTC); +#endif + return (now.tv_sec * 1e9 + now.tv_nsec) * 0.000000001; +} + +} // namespace OVRFW diff --git a/Samples/SampleXrFramework/Src/System.h b/Samples/SampleXrFramework/Src/System.h new file mode 100755 index 0000000..40f7bb8 --- /dev/null +++ b/Samples/SampleXrFramework/Src/System.h @@ -0,0 +1,21 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/******************************************************************************* + +Filename : System.h +Content : Global system functions. +Created : February 21, 2018 +Authors : J.M.P. van Waveren, Jonathan Wright +Language : C++ + +*******************************************************************************/ + +#pragma once + +#include "Misc/Log.h" + +namespace OVRFW { + +double GetTimeInSeconds(); + +} // namespace OVRFW diff --git a/Samples/SampleXrFramework/Src/XrApp.cpp b/Samples/SampleXrFramework/Src/XrApp.cpp new file mode 100755 index 0000000..42dc48e --- /dev/null +++ b/Samples/SampleXrFramework/Src/XrApp.cpp @@ -0,0 +1,1667 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/******************************************************************************* + +Filename : XrApp.cpp +Content : OpenXR application base class. +Created : July 2020 +Authors : Federico Schliemann +Language : c++ + +*******************************************************************************/ + +#include "XrApp.h" + +#if defined(ANDROID) +#include +#include +#include +#include +#include +#include // for prctl( PR_SET_NAME ) +#elif defined(WIN32) +// Favor the high performance NVIDIA or AMD GPUs +extern "C" { +// http://developer.download.nvidia.com/devzone/devcenter/gamegraphics/files/OptimusRenderingPolicies.pdf +__declspec(dllexport) DWORD NvOptimusEnablement = 0x00000001; +// https://gpuopen.com/learn/amdpowerxpressrequesthighperformance/ +__declspec(dllexport) DWORD AmdPowerXpressRequestHighPerformance = 0x00000001; +} +#endif // defined(WIN32) defined(ANDROID) + +using OVR::Bounds3f; +using OVR::Matrix4f; +using OVR::Posef; +using OVR::Quatf; +using OVR::Vector2f; +using OVR::Vector3f; +using OVR::Vector4f; + +std::string OXR_ResultToString(XrInstance instance, XrResult result) { + char errorBuffer[XR_MAX_RESULT_STRING_SIZE]{}; + xrResultToString(instance, result, errorBuffer); + return std::string{errorBuffer}; +} + +void OXR_CheckErrors(XrInstance instance, XrResult result, const char* function, bool failOnError) { + if (XR_FAILED(result)) { + const std::string error = OXR_ResultToString(instance, result); + if (failOnError) { + ALOGE("OpenXR error: %s: %s\n", function, error.c_str()); + } else { + ALOGV("OpenXR error: %s: %s\n", function, error.c_str()); + } + } +} + +inline OVR::Matrix4f XrMatrix4x4f_To_OVRMatrix4f(const XrMatrix4x4f& src) { + // col major to row major ==> transpose + return OVR::Matrix4f( + src.m[0], + src.m[4], + src.m[8], + src.m[12], + src.m[1], + src.m[5], + src.m[9], + src.m[13], + src.m[2], + src.m[6], + src.m[10], + src.m[14], + src.m[3], + src.m[7], + src.m[11], + src.m[15]); +} + +inline OVR::Posef XrPosef_To_OVRPosef(const XrPosef& src) { + return OVR::Posef{ + {src.orientation.x, src.orientation.y, src.orientation.z, src.orientation.w}, + {src.position.x, src.position.y, src.position.z}}; +} + +inline OVR::Vector2f XrVector2f_To_OVRVector2f(const XrVector2f& src) { + return OVR::Vector2f{src.x, src.y}; +} + +namespace OVRFW { + +#if defined(ANDROID) +/** + * Process the next main command. + */ +static void app_handle_cmd(struct android_app* app, int32_t cmd) { + XrApp* appState = (XrApp*)app->userData; + appState->HandleAndroidCmd(app, cmd); +} + +void XrApp::HandleAndroidCmd(struct android_app* app, int32_t cmd) { + switch (cmd) { + // There is no APP_CMD_CREATE. The ANativeActivity creates the + // application thread from onCreate(). The application thread + // then calls android_main(). + case APP_CMD_START: { + ALOGV("onStart()"); + ALOGV(" APP_CMD_START"); + break; + } + case APP_CMD_RESUME: { + ALOGV("onResume()"); + ALOGV(" APP_CMD_RESUME"); + Resumed = true; + break; + } + case APP_CMD_PAUSE: { + ALOGV("onPause()"); + ALOGV(" APP_CMD_PAUSE"); + Resumed = false; + break; + } + case APP_CMD_STOP: { + ALOGV("onStop()"); + ALOGV(" APP_CMD_STOP"); + break; + } + case APP_CMD_DESTROY: { + ALOGV("onDestroy()"); + ALOGV(" APP_CMD_DESTROY"); + break; + } + case APP_CMD_INIT_WINDOW: { + ALOGV("surfaceCreated()"); + ALOGV(" APP_CMD_INIT_WINDOW"); + break; + } + case APP_CMD_TERM_WINDOW: { + ALOGV("surfaceDestroyed()"); + ALOGV(" APP_CMD_TERM_WINDOW"); + break; + } + } +} +#endif // defined(ANDROID) + +void XrApp::HandleSessionStateChanges(XrSessionState state) { + if (state == XR_SESSION_STATE_READY) { +#if defined(ANDROID) + assert(Resumed); +#endif // defined(ANDROID) + assert(SessionActive == false); + + XrSessionBeginInfo sessionBeginInfo = {XR_TYPE_SESSION_BEGIN_INFO}; + sessionBeginInfo.primaryViewConfigurationType = ViewportConfig.viewConfigurationType; + + XrResult result; + OXR(result = xrBeginSession(Session, &sessionBeginInfo)); + SessionActive = (result == XR_SUCCESS); + + // Set session state once we have entered VR mode and have a valid session object. + if (SessionActive) { +#if defined(ANDROID) + XrPerfSettingsLevelEXT cpuPerfLevel = XR_PERF_SETTINGS_LEVEL_SUSTAINED_HIGH_EXT; + switch (CpuLevel) { + case 0: + cpuPerfLevel = XR_PERF_SETTINGS_LEVEL_POWER_SAVINGS_EXT; + break; + case 1: + cpuPerfLevel = XR_PERF_SETTINGS_LEVEL_SUSTAINED_LOW_EXT; + break; + case 2: + cpuPerfLevel = XR_PERF_SETTINGS_LEVEL_SUSTAINED_HIGH_EXT; + break; + case 3: + cpuPerfLevel = XR_PERF_SETTINGS_LEVEL_BOOST_EXT; + break; + default: + ALOGE("Invalid CPU level %d", CpuLevel); + break; + } + + XrPerfSettingsLevelEXT gpuPerfLevel = XR_PERF_SETTINGS_LEVEL_SUSTAINED_HIGH_EXT; + switch (GpuLevel) { + case 0: + gpuPerfLevel = XR_PERF_SETTINGS_LEVEL_POWER_SAVINGS_EXT; + break; + case 1: + gpuPerfLevel = XR_PERF_SETTINGS_LEVEL_SUSTAINED_LOW_EXT; + break; + case 2: + gpuPerfLevel = XR_PERF_SETTINGS_LEVEL_SUSTAINED_HIGH_EXT; + break; + case 3: + gpuPerfLevel = XR_PERF_SETTINGS_LEVEL_BOOST_EXT; + break; + default: + ALOGE("Invalid GPU level %d", GpuLevel); + break; + } + + PFN_xrPerfSettingsSetPerformanceLevelEXT pfnPerfSettingsSetPerformanceLevelEXT = NULL; + OXR(xrGetInstanceProcAddr( + Instance, + "xrPerfSettingsSetPerformanceLevelEXT", + (PFN_xrVoidFunction*)(&pfnPerfSettingsSetPerformanceLevelEXT))); + + OXR(pfnPerfSettingsSetPerformanceLevelEXT( + Session, XR_PERF_SETTINGS_DOMAIN_CPU_EXT, cpuPerfLevel)); + OXR(pfnPerfSettingsSetPerformanceLevelEXT( + Session, XR_PERF_SETTINGS_DOMAIN_GPU_EXT, gpuPerfLevel)); + + PFN_xrSetAndroidApplicationThreadKHR pfnSetAndroidApplicationThreadKHR = NULL; + OXR(xrGetInstanceProcAddr( + Instance, + "xrSetAndroidApplicationThreadKHR", + (PFN_xrVoidFunction*)(&pfnSetAndroidApplicationThreadKHR))); + + OXR(pfnSetAndroidApplicationThreadKHR( + Session, XR_ANDROID_THREAD_TYPE_APPLICATION_MAIN_KHR, MainThreadTid)); + OXR(pfnSetAndroidApplicationThreadKHR( + Session, XR_ANDROID_THREAD_TYPE_RENDERER_MAIN_KHR, RenderThreadTid)); +#endif // defined(ANDROID) + } + } else if (state == XR_SESSION_STATE_STOPPING) { +#if defined(ANDROID) + assert(Resumed == false); +#endif // defined(ANDROID) + assert(SessionActive); + + OXR(xrEndSession(Session)); + SessionActive = false; + } +} + +void XrApp::HandleXrEvents() { + XrEventDataBuffer eventDataBuffer = {}; + + // Poll for events + for (;;) { + XrEventDataBaseHeader* baseEventHeader = (XrEventDataBaseHeader*)(&eventDataBuffer); + baseEventHeader->type = XR_TYPE_EVENT_DATA_BUFFER; + baseEventHeader->next = NULL; + XrResult r; + OXR(r = xrPollEvent(Instance, &eventDataBuffer)); + if (r != XR_SUCCESS) { + break; + } + + switch (baseEventHeader->type) { + case XR_TYPE_EVENT_DATA_EVENTS_LOST: + ALOGV("xrPollEvent: received XR_TYPE_EVENT_DATA_EVENTS_LOST event"); + break; + case XR_TYPE_EVENT_DATA_INSTANCE_LOSS_PENDING: + ALOGV("xrPollEvent: received XR_TYPE_EVENT_DATA_INSTANCE_LOSS_PENDING event"); + break; + case XR_TYPE_EVENT_DATA_INTERACTION_PROFILE_CHANGED: + ALOGV("xrPollEvent: received XR_TYPE_EVENT_DATA_INTERACTION_PROFILE_CHANGED event"); + break; + case XR_TYPE_EVENT_DATA_PERF_SETTINGS_EXT: { + const XrEventDataPerfSettingsEXT* perf_settings_event = + (XrEventDataPerfSettingsEXT*)(baseEventHeader); + ALOGV( + "xrPollEvent: received XR_TYPE_EVENT_DATA_PERF_SETTINGS_EXT event: type %d subdomain %d : level %d -> level %d", + perf_settings_event->type, + perf_settings_event->subDomain, + perf_settings_event->fromLevel, + perf_settings_event->toLevel); + } break; + case XR_TYPE_EVENT_DATA_REFERENCE_SPACE_CHANGE_PENDING: + ALOGV( + "xrPollEvent: received XR_TYPE_EVENT_DATA_REFERENCE_SPACE_CHANGE_PENDING event"); + break; + case XR_TYPE_EVENT_DATA_SESSION_STATE_CHANGED: { + const XrEventDataSessionStateChanged* session_state_changed_event = + (XrEventDataSessionStateChanged*)(baseEventHeader); + ALOGV( + "xrPollEvent: received XR_TYPE_EVENT_DATA_SESSION_STATE_CHANGED: %d for session %p at time %f", + session_state_changed_event->state, + (void*)session_state_changed_event->session, + FromXrTime(session_state_changed_event->time)); + SessionStateChanged(session_state_changed_event->state); + + switch (session_state_changed_event->state) { + case XR_SESSION_STATE_FOCUSED: + Focused = true; + break; + case XR_SESSION_STATE_VISIBLE: + Focused = false; + break; + case XR_SESSION_STATE_READY: + HandleSessionStateChanges(session_state_changed_event->state); + break; + case XR_SESSION_STATE_STOPPING: + HandleSessionStateChanges(session_state_changed_event->state); + break; + case XR_SESSION_STATE_EXITING: + ShouldExit = true; + break; + default: + break; + } + } break; + default: + ALOGV("xrPollEvent: Unknown event"); + break; + } + } +} + +XrActionSet XrApp::CreateActionSet(uint32_t priority, const char* name, const char* localizedName) { + XrActionSetCreateInfo asci = {XR_TYPE_ACTION_SET_CREATE_INFO}; + asci.priority = priority; + strcpy(asci.actionSetName, name); + strcpy(asci.localizedActionSetName, localizedName); + XrActionSet actionSet = XR_NULL_HANDLE; + OXR(xrCreateActionSet(Instance, &asci, &actionSet)); + return actionSet; +} + +XrAction XrApp::CreateAction( + XrActionSet actionSet, + XrActionType type, + const char* actionName, + const char* localizedName, + int countSubactionPaths, + XrPath* subactionPaths) { + ALOGV("CreateAction %s, %" PRIi32, actionName, countSubactionPaths); + + XrActionCreateInfo aci = {XR_TYPE_ACTION_CREATE_INFO}; + aci.actionType = type; + if (countSubactionPaths > 0) { + aci.countSubactionPaths = countSubactionPaths; + aci.subactionPaths = subactionPaths; + } + strcpy(aci.actionName, actionName); + strcpy(aci.localizedActionName, localizedName ? localizedName : actionName); + XrAction action = XR_NULL_HANDLE; + OXR(xrCreateAction(actionSet, &aci, &action)); + return action; +} + +XrActionSuggestedBinding XrApp::ActionSuggestedBinding(XrAction action, const char* bindingString) { + XrActionSuggestedBinding asb; + asb.action = action; + XrPath bindingPath; + OXR(xrStringToPath(Instance, bindingString, &bindingPath)); + asb.binding = bindingPath; + return asb; +} + +XrSpace XrApp::CreateActionSpace(XrAction poseAction, XrPath subactionPath) { + XrActionSpaceCreateInfo asci = {XR_TYPE_ACTION_SPACE_CREATE_INFO}; + asci.action = poseAction; + asci.poseInActionSpace.orientation.w = 1.0f; + asci.subactionPath = subactionPath; + XrSpace actionSpace = XR_NULL_HANDLE; + OXR(xrCreateActionSpace(Session, &asci, &actionSpace)); + return actionSpace; +} + +XrActionStateBoolean XrApp::GetActionStateBoolean(XrAction action, XrPath subactionPath) { + XrActionStateGetInfo getInfo = {XR_TYPE_ACTION_STATE_GET_INFO}; + getInfo.action = action; + getInfo.subactionPath = subactionPath; + XrActionStateBoolean state = {XR_TYPE_ACTION_STATE_BOOLEAN}; + OXR(xrGetActionStateBoolean(Session, &getInfo, &state)); + return state; +} + +XrActionStateFloat XrApp::GetActionStateFloat(XrAction action, XrPath subactionPath) { + XrActionStateGetInfo getInfo = {XR_TYPE_ACTION_STATE_GET_INFO}; + getInfo.action = action; + getInfo.subactionPath = subactionPath; + XrActionStateFloat state = {XR_TYPE_ACTION_STATE_FLOAT}; + OXR(xrGetActionStateFloat(Session, &getInfo, &state)); + return state; +} + +XrActionStateVector2f XrApp::GetActionStateVector2(XrAction action, XrPath subactionPath) { + XrActionStateGetInfo getInfo = {XR_TYPE_ACTION_STATE_GET_INFO}; + getInfo.action = action; + getInfo.subactionPath = subactionPath; + XrActionStateVector2f state = {XR_TYPE_ACTION_STATE_VECTOR2F}; + OXR(xrGetActionStateVector2f(Session, &getInfo, &state)); + return state; +} + +bool XrApp::ActionPoseIsActive(XrAction action, XrPath subactionPath) { + XrActionStateGetInfo getInfo = {XR_TYPE_ACTION_STATE_GET_INFO}; + getInfo.action = action; + getInfo.subactionPath = subactionPath; + XrActionStatePose state = {XR_TYPE_ACTION_STATE_POSE}; + OXR(xrGetActionStatePose(Session, &getInfo, &state)); + return state.isActive != XR_FALSE; +} + +XrApp::LocVel XrApp::GetSpaceLocVel(XrSpace space, XrTime time) { + XrApp::LocVel lv = {{XR_TYPE_SPACE_LOCATION}, {XR_TYPE_SPACE_VELOCITY}}; + lv.loc.next = &lv.vel; + OXR(xrLocateSpace(space, CurrentSpace, time, &lv.loc)); + lv.loc.next = NULL; // pointer no longer valid or necessary + return lv; +} + +// Returns a list of OpenXr extensions needed for this app +std::vector XrApp::GetExtensions() { + std::vector extensions = { +#if defined(XR_USE_GRAPHICS_API_OPENGL_ES) + XR_KHR_OPENGL_ES_ENABLE_EXTENSION_NAME, +#elif defined(XR_USE_GRAPHICS_API_OPENGL) + XR_KHR_OPENGL_ENABLE_EXTENSION_NAME, +#endif // defined(XR_USE_GRAPHICS_API_OPENGL_ES) + XR_KHR_COMPOSITION_LAYER_COLOR_SCALE_BIAS_EXTENSION_NAME, +#if defined(XR_USE_PLATFORM_ANDROID) + XR_EXT_PERFORMANCE_SETTINGS_EXTENSION_NAME, + XR_KHR_ANDROID_THREAD_SETTINGS_EXTENSION_NAME, +#endif // defined(XR_USE_PLATFORM_ANDROID) + XR_KHR_COMPOSITION_LAYER_CUBE_EXTENSION_NAME, + XR_KHR_COMPOSITION_LAYER_CYLINDER_EXTENSION_NAME}; + return extensions; +} + +std::vector XrApp::GetXrExtensionProperties() const { + XrResult result; + PFN_xrEnumerateInstanceExtensionProperties xrEnumerateInstanceExtensionProperties; + OXR(result = xrGetInstanceProcAddr( + XR_NULL_HANDLE, + "xrEnumerateInstanceExtensionProperties", + (PFN_xrVoidFunction*)&xrEnumerateInstanceExtensionProperties)); + if (result != XR_SUCCESS) { + ALOGE("Failed to get xrEnumerateInstanceExtensionProperties function pointer."); + exit(1); + } + + uint32_t numInputExtensions = 0; + uint32_t numOutputExtensions = 0; + OXR(xrEnumerateInstanceExtensionProperties( + NULL, numInputExtensions, &numOutputExtensions, NULL)); + ALOGV("xrEnumerateInstanceExtensionProperties found %u extension(s).", numOutputExtensions); + + numInputExtensions = numOutputExtensions; + + std::vector extensionProperties( + numOutputExtensions, {XR_TYPE_EXTENSION_PROPERTIES}); + + OXR(xrEnumerateInstanceExtensionProperties( + NULL, numInputExtensions, &numOutputExtensions, extensionProperties.data())); + for (uint32_t i = 0; i < numOutputExtensions; i++) { + ALOGV("Extension #%d = '%s'.", i, extensionProperties[i].extensionName); + } + + return extensionProperties; +}; + +// Returns a map from interaction profile paths to vectors of suggested bindings. +// xrSuggestInteractionProfileBindings() is called once for each interaction profile path in the +// returned map. +// Apps are encouraged to suggest bindings for every device/interaction profile they support. +// Override this for custom action bindings, or modify the default bindings. +std::unordered_map> XrApp::GetSuggestedBindings( + XrInstance instance) { + if (SkipInputHandling) { + return std::unordered_map>{}; + } + + std::unordered_map> suggestedBindings{}; + // By default we support "oculus/touch_controller" and "khr/simple_controller" as a fallback + // All supported controllers should be explicitly listed here + + XrPath simpleInteractionProfile = XR_NULL_PATH; + OXR(xrStringToPath( + instance, "/interaction_profiles/khr/simple_controller", &simpleInteractionProfile)); + + XrPath touchInteractionProfile = XR_NULL_PATH; + OXR(xrStringToPath( + instance, "/interaction_profiles/oculus/touch_controller", &touchInteractionProfile)); + + // ----------------------------------------- + // Bindings for oculus/touch_controller + // ----------------------------------------- + // Note: using the fact that operator[] creates an object if it doesn't exist in the map + suggestedBindings[touchInteractionProfile].emplace_back( + ActionSuggestedBinding(AimPoseAction, "/user/hand/left/input/aim/pose")); + suggestedBindings[touchInteractionProfile].emplace_back( + ActionSuggestedBinding(AimPoseAction, "/user/hand/right/input/aim/pose")); + suggestedBindings[touchInteractionProfile].emplace_back( + ActionSuggestedBinding(GripPoseAction, "/user/hand/left/input/grip/pose")); + suggestedBindings[touchInteractionProfile].emplace_back( + ActionSuggestedBinding(GripPoseAction, "/user/hand/right/input/grip/pose")); + suggestedBindings[touchInteractionProfile].emplace_back( + ActionSuggestedBinding(JoystickAction, "/user/hand/left/input/thumbstick")); + suggestedBindings[touchInteractionProfile].emplace_back( + ActionSuggestedBinding(JoystickAction, "/user/hand/right/input/thumbstick")); + suggestedBindings[touchInteractionProfile].emplace_back( + ActionSuggestedBinding(thumbstickClickAction, "/user/hand/left/input/thumbstick/click")); + suggestedBindings[touchInteractionProfile].emplace_back( + ActionSuggestedBinding(thumbstickClickAction, "/user/hand/right/input/thumbstick/click")); + suggestedBindings[touchInteractionProfile].emplace_back( + ActionSuggestedBinding(IndexTriggerAction, "/user/hand/left/input/trigger/value")); + suggestedBindings[touchInteractionProfile].emplace_back( + ActionSuggestedBinding(IndexTriggerAction, "/user/hand/right/input/trigger/value")); + suggestedBindings[touchInteractionProfile].emplace_back( + ActionSuggestedBinding(GripTriggerAction, "/user/hand/left/input/squeeze/value")); + suggestedBindings[touchInteractionProfile].emplace_back( + ActionSuggestedBinding(GripTriggerAction, "/user/hand/right/input/squeeze/value")); + suggestedBindings[touchInteractionProfile].emplace_back( + ActionSuggestedBinding(ButtonAAction, "/user/hand/right/input/a/click")); + suggestedBindings[touchInteractionProfile].emplace_back( + ActionSuggestedBinding(ButtonBAction, "/user/hand/right/input/b/click")); + suggestedBindings[touchInteractionProfile].emplace_back( + ActionSuggestedBinding(ButtonXAction, "/user/hand/left/input/x/click")); + suggestedBindings[touchInteractionProfile].emplace_back( + ActionSuggestedBinding(ButtonYAction, "/user/hand/left/input/y/click")); + suggestedBindings[touchInteractionProfile].emplace_back( + ActionSuggestedBinding(ButtonMenuAction, "/user/hand/left/input/menu/click")); + suggestedBindings[touchInteractionProfile].emplace_back( + ActionSuggestedBinding(ThumbStickTouchAction, "/user/hand/left/input/thumbstick/touch")); + suggestedBindings[touchInteractionProfile].emplace_back( + ActionSuggestedBinding(ThumbStickTouchAction, "/user/hand/right/input/thumbstick/touch")); + suggestedBindings[touchInteractionProfile].emplace_back( + ActionSuggestedBinding(ThumbRestTouchAction, "/user/hand/left/input/thumbrest/touch")); + suggestedBindings[touchInteractionProfile].emplace_back( + ActionSuggestedBinding(ThumbRestTouchAction, "/user/hand/right/input/thumbrest/touch")); + suggestedBindings[touchInteractionProfile].emplace_back( + ActionSuggestedBinding(TriggerTouchAction, "/user/hand/left/input/trigger/touch")); + suggestedBindings[touchInteractionProfile].emplace_back( + ActionSuggestedBinding(TriggerTouchAction, "/user/hand/right/input/trigger/touch")); + + // ----------------------------------------- + // Default bindings for khr/simple_controller + // ----------------------------------------- + suggestedBindings[simpleInteractionProfile].emplace_back( + ActionSuggestedBinding(AimPoseAction, "/user/hand/left/input/aim/pose")); + suggestedBindings[simpleInteractionProfile].emplace_back( + ActionSuggestedBinding(AimPoseAction, "/user/hand/right/input/aim/pose")); + suggestedBindings[simpleInteractionProfile].emplace_back( + ActionSuggestedBinding(GripPoseAction, "/user/hand/right/input/grip/pose")); + suggestedBindings[simpleInteractionProfile].emplace_back( + ActionSuggestedBinding(GripPoseAction, "/user/hand/left/input/grip/pose")); + + suggestedBindings[simpleInteractionProfile].emplace_back( + ActionSuggestedBinding(IndexTriggerAction, "/user/hand/right/input/select/click")); + suggestedBindings[simpleInteractionProfile].emplace_back( + ActionSuggestedBinding(IndexTriggerAction, "/user/hand/left/input/select/click")); + + suggestedBindings[simpleInteractionProfile].emplace_back( + ActionSuggestedBinding(ButtonBAction, "/user/hand/right/input/menu/click")); + suggestedBindings[simpleInteractionProfile].emplace_back( + ActionSuggestedBinding(ButtonMenuAction, "/user/hand/left/input/menu/click")); + + return suggestedBindings; +} + +void XrApp::SuggestInteractionProfileBindings( + const std::unordered_map> allSuggestedBindings) { + // Best practice is for apps to suggest bindings for *ALL* interaction profiles + // that the app supports. Loop over all interaction profiles we support and suggest + // bindings: + for (auto& [interactionProfilePath, bindings] : allSuggestedBindings) { + XrInteractionProfileSuggestedBinding suggestedBindings = { + XR_TYPE_INTERACTION_PROFILE_SUGGESTED_BINDING}; + suggestedBindings.interactionProfile = interactionProfilePath; + suggestedBindings.suggestedBindings = (const XrActionSuggestedBinding*)bindings.data(); + suggestedBindings.countSuggestedBindings = (uint32_t)bindings.size(); + + OXR(xrSuggestInteractionProfileBindings(Instance, &suggestedBindings)); + } +} + +const void* XrApp::GetInstanceCreateInfoNextChain() { + return nullptr; +} + +void XrApp::FreeInstanceCreateInfoNextChain(const void* nextChain) {} + +const void* XrApp::GetSessionCreateInfoNextChain() { + return nullptr; +} + +void XrApp::FreeSessionCreateInfoNextChain(const void* nextChain) {} + +void XrApp::GetInitialSceneUri(std::string& sceneUri) const { + sceneUri = "apk:///assets/box.ovrscene"; +} + +// Called one time when the application process starts. +// Returns true if the application initialized successfully. +bool XrApp::Init(const xrJava& context) { +#if defined(ANDROID) + // Loader + PFN_xrInitializeLoaderKHR xrInitializeLoaderKHR; + xrGetInstanceProcAddr( + XR_NULL_HANDLE, "xrInitializeLoaderKHR", (PFN_xrVoidFunction*)&xrInitializeLoaderKHR); + if (xrInitializeLoaderKHR != NULL) { + XrLoaderInitInfoAndroidKHR loaderInitializeInfoAndroid = { + XR_TYPE_LOADER_INIT_INFO_ANDROID_KHR}; + loaderInitializeInfoAndroid.applicationVM = context.Vm; + loaderInitializeInfoAndroid.applicationContext = context.ActivityObject; + xrInitializeLoaderKHR((XrLoaderInitInfoBaseHeaderKHR*)&loaderInitializeInfoAndroid); + } +#endif // defined(ANDROID) + + // Log available layers. + { + XrResult result; + + PFN_xrEnumerateApiLayerProperties xrEnumerateApiLayerProperties; + OXR(result = xrGetInstanceProcAddr( + XR_NULL_HANDLE, + "xrEnumerateApiLayerProperties", + (PFN_xrVoidFunction*)&xrEnumerateApiLayerProperties)); + if (result != XR_SUCCESS) { + ALOGE("Failed to get xrEnumerateApiLayerProperties function pointer."); + exit(1); + } + + uint32_t numInputLayers = 0; + uint32_t numOutputLayers = 0; + OXR(xrEnumerateApiLayerProperties(numInputLayers, &numOutputLayers, NULL)); + + numInputLayers = numOutputLayers; + + std::vector layerProperties( + numOutputLayers, {XR_TYPE_API_LAYER_PROPERTIES}); + + OXR(xrEnumerateApiLayerProperties( + numInputLayers, &numOutputLayers, layerProperties.data())); + + for (uint32_t i = 0; i < numOutputLayers; i++) { + ALOGV("Found layer %s", layerProperties[i].layerName); + } + } + + // Check that the extensions required are present. + std::vector extensions = GetExtensions(); + ALOGV("Required extension from app (num=%i): ", extensions.size()); + for (auto extension : extensions) { + ALOGV("\t%s", extension); + } + + // Check the list of required extensions against what is supported by the runtime. + // And remove from required list if it is not supported. + { + const auto extensionProperties = GetXrExtensionProperties(); + std::vector removedExtensions; + + extensions.erase( + std::remove_if( + extensions.begin(), + extensions.end(), + [&extensionProperties, &removedExtensions](const char* requiredExtensionName) { + bool found = false; + for (auto extensionProperty : extensionProperties) { + if (!strcmp(requiredExtensionName, extensionProperty.extensionName)) { + ALOGV("Found required extension %s", requiredExtensionName); + found = true; + break; + } + } + + if (!found) { + ALOGW( + "WARNING - Failed to find required extension %s", + requiredExtensionName); + removedExtensions.push_back(requiredExtensionName); + return true; + } + return false; + }), + extensions.end()); + + if (!removedExtensions.empty()) { + ALOGW( + "Following required extensions from app were excluded based on their existence (num=%i): ", + removedExtensions.size()); + for (auto extension : removedExtensions) { + ALOGW("\t%s", extension.c_str()); + } + } + } + + // Create the OpenXR instance. + XrApplicationInfo appInfo; + memset(&appInfo, 0, sizeof(appInfo)); + strcpy(appInfo.applicationName, "OpenXR_NativeActivity"); + appInfo.applicationVersion = 0; + strcpy(appInfo.engineName, "Oculus Mobile Sample"); + appInfo.engineVersion = 0; + appInfo.apiVersion = XR_MAKE_VERSION(1, 0, 34); + + const void* nextChain = GetInstanceCreateInfoNextChain(); + + XrInstanceCreateInfo instanceCreateInfo = {XR_TYPE_INSTANCE_CREATE_INFO}; + instanceCreateInfo.next = nextChain; + instanceCreateInfo.createFlags = 0; + instanceCreateInfo.applicationInfo = appInfo; + instanceCreateInfo.enabledApiLayerCount = 0; + instanceCreateInfo.enabledApiLayerNames = NULL; + instanceCreateInfo.enabledExtensionCount = extensions.size(); + instanceCreateInfo.enabledExtensionNames = extensions.data(); + + XrResult initResult; + OXR(initResult = xrCreateInstance(&instanceCreateInfo, &Instance)); + if (initResult != XR_SUCCESS) { + ALOGE("Failed to create XR instance: %d.", initResult); + exit(1); + } + + FreeInstanceCreateInfoNextChain(nextChain); + /// + + XrInstanceProperties instanceInfo = {XR_TYPE_INSTANCE_PROPERTIES}; + OXR(xrGetInstanceProperties(Instance, &instanceInfo)); + ALOGV( + "Runtime %s: Version : %u.%u.%u", + instanceInfo.runtimeName, + XR_VERSION_MAJOR(instanceInfo.runtimeVersion), + XR_VERSION_MINOR(instanceInfo.runtimeVersion), + XR_VERSION_PATCH(instanceInfo.runtimeVersion)); + + XrSystemGetInfo systemGetInfo = {XR_TYPE_SYSTEM_GET_INFO}; + systemGetInfo.formFactor = XR_FORM_FACTOR_HEAD_MOUNTED_DISPLAY; + + XrSystemId systemId; + OXR(initResult = xrGetSystem(Instance, &systemGetInfo, &systemId)); + if (initResult != XR_SUCCESS) { + if (initResult == XR_ERROR_FORM_FACTOR_UNAVAILABLE) { + ALOGE( + "Failed to get system; the specified form factor is not available. Is your headset connected?"); + } else { + ALOGE("xrGetSystem failed, error %d", initResult); + } + exit(1); + } + + XrSystemProperties systemProperties = {XR_TYPE_SYSTEM_PROPERTIES}; + OXR(xrGetSystemProperties(Instance, systemId, &systemProperties)); + ALOGV( + "System Properties: Name=%s VendorId=%x", + systemProperties.systemName, + systemProperties.vendorId); + ALOGV( + "System Graphics Properties: MaxWidth=%d MaxHeight=%d MaxLayers=%d", + systemProperties.graphicsProperties.maxSwapchainImageWidth, + systemProperties.graphicsProperties.maxSwapchainImageHeight, + systemProperties.graphicsProperties.maxLayerCount); + ALOGV( + "System Tracking Properties: OrientationTracking=%s PositionTracking=%s", + systemProperties.trackingProperties.orientationTracking ? "True" : "False", + systemProperties.trackingProperties.positionTracking ? "True" : "False"); + assert(MAX_NUM_LAYERS <= systemProperties.graphicsProperties.maxLayerCount); + + // Get the graphics requirements. +#if defined(XR_USE_GRAPHICS_API_OPENGL_ES) + PFN_xrGetOpenGLESGraphicsRequirementsKHR pfnGetOpenGLESGraphicsRequirementsKHR = NULL; + OXR(xrGetInstanceProcAddr( + Instance, + "xrGetOpenGLESGraphicsRequirementsKHR", + (PFN_xrVoidFunction*)(&pfnGetOpenGLESGraphicsRequirementsKHR))); + + XrGraphicsRequirementsOpenGLESKHR graphicsRequirements = { + XR_TYPE_GRAPHICS_REQUIREMENTS_OPENGL_ES_KHR}; + OXR(pfnGetOpenGLESGraphicsRequirementsKHR(Instance, systemId, &graphicsRequirements)); +#elif defined(XR_USE_GRAPHICS_API_OPENGL) + // Get the graphics requirements. + PFN_xrGetOpenGLGraphicsRequirementsKHR pfnGetOpenGLGraphicsRequirementsKHR = NULL; + OXR(xrGetInstanceProcAddr( + Instance, + "xrGetOpenGLGraphicsRequirementsKHR", + (PFN_xrVoidFunction*)(&pfnGetOpenGLGraphicsRequirementsKHR))); + + XrGraphicsRequirementsOpenGLKHR graphicsRequirements = { + XR_TYPE_GRAPHICS_REQUIREMENTS_OPENGL_KHR}; + OXR(pfnGetOpenGLGraphicsRequirementsKHR(Instance, systemId, &graphicsRequirements)); +#endif // defined(XR_USE_GRAPHICS_API_OPENGL_ES) + + // Create the EGL Context + ovrEgl_CreateContext(&Egl, NULL); + + // Check the graphics requirements. + int eglMajor = 0; + int eglMinor = 0; + glGetIntegerv(GL_MAJOR_VERSION, &eglMajor); + glGetIntegerv(GL_MINOR_VERSION, &eglMinor); + const XrVersion eglVersion = XR_MAKE_VERSION(eglMajor, eglMinor, 0); + if (eglVersion < graphicsRequirements.minApiVersionSupported || + eglVersion > graphicsRequirements.maxApiVersionSupported) { + ALOGE("GLES version %d.%d not supported", eglMajor, eglMinor); + exit(0); + } + EglInitExtensions(); + + CpuLevel = CPU_LEVEL; + GpuLevel = GPU_LEVEL; +#if defined(ANDROID) + MainThreadTid = gettid(); +#else + MainThreadTid = 0; +#endif // defined(ANDROID) + SystemId = systemId; + + // Actions + if (!SkipInputHandling) { + BaseActionSet = CreateActionSet(1, "base_action_set", "Action Set used on main loop"); + + OXR(xrStringToPath(Instance, "/user/hand/left", &LeftHandPath)); + OXR(xrStringToPath(Instance, "/user/hand/right", &RightHandPath)); + XrPath handSubactionPaths[2] = {LeftHandPath, RightHandPath}; + + AimPoseAction = CreateAction( + BaseActionSet, XR_ACTION_TYPE_POSE_INPUT, "aim_pose", NULL, 2, handSubactionPaths); + GripPoseAction = CreateAction( + BaseActionSet, XR_ACTION_TYPE_POSE_INPUT, "grip_pose", NULL, 2, handSubactionPaths); + + JoystickAction = CreateAction( + BaseActionSet, + XR_ACTION_TYPE_VECTOR2F_INPUT, + "move_on_joy", + NULL, + 2, + handSubactionPaths); + + IndexTriggerAction = CreateAction( + BaseActionSet, + XR_ACTION_TYPE_FLOAT_INPUT, + "index_trigger", + NULL, + 2, + handSubactionPaths); + + GripTriggerAction = CreateAction( + BaseActionSet, XR_ACTION_TYPE_FLOAT_INPUT, "grip_trigger", NULL, 2, handSubactionPaths); + ButtonAAction = CreateAction(BaseActionSet, XR_ACTION_TYPE_BOOLEAN_INPUT, "button_a", NULL); + ButtonBAction = CreateAction(BaseActionSet, XR_ACTION_TYPE_BOOLEAN_INPUT, "button_b", NULL); + ButtonXAction = CreateAction(BaseActionSet, XR_ACTION_TYPE_BOOLEAN_INPUT, "button_x", NULL); + ButtonYAction = CreateAction(BaseActionSet, XR_ACTION_TYPE_BOOLEAN_INPUT, "button_y", NULL); + ButtonMenuAction = + CreateAction(BaseActionSet, XR_ACTION_TYPE_BOOLEAN_INPUT, "button_menu", NULL); + ThumbStickTouchAction = + CreateAction(BaseActionSet, XR_ACTION_TYPE_BOOLEAN_INPUT, "thumb_stick_touch", NULL); + ThumbRestTouchAction = + CreateAction(BaseActionSet, XR_ACTION_TYPE_BOOLEAN_INPUT, "thumb_rest_touch", NULL); + TriggerTouchAction = + CreateAction(BaseActionSet, XR_ACTION_TYPE_BOOLEAN_INPUT, "index_trigger_touch", NULL); + + thumbstickClickAction = + CreateAction(BaseActionSet, XR_ACTION_TYPE_BOOLEAN_INPUT, "thumbstick_click", NULL); + } + + /// Interaction profile can be overridden + const std::unordered_map> allSuggestedBindings = + GetSuggestedBindings(GetInstance()); + + SuggestInteractionProfileBindings(allSuggestedBindings); + + FileSys = std::unique_ptr(ovrFileSys::Create(context)); + if (FileSys) { + OVRFW::ovrFileSys& fs = *FileSys; + MaterialParms materialParms; + materialParms.UseSrgbTextureFormats = false; + std::string sceneUri; + GetInitialSceneUri(sceneUri); + if (!sceneUri.empty()) { + SceneModel = std::unique_ptr( + LoadModelFile(fs, sceneUri.c_str(), Scene.GetDefaultGLPrograms(), materialParms)); + if (SceneModel != nullptr) { + Scene.SetWorldModel(*SceneModel); + } + } + } + SurfaceRender.Init(); + + return AppInit(&context); +} + +bool XrApp::InitSession() { + // Create the OpenXR Session. + const void* nextChain = GetSessionCreateInfoNextChain(); + +#if defined(XR_USE_GRAPHICS_API_OPENGL_ES) + XrGraphicsBindingOpenGLESAndroidKHR graphicsBindingAndroidGLES = { + XR_TYPE_GRAPHICS_BINDING_OPENGL_ES_ANDROID_KHR}; + graphicsBindingAndroidGLES.next = nextChain; + graphicsBindingAndroidGLES.display = Egl.Display; + graphicsBindingAndroidGLES.config = Egl.Config; + graphicsBindingAndroidGLES.context = Egl.Context; +#elif defined(XR_USE_GRAPHICS_API_OPENGL) + XrGraphicsBindingOpenGLWin32KHR graphicsBindingGL = {XR_TYPE_GRAPHICS_BINDING_OPENGL_WIN32_KHR}; + graphicsBindingGL.next = nextChain; + graphicsBindingGL.hDC = Egl.hDC; + graphicsBindingGL.hGLRC = Egl.hGLRC; +#endif // defined(XR_USE_GRAPHICS_API_OPENGL_ES) + + XrSessionCreateInfo sessionCreateInfo = {XR_TYPE_SESSION_CREATE_INFO}; +#if defined(XR_USE_GRAPHICS_API_OPENGL_ES) + sessionCreateInfo.next = &graphicsBindingAndroidGLES; +#elif defined(XR_USE_GRAPHICS_API_OPENGL) + sessionCreateInfo.next = &graphicsBindingGL; +#endif + sessionCreateInfo.createFlags = 0; + sessionCreateInfo.systemId = SystemId; + + PreCreateSession(sessionCreateInfo); + XrResult initResult; + OXR(initResult = xrCreateSession(Instance, &sessionCreateInfo, &Session)); + if (initResult != XR_SUCCESS) { + ALOGE("Failed to create XR session: %d.", initResult); + exit(1); + } + PostCreateSession(sessionCreateInfo); + + FreeSessionCreateInfoNextChain(nextChain); + + // App only supports the primary stereo view config. + const XrViewConfigurationType supportedViewConfigType = + XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO; + + // Enumerate the viewport configurations. + { + uint32_t viewportConfigTypeCount = 0; + OXR(xrEnumerateViewConfigurations(Instance, SystemId, 0, &viewportConfigTypeCount, NULL)); + + std::vector viewportConfigurationTypes(viewportConfigTypeCount); + + OXR(xrEnumerateViewConfigurations( + Instance, + SystemId, + viewportConfigTypeCount, + &viewportConfigTypeCount, + viewportConfigurationTypes.data())); + + ALOGV("Available Viewport Configuration Types: %d", viewportConfigTypeCount); + for (uint32_t i = 0; i < viewportConfigTypeCount; i++) { + const XrViewConfigurationType viewportConfigType = viewportConfigurationTypes[i]; + ALOGV( + "Viewport configuration type %d : %s", + viewportConfigType, + viewportConfigType == supportedViewConfigType ? "Selected" : ""); + XrViewConfigurationProperties viewportConfig{XR_TYPE_VIEW_CONFIGURATION_PROPERTIES}; + OXR(xrGetViewConfigurationProperties( + Instance, SystemId, viewportConfigType, &viewportConfig)); + ALOGV( + "FovMutable=%s ConfigurationType %d", + viewportConfig.fovMutable ? "true" : "false", + viewportConfig.viewConfigurationType); + + uint32_t viewCount; + OXR(xrEnumerateViewConfigurationViews( + Instance, SystemId, viewportConfigType, 0, &viewCount, NULL)); + + if (viewCount > 0) { + std::vector elements( + viewCount, {XR_TYPE_VIEW_CONFIGURATION_VIEW}); + + OXR(xrEnumerateViewConfigurationViews( + Instance, + SystemId, + viewportConfigType, + viewCount, + &viewCount, + elements.data())); + + // Log the view config info for each view type for debugging purposes. + for (uint32_t e = 0; e < viewCount; e++) { + const XrViewConfigurationView* element = &elements[e]; + + ALOGV( + "Viewport [%d]: Recommended Width=%d Height=%d SampleCount=%d", + e, + element->recommendedImageRectWidth, + element->recommendedImageRectHeight, + element->recommendedSwapchainSampleCount); + + ALOGV( + "Viewport [%d]: Max Width=%d Height=%d SampleCount=%d", + e, + element->maxImageRectWidth, + element->maxImageRectHeight, + element->maxSwapchainSampleCount); + } + + // Cache the view config properties for the selected config type. + if (viewportConfigType == supportedViewConfigType) { + assert(viewCount == MAX_NUM_EYES); + for (uint32_t e = 0; e < viewCount; e++) { + ViewConfigurationView[e] = elements[e]; + } + } + } else { + ALOGE("Empty viewport configuration type: %d", viewCount); + } + } + } + + // Get the viewport configuration info for the chosen viewport configuration type. + OXR(xrGetViewConfigurationProperties( + Instance, SystemId, supportedViewConfigType, &ViewportConfig)); + + for (int eye = 0; eye < MAX_NUM_EYES; eye++) { + Projections[eye] = XrView{XR_TYPE_VIEW}; + } + + bool stageSupported = false; + uint32_t numOutputSpaces = 0; + OXR(xrEnumerateReferenceSpaces(Session, 0, &numOutputSpaces, NULL)); + + std::vector referenceSpaces(numOutputSpaces); + OXR(xrEnumerateReferenceSpaces( + Session, numOutputSpaces, &numOutputSpaces, referenceSpaces.data())); + for (uint32_t i = 0; i < numOutputSpaces; i++) { + if (referenceSpaces[i] == XR_REFERENCE_SPACE_TYPE_STAGE) { + stageSupported = true; + break; + } + } + + // Create a space to the first path + XrReferenceSpaceCreateInfo spaceCreateInfo = {XR_TYPE_REFERENCE_SPACE_CREATE_INFO}; + spaceCreateInfo.referenceSpaceType = XR_REFERENCE_SPACE_TYPE_VIEW; + spaceCreateInfo.poseInReferenceSpace.orientation.w = 1.0f; + OXR(xrCreateReferenceSpace(Session, &spaceCreateInfo, &HeadSpace)); + + spaceCreateInfo.referenceSpaceType = XR_REFERENCE_SPACE_TYPE_LOCAL; + OXR(xrCreateReferenceSpace(Session, &spaceCreateInfo, &LocalSpace)); + + // xrCreateActionSpace requires Session, create them here. + LeftControllerAimSpace = CreateActionSpace(AimPoseAction, LeftHandPath); + RightControllerAimSpace = CreateActionSpace(AimPoseAction, RightHandPath); + LeftControllerGripSpace = CreateActionSpace(GripPoseAction, LeftHandPath); + RightControllerGripSpace = CreateActionSpace(GripPoseAction, RightHandPath); + + // to use as fake stage + if (stageSupported) { + spaceCreateInfo.referenceSpaceType = XR_REFERENCE_SPACE_TYPE_STAGE; + spaceCreateInfo.poseInReferenceSpace.position.y = 0.0f; + OXR(xrCreateReferenceSpace(Session, &spaceCreateInfo, &StageSpace)); + ALOGV("Created stage space"); + CurrentSpace = StageSpace; + } + + // Create the frame buffers. + for (int eye = 0; eye < MAX_NUM_EYES; eye++) { + ovrFramebuffer_Create( + Session, + &FrameBuffer[eye], + GL_SRGB8_ALPHA8, + ViewConfigurationView[0].recommendedImageRectWidth, + ViewConfigurationView[0].recommendedImageRectHeight, + NUM_MULTI_SAMPLES); + } + + // xrAttachSessionActionSets can only be called once, so skip it if the application + // is doing it manually + if (!SkipInputHandling) { + // Attach to session + AttachActionSets(); + } + + return SessionInit(); +} + +void XrApp::EndSession() { + for (int eye = 0; eye < MAX_NUM_EYES; eye++) { + ovrFramebuffer_Destroy(&FrameBuffer[eye]); + } + + OXR(xrDestroySpace(HeadSpace)); + OXR(xrDestroySpace(LocalSpace)); + // StageSpace is optional. + if (StageSpace != XR_NULL_HANDLE) { + OXR(xrDestroySpace(StageSpace)); + } + CurrentSpace = XR_NULL_HANDLE; + SessionEnd(); + OXR(xrDestroySession(Session)); + + ovrEgl_DestroyContext(&Egl); +} + +// Called one time when the applicatoin process exits +void XrApp::Shutdown(const xrJava& context) { + AppShutdown(&context); + OXR(xrDestroyInstance(Instance)); + Clear(); +} + +// Called on each Shutdown, reset all member variable to initial state +void XrApp::Clear() { +#if defined(ANDROID) + Resumed = false; +#endif // defined(ANDROID) + ShouldExit = false; + Focused = false; + SkipInputHandling = false; + + Instance = XR_NULL_HANDLE; + Session = XR_NULL_HANDLE; + ViewportConfig = {}; + for (int i = 0; i < MAX_NUM_EYES; i++) { + ViewConfigurationView[i] = {}; + } + for (int i = 0; i < MAX_NUM_EYES; i++) { + Projections[i] = {}; + } + SystemId = XR_NULL_SYSTEM_ID; + HeadSpace = XR_NULL_HANDLE; + LocalSpace = XR_NULL_HANDLE; + StageSpace = XR_NULL_HANDLE; + CurrentSpace = XR_NULL_HANDLE; + SessionActive = false; + + BaseActionSet = XR_NULL_HANDLE; + LeftHandPath = XR_NULL_PATH; + RightHandPath = XR_NULL_PATH; + AimPoseAction = XR_NULL_HANDLE; + GripPoseAction = XR_NULL_HANDLE; + JoystickAction = XR_NULL_HANDLE; + thumbstickClickAction = XR_NULL_HANDLE; + IndexTriggerAction = XR_NULL_HANDLE; + IndexTriggerClickAction = XR_NULL_HANDLE; + GripTriggerAction = XR_NULL_HANDLE; + ButtonAAction = XR_NULL_HANDLE; + ButtonBAction = XR_NULL_HANDLE; + ButtonXAction = XR_NULL_HANDLE; + ButtonYAction = XR_NULL_HANDLE; + ButtonMenuAction = XR_NULL_HANDLE; + /// common touch actions + ThumbStickTouchAction = XR_NULL_HANDLE; + ThumbRestTouchAction = XR_NULL_HANDLE; + TriggerTouchAction = XR_NULL_HANDLE; + + LeftControllerAimSpace = XR_NULL_HANDLE; + RightControllerAimSpace = XR_NULL_HANDLE; + LeftControllerGripSpace = XR_NULL_HANDLE; + RightControllerGripSpace = XR_NULL_HANDLE; + LastFrameAllButtons = 0u; + LastFrameAllTouches = 0u; +} + +// Internal Input +void XrApp::AttachActionSets() { + XrSessionActionSetsAttachInfo attachInfo = {XR_TYPE_SESSION_ACTION_SETS_ATTACH_INFO}; + attachInfo.countActionSets = 1; + attachInfo.actionSets = &BaseActionSet; + OXR(xrAttachSessionActionSets(Session, &attachInfo)); +} + +void XrApp::SyncActionSets(ovrApplFrameIn& in) { + // sync action data + XrActiveActionSet activeActionSet{BaseActionSet}; + XrActionsSyncInfo syncInfo{XR_TYPE_ACTIONS_SYNC_INFO}; + syncInfo.countActiveActionSets = 1; + syncInfo.activeActionSets = &activeActionSet; + OXR(xrSyncActions(Session, &syncInfo)); + + // query input action states + XrActionStateGetInfo getInfo = {XR_TYPE_ACTION_STATE_GET_INFO}; + getInfo.subactionPath = XR_NULL_PATH; + + XrAction controller[] = {AimPoseAction, GripPoseAction, AimPoseAction, GripPoseAction}; + XrPath subactionPath[] = {LeftHandPath, LeftHandPath, RightHandPath, RightHandPath}; + XrSpace controllerSpace[] = { + LeftControllerAimSpace, + LeftControllerGripSpace, + RightControllerAimSpace, + RightControllerGripSpace, + }; + bool ControllerPoseActive[] = {false, false, false, false}; + XrPosef ControllerPose[] = {{}, {}, {}, {}}; + for (int i = 0; i < 4; i++) { + if (ActionPoseIsActive(controller[i], subactionPath[i])) { + LocVel lv = GetSpaceLocVel(controllerSpace[i], ToXrTime(in.PredictedDisplayTime)); + ControllerPoseActive[i] = + (lv.loc.locationFlags & XR_SPACE_LOCATION_POSITION_VALID_BIT) != 0; + ControllerPose[i] = lv.loc.pose; + } else { + ControllerPoseActive[i] = false; + XrPosef_CreateIdentity(&ControllerPose[i]); + } + } + +#if 0 // Enable for debugging current interaction profile issues + XrPath topLevelUserPath = XR_NULL_PATH; + OXR(xrStringToPath(Instance, "/user/hand/left", &topLevelUserPath)); + XrInteractionProfileState ipState{XR_TYPE_INTERACTION_PROFILE_STATE}; + xrGetCurrentInteractionProfile(Session, topLevelUserPath, &ipState); + uint32_t ipPathOutSize = 0; + char ipPath[256]; + xrPathToString(Instance, ipState.interactionProfile, sizeof(ipPath), &ipPathOutSize, ipPath); + ALOG( "Current interaction profile is: '%s'", ipPath); +#endif + + /// Update pose + XrSpaceLocation loc = {XR_TYPE_SPACE_LOCATION}; + OXR(xrLocateSpace(HeadSpace, CurrentSpace, ToXrTime(in.PredictedDisplayTime), &loc)); + in.HeadPose = XrPosef_To_OVRPosef(loc.pose); + /// grip & point space + in.LeftRemotePointPose = XrPosef_To_OVRPosef(ControllerPose[0]); + in.LeftRemotePose = XrPosef_To_OVRPosef(ControllerPose[1]); + in.RightRemotePointPose = XrPosef_To_OVRPosef(ControllerPose[2]); + in.RightRemotePose = XrPosef_To_OVRPosef(ControllerPose[3]); + in.LeftRemoteTracked = ControllerPoseActive[1]; + in.RightRemoteTracked = ControllerPoseActive[3]; + + in.LeftRemoteIndexTrigger = GetActionStateFloat(IndexTriggerAction, LeftHandPath).currentState; + in.RightRemoteIndexTrigger = + GetActionStateFloat(IndexTriggerAction, RightHandPath).currentState; + in.LeftRemoteGripTrigger = GetActionStateFloat(GripTriggerAction, LeftHandPath).currentState; + in.RightRemoteGripTrigger = GetActionStateFloat(GripTriggerAction, RightHandPath).currentState; + in.LeftRemoteJoystick = + XrVector2f_To_OVRVector2f(GetActionStateVector2(JoystickAction, LeftHandPath).currentState); + in.RightRemoteJoystick = XrVector2f_To_OVRVector2f( + GetActionStateVector2(JoystickAction, RightHandPath).currentState); + + bool aPressed = GetActionStateBoolean(ButtonAAction).currentState; + bool bPressed = GetActionStateBoolean(ButtonBAction).currentState; + bool xPressed = GetActionStateBoolean(ButtonXAction).currentState; + bool yPressed = GetActionStateBoolean(ButtonYAction).currentState; + bool menuPressed = GetActionStateBoolean(ButtonMenuAction).currentState; + bool leftThumbPressed = GetActionStateBoolean(thumbstickClickAction, LeftHandPath).currentState; + bool rightThumbPressed = + GetActionStateBoolean(thumbstickClickAction, RightHandPath).currentState; + + in.LastFrameAllButtons = LastFrameAllButtons; + in.AllButtons = 0u; + + if (aPressed) { + in.AllButtons |= ovrApplFrameIn::kButtonA; + } + if (bPressed) { + in.AllButtons |= ovrApplFrameIn::kButtonB; + } + if (xPressed) { + in.AllButtons |= ovrApplFrameIn::kButtonX; + } + if (yPressed) { + in.AllButtons |= ovrApplFrameIn::kButtonY; + } + if (menuPressed) { + in.AllButtons |= ovrApplFrameIn::kButtonMenu; + } + if (leftThumbPressed) { + in.AllButtons |= ovrApplFrameIn::kButtonLeftThumbStick; + } + if (rightThumbPressed) { + in.AllButtons |= ovrApplFrameIn::kButtonRightThumbStick; + } + if (in.LeftRemoteIndexTrigger > 0.1f) { + in.AllButtons |= ovrApplFrameIn::kTrigger; + } + if (in.RightRemoteIndexTrigger > 0.1f) { + in.AllButtons |= ovrApplFrameIn::kTrigger; + } + if (in.LeftRemoteGripTrigger > 0.1f) { + in.AllButtons |= ovrApplFrameIn::kGripTrigger; + } + if (in.RightRemoteGripTrigger > 0.1f) { + in.AllButtons |= ovrApplFrameIn::kGripTrigger; + } + LastFrameAllButtons = in.AllButtons; + + /// touch + in.LastFrameAllTouches = LastFrameAllTouches; + in.AllTouches = 0u; + + const bool thumbstickTouched = GetActionStateBoolean(ThumbStickTouchAction).currentState; + const bool thumbrestTouched = GetActionStateBoolean(ThumbRestTouchAction).currentState; + const bool triggerTouched = GetActionStateBoolean(TriggerTouchAction).currentState; + + if (thumbstickTouched) { + in.AllTouches |= ovrApplFrameIn::kTouchJoystick; + } + if (thumbrestTouched) { + in.AllTouches |= ovrApplFrameIn::kTouchThumbrest; + } + if (triggerTouched) { + in.AllTouches |= ovrApplFrameIn::kTouchTrigger; + } + + LastFrameAllTouches = in.AllTouches; + + /* + /// timing + double RealTimeInSeconds = 0.0; + float DeltaSeconds = 0.0f; + /// device config + float IPD = 0.065f; + float EyeHeight = 1.6750f; + int32_t RecenterCount = 0; + */ +} + +void XrApp::HandleInput(ovrApplFrameIn& in) { + if (!SkipInputHandling) { + // Sync default actions + SyncActionSets(in); + } + + // Call application Update function + Update(in); +} + +// Called once per frame to allow the application to render eye buffers. +void XrApp::AppRenderFrame(const OVRFW::ovrApplFrameIn& in, OVRFW::ovrRendererOutput& out) { + Scene.SetFreeMove(FreeMove); + /// create a local copy + OVRFW::ovrApplFrameIn localIn = in; + if (false == FreeMove) { + localIn.LeftRemoteJoystick.x = 0.0f; + localIn.LeftRemoteJoystick.y = 0.0f; + localIn.RightRemoteJoystick.x = 0.0f; + localIn.RightRemoteJoystick.y = 0.0f; + } + Scene.Frame(localIn); + Scene.GenerateFrameSurfaceList(out.FrameMatrices, out.Surfaces); + if (ShouldRender) { + Render(in, out); + } + + for (int eye = 0; eye < MAX_NUM_EYES; eye++) { + ovrFramebuffer* frameBuffer = &FrameBuffer[eye]; + ovrFramebuffer_Acquire(frameBuffer); + ovrFramebuffer_SetCurrent(frameBuffer); + + AppEyeGLStateSetup(in, frameBuffer, eye); + AppRenderEye(in, out, eye); + + ovrFramebuffer_Resolve(frameBuffer); + ovrFramebuffer_Release(frameBuffer); + } + ovrFramebuffer_SetNone(); +} + +void XrApp::AppRenderEye(const OVRFW::ovrApplFrameIn& in, OVRFW::ovrRendererOutput& out, int eye) { + // Render the surfaces returned by Frame. + SurfaceRender.RenderSurfaceList( + out.Surfaces, + out.FrameMatrices.EyeView[0], // always use 0 as it assumes an array + out.FrameMatrices.EyeProjection[0], // always use 0 as it assumes an array + eye); +} + +// Called once per eye each frame for default renderer +void XrApp::AppEyeGLStateSetup(const ovrApplFrameIn& in, const ovrFramebuffer* fb, int eye) { + GL(glEnable(GL_SCISSOR_TEST)); + GL(glDepthMask(GL_TRUE)); + GL(glEnable(GL_DEPTH_TEST)); + GL(glDepthFunc(GL_LEQUAL)); + GL(glEnable(GL_CULL_FACE)); + GL(glViewport(0, 0, fb->Width, fb->Height)); + GL(glScissor(0, 0, fb->Width, fb->Height)); + GL(glClearColor(BackgroundColor.x, BackgroundColor.y, BackgroundColor.z, BackgroundColor.w)); + GL(glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)); +} + +// Called when the application initializes. +// Overridden by the actual app +// Must return true if the application initializes successfully. +bool XrApp::AppInit(const xrJava* context) { + return true; +} + +// Called when the application shuts down +void XrApp::AppShutdown(const xrJava* context) { + SurfaceRender.Shutdown(); +} + +// Called when the application is resumed by the system. +void XrApp::AppResumed(const xrJava* contet) {} + +// Called when the application is paused by the system. +void XrApp::AppPaused(const xrJava* context) {} + +// Called when app loses focus +void XrApp::AppLostFocus() {} + +// Called when app re-gains focus +void XrApp::AppGainedFocus() {} + +bool XrApp::SessionInit() { + return true; +} + +void XrApp::SessionEnd() {} + +void XrApp::SessionStateChanged(XrSessionState) {} + +void XrApp::Update(const ovrApplFrameIn& in) {} + +void XrApp::Render(const ovrApplFrameIn& in, ovrRendererOutput& out) {} + +#if defined(ANDROID) +void ActivityMainLoopContext::HandleOsEvents() { + // Read all pending events. + for (;;) { + int events; + struct android_poll_source* source; + // If the timeout is zero, returns immediately without blocking. + // If the timeout is negative, waits indefinitely until an event appears. + const int timeoutMilliseconds = + (xrApp_->GetResumed() == false && xrApp_->GetSessionActive() == false && + app_->destroyRequested == 0) + ? -1 + : 0; + if (ALooper_pollAll(timeoutMilliseconds, NULL, &events, (void**)&source) < 0) { + break; + } + + // Process this event. + if (source != NULL) { + source->process(app_, source); + } + } +} + +bool ActivityMainLoopContext::ShouldExitMainLoop() const { + return app_->destroyRequested; +} + +bool ActivityMainLoopContext::IsExitRequested() const { + return xrApp_->GetShouldExit(); +} +#elif defined(WIN32) +void WindowsMainLoopContext::HandleOsEvents() { + MSG msg; + while (::PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) > 0) { + if (msg.message == WM_QUIT) { + xrApp_->SetShouldExit(true); + } else { + ::TranslateMessage(&msg); + ::DispatchMessage(&msg); + } + } +} + +bool WindowsMainLoopContext::ShouldExitMainLoop() const { + return false; +} + +bool WindowsMainLoopContext::IsExitRequested() const { + return xrApp_->GetShouldExit(); +} +#else +#error "Platform not supported!" +#endif // defined(ANDROID) + +// Main application loop. The MainLoopContext is a functor that allows +// an application to overload exit condition and event polling within the +// loop. This allows Android Activity-based apps, Android Service-based apps, +// and Windows apps to all use the same thread loop. +void XrApp::MainLoop(MainLoopContext& loopContext) { + if (!Init(loopContext.GetJavaContext())) { + ALOGE("Application failed to initialize."); + return; + } + + InitSession(); + + bool stageBoundsDirty = true; + int frameCount = -1; + + while (!loopContext.ShouldExitMainLoop()) { + frameCount++; + + loopContext.HandleOsEvents(); + + HandleXrEvents(); + + if (loopContext.IsExitRequested()) { + break; + } + + if (SessionActive == false) { + continue; + } + + if (stageBoundsDirty) { + XrExtent2Df stageBounds = {}; + XrResult result; + OXR(result = xrGetReferenceSpaceBoundsRect( + Session, XR_REFERENCE_SPACE_TYPE_STAGE, &stageBounds)); + stageBoundsDirty = false; + } + + // NOTE: OpenXR does not use the concept of frame indices. Instead, + // XrWaitFrame returns the predicted display time. + XrFrameWaitInfo waitFrameInfo = {XR_TYPE_FRAME_WAIT_INFO}; + + PreWaitFrame(waitFrameInfo); + + XrFrameState frameState = {XR_TYPE_FRAME_STATE}; + + OXR(xrWaitFrame(Session, &waitFrameInfo, &frameState)); + + // Get the HMD pose, predicted for the middle of the time period during which + // the new eye images will be displayed. The number of frames predicted ahead + // depends on the pipeline depth of the engine and the synthesis rate. + // The better the prediction, the less black will be pulled in at the edges. + XrFrameBeginInfo beginFrameDesc = {XR_TYPE_FRAME_BEGIN_INFO}; + OXR(xrBeginFrame(Session, &beginFrameDesc)); + ShouldRender = frameState.shouldRender; + + XrSpaceLocation loc = {XR_TYPE_SPACE_LOCATION}; + OXR(xrLocateSpace(HeadSpace, CurrentSpace, frameState.predictedDisplayTime, &loc)); + XrPosef xfStageFromHead = loc.pose; + OXR(xrLocateSpace(HeadSpace, LocalSpace, frameState.predictedDisplayTime, &loc)); + + XrViewState viewState = {XR_TYPE_VIEW_STATE}; + + XrViewLocateInfo projectionInfo = {XR_TYPE_VIEW_LOCATE_INFO}; + projectionInfo.viewConfigurationType = ViewportConfig.viewConfigurationType; + projectionInfo.displayTime = frameState.predictedDisplayTime; + projectionInfo.space = HeadSpace; + + uint32_t projectionCapacityInput = MAX_NUM_EYES; + uint32_t projectionCountOutput = projectionCapacityInput; + + OXR(xrLocateViews( + Session, + &projectionInfo, + &viewState, + projectionCapacityInput, + &projectionCountOutput, + Projections)); + + OVRFW::ovrApplFrameIn in = {}; + OVRFW::ovrRendererOutput out = {}; + in.FrameIndex = frameCount; + + /// time accounting + in.PredictedDisplayTime = FromXrTime(frameState.predictedDisplayTime); + if (PrevDisplayTime > 0) { + in.DeltaSeconds = FromXrTime(frameState.predictedDisplayTime - PrevDisplayTime); + } + PrevDisplayTime = frameState.predictedDisplayTime; + + for (int eye = 0; eye < MAX_NUM_EYES; eye++) { + XrPosef xfHeadFromEye = Projections[eye].pose; + XrPosef xfStageFromEye{}; + XrPosef_Multiply(&xfStageFromEye, &xfStageFromHead, &xfHeadFromEye); + XrPosef_Invert(&ViewTransform[eye], &xfStageFromEye); + XrMatrix4x4f viewMat{}; + XrMatrix4x4f_CreateFromRigidTransform(&viewMat, &ViewTransform[eye]); + const XrFovf fov = Projections[eye].fov; + XrMatrix4x4f projMat; + XrMatrix4x4f_CreateProjectionFov(&projMat, GRAPHICS_OPENGL_ES, fov, 0.1f, 0.0f); + out.FrameMatrices.EyeView[eye] = XrMatrix4x4f_To_OVRMatrix4f(viewMat); + out.FrameMatrices.EyeProjection[eye] = XrMatrix4x4f_To_OVRMatrix4f(projMat); + in.Eye[eye].ViewMatrix = out.FrameMatrices.EyeView[eye]; + in.Eye[eye].ProjectionMatrix = out.FrameMatrices.EyeProjection[eye]; + } + + XrPosef centerView; + XrPosef_Invert(¢erView, &xfStageFromHead); + XrMatrix4x4f viewMat{}; + XrMatrix4x4f_CreateFromRigidTransform(&viewMat, ¢erView); + out.FrameMatrices.CenterView = XrMatrix4x4f_To_OVRMatrix4f(viewMat); + + // Input + HandleInput(in); + + LayerCount = 0; + memset(Layers, 0, sizeof(xrCompositorLayerUnion) * MAX_NUM_LAYERS); + + // allow apps to submit a layer before the world view projection layer (uncommon) + PreProjectionAddLayer(Layers, LayerCount); + + // Render the world-view layer (projection) + AppRenderFrame(in, out); + ProjectionAddLayer(Layers, LayerCount); + + // allow apps to submit a layer after the world view projection layer (uncommon) + PostProjectionAddLayer(Layers, LayerCount); + + // Compose the layers for this frame. + const XrCompositionLayerBaseHeader* layers[MAX_NUM_LAYERS] = {}; + for (int i = 0; i < LayerCount; i++) { + layers[i] = (const XrCompositionLayerBaseHeader*)&Layers[i]; + } + + XrFrameEndInfo endFrameInfo = {XR_TYPE_FRAME_END_INFO}; + endFrameInfo.displayTime = frameState.predictedDisplayTime; + endFrameInfo.environmentBlendMode = XR_ENVIRONMENT_BLEND_MODE_OPAQUE; + endFrameInfo.layerCount = LayerCount; + endFrameInfo.layers = layers; + + OXR(xrEndFrame(Session, &endFrameInfo)); + } + + EndSession(); + Shutdown(loopContext.GetJavaContext()); +} + +void XrApp::ProjectionAddLayer(xrCompositorLayerUnion* layers, int& layerCount) { + // Set-up the compositor layers for this frame. + // NOTE: Multiple independent layers are allowed, but they need to be added + // in a depth consistent order. + XrCompositionLayerProjection projection_layer = {XR_TYPE_COMPOSITION_LAYER_PROJECTION}; + projection_layer.layerFlags = XR_COMPOSITION_LAYER_BLEND_TEXTURE_SOURCE_ALPHA_BIT; + projection_layer.layerFlags |= XR_COMPOSITION_LAYER_CORRECT_CHROMATIC_ABERRATION_BIT; + projection_layer.space = CurrentSpace; + projection_layer.viewCount = MAX_NUM_EYES; + projection_layer.views = ProjectionLayerElements; + + for (int eye = 0; eye < MAX_NUM_EYES; eye++) { + ovrFramebuffer* frameBuffer = &FrameBuffer[eye]; + memset(&ProjectionLayerElements[eye], 0, sizeof(XrCompositionLayerProjectionView)); + ProjectionLayerElements[eye].type = XR_TYPE_COMPOSITION_LAYER_PROJECTION_VIEW; + XrPosef_Invert(&ProjectionLayerElements[eye].pose, &ViewTransform[eye]); + ProjectionLayerElements[eye].fov = Projections[eye].fov; + memset(&ProjectionLayerElements[eye].subImage, 0, sizeof(XrSwapchainSubImage)); + ProjectionLayerElements[eye].subImage.swapchain = frameBuffer->ColorSwapChain.Handle; + ProjectionLayerElements[eye].subImage.imageRect.offset.x = 0; + ProjectionLayerElements[eye].subImage.imageRect.offset.y = 0; + ProjectionLayerElements[eye].subImage.imageRect.extent.width = + frameBuffer->ColorSwapChain.Width; + ProjectionLayerElements[eye].subImage.imageRect.extent.height = + frameBuffer->ColorSwapChain.Height; + ProjectionLayerElements[eye].subImage.imageArrayIndex = 0; + } + + layers[layerCount++].Projection = projection_layer; +} + +// App entry point +#if defined(ANDROID) +void XrApp::Run(struct android_app* app) { + // Setup Activity-specific state + ALOGV("----------------------------------------------------------------"); + ALOGV("android_app_entry()"); + ALOGV(" android_main()"); + + // TODO: We should make this not required for OOPC apps. + ANativeActivity_setWindowFlags(app->activity, AWINDOW_FLAG_KEEP_SCREEN_ON, 0); + + JNIEnv* Env; + (*app->activity->vm).AttachCurrentThread(&Env, nullptr); + + // Note that AttachCurrentThread will reset the thread name. + prctl(PR_SET_NAME, (long)"XrApp::Main", 0, 0, 0); + + Context.Vm = app->activity->vm; + Context.Env = Env; + Context.ActivityObject = app->activity->clazz; + + app->userData = this; + app->onAppCmd = app_handle_cmd; + + ActivityMainLoopContext loopContext(Context, this, app); +#elif defined(WIN32) +void XrApp::Run() { + Context.Vm = nullptr; + Context.Env = nullptr; + Context.ActivityObject = nullptr; + + WindowsMainLoopContext loopContext(Context, this); +#else +#error "Platform not supported!" +#endif // defined(ANDROID) + + MainLoop(loopContext); + +#if defined(ANDROID) + (*app->activity->vm).DetachCurrentThread(); +#endif // defined(ANDROID) +} + +} // namespace OVRFW diff --git a/Samples/SampleXrFramework/Src/XrApp.h b/Samples/SampleXrFramework/Src/XrApp.h new file mode 100755 index 0000000..ddd4a76 --- /dev/null +++ b/Samples/SampleXrFramework/Src/XrApp.h @@ -0,0 +1,638 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/******************************************************************************* + +Filename : XrApp.h +Content : OpenXR application base class. +Created : July 2020 +Authors : Federico Schliemann +Language : c++ + +*******************************************************************************/ + +#pragma once + +#include +#include +#include +#include +#include + +#include "OVR_Math.h" + +#include "System.h" +#include "FrameParams.h" +#include "OVR_FileSys.h" + +#if defined(ANDROID) +#include +#include +#include +#include + +#include +#include +#include +#include + +#if !defined(EGL_OPENGL_ES3_BIT_KHR) +#define EGL_OPENGL_ES3_BIT_KHR 0x0040 +#endif +#elif defined(WIN32) +#include "windows.h" +#endif // defined(ANDROID) + +// EXT_texture_border_clamp +#ifndef GL_CLAMP_TO_BORDER +#define GL_CLAMP_TO_BORDER 0x812D +#endif + +#ifndef GL_TEXTURE_BORDER_COLOR +#define GL_TEXTURE_BORDER_COLOR 0x1004 +#endif + +#if !defined(GL_EXT_multisampled_render_to_texture) +typedef void(GL_APIENTRY* PFNGLRENDERBUFFERSTORAGEMULTISAMPLEEXTPROC)( + GLenum target, + GLsizei samples, + GLenum internalformat, + GLsizei width, + GLsizei height); +typedef void(GL_APIENTRY* PFNGLFRAMEBUFFERTEXTURE2DMULTISAMPLEEXTPROC)( + GLenum target, + GLenum attachment, + GLenum textarget, + GLuint texture, + GLint level, + GLsizei samples); +#endif + +// GL_EXT_texture_cube_map_array +#if !defined(GL_TEXTURE_CUBE_MAP_ARRAY) +#define GL_TEXTURE_CUBE_MAP_ARRAY 0x9009 +#endif + +#if defined(ANDROID) +#define XR_USE_GRAPHICS_API_OPENGL_ES 1 +#define XR_USE_PLATFORM_ANDROID 1 +#elif defined(WIN32) +#include +#define XR_USE_GRAPHICS_API_OPENGL 1 +#define XR_USE_PLATFORM_WIN32 1 +#endif // defined(ANDROID) + +#include +#include +#include + +#include "Model/SceneView.h" +#include "Render/Egl.h" +#include "Render/Framebuffer.h" +#include "Render/SurfaceRender.h" + +std::string OXR_ResultToString(XrInstance instance, XrResult result); +void OXR_CheckErrors(XrInstance instance, XrResult result, const char* function, bool failOnError); + +#if defined(DEBUG) || defined(_DEBUG) +#define OXR(func) OXR_CheckErrors(Instance, func, #func, true); +#else +#define OXR(func) OXR_CheckErrors(Instance, func, #func, false); +#endif + +static inline XrVector2f ToXrVector2f(const OVR::Vector2f& s) { + XrVector2f r; + r.x = s.x; + r.y = s.y; + return r; +} + +static inline OVR::Vector2f FromXrVector2f(const XrVector2f& s) { + OVR::Vector2f r; + r.x = s.x; + r.y = s.y; + return r; +} + +static inline OVR::Vector2f FromXrExtent2Df(const XrExtent2Df& s) { + OVR::Vector2f r; + r.x = s.width; + r.y = s.height; + return r; +} + +static inline XrVector3f ToXrVector3f(const OVR::Vector3f& s) { + XrVector3f r; + r.x = s.x; + r.y = s.y; + r.z = s.z; + return r; +} + +static inline OVR::Vector3f FromXrVector3f(const XrVector3f& s) { + OVR::Vector3f r; + r.x = s.x; + r.y = s.y; + r.z = s.z; + return r; +} + +static inline OVR::Vector4f FromXrVector4f(const XrVector4f& s) { + OVR::Vector4f r; + r.x = s.x; + r.y = s.y; + r.z = s.z; + r.w = s.w; + return r; +} + +static inline OVR::Vector4f FromXrColor4f(const XrColor4f& s) { + OVR::Vector4f r; + r.x = s.r; + r.y = s.g; + r.z = s.b; + r.w = s.a; + return r; +} + +static inline XrQuaternionf ToXrQuaternionf(const OVR::Quatf& s) { + XrQuaternionf r; + r.x = s.x; + r.y = s.y; + r.z = s.z; + r.w = s.w; + return r; +} + +static inline OVR::Quatf FromXrQuaternionf(const XrQuaternionf& s) { + OVR::Quatf r; + r.x = s.x; + r.y = s.y; + r.z = s.z; + r.w = s.w; + return r; +} + +static inline XrPosef ToXrPosef(const OVR::Posef& s) { + XrPosef r; + r.orientation = ToXrQuaternionf(s.Rotation); + r.position = ToXrVector3f(s.Translation); + return r; +} + +static inline OVR::Posef FromXrPosef(const XrPosef& s) { + OVR::Posef r; + r.Rotation = FromXrQuaternionf(s.orientation); + r.Translation = FromXrVector3f(s.position); + return r; +} + +namespace OVRFW { + +class XrApp; + +typedef struct XrStructureHeader { + XrStructureType type; + void* XR_MAY_ALIAS next; +} XrStructureHeader; + +class MainLoopContext { + public: + MainLoopContext(xrJava& javaContext, XrApp* xrApp) : javaContext_(javaContext), xrApp_(xrApp) {} + virtual ~MainLoopContext() { + xrApp_ = nullptr; + } + + const xrJava& GetJavaContext() const { + return javaContext_; + } + virtual void HandleOsEvents() = 0; + virtual bool ShouldExitMainLoop() const = 0; + virtual bool IsExitRequested() const = 0; + + protected: + const xrJava javaContext_; + class XrApp* xrApp_ = nullptr; +}; + +#if defined(ANDROID) +class ActivityMainLoopContext : public MainLoopContext { + public: + ActivityMainLoopContext(xrJava& javaContext, XrApp* xrApp, struct android_app* app) + : MainLoopContext(javaContext, xrApp), app_(app) {} + ~ActivityMainLoopContext() override { + app_ = nullptr; + } + + void HandleOsEvents() override; + bool ShouldExitMainLoop() const override; + bool IsExitRequested() const override; + + private: + struct android_app* app_ = nullptr; +}; +#elif defined(WIN32) +class WindowsMainLoopContext : public MainLoopContext { + public: + WindowsMainLoopContext(xrJava& javaContext, XrApp* xrApp) + : MainLoopContext(javaContext, xrApp) {} + ~WindowsMainLoopContext() override {} + + void HandleOsEvents() override; + bool ShouldExitMainLoop() const override; + bool IsExitRequested() const override; +}; +#endif + +class XrApp { + public: + //============================ + // public interface + enum ovrLifecycle { LIFECYCLE_UNKNOWN, LIFECYCLE_RESUMED, LIFECYCLE_PAUSED }; + + enum ovrRenderState { + RENDER_STATE_LOADING, // show the loading icon + RENDER_STATE_RUNNING, // render frames + }; + + static const int CPU_LEVEL = 2; + static const int GPU_LEVEL = 3; + static const int NUM_MULTI_SAMPLES = 4; + static const int MAX_NUM_EYES = 2; + static const int MAX_NUM_LAYERS = 16; + + XrApp( + const int32_t mainThreadTid, + const int32_t renderThreadTid, + const int cpuLevel, + const int gpuLevel) + : BackgroundColor(0.0f, 0.6f, 0.1f, 1.0f), + CpuLevel(cpuLevel), + GpuLevel(gpuLevel), + MainThreadTid(mainThreadTid), + RenderThreadTid(renderThreadTid), + NumFramebuffers(MAX_NUM_EYES) {} + XrApp() : XrApp(0, 0, CPU_LEVEL, GPU_LEVEL) {} + virtual ~XrApp() = default; + + // Android Service-based apps should just call MainLoop from their main thead + void MainLoop(MainLoopContext& context); + +// Android Activity-based apps and Windows apps can call Run from their entry point +#if defined(ANDROID) + void Run(struct android_app* app); +#else + void Run(); +#endif // defined(ANDROID) + + //============================ + // public context interface + + // Returns the application's context + const xrJava* GetContext() const { + return &Context; + } + + // App state share + OVRFW::ovrFileSys* GetFileSys() { + return FileSys.get(); + } + OVRFW::ovrSurfaceRender& GetSurfaceRender() { + return SurfaceRender; + } + OVRFW::OvrSceneView& GetScene() { + return Scene; + } + + void SetRunWhilePaused(bool b) { + RunWhilePaused = b; + } + +#if defined(ANDROID) + void HandleAndroidCmd(struct android_app* app, int32_t cmd); +#endif // defined(ANDROID) + + bool GetShouldExit() const { + return ShouldExit; + } + void SetShouldExit(const bool b) { + ShouldExit = b; + } + +#if defined(ANDROID) + bool GetResumed() const { + return Resumed; + } + bool GetSessionActive() const { + return SessionActive; + } +#endif // ANDROID + + protected: + int GetNumFramebuffers() const { + return NumFramebuffers; + } + ovrFramebuffer* GetFrameBuffer(int eye) { + return &FrameBuffer[eye]; + } + + std::vector GetXrExtensionProperties() const; + //============================ + // App functions + // All App* function can be overridden by the derived application class to + // implement application-specific behaviors + + // Returns a list of OpenXr extensions needed for this app + virtual std::vector GetExtensions(); + + // Apps can override this to return next chain that will be linked into + // XrCreateInstanceInfo.next. Note that all of the structures returned in the next chain must + // persist until the call to FreeInstanceCreateInfoNextChain() at which point the app may free + // the structures if it allocated them dynamically. + virtual const void* GetInstanceCreateInfoNextChain(); + virtual void FreeInstanceCreateInfoNextChain(const void* nextChain); + + // Apps can override this to return next chain that will be linked into + // XrCreateSessionInfo.next. Note that all of the structures returned in the next chain must + // persist until the call to FreeSessionCreateInfoNextChain() at which point the app may free + // the structures if it allocated them dynamically. + virtual const void* GetSessionCreateInfoNextChain(); + virtual void FreeSessionCreateInfoNextChain(const void* nextChain); + + virtual void GetInitialSceneUri(std::string& sceneUri) const; + + // Called when the application initializes. + // Must return true if the application initializes successfully. + virtual bool AppInit(const xrJava* context); + // Called when the application shuts down + virtual void AppShutdown(const xrJava* context); + // Called when the application is resumed by the system. + virtual void AppResumed(const xrJava* contet); + // Called when the application is paused by the system. + virtual void AppPaused(const xrJava* context); + // Called when app loses focus + virtual void AppLostFocus(); + // Called when app re-gains focus + virtual void AppGainedFocus(); + // Called once per frame to allow the application to render eye buffers. + virtual void AppRenderFrame(const OVRFW::ovrApplFrameIn& in, OVRFW::ovrRendererOutput& out); + // Called once per eye each frame for default renderer + virtual void + AppRenderEye(const OVRFW::ovrApplFrameIn& in, OVRFW::ovrRendererOutput& out, int eye); + // Called once per eye each frame for default renderer + virtual void AppEyeGLStateSetup(const ovrApplFrameIn& in, const ovrFramebuffer* fb, int eye); + + virtual bool SessionInit(); + virtual void SessionEnd(); + virtual void SessionStateChanged(XrSessionState state); + virtual void Update(const ovrApplFrameIn& in); + virtual void Render(const ovrApplFrameIn& in, ovrRendererOutput& out); + + /// composition override + union xrCompositorLayerUnion { + XrCompositionLayerProjection Projection; + XrCompositionLayerQuad Quad; + XrCompositionLayerCylinderKHR Cylinder; + XrCompositionLayerCubeKHR Cube; + XrCompositionLayerEquirectKHR Equirect; + XrCompositionLayerPassthroughFB Passthrough; + }; + virtual void PreProjectionAddLayer(xrCompositorLayerUnion* layers, int& layerCount) { + /// do nothing + } + /// Default proejction layer + virtual void ProjectionAddLayer(xrCompositorLayerUnion* layers, int& layerCount); + virtual void PostProjectionAddLayer(xrCompositorLayerUnion* layers, int& layerCount) { + /// do nothing + } + + // Add application specified SessionCreateInfo + virtual void PreCreateSession(XrSessionCreateInfo& sci) { + // do nothing + } + virtual void PostCreateSession(XrSessionCreateInfo& sci) { + // do nothing + } + + // Returns a map from interaction profile paths to vectors of suggested bindings. + // xrSuggestInteractionProfileBindings() is called once for each interaction profile path in the + // returned map. + // Apps are encouraged to suggest bindings for every device/interaction profile they support. + // Override this for custom action bindings, or modify the default bindings. + virtual std::unordered_map> GetSuggestedBindings( + XrInstance instance); + virtual void SuggestInteractionProfileBindings( + const std::unordered_map> + allSuggestedBindings); + + virtual void PreWaitFrame(XrFrameWaitInfo& waitFrameInfo) {} + + /// Xr Helpers + XrInstance& GetInstance() { + return Instance; + }; + XrSession& GetSession() { + return Session; + } + XrSystemId& GetSystemId() { + return SystemId; + } + + XrSpace& GetHeadSpace() { + return HeadSpace; + } + XrSpace& GetLocalSpace() { + return LocalSpace; + } + XrSpace& GetStageSpace() { + return StageSpace; + } + XrSpace& GetCurrentSpace() { + return CurrentSpace; + } + + XrActionSet CreateActionSet(uint32_t priority, const char* name, const char* localizedName); + XrAction CreateAction( + XrActionSet actionSet, + XrActionType type, + const char* actionName, + const char* localizedName, + int countSubactionPaths = 0, + XrPath* subactionPaths = nullptr); + XrActionSuggestedBinding ActionSuggestedBinding(XrAction action, const char* bindingString); + XrSpace CreateActionSpace(XrAction poseAction, XrPath subactionPath); + XrActionStateBoolean GetActionStateBoolean( + XrAction action, + XrPath subactionPath = XR_NULL_PATH); + XrActionStateFloat GetActionStateFloat(XrAction action, XrPath subactionPath = XR_NULL_PATH); + XrActionStateVector2f GetActionStateVector2( + XrAction action, + XrPath subactionPath = XR_NULL_PATH); + bool ActionPoseIsActive(XrAction action, XrPath subactionPath); + struct LocVel { + XrSpaceLocation loc; + XrSpaceVelocity vel; + }; + XrApp::LocVel GetSpaceLocVel(XrSpace space, XrTime time); + + /// XR Input state overrides + virtual void AttachActionSets(); + virtual void SyncActionSets(ovrApplFrameIn& in); + + // Called to deal with lifetime + void HandleSessionStateChanges(XrSessionState state); + + private: + // Called one time when the application process starts. + // Returns true if the application initialized successfully. + bool Init(const xrJava& context); + + // Called on each session creation + bool InitSession(); + + // Called on each session end + void EndSession(); + + // Called one time when the applicatoin process exits + void Shutdown(const xrJava& context); + + // Called on each Shutdown, reset all member variable to initial state + void Clear(); + + // Called to handle any lifecycle state changes. This will call + // AppPaused() and AppResumed() + void HandleLifecycle(const xrJava* context); + + // Events + virtual void HandleXrEvents(); + + // Internal Input + void HandleInput(ovrApplFrameIn& in); + + // Internal Render + void RenderFrame(const ovrApplFrameIn& in, ovrRendererOutput& out); + + public: + OVR::Vector4f BackgroundColor; + bool FreeMove; + + protected: + xrJava Context; + ovrLifecycle Lifecycle = LIFECYCLE_UNKNOWN; + ovrEgl Egl = { +#if defined(XR_USE_GRAPHICS_API_OPENGL_ES) + 0, + 0, + EGL_NO_DISPLAY, + EGL_CAST(EGLConfig, 0), + EGL_NO_SURFACE, + EGL_NO_SURFACE, + EGL_NO_CONTEXT +#endif // defined(XR_USE_GRAPHICS_API_OPENGL_ES) + }; + +#if defined(ANDROID) + bool Resumed = false; +#endif // defined(ANDROID) + bool ShouldExit = false; + bool Focused = false; + + // When set the framework will not bind any actions and will + // skip calling SyncActionSets(), this is useful if an app + // wants control over xrSyncAction + // Note: This means input in ovrApplFrameIn won't be set + bool SkipInputHandling = false; + + XrInstance Instance = XR_NULL_HANDLE; + XrSession Session = XR_NULL_HANDLE; + XrViewConfigurationProperties ViewportConfig{XR_TYPE_VIEW_CONFIGURATION_PROPERTIES}; + XrViewConfigurationView ViewConfigurationView[MAX_NUM_EYES]; + XrCompositionLayerProjectionView ProjectionLayerElements[MAX_NUM_EYES]; + XrView Projections[MAX_NUM_EYES]; + XrPosef ViewTransform[MAX_NUM_EYES]; + XrSystemId SystemId = XR_NULL_SYSTEM_ID; + XrSpace HeadSpace = XR_NULL_HANDLE; + XrSpace LocalSpace = XR_NULL_HANDLE; + XrSpace StageSpace = XR_NULL_HANDLE; + XrSpace CurrentSpace = XR_NULL_HANDLE; + bool SessionActive = false; + + XrActionSet BaseActionSet = XR_NULL_HANDLE; + XrPath LeftHandPath = XR_NULL_PATH; + XrPath RightHandPath = XR_NULL_PATH; + XrAction AimPoseAction = XR_NULL_HANDLE; + XrAction GripPoseAction = XR_NULL_HANDLE; + XrAction JoystickAction = XR_NULL_HANDLE; + XrAction thumbstickClickAction = XR_NULL_HANDLE; + XrAction IndexTriggerAction = XR_NULL_HANDLE; + XrAction IndexTriggerClickAction = XR_NULL_HANDLE; + XrAction GripTriggerAction = XR_NULL_HANDLE; + XrAction ButtonAAction = XR_NULL_HANDLE; + XrAction ButtonBAction = XR_NULL_HANDLE; + XrAction ButtonXAction = XR_NULL_HANDLE; + XrAction ButtonYAction = XR_NULL_HANDLE; + XrAction ButtonMenuAction = XR_NULL_HANDLE; + /// common touch actions + XrAction ThumbStickTouchAction = XR_NULL_HANDLE; + XrAction ThumbRestTouchAction = XR_NULL_HANDLE; + XrAction TriggerTouchAction = XR_NULL_HANDLE; + + XrSpace LeftControllerAimSpace = XR_NULL_HANDLE; + XrSpace RightControllerAimSpace = XR_NULL_HANDLE; + XrSpace LeftControllerGripSpace = XR_NULL_HANDLE; + XrSpace RightControllerGripSpace = XR_NULL_HANDLE; + uint32_t LastFrameAllButtons = 0u; + uint32_t LastFrameAllTouches = 0u; + + OVRFW::ovrSurfaceRender SurfaceRender; + OVRFW::OvrSceneView Scene; + std::unique_ptr FileSys; + std::unique_ptr SceneModel; + + private: + XrTime PrevDisplayTime = 0.0; + int SwapInterval; + int CpuLevel = CPU_LEVEL; + int GpuLevel = GPU_LEVEL; + int MainThreadTid; + int RenderThreadTid; + + xrCompositorLayerUnion Layers[MAX_NUM_LAYERS]; + int LayerCount; + + ovrFramebuffer FrameBuffer[MAX_NUM_EYES]; + int NumFramebuffers = MAX_NUM_EYES; + bool IsAppFocused = false; + bool RunWhilePaused = false; + bool ShouldRender = true; +}; + +} // namespace OVRFW + +#if defined(ANDROID) + +#define ENTRY_POINT(appClass) \ + void android_main(struct android_app* app) { \ + auto appl = std::make_unique(); \ + appl->Run(app); \ + } + +#elif defined(WIN32) + +#define ENTRY_POINT(appClass) \ + __pragma(comment(linker, "/SUBSYSTEM:WINDOWS")); \ + int APIENTRY WinMain(HINSTANCE, HINSTANCE, PSTR, int) { \ + auto appl = std::make_unique(); \ + appl->Run(); \ + return 0; \ + } + +#else + +#define ENTRY_POINT(appClass) \ + int main(int, const char**) { \ + auto appl = std::make_unique(); \ + appl->Run(); \ + return 0; \ + } + +#endif // defined(ANDROID) diff --git a/Samples/SampleXrFramework/res/raw/efigs.fnt b/Samples/SampleXrFramework/res/raw/efigs.fnt new file mode 100644 index 0000000..e1262c5 --- /dev/null +++ b/Samples/SampleXrFramework/res/raw/efigs.fnt @@ -0,0 +1,3899 @@ +{ + "FontName": "efigs.fnt", + "CommandLine": "OculusSans-Medium.otf efigs\\efigs -co -0.01 -ts 1.0 -hpad 128 -vpad 128 -sdf 256 1024 1024 -cf ..\\..\\..\\VrAppFramework\\res\\values\\strings.xml -cf ..\\..\\..\\VrAppFramework\\res\\values-de\\strings.xml -cf ..\\..\\..\\VrAppFramework\\res\\values-en-rGB\\strings.xml -cf ..\\..\\..\\VrAppFramework\\res\\values-es\\strings.xml -cf ..\\..\\..\\VrAppFramework\\res\\values-es-rES\\strings.xml -cf ..\\..\\..\\VrAppFramework\\res\\values-fr\\strings.xml -cf ..\\..\\..\\VrAppFramework\\res\\values-it\\strings.xml -cf strings\\values\\strings.xml -cf strings\\values-de\\strings.xml -cf strings\\values-en-rGB\\strings.xml -cf strings\\values-es\\strings.xml -cf strings\\values-es-rES\\strings.xml -cf strings\\values-fr\\strings.xml -cf strings\\values-it\\strings.xml -cf EFIGS_extended.txt", + "Version": 1, + "ImageFileName": "efigs_sdf.ktx", + "NaturalWidth": 8192, + "NaturalHeight": 3435, + "HorizontalPad": 128, + "VerticalPad": 128, + "FontHeight": 307, + "CenterOffset": -0.010000, + "TweakScale": 1, + "EdgeWidth": 32, + "NumGlyphs": 387, + "Weights": [{ + "AlphaCenterOffset": 0.0, + "ColorCenterOffset": 0.0 + }, { + "AlphaCenterOffset": -0.025, + "ColorCenterOffset": -0.025 + }, { + "AlphaCenterOffset": -0.0385, + "ColorCenterOffset": -0.0385 + }, { + "AlphaCenterOffset": -0.05, + "ColorCenterOffset": -0.05 + }], + "Glyphs": [{ + "CharCode": 0, + "X": -1, + "Y": -1, + "Width": 0, + "Height": 0, + "AdvanceX": 0, + "AdvanceY": 0, + "BearingX": 0, + "BearingY": 0 + }, { + "CharCode": 1, + "X": -1, + "Y": -1, + "Width": 0, + "Height": 0, + "AdvanceX": 0, + "AdvanceY": 0, + "BearingX": 0, + "BearingY": 0 + }, { + "CharCode": 2, + "X": -1, + "Y": -1, + "Width": 0, + "Height": 0, + "AdvanceX": 0, + "AdvanceY": 0, + "BearingX": 0, + "BearingY": 0 + }, { + "CharCode": 3, + "X": -1, + "Y": -1, + "Width": 0, + "Height": 0, + "AdvanceX": 0, + "AdvanceY": 0, + "BearingX": 0, + "BearingY": 0 + }, { + "CharCode": 4, + "X": -1, + "Y": -1, + "Width": 0, + "Height": 0, + "AdvanceX": 0, + "AdvanceY": 0, + "BearingX": 0, + "BearingY": 0 + }, { + "CharCode": 5, + "X": -1, + "Y": -1, + "Width": 0, + "Height": 0, + "AdvanceX": 0, + "AdvanceY": 0, + "BearingX": 0, + "BearingY": 0 + }, { + "CharCode": 6, + "X": -1, + "Y": -1, + "Width": 0, + "Height": 0, + "AdvanceX": 0, + "AdvanceY": 0, + "BearingX": 0, + "BearingY": 0 + }, { + "CharCode": 7, + "X": -1, + "Y": -1, + "Width": 0, + "Height": 0, + "AdvanceX": 0, + "AdvanceY": 0, + "BearingX": 0, + "BearingY": 0 + }, { + "CharCode": 8, + "X": -1, + "Y": -1, + "Width": 0, + "Height": 0, + "AdvanceX": 0, + "AdvanceY": 0, + "BearingX": 0, + "BearingY": 0 + }, { + "CharCode": 9, + "X": -1, + "Y": -1, + "Width": 0, + "Height": 0, + "AdvanceX": 0, + "AdvanceY": 0, + "BearingX": 0, + "BearingY": 0 + }, { + "CharCode": 10, + "X": -1, + "Y": -1, + "Width": 0, + "Height": 0, + "AdvanceX": 0, + "AdvanceY": 0, + "BearingX": 0, + "BearingY": 0 + }, { + "CharCode": 11, + "X": -1, + "Y": -1, + "Width": 0, + "Height": 0, + "AdvanceX": 0, + "AdvanceY": 0, + "BearingX": 0, + "BearingY": 0 + }, { + "CharCode": 12, + "X": -1, + "Y": -1, + "Width": 0, + "Height": 0, + "AdvanceX": 0, + "AdvanceY": 0, + "BearingX": 0, + "BearingY": 0 + }, { + "CharCode": 138, + "X": -1, + "Y": -1, + "Width": 0, + "Height": 0, + "AdvanceX": 0, + "AdvanceY": 0, + "BearingX": 0, + "BearingY": 0 + }, { + "CharCode": 14, + "X": -1, + "Y": -1, + "Width": 0, + "Height": 0, + "AdvanceX": 0, + "AdvanceY": 0, + "BearingX": 0, + "BearingY": 0 + }, { + "CharCode": 15, + "X": -1, + "Y": -1, + "Width": 0, + "Height": 0, + "AdvanceX": 0, + "AdvanceY": 0, + "BearingX": 0, + "BearingY": 0 + }, { + "CharCode": 16, + "X": -1, + "Y": -1, + "Width": 0, + "Height": 0, + "AdvanceX": 0, + "AdvanceY": 0, + "BearingX": 0, + "BearingY": 0 + }, { + "CharCode": 17, + "X": -1, + "Y": -1, + "Width": 0, + "Height": 0, + "AdvanceX": 0, + "AdvanceY": 0, + "BearingX": 0, + "BearingY": 0 + }, { + "CharCode": 18, + "X": -1, + "Y": -1, + "Width": 0, + "Height": 0, + "AdvanceX": 0, + "AdvanceY": 0, + "BearingX": 0, + "BearingY": 0 + }, { + "CharCode": 19, + "X": -1, + "Y": -1, + "Width": 0, + "Height": 0, + "AdvanceX": 0, + "AdvanceY": 0, + "BearingX": 0, + "BearingY": 0 + }, { + "CharCode": 20, + "X": -1, + "Y": -1, + "Width": 0, + "Height": 0, + "AdvanceX": 0, + "AdvanceY": 0, + "BearingX": 0, + "BearingY": 0 + }, { + "CharCode": 21, + "X": -1, + "Y": -1, + "Width": 0, + "Height": 0, + "AdvanceX": 0, + "AdvanceY": 0, + "BearingX": 0, + "BearingY": 0 + }, { + "CharCode": 22, + "X": -1, + "Y": -1, + "Width": 0, + "Height": 0, + "AdvanceX": 0, + "AdvanceY": 0, + "BearingX": 0, + "BearingY": 0 + }, { + "CharCode": 23, + "X": -1, + "Y": -1, + "Width": 0, + "Height": 0, + "AdvanceX": 0, + "AdvanceY": 0, + "BearingX": 0, + "BearingY": 0 + }, { + "CharCode": 24, + "X": -1, + "Y": -1, + "Width": 0, + "Height": 0, + "AdvanceX": 0, + "AdvanceY": 0, + "BearingX": 0, + "BearingY": 0 + }, { + "CharCode": 25, + "X": -1, + "Y": -1, + "Width": 0, + "Height": 0, + "AdvanceX": 0, + "AdvanceY": 0, + "BearingX": 0, + "BearingY": 0 + }, { + "CharCode": 26, + "X": -1, + "Y": -1, + "Width": 0, + "Height": 0, + "AdvanceX": 0, + "AdvanceY": 0, + "BearingX": 0, + "BearingY": 0 + }, { + "CharCode": 27, + "X": -1, + "Y": -1, + "Width": 0, + "Height": 0, + "AdvanceX": 0, + "AdvanceY": 0, + "BearingX": 0, + "BearingY": 0 + }, { + "CharCode": 28, + "X": -1, + "Y": -1, + "Width": 0, + "Height": 0, + "AdvanceX": 0, + "AdvanceY": 0, + "BearingX": 0, + "BearingY": 0 + }, { + "CharCode": 29, + "X": -1, + "Y": -1, + "Width": 0, + "Height": 0, + "AdvanceX": 0, + "AdvanceY": 0, + "BearingX": 0, + "BearingY": 0 + }, { + "CharCode": 30, + "X": -1, + "Y": -1, + "Width": 0, + "Height": 0, + "AdvanceX": 0, + "AdvanceY": 0, + "BearingX": 0, + "BearingY": 0 + }, { + "CharCode": 31, + "X": -1, + "Y": -1, + "Width": 0, + "Height": 0, + "AdvanceX": 0, + "AdvanceY": 0, + "BearingX": 0, + "BearingY": 0 + }, { + "CharCode": 139, + "X": -1, + "Y": -1, + "Width": 0, + "Height": 0, + "AdvanceX": 0, + "AdvanceY": 0, + "BearingX": 0, + "BearingY": 0 + }, { + "CharCode": 149, + "X": -1, + "Y": -1, + "Width": 0, + "Height": 0, + "AdvanceX": 0, + "AdvanceY": 0, + "BearingX": 0, + "BearingY": 0 + }, { + "CharCode": 140, + "X": -1, + "Y": -1, + "Width": 0, + "Height": 0, + "AdvanceX": 0, + "AdvanceY": 0, + "BearingX": 0, + "BearingY": 0 + }, { + "CharCode": 150, + "X": -1, + "Y": -1, + "Width": 0, + "Height": 0, + "AdvanceX": 0, + "AdvanceY": 0, + "BearingX": 0, + "BearingY": 0 + }, { + "CharCode": 151, + "X": -1, + "Y": -1, + "Width": 0, + "Height": 0, + "AdvanceX": 0, + "AdvanceY": 0, + "BearingX": 0, + "BearingY": 0 + }, { + "CharCode": 152, + "X": -1, + "Y": -1, + "Width": 0, + "Height": 0, + "AdvanceX": 0, + "AdvanceY": 0, + "BearingX": 0, + "BearingY": 0 + }, { + "CharCode": 153, + "X": -1, + "Y": -1, + "Width": 0, + "Height": 0, + "AdvanceX": 0, + "AdvanceY": 0, + "BearingX": 0, + "BearingY": 0 + }, { + "CharCode": 141, + "X": -1, + "Y": -1, + "Width": 0, + "Height": 0, + "AdvanceX": 0, + "AdvanceY": 0, + "BearingX": 0, + "BearingY": 0 + }, { + "CharCode": 154, + "X": -1, + "Y": -1, + "Width": 0, + "Height": 0, + "AdvanceX": 0, + "AdvanceY": 0, + "BearingX": 0, + "BearingY": 0 + }, { + "CharCode": 155, + "X": -1, + "Y": -1, + "Width": 0, + "Height": 0, + "AdvanceX": 0, + "AdvanceY": 0, + "BearingX": 0, + "BearingY": 0 + }, { + "CharCode": 142, + "X": -1, + "Y": -1, + "Width": 0, + "Height": 0, + "AdvanceX": 0, + "AdvanceY": 0, + "BearingX": 0, + "BearingY": 0 + }, { + "CharCode": 143, + "X": -1, + "Y": -1, + "Width": 0, + "Height": 0, + "AdvanceX": 0, + "AdvanceY": 0, + "BearingX": 0, + "BearingY": 0 + }, { + "CharCode": 144, + "X": -1, + "Y": -1, + "Width": 0, + "Height": 0, + "AdvanceX": 0, + "AdvanceY": 0, + "BearingX": 0, + "BearingY": 0 + }, { + "CharCode": 145, + "X": -1, + "Y": -1, + "Width": 0, + "Height": 0, + "AdvanceX": 0, + "AdvanceY": 0, + "BearingX": 0, + "BearingY": 0 + }, { + "CharCode": 146, + "X": -1, + "Y": -1, + "Width": 0, + "Height": 0, + "AdvanceX": 0, + "AdvanceY": 0, + "BearingX": 0, + "BearingY": 0 + }, { + "CharCode": 156, + "X": -1, + "Y": -1, + "Width": 0, + "Height": 0, + "AdvanceX": 0, + "AdvanceY": 0, + "BearingX": 0, + "BearingY": 0 + }, { + "CharCode": 147, + "X": -1, + "Y": -1, + "Width": 0, + "Height": 0, + "AdvanceX": 0, + "AdvanceY": 0, + "BearingX": 0, + "BearingY": 0 + }, { + "CharCode": 157, + "X": -1, + "Y": -1, + "Width": 0, + "Height": 0, + "AdvanceX": 0, + "AdvanceY": 0, + "BearingX": 0, + "BearingY": 0 + }, { + "CharCode": 158, + "X": -1, + "Y": -1, + "Width": 0, + "Height": 0, + "AdvanceX": 0, + "AdvanceY": 0, + "BearingX": 0, + "BearingY": 0 + }, { + "CharCode": 159, + "X": -1, + "Y": -1, + "Width": 0, + "Height": 0, + "AdvanceX": 0, + "AdvanceY": 0, + "BearingX": 0, + "BearingY": 0 + }, { + "CharCode": 148, + "X": -1, + "Y": -1, + "Width": 0, + "Height": 0, + "AdvanceX": 0, + "AdvanceY": 0, + "BearingX": 0, + "BearingY": 0 + }, { + "CharCode": 130, + "X": -1, + "Y": -1, + "Width": 0, + "Height": 0, + "AdvanceX": 0, + "AdvanceY": 0, + "BearingX": 0, + "BearingY": 0 + }, { + "CharCode": 127, + "X": -1, + "Y": -1, + "Width": 0, + "Height": 0, + "AdvanceX": 0, + "AdvanceY": 0, + "BearingX": 0, + "BearingY": 0 + }, { + "CharCode": 128, + "X": -1, + "Y": -1, + "Width": 0, + "Height": 0, + "AdvanceX": 0, + "AdvanceY": 0, + "BearingX": 0, + "BearingY": 0 + }, { + "CharCode": 129, + "X": -1, + "Y": -1, + "Width": 0, + "Height": 0, + "AdvanceX": 0, + "AdvanceY": 0, + "BearingX": 0, + "BearingY": 0 + }, { + "CharCode": 131, + "X": -1, + "Y": -1, + "Width": 0, + "Height": 0, + "AdvanceX": 0, + "AdvanceY": 0, + "BearingX": 0, + "BearingY": 0 + }, { + "CharCode": 132, + "X": -1, + "Y": -1, + "Width": 0, + "Height": 0, + "AdvanceX": 0, + "AdvanceY": 0, + "BearingX": 0, + "BearingY": 0 + }, { + "CharCode": 133, + "X": -1, + "Y": -1, + "Width": 0, + "Height": 0, + "AdvanceX": 0, + "AdvanceY": 0, + "BearingX": 0, + "BearingY": 0 + }, { + "CharCode": 134, + "X": -1, + "Y": -1, + "Width": 0, + "Height": 0, + "AdvanceX": 0, + "AdvanceY": 0, + "BearingX": 0, + "BearingY": 0 + }, { + "CharCode": 135, + "X": -1, + "Y": -1, + "Width": 0, + "Height": 0, + "AdvanceX": 0, + "AdvanceY": 0, + "BearingX": 0, + "BearingY": 0 + }, { + "CharCode": 136, + "X": -1, + "Y": -1, + "Width": 0, + "Height": 0, + "AdvanceX": 0, + "AdvanceY": 0, + "BearingX": 0, + "BearingY": 0 + }, { + "CharCode": 137, + "X": -1, + "Y": -1, + "Width": 0, + "Height": 0, + "AdvanceX": 0, + "AdvanceY": 0, + "BearingX": 0, + "BearingY": 0 + }, { + "CharCode": 13, + "X": 1365, + "Y": 3220, + "Width": 128, + "Height": 128, + "AdvanceX": 116, + "AdvanceY": 253, + "BearingX": -64, + "BearingY": 64 + }, { + "CharCode": 32, + "X": 1237, + "Y": 3220, + "Width": 128, + "Height": 128, + "AdvanceX": 119, + "AdvanceY": 253, + "BearingX": -64, + "BearingY": 64 + }, { + "CharCode": 160, + "X": 1109, + "Y": 3220, + "Width": 128, + "Height": 128, + "AdvanceX": 119, + "AdvanceY": 253, + "BearingX": -64, + "BearingY": 64 + }, { + "CharCode": 95, + "X": 853, + "Y": 3220, + "Width": 256, + "Height": 143, + "AdvanceX": 128, + "AdvanceY": 253, + "BearingX": -64, + "BearingY": 44 + }, { + "CharCode": 175, + "X": 653, + "Y": 3220, + "Width": 200, + "Height": 145, + "AdvanceX": 65, + "AdvanceY": 253, + "BearingX": -68, + "BearingY": 229 + }, { + "CharCode": 173, + "X": 463, + "Y": 3220, + "Width": 190, + "Height": 149, + "AdvanceX": 82, + "AdvanceY": 257, + "BearingX": -54, + "BearingY": 143 + }, { + "CharCode": 45, + "X": 259, + "Y": 3220, + "Width": 204, + "Height": 151, + "AdvanceX": 98, + "AdvanceY": 253, + "BearingX": -53, + "BearingY": 141 + }, { + "CharCode": 8211, + "X": 0, + "Y": 3220, + "Width": 259, + "Height": 151, + "AdvanceX": 153, + "AdvanceY": 253, + "BearingX": -53, + "BearingY": 141 + }, { + "CharCode": 168, + "X": 7938, + "Y": 2964, + "Width": 216, + "Height": 160, + "AdvanceX": 65, + "AdvanceY": 253, + "BearingX": -76, + "BearingY": 240 + }, { + "CharCode": 46, + "X": 7774, + "Y": 2964, + "Width": 164, + "Height": 164, + "AdvanceX": 72, + "AdvanceY": 253, + "BearingX": -46, + "BearingY": 97 + }, { + "CharCode": 183, + "X": 7610, + "Y": 2964, + "Width": 164, + "Height": 164, + "AdvanceX": 72, + "AdvanceY": 253, + "BearingX": -46, + "BearingY": 161 + }, { + "CharCode": 180, + "X": 7428, + "Y": 2964, + "Width": 182, + "Height": 170, + "AdvanceX": 65, + "AdvanceY": 253, + "BearingX": -49, + "BearingY": 245 + }, { + "CharCode": 96, + "X": 7245, + "Y": 2964, + "Width": 183, + "Height": 170, + "AdvanceX": 65, + "AdvanceY": 253, + "BearingX": -67, + "BearingY": 245 + }, { + "CharCode": 126, + "X": 6995, + "Y": 2964, + "Width": 250, + "Height": 172, + "AdvanceX": 167, + "AdvanceY": 253, + "BearingX": -41, + "BearingY": 165 + }, { + "CharCode": 184, + "X": 6809, + "Y": 2964, + "Width": 186, + "Height": 186, + "AdvanceX": 65, + "AdvanceY": 253, + "BearingX": -58, + "BearingY": 64 + }, { + "CharCode": 39, + "X": 6656, + "Y": 2964, + "Width": 153, + "Height": 197, + "AdvanceX": 70, + "AdvanceY": 253, + "BearingX": -41, + "BearingY": 250 + }, { + "CharCode": 8217, + "X": 6483, + "Y": 2964, + "Width": 173, + "Height": 197, + "AdvanceX": 73, + "AdvanceY": 253, + "BearingX": -53, + "BearingY": 250 + }, { + "CharCode": 44, + "X": 6309, + "Y": 2964, + "Width": 174, + "Height": 197, + "AdvanceX": 72, + "AdvanceY": 253, + "BearingX": -54, + "BearingY": 96 + }, { + "CharCode": 34, + "X": 6106, + "Y": 2964, + "Width": 203, + "Height": 197, + "AdvanceX": 121, + "AdvanceY": 253, + "BearingX": -41, + "BearingY": 250 + }, { + "CharCode": 8220, + "X": 5882, + "Y": 2964, + "Width": 224, + "Height": 197, + "AdvanceX": 124, + "AdvanceY": 253, + "BearingX": -47, + "BearingY": 251 + }, { + "CharCode": 8222, + "X": 5658, + "Y": 2964, + "Width": 224, + "Height": 197, + "AdvanceX": 122, + "AdvanceY": 253, + "BearingX": -54, + "BearingY": 96 + }, { + "CharCode": 172, + "X": 5395, + "Y": 2964, + "Width": 263, + "Height": 199, + "AdvanceX": 175, + "AdvanceY": 253, + "BearingX": -44, + "BearingY": 170 + }, { + "CharCode": 61, + "X": 5132, + "Y": 2964, + "Width": 263, + "Height": 200, + "AdvanceX": 175, + "AdvanceY": 253, + "BearingX": -44, + "BearingY": 183 + }, { + "CharCode": 176, + "X": 4921, + "Y": 2964, + "Width": 211, + "Height": 211, + "AdvanceX": 105, + "AdvanceY": 253, + "BearingX": -53, + "BearingY": 254 + }, { + "CharCode": 42, + "X": 4683, + "Y": 2964, + "Width": 238, + "Height": 232, + "AdvanceX": 127, + "AdvanceY": 253, + "BearingX": -56, + "BearingY": 250 + }, { + "CharCode": 185, + "X": 4503, + "Y": 2964, + "Width": 180, + "Height": 240, + "AdvanceX": 88, + "AdvanceY": 253, + "BearingX": -53, + "BearingY": 288 + }, { + "CharCode": 171, + "X": 4264, + "Y": 2964, + "Width": 239, + "Height": 242, + "AdvanceX": 146, + "AdvanceY": 253, + "BearingX": -49, + "BearingY": 183 + }, { + "CharCode": 178, + "X": 4055, + "Y": 2964, + "Width": 209, + "Height": 243, + "AdvanceX": 99, + "AdvanceY": 253, + "BearingX": -55, + "BearingY": 291 + }, { + "CharCode": 187, + "X": 3816, + "Y": 2964, + "Width": 239, + "Height": 243, + "AdvanceX": 146, + "AdvanceY": 253, + "BearingX": -44, + "BearingY": 183 + }, { + "CharCode": 94, + "X": 3559, + "Y": 2964, + "Width": 257, + "Height": 244, + "AdvanceX": 169, + "AdvanceY": 253, + "BearingX": -44, + "BearingY": 256 + }, { + "CharCode": 179, + "X": 3347, + "Y": 2964, + "Width": 212, + "Height": 245, + "AdvanceX": 102, + "AdvanceY": 253, + "BearingX": -55, + "BearingY": 291 + }, { + "CharCode": 170, + "X": 3137, + "Y": 2964, + "Width": 210, + "Height": 249, + "AdvanceX": 99, + "AdvanceY": 253, + "BearingX": -57, + "BearingY": 254 + }, { + "CharCode": 186, + "X": 2916, + "Y": 2964, + "Width": 221, + "Height": 249, + "AdvanceX": 107, + "AdvanceY": 253, + "BearingX": -57, + "BearingY": 254 + }, { + "CharCode": 247, + "X": 2653, + "Y": 2964, + "Width": 263, + "Height": 249, + "AdvanceX": 175, + "AdvanceY": 253, + "BearingX": -44, + "BearingY": 208 + }, { + "CharCode": 215, + "X": 2403, + "Y": 2964, + "Width": 250, + "Height": 250, + "AdvanceX": 165, + "AdvanceY": 253, + "BearingX": -42, + "BearingY": 209 + }, { + "CharCode": 305, + "X": 2249, + "Y": 2964, + "Width": 154, + "Height": 251, + "AdvanceX": 64, + "AdvanceY": 253, + "BearingX": -45, + "BearingY": 187 + }, { + "CharCode": 118, + "X": 1994, + "Y": 2964, + "Width": 255, + "Height": 251, + "AdvanceX": 133, + "AdvanceY": 253, + "BearingX": -61, + "BearingY": 187 + }, { + "CharCode": 120, + "X": 1738, + "Y": 2964, + "Width": 256, + "Height": 251, + "AdvanceX": 134, + "AdvanceY": 253, + "BearingX": -61, + "BearingY": 187 + }, { + "CharCode": 119, + "X": 1413, + "Y": 2964, + "Width": 325, + "Height": 251, + "AdvanceX": 203, + "AdvanceY": 253, + "BearingX": -61, + "BearingY": 187 + }, { + "CharCode": 122, + "X": 1178, + "Y": 2964, + "Width": 235, + "Height": 252, + "AdvanceX": 119, + "AdvanceY": 253, + "BearingX": -57, + "BearingY": 187 + }, { + "CharCode": 114, + "X": 974, + "Y": 2964, + "Width": 204, + "Height": 255, + "AdvanceX": 98, + "AdvanceY": 253, + "BearingX": -47, + "BearingY": 191 + }, { + "CharCode": 58, + "X": 810, + "Y": 2964, + "Width": 164, + "Height": 256, + "AdvanceX": 72, + "AdvanceY": 253, + "BearingX": -46, + "BearingY": 189 + }, { + "CharCode": 117, + "X": 568, + "Y": 2964, + "Width": 242, + "Height": 256, + "AdvanceX": 147, + "AdvanceY": 253, + "BearingX": -47, + "BearingY": 188 + }, { + "CharCode": 110, + "X": 324, + "Y": 2964, + "Width": 244, + "Height": 256, + "AdvanceX": 150, + "AdvanceY": 253, + "BearingX": -47, + "BearingY": 191 + }, { + "CharCode": 109, + "X": 0, + "Y": 2964, + "Width": 324, + "Height": 256, + "AdvanceX": 230, + "AdvanceY": 253, + "BearingX": -47, + "BearingY": 191 + }, { + "CharCode": 97, + "X": 7654, + "Y": 2693, + "Width": 244, + "Height": 257, + "AdvanceX": 145, + "AdvanceY": 253, + "BearingX": -53, + "BearingY": 190 + }, { + "CharCode": 115, + "X": 7424, + "Y": 2693, + "Width": 230, + "Height": 258, + "AdvanceX": 123, + "AdvanceY": 253, + "BearingX": -54, + "BearingY": 191 + }, { + "CharCode": 99, + "X": 7185, + "Y": 2693, + "Width": 239, + "Height": 259, + "AdvanceX": 132, + "AdvanceY": 253, + "BearingX": -53, + "BearingY": 191 + }, { + "CharCode": 101, + "X": 6937, + "Y": 2693, + "Width": 248, + "Height": 259, + "AdvanceX": 142, + "AdvanceY": 253, + "BearingX": -53, + "BearingY": 191 + }, { + "CharCode": 111, + "X": 6676, + "Y": 2693, + "Width": 261, + "Height": 259, + "AdvanceX": 155, + "AdvanceY": 253, + "BearingX": -53, + "BearingY": 191 + }, { + "CharCode": 230, + "X": 6345, + "Y": 2693, + "Width": 331, + "Height": 259, + "AdvanceX": 225, + "AdvanceY": 253, + "BearingX": -53, + "BearingY": 191 + }, { + "CharCode": 339, + "X": 5997, + "Y": 2693, + "Width": 348, + "Height": 259, + "AdvanceX": 241, + "AdvanceY": 253, + "BearingX": -53, + "BearingY": 191 + }, { + "CharCode": 164, + "X": 5734, + "Y": 2693, + "Width": 263, + "Height": 262, + "AdvanceX": 147, + "AdvanceY": 253, + "BearingX": -58, + "BearingY": 224 + }, { + "CharCode": 43, + "X": 5471, + "Y": 2693, + "Width": 263, + "Height": 263, + "AdvanceX": 173, + "AdvanceY": 253, + "BearingX": -44, + "BearingY": 215 + }, { + "CharCode": 1075, + "X": 5268, + "Y": 2693, + "Width": 203, + "Height": 265, + "AdvanceX": 102, + "AdvanceY": 257, + "BearingX": -42, + "BearingY": 201 + }, { + "CharCode": 1090, + "X": 5037, + "Y": 2693, + "Width": 231, + "Height": 265, + "AdvanceX": 113, + "AdvanceY": 257, + "BearingX": -59, + "BearingY": 201 + }, { + "CharCode": 1082, + "X": 4805, + "Y": 2693, + "Width": 232, + "Height": 265, + "AdvanceX": 126, + "AdvanceY": 257, + "BearingX": -42, + "BearingY": 201 + }, { + "CharCode": 1087, + "X": 4569, + "Y": 2693, + "Width": 236, + "Height": 265, + "AdvanceX": 152, + "AdvanceY": 257, + "BearingX": -42, + "BearingY": 201 + }, { + "CharCode": 1100, + "X": 4332, + "Y": 2693, + "Width": 237, + "Height": 265, + "AdvanceX": 145, + "AdvanceY": 257, + "BearingX": -42, + "BearingY": 201 + }, { + "CharCode": 1095, + "X": 4095, + "Y": 2693, + "Width": 237, + "Height": 265, + "AdvanceX": 150, + "AdvanceY": 257, + "BearingX": -45, + "BearingY": 201 + }, { + "CharCode": 1074, + "X": 3858, + "Y": 2693, + "Width": 237, + "Height": 265, + "AdvanceX": 145, + "AdvanceY": 257, + "BearingX": -42, + "BearingY": 201 + }, { + "CharCode": 1103, + "X": 3620, + "Y": 2693, + "Width": 238, + "Height": 265, + "AdvanceX": 136, + "AdvanceY": 257, + "BearingX": -60, + "BearingY": 201 + }, { + "CharCode": 1080, + "X": 3379, + "Y": 2693, + "Width": 241, + "Height": 265, + "AdvanceX": 157, + "AdvanceY": 257, + "BearingX": -42, + "BearingY": 201 + }, { + "CharCode": 1085, + "X": 3138, + "Y": 2693, + "Width": 241, + "Height": 265, + "AdvanceX": 157, + "AdvanceY": 257, + "BearingX": -42, + "BearingY": 201 + }, { + "CharCode": 1093, + "X": 2891, + "Y": 2693, + "Width": 247, + "Height": 265, + "AdvanceX": 128, + "AdvanceY": 257, + "BearingX": -60, + "BearingY": 201 + }, { + "CharCode": 1084, + "X": 2623, + "Y": 2693, + "Width": 268, + "Height": 265, + "AdvanceX": 184, + "AdvanceY": 257, + "BearingX": -42, + "BearingY": 201 + }, { + "CharCode": 1099, + "X": 2347, + "Y": 2693, + "Width": 276, + "Height": 265, + "AdvanceX": 192, + "AdvanceY": 257, + "BearingX": -42, + "BearingY": 201 + }, { + "CharCode": 1098, + "X": 2064, + "Y": 2693, + "Width": 283, + "Height": 265, + "AdvanceX": 174, + "AdvanceY": 257, + "BearingX": -59, + "BearingY": 201 + }, { + "CharCode": 1096, + "X": 1757, + "Y": 2693, + "Width": 307, + "Height": 265, + "AdvanceX": 223, + "AdvanceY": 257, + "BearingX": -42, + "BearingY": 201 + }, { + "CharCode": 1078, + "X": 1445, + "Y": 2693, + "Width": 312, + "Height": 265, + "AdvanceX": 184, + "AdvanceY": 257, + "BearingX": -64, + "BearingY": 201 + }, { + "CharCode": 1083, + "X": 1201, + "Y": 2693, + "Width": 244, + "Height": 267, + "AdvanceX": 140, + "AdvanceY": 257, + "BearingX": -62, + "BearingY": 201 + }, { + "CharCode": 248, + "X": 938, + "Y": 2693, + "Width": 263, + "Height": 267, + "AdvanceX": 155, + "AdvanceY": 253, + "BearingX": -54, + "BearingY": 195 + }, { + "CharCode": 177, + "X": 675, + "Y": 2693, + "Width": 263, + "Height": 268, + "AdvanceX": 173, + "AdvanceY": 253, + "BearingX": -44, + "BearingY": 215 + }, { + "CharCode": 1089, + "X": 451, + "Y": 2693, + "Width": 224, + "Height": 271, + "AdvanceX": 119, + "AdvanceY": 257, + "BearingX": -50, + "BearingY": 204 + }, { + "CharCode": 1101, + "X": 226, + "Y": 2693, + "Width": 225, + "Height": 271, + "AdvanceX": 118, + "AdvanceY": 257, + "BearingX": -57, + "BearingY": 204 + }, { + "CharCode": 1079, + "X": 0, + "Y": 2693, + "Width": 226, + "Height": 271, + "AdvanceX": 118, + "AdvanceY": 257, + "BearingX": -56, + "BearingY": 204 + }, { + "CharCode": 1072, + "X": 7846, + "Y": 2383, + "Width": 232, + "Height": 271, + "AdvanceX": 136, + "AdvanceY": 257, + "BearingX": -52, + "BearingY": 204 + }, { + "CharCode": 1077, + "X": 7608, + "Y": 2383, + "Width": 238, + "Height": 271, + "AdvanceX": 137, + "AdvanceY": 257, + "BearingX": -50, + "BearingY": 204 + }, { + "CharCode": 1086, + "X": 7360, + "Y": 2383, + "Width": 248, + "Height": 271, + "AdvanceX": 148, + "AdvanceY": 257, + "BearingX": -50, + "BearingY": 204 + }, { + "CharCode": 1102, + "X": 7061, + "Y": 2383, + "Width": 299, + "Height": 271, + "AdvanceX": 207, + "AdvanceY": 257, + "BearingX": -42, + "BearingY": 204 + }, { + "CharCode": 62, + "X": 6805, + "Y": 2383, + "Width": 256, + "Height": 280, + "AdvanceX": 168, + "AdvanceY": 253, + "BearingX": -44, + "BearingY": 219 + }, { + "CharCode": 60, + "X": 6549, + "Y": 2383, + "Width": 256, + "Height": 280, + "AdvanceX": 168, + "AdvanceY": 253, + "BearingX": -44, + "BearingY": 219 + }, { + "CharCode": 59, + "X": 6375, + "Y": 2383, + "Width": 174, + "Height": 290, + "AdvanceX": 72, + "AdvanceY": 253, + "BearingX": -54, + "BearingY": 189 + }, { + "CharCode": 116, + "X": 6163, + "Y": 2383, + "Width": 212, + "Height": 295, + "AdvanceX": 95, + "AdvanceY": 253, + "BearingX": -59, + "BearingY": 227 + }, { + "CharCode": 257, + "X": 5919, + "Y": 2383, + "Width": 244, + "Height": 296, + "AdvanceX": 145, + "AdvanceY": 253, + "BearingX": -53, + "BearingY": 229 + }, { + "CharCode": 241, + "X": 5675, + "Y": 2383, + "Width": 244, + "Height": 304, + "AdvanceX": 150, + "AdvanceY": 253, + "BearingX": -47, + "BearingY": 239 + }, { + "CharCode": 239, + "X": 5459, + "Y": 2383, + "Width": 216, + "Height": 305, + "AdvanceX": 64, + "AdvanceY": 253, + "BearingX": -76, + "BearingY": 240 + }, { + "CharCode": 380, + "X": 5224, + "Y": 2383, + "Width": 235, + "Height": 305, + "AdvanceX": 119, + "AdvanceY": 253, + "BearingX": -57, + "BearingY": 240 + }, { + "CharCode": 238, + "X": 5012, + "Y": 2383, + "Width": 212, + "Height": 306, + "AdvanceX": 64, + "AdvanceY": 253, + "BearingX": -74, + "BearingY": 242 + }, { + "CharCode": 345, + "X": 4798, + "Y": 2383, + "Width": 214, + "Height": 306, + "AdvanceX": 98, + "AdvanceY": 253, + "BearingX": -57, + "BearingY": 242 + }, { + "CharCode": 227, + "X": 4554, + "Y": 2383, + "Width": 244, + "Height": 306, + "AdvanceX": 145, + "AdvanceY": 253, + "BearingX": -53, + "BearingY": 239 + }, { + "CharCode": 382, + "X": 4319, + "Y": 2383, + "Width": 235, + "Height": 307, + "AdvanceX": 119, + "AdvanceY": 253, + "BearingX": -57, + "BearingY": 242 + }, { + "CharCode": 228, + "X": 4075, + "Y": 2383, + "Width": 244, + "Height": 307, + "AdvanceX": 145, + "AdvanceY": 253, + "BearingX": -53, + "BearingY": 240 + }, { + "CharCode": 328, + "X": 3831, + "Y": 2383, + "Width": 244, + "Height": 307, + "AdvanceX": 150, + "AdvanceY": 253, + "BearingX": -47, + "BearingY": 242 + }, { + "CharCode": 281, + "X": 3582, + "Y": 2383, + "Width": 249, + "Height": 307, + "AdvanceX": 142, + "AdvanceY": 253, + "BearingX": -53, + "BearingY": 191 + }, { + "CharCode": 245, + "X": 3321, + "Y": 2383, + "Width": 261, + "Height": 307, + "AdvanceX": 155, + "AdvanceY": 253, + "BearingX": -53, + "BearingY": 239 + }, { + "CharCode": 252, + "X": 3079, + "Y": 2383, + "Width": 242, + "Height": 308, + "AdvanceX": 147, + "AdvanceY": 253, + "BearingX": -47, + "BearingY": 240 + }, { + "CharCode": 259, + "X": 2835, + "Y": 2383, + "Width": 244, + "Height": 308, + "AdvanceX": 145, + "AdvanceY": 253, + "BearingX": -53, + "BearingY": 241 + }, { + "CharCode": 235, + "X": 2587, + "Y": 2383, + "Width": 248, + "Height": 308, + "AdvanceX": 142, + "AdvanceY": 253, + "BearingX": -53, + "BearingY": 240 + }, { + "CharCode": 246, + "X": 2326, + "Y": 2383, + "Width": 261, + "Height": 308, + "AdvanceX": 155, + "AdvanceY": 253, + "BearingX": -53, + "BearingY": 240 + }, { + "CharCode": 353, + "X": 2096, + "Y": 2383, + "Width": 230, + "Height": 309, + "AdvanceX": 123, + "AdvanceY": 253, + "BearingX": -54, + "BearingY": 242 + }, { + "CharCode": 226, + "X": 1852, + "Y": 2383, + "Width": 244, + "Height": 309, + "AdvanceX": 145, + "AdvanceY": 253, + "BearingX": -53, + "BearingY": 242 + }, { + "CharCode": 237, + "X": 1667, + "Y": 2383, + "Width": 185, + "Height": 310, + "AdvanceX": 64, + "AdvanceY": 253, + "BearingX": -45, + "BearingY": 246 + }, { + "CharCode": 236, + "X": 1482, + "Y": 2383, + "Width": 185, + "Height": 310, + "AdvanceX": 64, + "AdvanceY": 253, + "BearingX": -76, + "BearingY": 246 + }, { + "CharCode": 269, + "X": 1243, + "Y": 2383, + "Width": 239, + "Height": 310, + "AdvanceX": 132, + "AdvanceY": 253, + "BearingX": -53, + "BearingY": 242 + }, { + "CharCode": 251, + "X": 1001, + "Y": 2383, + "Width": 242, + "Height": 310, + "AdvanceX": 147, + "AdvanceY": 253, + "BearingX": -47, + "BearingY": 242 + }, { + "CharCode": 261, + "X": 757, + "Y": 2383, + "Width": 244, + "Height": 310, + "AdvanceX": 145, + "AdvanceY": 253, + "BearingX": -53, + "BearingY": 190 + }, { + "CharCode": 234, + "X": 509, + "Y": 2383, + "Width": 248, + "Height": 310, + "AdvanceX": 142, + "AdvanceY": 253, + "BearingX": -53, + "BearingY": 242 + }, { + "CharCode": 283, + "X": 261, + "Y": 2383, + "Width": 248, + "Height": 310, + "AdvanceX": 142, + "AdvanceY": 253, + "BearingX": -53, + "BearingY": 242 + }, { + "CharCode": 244, + "X": 0, + "Y": 2383, + "Width": 261, + "Height": 310, + "AdvanceX": 155, + "AdvanceY": 253, + "BearingX": -53, + "BearingY": 242 + }, { + "CharCode": 105, + "X": 7986, + "Y": 2070, + "Width": 162, + "Height": 311, + "AdvanceX": 64, + "AdvanceY": 253, + "BearingX": -49, + "BearingY": 247 + }, { + "CharCode": 1045, + "X": 7764, + "Y": 2070, + "Width": 222, + "Height": 311, + "AdvanceX": 135, + "AdvanceY": 257, + "BearingX": -39, + "BearingY": 247 + }, { + "CharCode": 1043, + "X": 7541, + "Y": 2070, + "Width": 223, + "Height": 311, + "AdvanceX": 126, + "AdvanceY": 257, + "BearingX": -39, + "BearingY": 247 + }, { + "CharCode": 378, + "X": 7306, + "Y": 2070, + "Width": 235, + "Height": 311, + "AdvanceX": 119, + "AdvanceY": 253, + "BearingX": -57, + "BearingY": 246 + }, { + "CharCode": 1041, + "X": 7068, + "Y": 2070, + "Width": 238, + "Height": 311, + "AdvanceX": 148, + "AdvanceY": 257, + "BearingX": -39, + "BearingY": 247 + }, { + "CharCode": 1056, + "X": 6830, + "Y": 2070, + "Width": 238, + "Height": 311, + "AdvanceX": 148, + "AdvanceY": 257, + "BearingX": -39, + "BearingY": 247 + }, { + "CharCode": 1068, + "X": 6592, + "Y": 2070, + "Width": 238, + "Height": 311, + "AdvanceX": 148, + "AdvanceY": 257, + "BearingX": -39, + "BearingY": 247 + }, { + "CharCode": 324, + "X": 6348, + "Y": 2070, + "Width": 244, + "Height": 311, + "AdvanceX": 150, + "AdvanceY": 253, + "BearingX": -47, + "BearingY": 246 + }, { + "CharCode": 1042, + "X": 6100, + "Y": 2070, + "Width": 248, + "Height": 311, + "AdvanceX": 159, + "AdvanceY": 257, + "BearingX": -39, + "BearingY": 247 + }, { + "CharCode": 1063, + "X": 5849, + "Y": 2070, + "Width": 251, + "Height": 311, + "AdvanceX": 169, + "AdvanceY": 257, + "BearingX": -43, + "BearingY": 247 + }, { + "CharCode": 1071, + "X": 5598, + "Y": 2070, + "Width": 251, + "Height": 311, + "AdvanceX": 151, + "AdvanceY": 257, + "BearingX": -61, + "BearingY": 247 + }, { + "CharCode": 1050, + "X": 5346, + "Y": 2070, + "Width": 252, + "Height": 311, + "AdvanceX": 148, + "AdvanceY": 257, + "BearingX": -39, + "BearingY": 247 + }, { + "CharCode": 1058, + "X": 5091, + "Y": 2070, + "Width": 255, + "Height": 311, + "AdvanceX": 133, + "AdvanceY": 257, + "BearingX": -61, + "BearingY": 247 + }, { + "CharCode": 1055, + "X": 4836, + "Y": 2070, + "Width": 255, + "Height": 311, + "AdvanceX": 177, + "AdvanceY": 257, + "BearingX": -39, + "BearingY": 247 + }, { + "CharCode": 1053, + "X": 4578, + "Y": 2070, + "Width": 258, + "Height": 311, + "AdvanceX": 180, + "AdvanceY": 257, + "BearingX": -39, + "BearingY": 247 + }, { + "CharCode": 1048, + "X": 4313, + "Y": 2070, + "Width": 265, + "Height": 311, + "AdvanceX": 187, + "AdvanceY": 257, + "BearingX": -39, + "BearingY": 247 + }, { + "CharCode": 1061, + "X": 4043, + "Y": 2070, + "Width": 270, + "Height": 311, + "AdvanceX": 140, + "AdvanceY": 257, + "BearingX": -65, + "BearingY": 247 + }, { + "CharCode": 1066, + "X": 3763, + "Y": 2070, + "Width": 280, + "Height": 311, + "AdvanceX": 168, + "AdvanceY": 257, + "BearingX": -61, + "BearingY": 247 + }, { + "CharCode": 1067, + "X": 3474, + "Y": 2070, + "Width": 289, + "Height": 311, + "AdvanceX": 211, + "AdvanceY": 257, + "BearingX": -39, + "BearingY": 247 + }, { + "CharCode": 1052, + "X": 3173, + "Y": 2070, + "Width": 301, + "Height": 311, + "AdvanceX": 223, + "AdvanceY": 257, + "BearingX": -39, + "BearingY": 247 + }, { + "CharCode": 1064, + "X": 2840, + "Y": 2070, + "Width": 333, + "Height": 311, + "AdvanceX": 255, + "AdvanceY": 257, + "BearingX": -39, + "BearingY": 247 + }, { + "CharCode": 1046, + "X": 2502, + "Y": 2070, + "Width": 338, + "Height": 311, + "AdvanceX": 209, + "AdvanceY": 257, + "BearingX": -64, + "BearingY": 247 + }, { + "CharCode": 224, + "X": 2258, + "Y": 2070, + "Width": 244, + "Height": 312, + "AdvanceX": 145, + "AdvanceY": 253, + "BearingX": -53, + "BearingY": 245 + }, { + "CharCode": 225, + "X": 2014, + "Y": 2070, + "Width": 244, + "Height": 312, + "AdvanceX": 145, + "AdvanceY": 253, + "BearingX": -53, + "BearingY": 245 + }, { + "CharCode": 1040, + "X": 1728, + "Y": 2070, + "Width": 286, + "Height": 312, + "AdvanceX": 156, + "AdvanceY": 257, + "BearingX": -65, + "BearingY": 248 + }, { + "CharCode": 347, + "X": 1498, + "Y": 2070, + "Width": 230, + "Height": 313, + "AdvanceX": 123, + "AdvanceY": 253, + "BearingX": -54, + "BearingY": 246 + }, { + "CharCode": 351, + "X": 1268, + "Y": 2070, + "Width": 230, + "Height": 313, + "AdvanceX": 123, + "AdvanceY": 253, + "BearingX": -54, + "BearingY": 191 + }, { + "CharCode": 231, + "X": 1029, + "Y": 2070, + "Width": 239, + "Height": 313, + "AdvanceX": 132, + "AdvanceY": 253, + "BearingX": -53, + "BearingY": 191 + }, { + "CharCode": 121, + "X": 774, + "Y": 2070, + "Width": 255, + "Height": 313, + "AdvanceX": 133, + "AdvanceY": 253, + "BearingX": -61, + "BearingY": 187 + }, { + "CharCode": 1094, + "X": 518, + "Y": 2070, + "Width": 256, + "Height": 313, + "AdvanceX": 154, + "AdvanceY": 257, + "BearingX": -42, + "BearingY": 201 + }, { + "CharCode": 1076, + "X": 260, + "Y": 2070, + "Width": 258, + "Height": 313, + "AdvanceX": 140, + "AdvanceY": 257, + "BearingX": -59, + "BearingY": 201 + }, { + "CharCode": 182, + "X": 0, + "Y": 2070, + "Width": 260, + "Height": 313, + "AdvanceX": 162, + "AdvanceY": 253, + "BearingX": -44, + "BearingY": 187 + }, { + "CharCode": 1097, + "X": 7745, + "Y": 1756, + "Width": 327, + "Height": 313, + "AdvanceX": 225, + "AdvanceY": 257, + "BearingX": -42, + "BearingY": 201 + }, { + "CharCode": 73, + "X": 7589, + "Y": 1756, + "Width": 156, + "Height": 314, + "AdvanceX": 72, + "AdvanceY": 253, + "BearingX": -42, + "BearingY": 250 + }, { + "CharCode": 49, + "X": 7387, + "Y": 1756, + "Width": 202, + "Height": 314, + "AdvanceX": 119, + "AdvanceY": 253, + "BearingX": -52, + "BearingY": 250 + }, { + "CharCode": 162, + "X": 7148, + "Y": 1756, + "Width": 239, + "Height": 314, + "AdvanceX": 134, + "AdvanceY": 253, + "BearingX": -53, + "BearingY": 250 + }, { + "CharCode": 263, + "X": 6907, + "Y": 1756, + "Width": 241, + "Height": 314, + "AdvanceX": 132, + "AdvanceY": 253, + "BearingX": -53, + "BearingY": 246 + }, { + "CharCode": 250, + "X": 6665, + "Y": 1756, + "Width": 242, + "Height": 314, + "AdvanceX": 147, + "AdvanceY": 253, + "BearingX": -47, + "BearingY": 246 + }, { + "CharCode": 249, + "X": 6423, + "Y": 1756, + "Width": 242, + "Height": 314, + "AdvanceX": 147, + "AdvanceY": 253, + "BearingX": -47, + "BearingY": 246 + }, { + "CharCode": 369, + "X": 6181, + "Y": 1756, + "Width": 242, + "Height": 314, + "AdvanceX": 147, + "AdvanceY": 253, + "BearingX": -47, + "BearingY": 246 + }, { + "CharCode": 76, + "X": 5935, + "Y": 1756, + "Width": 246, + "Height": 314, + "AdvanceX": 141, + "AdvanceY": 253, + "BearingX": -42, + "BearingY": 250 + }, { + "CharCode": 232, + "X": 5687, + "Y": 1756, + "Width": 248, + "Height": 314, + "AdvanceX": 142, + "AdvanceY": 253, + "BearingX": -53, + "BearingY": 246 + }, { + "CharCode": 233, + "X": 5439, + "Y": 1756, + "Width": 248, + "Height": 314, + "AdvanceX": 142, + "AdvanceY": 253, + "BearingX": -53, + "BearingY": 246 + }, { + "CharCode": 70, + "X": 5189, + "Y": 1756, + "Width": 250, + "Height": 314, + "AdvanceX": 153, + "AdvanceY": 253, + "BearingX": -42, + "BearingY": 250 + }, { + "CharCode": 55, + "X": 4936, + "Y": 1756, + "Width": 253, + "Height": 314, + "AdvanceX": 143, + "AdvanceY": 253, + "BearingX": -55, + "BearingY": 250 + }, { + "CharCode": 69, + "X": 4681, + "Y": 1756, + "Width": 255, + "Height": 314, + "AdvanceX": 163, + "AdvanceY": 253, + "BearingX": -42, + "BearingY": 250 + }, { + "CharCode": 337, + "X": 4420, + "Y": 1756, + "Width": 261, + "Height": 314, + "AdvanceX": 155, + "AdvanceY": 253, + "BearingX": -53, + "BearingY": 246 + }, { + "CharCode": 321, + "X": 4159, + "Y": 1756, + "Width": 261, + "Height": 314, + "AdvanceX": 141, + "AdvanceY": 253, + "BearingX": -57, + "BearingY": 250 + }, { + "CharCode": 242, + "X": 3898, + "Y": 1756, + "Width": 261, + "Height": 314, + "AdvanceX": 155, + "AdvanceY": 253, + "BearingX": -53, + "BearingY": 246 + }, { + "CharCode": 243, + "X": 3637, + "Y": 1756, + "Width": 261, + "Height": 314, + "AdvanceX": 155, + "AdvanceY": 253, + "BearingX": -53, + "BearingY": 246 + }, { + "CharCode": 80, + "X": 3374, + "Y": 1756, + "Width": 263, + "Height": 314, + "AdvanceX": 166, + "AdvanceY": 253, + "BearingX": -42, + "BearingY": 250 + }, { + "CharCode": 52, + "X": 3102, + "Y": 1756, + "Width": 272, + "Height": 314, + "AdvanceX": 158, + "AdvanceY": 253, + "BearingX": -56, + "BearingY": 250 + }, { + "CharCode": 90, + "X": 2828, + "Y": 1756, + "Width": 274, + "Height": 314, + "AdvanceX": 167, + "AdvanceY": 253, + "BearingX": -53, + "BearingY": 250 + }, { + "CharCode": 1051, + "X": 2554, + "Y": 1756, + "Width": 274, + "Height": 314, + "AdvanceX": 171, + "AdvanceY": 257, + "BearingX": -64, + "BearingY": 247 + }, { + "CharCode": 35, + "X": 2278, + "Y": 1756, + "Width": 276, + "Height": 314, + "AdvanceX": 158, + "AdvanceY": 253, + "BearingX": -59, + "BearingY": 250 + }, { + "CharCode": 1059, + "X": 2001, + "Y": 1756, + "Width": 277, + "Height": 314, + "AdvanceX": 150, + "AdvanceY": 257, + "BearingX": -62, + "BearingY": 247 + }, { + "CharCode": 66, + "X": 1724, + "Y": 1756, + "Width": 277, + "Height": 314, + "AdvanceX": 181, + "AdvanceY": 253, + "BearingX": -42, + "BearingY": 250 + }, { + "CharCode": 84, + "X": 1445, + "Y": 1756, + "Width": 279, + "Height": 314, + "AdvanceX": 159, + "AdvanceY": 253, + "BearingX": -60, + "BearingY": 250 + }, { + "CharCode": 75, + "X": 1165, + "Y": 1756, + "Width": 280, + "Height": 314, + "AdvanceX": 179, + "AdvanceY": 253, + "BearingX": -41, + "BearingY": 250 + }, { + "CharCode": 72, + "X": 880, + "Y": 1756, + "Width": 285, + "Height": 314, + "AdvanceX": 201, + "AdvanceY": 253, + "BearingX": -42, + "BearingY": 250 + }, { + "CharCode": 89, + "X": 588, + "Y": 1756, + "Width": 292, + "Height": 314, + "AdvanceX": 174, + "AdvanceY": 253, + "BearingX": -59, + "BearingY": 250 + }, { + "CharCode": 78, + "X": 296, + "Y": 1756, + "Width": 292, + "Height": 314, + "AdvanceX": 208, + "AdvanceY": 253, + "BearingX": -42, + "BearingY": 250 + }, { + "CharCode": 68, + "X": 0, + "Y": 1756, + "Width": 296, + "Height": 314, + "AdvanceX": 200, + "AdvanceY": 253, + "BearingX": -42, + "BearingY": 250 + }, { + "CharCode": 88, + "X": 7827, + "Y": 1438, + "Width": 297, + "Height": 314, + "AdvanceX": 175, + "AdvanceY": 253, + "BearingX": -61, + "BearingY": 250 + }, { + "CharCode": 86, + "X": 7527, + "Y": 1438, + "Width": 300, + "Height": 314, + "AdvanceX": 182, + "AdvanceY": 253, + "BearingX": -59, + "BearingY": 250 + }, { + "CharCode": 77, + "X": 7199, + "Y": 1438, + "Width": 328, + "Height": 314, + "AdvanceX": 240, + "AdvanceY": 253, + "BearingX": -44, + "BearingY": 250 + }, { + "CharCode": 338, + "X": 6825, + "Y": 1438, + "Width": 374, + "Height": 314, + "AdvanceX": 277, + "AdvanceY": 253, + "BearingX": -48, + "BearingY": 250 + }, { + "CharCode": 198, + "X": 6449, + "Y": 1438, + "Width": 376, + "Height": 314, + "AdvanceX": 266, + "AdvanceY": 253, + "BearingX": -61, + "BearingY": 250 + }, { + "CharCode": 87, + "X": 6065, + "Y": 1438, + "Width": 384, + "Height": 314, + "AdvanceX": 265, + "AdvanceY": 253, + "BearingX": -59, + "BearingY": 250 + }, { + "CharCode": 222, + "X": 5807, + "Y": 1438, + "Width": 258, + "Height": 315, + "AdvanceX": 162, + "AdvanceY": 253, + "BearingX": -42, + "BearingY": 250 + }, { + "CharCode": 82, + "X": 5536, + "Y": 1438, + "Width": 271, + "Height": 315, + "AdvanceX": 172, + "AdvanceY": 253, + "BearingX": -42, + "BearingY": 250 + }, { + "CharCode": 165, + "X": 5254, + "Y": 1438, + "Width": 282, + "Height": 315, + "AdvanceX": 154, + "AdvanceY": 253, + "BearingX": -64, + "BearingY": 250 + }, { + "CharCode": 65, + "X": 4945, + "Y": 1438, + "Width": 309, + "Height": 315, + "AdvanceX": 187, + "AdvanceY": 253, + "BearingX": -61, + "BearingY": 250 + }, { + "CharCode": 208, + "X": 4633, + "Y": 1438, + "Width": 312, + "Height": 315, + "AdvanceX": 200, + "AdvanceY": 253, + "BearingX": -58, + "BearingY": 250 + }, { + "CharCode": 1047, + "X": 4388, + "Y": 1438, + "Width": 245, + "Height": 316, + "AdvanceX": 140, + "AdvanceY": 257, + "BearingX": -55, + "BearingY": 249 + }, { + "CharCode": 1069, + "X": 4131, + "Y": 1438, + "Width": 257, + "Height": 316, + "AdvanceX": 152, + "AdvanceY": 257, + "BearingX": -57, + "BearingY": 249 + }, { + "CharCode": 1057, + "X": 3872, + "Y": 1438, + "Width": 259, + "Height": 316, + "AdvanceX": 154, + "AdvanceY": 257, + "BearingX": -48, + "BearingY": 249 + }, { + "CharCode": 1060, + "X": 3574, + "Y": 1438, + "Width": 298, + "Height": 316, + "AdvanceX": 196, + "AdvanceY": 257, + "BearingX": -51, + "BearingY": 249 + }, { + "CharCode": 33, + "X": 3410, + "Y": 1438, + "Width": 164, + "Height": 317, + "AdvanceX": 106, + "AdvanceY": 253, + "BearingX": -29, + "BearingY": 250 + }, { + "CharCode": 161, + "X": 3246, + "Y": 1438, + "Width": 164, + "Height": 317, + "AdvanceX": 106, + "AdvanceY": 253, + "BearingX": -29, + "BearingY": 191 + }, { + "CharCode": 229, + "X": 3002, + "Y": 1438, + "Width": 244, + "Height": 317, + "AdvanceX": 145, + "AdvanceY": 253, + "BearingX": -53, + "BearingY": 250 + }, { + "CharCode": 163, + "X": 2746, + "Y": 1438, + "Width": 256, + "Height": 317, + "AdvanceX": 147, + "AdvanceY": 253, + "BearingX": -57, + "BearingY": 253 + }, { + "CharCode": 112, + "X": 2486, + "Y": 1438, + "Width": 260, + "Height": 317, + "AdvanceX": 161, + "AdvanceY": 253, + "BearingX": -47, + "BearingY": 191 + }, { + "CharCode": 113, + "X": 2225, + "Y": 1438, + "Width": 261, + "Height": 317, + "AdvanceX": 161, + "AdvanceY": 253, + "BearingX": -53, + "BearingY": 191 + }, { + "CharCode": 1054, + "X": 1939, + "Y": 1438, + "Width": 286, + "Height": 317, + "AdvanceX": 190, + "AdvanceY": 257, + "BearingX": -48, + "BearingY": 250 + }, { + "CharCode": 1070, + "X": 1592, + "Y": 1438, + "Width": 347, + "Height": 317, + "AdvanceX": 260, + "AdvanceY": 257, + "BearingX": -39, + "BearingY": 250 + }, { + "CharCode": 102, + "X": 1379, + "Y": 1438, + "Width": 213, + "Height": 318, + "AdvanceX": 87, + "AdvanceY": 253, + "BearingX": -60, + "BearingY": 254 + }, { + "CharCode": 74, + "X": 1146, + "Y": 1438, + "Width": 233, + "Height": 318, + "AdvanceX": 132, + "AdvanceY": 253, + "BearingX": -60, + "BearingY": 250 + }, { + "CharCode": 367, + "X": 904, + "Y": 1438, + "Width": 242, + "Height": 318, + "AdvanceX": 147, + "AdvanceY": 253, + "BearingX": -47, + "BearingY": 250 + }, { + "CharCode": 85, + "X": 620, + "Y": 1438, + "Width": 284, + "Height": 318, + "AdvanceX": 200, + "AdvanceY": 253, + "BearingX": -42, + "BearingY": 250 + }, { + "CharCode": 306, + "X": 333, + "Y": 1438, + "Width": 287, + "Height": 318, + "AdvanceX": 204, + "AdvanceY": 253, + "BearingX": -42, + "BearingY": 250 + }, { + "CharCode": 81, + "X": 0, + "Y": 1438, + "Width": 333, + "Height": 318, + "AdvanceX": 218, + "AdvanceY": 253, + "BearingX": -53, + "BearingY": 254 + }, { + "CharCode": 108, + "X": 7761, + "Y": 1114, + "Width": 155, + "Height": 319, + "AdvanceX": 65, + "AdvanceY": 253, + "BearingX": -45, + "BearingY": 255 + }, { + "CharCode": 322, + "X": 7570, + "Y": 1114, + "Width": 191, + "Height": 319, + "AdvanceX": 66, + "AdvanceY": 253, + "BearingX": -63, + "BearingY": 255 + }, { + "CharCode": 1105, + "X": 7332, + "Y": 1114, + "Width": 238, + "Height": 319, + "AdvanceX": 137, + "AdvanceY": 257, + "BearingX": -50, + "BearingY": 252 + }, { + "CharCode": 50, + "X": 7079, + "Y": 1114, + "Width": 253, + "Height": 319, + "AdvanceX": 151, + "AdvanceY": 253, + "BearingX": -52, + "BearingY": 254 + }, { + "CharCode": 53, + "X": 6824, + "Y": 1114, + "Width": 255, + "Height": 319, + "AdvanceX": 155, + "AdvanceY": 253, + "BearingX": -49, + "BearingY": 250 + }, { + "CharCode": 54, + "X": 6559, + "Y": 1114, + "Width": 265, + "Height": 319, + "AdvanceX": 161, + "AdvanceY": 253, + "BearingX": -51, + "BearingY": 251 + }, { + "CharCode": 107, + "X": 6315, + "Y": 1114, + "Width": 244, + "Height": 320, + "AdvanceX": 135, + "AdvanceY": 253, + "BearingX": -47, + "BearingY": 255 + }, { + "CharCode": 104, + "X": 6071, + "Y": 1114, + "Width": 244, + "Height": 320, + "AdvanceX": 150, + "AdvanceY": 253, + "BearingX": -47, + "BearingY": 255 + }, { + "CharCode": 357, + "X": 5825, + "Y": 1114, + "Width": 246, + "Height": 320, + "AdvanceX": 95, + "AdvanceY": 253, + "BearingX": -59, + "BearingY": 252 + }, { + "CharCode": 103, + "X": 5565, + "Y": 1114, + "Width": 260, + "Height": 320, + "AdvanceX": 160, + "AdvanceY": 253, + "BearingX": -51, + "BearingY": 191 + }, { + "CharCode": 57, + "X": 5300, + "Y": 1114, + "Width": 265, + "Height": 320, + "AdvanceX": 163, + "AdvanceY": 253, + "BearingX": -52, + "BearingY": 254 + }, { + "CharCode": 537, + "X": 5070, + "Y": 1114, + "Width": 230, + "Height": 321, + "AdvanceX": 123, + "AdvanceY": 253, + "BearingX": -54, + "BearingY": 191 + }, { + "CharCode": 191, + "X": 4822, + "Y": 1114, + "Width": 248, + "Height": 321, + "AdvanceX": 148, + "AdvanceY": 253, + "BearingX": -50, + "BearingY": 191 + }, { + "CharCode": 63, + "X": 4573, + "Y": 1114, + "Width": 249, + "Height": 321, + "AdvanceX": 148, + "AdvanceY": 253, + "BearingX": -51, + "BearingY": 254 + }, { + "CharCode": 83, + "X": 4312, + "Y": 1114, + "Width": 261, + "Height": 322, + "AdvanceX": 156, + "AdvanceY": 253, + "BearingX": -52, + "BearingY": 254 + }, { + "CharCode": 56, + "X": 4048, + "Y": 1114, + "Width": 264, + "Height": 322, + "AdvanceX": 162, + "AdvanceY": 253, + "BearingX": -51, + "BearingY": 254 + }, { + "CharCode": 48, + "X": 3773, + "Y": 1114, + "Width": 275, + "Height": 322, + "AdvanceX": 173, + "AdvanceY": 253, + "BearingX": -51, + "BearingY": 254 + }, { + "CharCode": 67, + "X": 3484, + "Y": 1114, + "Width": 289, + "Height": 322, + "AdvanceX": 181, + "AdvanceY": 253, + "BearingX": -53, + "BearingY": 254 + }, { + "CharCode": 38, + "X": 3184, + "Y": 1114, + "Width": 300, + "Height": 322, + "AdvanceX": 192, + "AdvanceY": 253, + "BearingX": -54, + "BearingY": 254 + }, { + "CharCode": 71, + "X": 2884, + "Y": 1114, + "Width": 300, + "Height": 322, + "AdvanceX": 200, + "AdvanceY": 253, + "BearingX": -53, + "BearingY": 254 + }, { + "CharCode": 174, + "X": 2562, + "Y": 1114, + "Width": 322, + "Height": 322, + "AdvanceX": 210, + "AdvanceY": 253, + "BearingX": -54, + "BearingY": 254 + }, { + "CharCode": 169, + "X": 2240, + "Y": 1114, + "Width": 322, + "Height": 322, + "AdvanceX": 210, + "AdvanceY": 253, + "BearingX": -54, + "BearingY": 254 + }, { + "CharCode": 79, + "X": 1915, + "Y": 1114, + "Width": 325, + "Height": 322, + "AdvanceX": 219, + "AdvanceY": 253, + "BearingX": -53, + "BearingY": 254 + }, { + "CharCode": 1081, + "X": 1674, + "Y": 1114, + "Width": 241, + "Height": 323, + "AdvanceX": 157, + "AdvanceY": 257, + "BearingX": -42, + "BearingY": 259 + }, { + "CharCode": 51, + "X": 1418, + "Y": 1114, + "Width": 256, + "Height": 323, + "AdvanceX": 155, + "AdvanceY": 253, + "BearingX": -50, + "BearingY": 254 + }, { + "CharCode": 223, + "X": 1158, + "Y": 1114, + "Width": 260, + "Height": 323, + "AdvanceX": 158, + "AdvanceY": 253, + "BearingX": -46, + "BearingY": 256 + }, { + "CharCode": 98, + "X": 898, + "Y": 1114, + "Width": 260, + "Height": 323, + "AdvanceX": 161, + "AdvanceY": 253, + "BearingX": -47, + "BearingY": 255 + }, { + "CharCode": 100, + "X": 637, + "Y": 1114, + "Width": 261, + "Height": 323, + "AdvanceX": 161, + "AdvanceY": 253, + "BearingX": -53, + "BearingY": 255 + }, { + "CharCode": 64, + "X": 318, + "Y": 1114, + "Width": 319, + "Height": 323, + "AdvanceX": 215, + "AdvanceY": 253, + "BearingX": -52, + "BearingY": 213 + }, { + "CharCode": 271, + "X": 0, + "Y": 1114, + "Width": 318, + "Height": 324, + "AdvanceX": 161, + "AdvanceY": 253, + "BearingX": -53, + "BearingY": 256 + }, { + "CharCode": 240, + "X": 7810, + "Y": 752, + "Width": 262, + "Height": 325, + "AdvanceX": 156, + "AdvanceY": 253, + "BearingX": -53, + "BearingY": 257 + }, { + "CharCode": 181, + "X": 7574, + "Y": 752, + "Width": 236, + "Height": 327, + "AdvanceX": 152, + "AdvanceY": 257, + "BearingX": -42, + "BearingY": 201 + }, { + "CharCode": 1073, + "X": 7329, + "Y": 752, + "Width": 245, + "Height": 327, + "AdvanceX": 146, + "AdvanceY": 257, + "BearingX": -49, + "BearingY": 260 + }, { + "CharCode": 1091, + "X": 7076, + "Y": 752, + "Width": 253, + "Height": 327, + "AdvanceX": 125, + "AdvanceY": 257, + "BearingX": -64, + "BearingY": 201 + }, { + "CharCode": 36, + "X": 6816, + "Y": 752, + "Width": 260, + "Height": 327, + "AdvanceX": 152, + "AdvanceY": 253, + "BearingX": -54, + "BearingY": 257 + }, { + "CharCode": 1088, + "X": 6574, + "Y": 752, + "Width": 242, + "Height": 330, + "AdvanceX": 150, + "AdvanceY": 257, + "BearingX": -42, + "BearingY": 204 + }, { + "CharCode": 216, + "X": 6249, + "Y": 752, + "Width": 325, + "Height": 331, + "AdvanceX": 219, + "AdvanceY": 253, + "BearingX": -53, + "BearingY": 259 + }, { + "CharCode": 189, + "X": 5900, + "Y": 752, + "Width": 349, + "Height": 331, + "AdvanceX": 240, + "AdvanceY": 253, + "BearingX": -53, + "BearingY": 258 + }, { + "CharCode": 188, + "X": 5550, + "Y": 752, + "Width": 350, + "Height": 331, + "AdvanceX": 236, + "AdvanceY": 253, + "BearingX": -53, + "BearingY": 258 + }, { + "CharCode": 190, + "X": 5184, + "Y": 752, + "Width": 366, + "Height": 331, + "AdvanceX": 251, + "AdvanceY": 253, + "BearingX": -55, + "BearingY": 258 + }, { + "CharCode": 47, + "X": 4951, + "Y": 752, + "Width": 233, + "Height": 333, + "AdvanceX": 104, + "AdvanceY": 253, + "BearingX": -66, + "BearingY": 256 + }, { + "CharCode": 92, + "X": 4717, + "Y": 752, + "Width": 234, + "Height": 333, + "AdvanceX": 103, + "AdvanceY": 253, + "BearingX": -65, + "BearingY": 256 + }, { + "CharCode": 37, + "X": 4379, + "Y": 752, + "Width": 338, + "Height": 333, + "AdvanceX": 234, + "AdvanceY": 253, + "BearingX": -52, + "BearingY": 259 + }, { + "CharCode": 7843, + "X": 4147, + "Y": 752, + "Width": 232, + "Height": 341, + "AdvanceX": 136, + "AdvanceY": 257, + "BearingX": -52, + "BearingY": 274 + }, { + "CharCode": 7867, + "X": 3909, + "Y": 752, + "Width": 238, + "Height": 341, + "AdvanceX": 137, + "AdvanceY": 257, + "BearingX": -50, + "BearingY": 274 + }, { + "CharCode": 355, + "X": 3697, + "Y": 752, + "Width": 212, + "Height": 349, + "AdvanceX": 95, + "AdvanceY": 253, + "BearingX": -59, + "BearingY": 227 + }, { + "CharCode": 167, + "X": 3460, + "Y": 752, + "Width": 237, + "Height": 349, + "AdvanceX": 153, + "AdvanceY": 253, + "BearingX": -42, + "BearingY": 254 + }, { + "CharCode": 124, + "X": 3314, + "Y": 752, + "Width": 146, + "Height": 354, + "AdvanceX": 68, + "AdvanceY": 253, + "BearingX": -39, + "BearingY": 272 + }, { + "CharCode": 166, + "X": 3168, + "Y": 752, + "Width": 146, + "Height": 354, + "AdvanceX": 68, + "AdvanceY": 253, + "BearingX": -39, + "BearingY": 272 + }, { + "CharCode": 539, + "X": 2956, + "Y": 752, + "Width": 212, + "Height": 357, + "AdvanceX": 95, + "AdvanceY": 253, + "BearingX": -59, + "BearingY": 227 + }, { + "CharCode": 1025, + "X": 2734, + "Y": 752, + "Width": 222, + "Height": 358, + "AdvanceX": 135, + "AdvanceY": 257, + "BearingX": -39, + "BearingY": 294 + }, { + "CharCode": 1062, + "X": 2457, + "Y": 752, + "Width": 277, + "Height": 359, + "AdvanceX": 179, + "AdvanceY": 257, + "BearingX": -39, + "BearingY": 247 + }, { + "CharCode": 1044, + "X": 2170, + "Y": 752, + "Width": 287, + "Height": 359, + "AdvanceX": 166, + "AdvanceY": 257, + "BearingX": -62, + "BearingY": 247 + }, { + "CharCode": 1065, + "X": 1815, + "Y": 752, + "Width": 355, + "Height": 359, + "AdvanceX": 255, + "AdvanceY": 257, + "BearingX": -39, + "BearingY": 247 + }, { + "CharCode": 203, + "X": 1560, + "Y": 752, + "Width": 255, + "Height": 361, + "AdvanceX": 163, + "AdvanceY": 253, + "BearingX": -42, + "BearingY": 297 + }, { + "CharCode": 209, + "X": 1268, + "Y": 752, + "Width": 292, + "Height": 361, + "AdvanceX": 208, + "AdvanceY": 253, + "BearingX": -42, + "BearingY": 296 + }, { + "CharCode": 258, + "X": 959, + "Y": 752, + "Width": 309, + "Height": 361, + "AdvanceX": 187, + "AdvanceY": 253, + "BearingX": -61, + "BearingY": 296 + }, { + "CharCode": 195, + "X": 650, + "Y": 752, + "Width": 309, + "Height": 361, + "AdvanceX": 187, + "AdvanceY": 253, + "BearingX": -61, + "BearingY": 296 + }, { + "CharCode": 304, + "X": 490, + "Y": 752, + "Width": 160, + "Height": 362, + "AdvanceX": 72, + "AdvanceY": 253, + "BearingX": -44, + "BearingY": 297 + }, { + "CharCode": 207, + "X": 274, + "Y": 752, + "Width": 216, + "Height": 362, + "AdvanceX": 72, + "AdvanceY": 253, + "BearingX": -72, + "BearingY": 297 + }, { + "CharCode": 379, + "X": 0, + "Y": 752, + "Width": 274, + "Height": 362, + "AdvanceX": 167, + "AdvanceY": 253, + "BearingX": -53, + "BearingY": 297 + }, { + "CharCode": 376, + "X": 7769, + "Y": 385, + "Width": 292, + "Height": 362, + "AdvanceX": 174, + "AdvanceY": 253, + "BearingX": -59, + "BearingY": 297 + }, { + "CharCode": 196, + "X": 7460, + "Y": 385, + "Width": 309, + "Height": 362, + "AdvanceX": 187, + "AdvanceY": 253, + "BearingX": -61, + "BearingY": 297 + }, { + "CharCode": 282, + "X": 7205, + "Y": 385, + "Width": 255, + "Height": 363, + "AdvanceX": 163, + "AdvanceY": 253, + "BearingX": -42, + "BearingY": 299 + }, { + "CharCode": 202, + "X": 6950, + "Y": 385, + "Width": 255, + "Height": 363, + "AdvanceX": 163, + "AdvanceY": 253, + "BearingX": -42, + "BearingY": 299 + }, { + "CharCode": 381, + "X": 6676, + "Y": 385, + "Width": 274, + "Height": 363, + "AdvanceX": 167, + "AdvanceY": 253, + "BearingX": -53, + "BearingY": 299 + }, { + "CharCode": 356, + "X": 6397, + "Y": 385, + "Width": 279, + "Height": 363, + "AdvanceX": 159, + "AdvanceY": 253, + "BearingX": -60, + "BearingY": 299 + }, { + "CharCode": 270, + "X": 6101, + "Y": 385, + "Width": 296, + "Height": 363, + "AdvanceX": 200, + "AdvanceY": 253, + "BearingX": -42, + "BearingY": 299 + }, { + "CharCode": 65533, + "X": 5738, + "Y": 385, + "Width": 363, + "Height": 363, + "AdvanceX": 256, + "AdvanceY": 257, + "BearingX": -54, + "BearingY": 259 + }, { + "CharCode": 206, + "X": 5526, + "Y": 385, + "Width": 212, + "Height": 364, + "AdvanceX": 72, + "AdvanceY": 253, + "BearingX": -70, + "BearingY": 299 + }, { + "CharCode": 344, + "X": 5255, + "Y": 385, + "Width": 271, + "Height": 364, + "AdvanceX": 172, + "AdvanceY": 253, + "BearingX": -42, + "BearingY": 299 + }, { + "CharCode": 327, + "X": 4963, + "Y": 385, + "Width": 292, + "Height": 364, + "AdvanceX": 208, + "AdvanceY": 253, + "BearingX": -42, + "BearingY": 299 + }, { + "CharCode": 286, + "X": 4663, + "Y": 385, + "Width": 300, + "Height": 364, + "AdvanceX": 200, + "AdvanceY": 253, + "BearingX": -53, + "BearingY": 296 + }, { + "CharCode": 194, + "X": 4354, + "Y": 385, + "Width": 309, + "Height": 364, + "AdvanceX": 187, + "AdvanceY": 253, + "BearingX": -61, + "BearingY": 299 + }, { + "CharCode": 213, + "X": 4029, + "Y": 385, + "Width": 325, + "Height": 364, + "AdvanceX": 219, + "AdvanceY": 253, + "BearingX": -53, + "BearingY": 296 + }, { + "CharCode": 204, + "X": 3845, + "Y": 385, + "Width": 184, + "Height": 365, + "AdvanceX": 72, + "AdvanceY": 253, + "BearingX": -70, + "BearingY": 301 + }, { + "CharCode": 205, + "X": 3660, + "Y": 385, + "Width": 185, + "Height": 365, + "AdvanceX": 72, + "AdvanceY": 253, + "BearingX": -42, + "BearingY": 301 + }, { + "CharCode": 201, + "X": 3405, + "Y": 385, + "Width": 255, + "Height": 365, + "AdvanceX": 163, + "AdvanceY": 253, + "BearingX": -42, + "BearingY": 301 + }, { + "CharCode": 200, + "X": 3150, + "Y": 385, + "Width": 255, + "Height": 365, + "AdvanceX": 163, + "AdvanceY": 253, + "BearingX": -42, + "BearingY": 301 + }, { + "CharCode": 1049, + "X": 2885, + "Y": 385, + "Width": 265, + "Height": 365, + "AdvanceX": 187, + "AdvanceY": 257, + "BearingX": -39, + "BearingY": 301 + }, { + "CharCode": 220, + "X": 2601, + "Y": 385, + "Width": 284, + "Height": 365, + "AdvanceX": 200, + "AdvanceY": 253, + "BearingX": -42, + "BearingY": 297 + }, { + "CharCode": 323, + "X": 2309, + "Y": 385, + "Width": 292, + "Height": 365, + "AdvanceX": 208, + "AdvanceY": 253, + "BearingX": -42, + "BearingY": 301 + }, { + "CharCode": 221, + "X": 2017, + "Y": 385, + "Width": 292, + "Height": 365, + "AdvanceX": 174, + "AdvanceY": 253, + "BearingX": -59, + "BearingY": 301 + }, { + "CharCode": 214, + "X": 1692, + "Y": 385, + "Width": 325, + "Height": 365, + "AdvanceX": 219, + "AdvanceY": 253, + "BearingX": -53, + "BearingY": 297 + }, { + "CharCode": 255, + "X": 1437, + "Y": 385, + "Width": 255, + "Height": 366, + "AdvanceX": 133, + "AdvanceY": 253, + "BearingX": -61, + "BearingY": 240 + }, { + "CharCode": 377, + "X": 1163, + "Y": 385, + "Width": 274, + "Height": 366, + "AdvanceX": 167, + "AdvanceY": 253, + "BearingX": -53, + "BearingY": 302 + }, { + "CharCode": 193, + "X": 854, + "Y": 385, + "Width": 309, + "Height": 366, + "AdvanceX": 187, + "AdvanceY": 253, + "BearingX": -61, + "BearingY": 301 + }, { + "CharCode": 192, + "X": 545, + "Y": 385, + "Width": 309, + "Height": 366, + "AdvanceX": 187, + "AdvanceY": 253, + "BearingX": -61, + "BearingY": 301 + }, { + "CharCode": 352, + "X": 284, + "Y": 385, + "Width": 261, + "Height": 367, + "AdvanceX": 156, + "AdvanceY": 253, + "BearingX": -52, + "BearingY": 299 + }, { + "CharCode": 219, + "X": 0, + "Y": 385, + "Width": 284, + "Height": 367, + "AdvanceX": 200, + "AdvanceY": 253, + "BearingX": -42, + "BearingY": 299 + }, { + "CharCode": 268, + "X": 7835, + "Y": 0, + "Width": 289, + "Height": 367, + "AdvanceX": 181, + "AdvanceY": 253, + "BearingX": -53, + "BearingY": 299 + }, { + "CharCode": 212, + "X": 7510, + "Y": 0, + "Width": 325, + "Height": 367, + "AdvanceX": 219, + "AdvanceY": 253, + "BearingX": -53, + "BearingY": 299 + }, { + "CharCode": 368, + "X": 7226, + "Y": 0, + "Width": 284, + "Height": 369, + "AdvanceX": 200, + "AdvanceY": 253, + "BearingX": -42, + "BearingY": 301 + }, { + "CharCode": 218, + "X": 6942, + "Y": 0, + "Width": 284, + "Height": 369, + "AdvanceX": 200, + "AdvanceY": 253, + "BearingX": -42, + "BearingY": 301 + }, { + "CharCode": 217, + "X": 6658, + "Y": 0, + "Width": 284, + "Height": 369, + "AdvanceX": 200, + "AdvanceY": 253, + "BearingX": -42, + "BearingY": 301 + }, { + "CharCode": 210, + "X": 6333, + "Y": 0, + "Width": 325, + "Height": 369, + "AdvanceX": 219, + "AdvanceY": 253, + "BearingX": -53, + "BearingY": 301 + }, { + "CharCode": 211, + "X": 6008, + "Y": 0, + "Width": 325, + "Height": 369, + "AdvanceX": 219, + "AdvanceY": 253, + "BearingX": -53, + "BearingY": 301 + }, { + "CharCode": 336, + "X": 5683, + "Y": 0, + "Width": 325, + "Height": 369, + "AdvanceX": 219, + "AdvanceY": 253, + "BearingX": -53, + "BearingY": 301 + }, { + "CharCode": 287, + "X": 5423, + "Y": 0, + "Width": 260, + "Height": 370, + "AdvanceX": 160, + "AdvanceY": 253, + "BearingX": -51, + "BearingY": 241 + }, { + "CharCode": 346, + "X": 5162, + "Y": 0, + "Width": 261, + "Height": 370, + "AdvanceX": 156, + "AdvanceY": 253, + "BearingX": -52, + "BearingY": 302 + }, { + "CharCode": 280, + "X": 4899, + "Y": 0, + "Width": 263, + "Height": 370, + "AdvanceX": 163, + "AdvanceY": 253, + "BearingX": -42, + "BearingY": 250 + }, { + "CharCode": 262, + "X": 4610, + "Y": 0, + "Width": 289, + "Height": 370, + "AdvanceX": 181, + "AdvanceY": 253, + "BearingX": -53, + "BearingY": 302 + }, { + "CharCode": 260, + "X": 4301, + "Y": 0, + "Width": 309, + "Height": 370, + "AdvanceX": 187, + "AdvanceY": 253, + "BearingX": -61, + "BearingY": 250 + }, { + "CharCode": 253, + "X": 4046, + "Y": 0, + "Width": 255, + "Height": 371, + "AdvanceX": 133, + "AdvanceY": 253, + "BearingX": -61, + "BearingY": 245 + }, { + "CharCode": 354, + "X": 3767, + "Y": 0, + "Width": 279, + "Height": 372, + "AdvanceX": 159, + "AdvanceY": 253, + "BearingX": -60, + "BearingY": 250 + }, { + "CharCode": 106, + "X": 3577, + "Y": 0, + "Width": 190, + "Height": 376, + "AdvanceX": 65, + "AdvanceY": 253, + "BearingX": -77, + "BearingY": 247 + }, { + "CharCode": 307, + "X": 3351, + "Y": 0, + "Width": 226, + "Height": 376, + "AdvanceX": 129, + "AdvanceY": 253, + "BearingX": -49, + "BearingY": 247 + }, { + "CharCode": 350, + "X": 3090, + "Y": 0, + "Width": 261, + "Height": 376, + "AdvanceX": 156, + "AdvanceY": 253, + "BearingX": -52, + "BearingY": 254 + }, { + "CharCode": 199, + "X": 2801, + "Y": 0, + "Width": 289, + "Height": 376, + "AdvanceX": 181, + "AdvanceY": 253, + "BearingX": -53, + "BearingY": 254 + }, { + "CharCode": 93, + "X": 2623, + "Y": 0, + "Width": 178, + "Height": 379, + "AdvanceX": 83, + "AdvanceY": 253, + "BearingX": -56, + "BearingY": 273 + }, { + "CharCode": 91, + "X": 2445, + "Y": 0, + "Width": 178, + "Height": 379, + "AdvanceX": 83, + "AdvanceY": 253, + "BearingX": -39, + "BearingY": 273 + }, { + "CharCode": 123, + "X": 2245, + "Y": 0, + "Width": 200, + "Height": 379, + "AdvanceX": 92, + "AdvanceY": 253, + "BearingX": -54, + "BearingY": 273 + }, { + "CharCode": 125, + "X": 2045, + "Y": 0, + "Width": 200, + "Height": 379, + "AdvanceX": 92, + "AdvanceY": 253, + "BearingX": -54, + "BearingY": 273 + }, { + "CharCode": 197, + "X": 1736, + "Y": 0, + "Width": 309, + "Height": 379, + "AdvanceX": 187, + "AdvanceY": 253, + "BearingX": -61, + "BearingY": 314 + }, { + "CharCode": 41, + "X": 1549, + "Y": 0, + "Width": 187, + "Height": 380, + "AdvanceX": 75, + "AdvanceY": 253, + "BearingX": -61, + "BearingY": 274 + }, { + "CharCode": 40, + "X": 1362, + "Y": 0, + "Width": 187, + "Height": 380, + "AdvanceX": 75, + "AdvanceY": 253, + "BearingX": -52, + "BearingY": 274 + }, { + "CharCode": 538, + "X": 1083, + "Y": 0, + "Width": 279, + "Height": 380, + "AdvanceX": 159, + "AdvanceY": 253, + "BearingX": -60, + "BearingY": 250 + }, { + "CharCode": 254, + "X": 823, + "Y": 0, + "Width": 260, + "Height": 382, + "AdvanceX": 161, + "AdvanceY": 253, + "BearingX": -47, + "BearingY": 256 + }, { + "CharCode": 366, + "X": 539, + "Y": 0, + "Width": 284, + "Height": 382, + "AdvanceX": 200, + "AdvanceY": 253, + "BearingX": -42, + "BearingY": 314 + }, { + "CharCode": 536, + "X": 278, + "Y": 0, + "Width": 261, + "Height": 384, + "AdvanceX": 156, + "AdvanceY": 253, + "BearingX": -52, + "BearingY": 254 + }, { + "CharCode": 1092, + "X": 0, + "Y": 0, + "Width": 278, + "Height": 385, + "AdvanceX": 178, + "AdvanceY": 257, + "BearingX": -50, + "BearingY": 259 + }] +} diff --git a/Samples/SampleXrFramework/res/raw/efigs_sdf.ktx b/Samples/SampleXrFramework/res/raw/efigs_sdf.ktx new file mode 100644 index 0000000..1f44e5e Binary files /dev/null and b/Samples/SampleXrFramework/res/raw/efigs_sdf.ktx differ diff --git a/Samples/SampleXrFramework/res/raw/loading_indicator.png b/Samples/SampleXrFramework/res/raw/loading_indicator.png new file mode 100644 index 0000000..d45ff95 Binary files /dev/null and b/Samples/SampleXrFramework/res/raw/loading_indicator.png differ diff --git a/Samples/SampleXrFramework/res/raw/sound_assets.json b/Samples/SampleXrFramework/res/raw/sound_assets.json new file mode 100644 index 0000000..324ca69 --- /dev/null +++ b/Samples/SampleXrFramework/res/raw/sound_assets.json @@ -0,0 +1,16 @@ +{ + "Sounds": { + "sv_active_to_drag": "sv_active_to_drag.wav", + "sv_deselect": "sv_deselect.wav", + "sv_focusgained": "sv_focusgained.wav", + "sv_panel_touch_down": "sv_panel_touch_down.wav", + "sv_panel_touch_up": "sv_panel_touch_up.wav", + "sv_release": "sv_release.wav", + "sv_release_active": "sv_release_active.wav", + "sv_select": "sv_select.wav", + "sv_simple_swipe": "sv_simple_swipe.wav", + "sv_swipe": "sv_swipe.wav", + "sv_touch_active": "sv_touch_active.wav", + "sv_touch_inactive": "sv_touch_inactive.wav" + } +} diff --git a/Samples/SampleXrFramework/res/values-el/assets.xml b/Samples/SampleXrFramework/res/values-el/assets.xml new file mode 100644 index 0000000..b9ad4bc --- /dev/null +++ b/Samples/SampleXrFramework/res/values-el/assets.xml @@ -0,0 +1,6 @@ + + + greek.fnt + diff --git a/Samples/SampleXrFramework/res/values-ja/assets.xml b/Samples/SampleXrFramework/res/values-ja/assets.xml new file mode 100644 index 0000000..43b9cc6 --- /dev/null +++ b/Samples/SampleXrFramework/res/values-ja/assets.xml @@ -0,0 +1,6 @@ + + + japanese.fnt + diff --git a/Samples/SampleXrFramework/res/values-ko/assets.xml b/Samples/SampleXrFramework/res/values-ko/assets.xml new file mode 100644 index 0000000..6502d8c --- /dev/null +++ b/Samples/SampleXrFramework/res/values-ko/assets.xml @@ -0,0 +1,6 @@ + + + korean.fnt + diff --git a/Samples/SampleXrFramework/res/values-zh-rCN/assets.xml b/Samples/SampleXrFramework/res/values-zh-rCN/assets.xml new file mode 100644 index 0000000..43b9cc6 --- /dev/null +++ b/Samples/SampleXrFramework/res/values-zh-rCN/assets.xml @@ -0,0 +1,6 @@ + + + japanese.fnt + diff --git a/Samples/SampleXrFramework/res/values-zh-rHK/assets.xml b/Samples/SampleXrFramework/res/values-zh-rHK/assets.xml new file mode 100644 index 0000000..43b9cc6 --- /dev/null +++ b/Samples/SampleXrFramework/res/values-zh-rHK/assets.xml @@ -0,0 +1,6 @@ + + + japanese.fnt + diff --git a/Samples/SampleXrFramework/res/values-zh-rTW/assets.xml b/Samples/SampleXrFramework/res/values-zh-rTW/assets.xml new file mode 100644 index 0000000..43b9cc6 --- /dev/null +++ b/Samples/SampleXrFramework/res/values-zh-rTW/assets.xml @@ -0,0 +1,6 @@ + + + japanese.fnt + diff --git a/Samples/SampleXrFramework/res/values/assets.xml b/Samples/SampleXrFramework/res/values/assets.xml new file mode 100644 index 0000000..c015a12 --- /dev/null +++ b/Samples/SampleXrFramework/res/values/assets.xml @@ -0,0 +1,6 @@ + + + efigs.fnt + diff --git a/Samples/XrSamples/CMakeLists.txt b/Samples/XrSamples/CMakeLists.txt new file mode 100755 index 0000000..02a9ea3 --- /dev/null +++ b/Samples/XrSamples/CMakeLists.txt @@ -0,0 +1,12 @@ +# Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved. +# Get all directories in the current directory +file(GLOB children RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/*) + +# Loop through the list of directories +foreach(child ${children}) + # If the current item is a directory + if(IS_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/${child}) + # Add it as a subdirectory + add_subdirectory(${child}) + endif() +endforeach() diff --git a/Samples/XrSamples/XrBodyFaceEyeSocial/CMakeLists.txt b/Samples/XrSamples/XrBodyFaceEyeSocial/CMakeLists.txt new file mode 100755 index 0000000..3ffa333 --- /dev/null +++ b/Samples/XrSamples/XrBodyFaceEyeSocial/CMakeLists.txt @@ -0,0 +1,43 @@ +# Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved. + +project(xrbodyfaceeyesocial) + +if(NOT TARGET OpenXR::openxr_loader) + find_package(OpenXR REQUIRED) +endif() + +file(GLOB_RECURSE SRC_FILES + Src/*.c + Src/*.cpp +) + +if(ANDROID) + add_library(${PROJECT_NAME} MODULE ${SRC_FILES}) + target_include_directories(${PROJECT_NAME} PUBLIC ${ANDROID_NDK}/sources/android/native_app_glue) + target_link_libraries(${PROJECT_NAME} PRIVATE + android + EGL + GLESv3 + log + ktx + ) + set_target_properties(${PROJECT_NAME} PROPERTIES LINK_FLAGS "-u ANativeActivity_onCreate") +elseif(WIN32) + add_definitions(-D_USE_MATH_DEFINES) + add_executable(${PROJECT_NAME} ${SRC_FILES}) + add_custom_command(TARGET ${PROJECT_NAME} PRE_BUILD + COMMAND "${CMAKE_COMMAND}" -E copy_directory + "${CMAKE_CURRENT_LIST_DIR}/assets" + "$/assets" + VERBATIM) + + add_custom_command(TARGET ${PROJECT_NAME} PRE_BUILD + COMMAND "${CMAKE_COMMAND}" -E copy_directory + "${CMAKE_SOURCE_DIR}/SampleXrFramework/res/raw" + "$/font/res/raw" + VERBATIM) +endif() + +# Common across platforms +target_include_directories(${PROJECT_NAME} PRIVATE Src) +target_link_libraries(${PROJECT_NAME} PRIVATE samplexrframework) diff --git a/Samples/XrSamples/XrBodyFaceEyeSocial/Projects/Android/AndroidManifest.xml b/Samples/XrSamples/XrBodyFaceEyeSocial/Projects/Android/AndroidManifest.xml new file mode 100755 index 0000000..c77b8f8 --- /dev/null +++ b/Samples/XrSamples/XrBodyFaceEyeSocial/Projects/Android/AndroidManifest.xml @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Samples/XrSamples/XrBodyFaceEyeSocial/Projects/Android/build.bat b/Samples/XrSamples/XrBodyFaceEyeSocial/Projects/Android/build.bat new file mode 100755 index 0000000..facf79f --- /dev/null +++ b/Samples/XrSamples/XrBodyFaceEyeSocial/Projects/Android/build.bat @@ -0,0 +1,29 @@ +@rem Only edit the master copy of this file in SDK_ROOT/bin/scripts/build/perproject + +@setlocal enableextensions enabledelayedexpansion + +@if not exist "build.gradle" @echo Build script must be executed from project directory. & goto :Abort + +@set P=.. + +:TryAgain + +@rem @echo P = %P% + +@if exist "%P%\bin\scripts\build\build.py.bat" goto :Found + +@if exist "%P%\bin\scripts\build" @echo "Could not find build.py.bat" & goto :Abort + +@set P=%P%\.. + +@goto :TryAgain + +:Found + +@set P=%P%\bin\scripts\build +@call %P%\build.py.bat %1 %2 %3 %4 %5 +@goto :End + +:Abort + +:End diff --git a/Samples/XrSamples/XrBodyFaceEyeSocial/Projects/Android/build.gradle b/Samples/XrSamples/XrBodyFaceEyeSocial/Projects/Android/build.gradle new file mode 100755 index 0000000..5df17da --- /dev/null +++ b/Samples/XrSamples/XrBodyFaceEyeSocial/Projects/Android/build.gradle @@ -0,0 +1,78 @@ +// Top-level build file where you can add configuration options common to all sub-projects/modules. +buildscript { + repositories { + google() + mavenCentral() + } + dependencies { + classpath "com.android.tools.build:gradle:7.0.3" + } +} + +repositories { + google() + mavenCentral() +} + +apply plugin: 'com.android.application' + +android { + compileSdk 32 + + defaultConfig { + applicationId "com.oculus.xrsamples.xrbodyfaceeyesocial" + minSdk 26 + targetSdk 32 + versionCode 1 + versionName "1.0" + + // override app plugin abiFilters for 64-bit support + externalNativeBuild { + ndk { + abiFilters 'arm64-v8a' + } + ndkBuild { + abiFilters 'arm64-v8a' + } + cmake { + targets "xrbodyfaceeyesocial" + } + } + } + + sourceSets { + main { + manifest.srcFile 'AndroidManifest.xml' + java.srcDirs = ['../../java'] + assets.srcDirs = ['../../assets'] + res.srcDirs = ['../../res'] + } + } + + buildTypes { + debug { + debuggable true + } + + release { + debuggable false + } + } + + externalNativeBuild { + cmake { + path file('../../../../CMakeLists.txt') + } + } + + // Enable prefab support for the OpenXR AAR + buildFeatures { + prefab true + } +} + +dependencies { + // Package/application AndroidManifest.xml properties, plus headers and libraries + // exposed to CMake + implementation 'org.khronos.openxr:openxr_loader_for_android:1.1.36' +} diff --git a/Samples/XrSamples/XrBodyFaceEyeSocial/Projects/Android/build.py b/Samples/XrSamples/XrBodyFaceEyeSocial/Projects/Android/build.py new file mode 100755 index 0000000..d4b6e58 --- /dev/null +++ b/Samples/XrSamples/XrBodyFaceEyeSocial/Projects/Android/build.py @@ -0,0 +1,35 @@ +#!/usr/bin/python +# (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +# This first bit of code is common bootstrapping code +# to determine the SDK root, and to set up the import +# path for additional python code. + +# begin bootstrap +import os +import sys + + +def init(): + root = os.path.realpath(os.path.dirname(os.path.realpath(__file__))) + os.chdir(root) # make sure we are always executing from the project directory + while os.path.isdir(os.path.join(root, "bin/scripts/build")) == False: + root = os.path.realpath(os.path.join(root, "..")) + if ( + len(root) <= 5 + ): # Should catch both Posix and Windows root directories (e.g. '/' and 'C:\') + print("Unable to find SDK root. Exiting.") + sys.exit(1) + root = os.path.abspath(root) + os.environ["OCULUS_SDK_PATH"] = root + sys.path.append(root + "/bin/scripts/build") + + +init() +import ovrbuild + +ovrbuild.init() +# end bootstrap + + +ovrbuild.build() diff --git a/Samples/XrSamples/XrBodyFaceEyeSocial/Projects/Android/gradle.properties b/Samples/XrSamples/XrBodyFaceEyeSocial/Projects/Android/gradle.properties new file mode 100644 index 0000000..3e927b1 --- /dev/null +++ b/Samples/XrSamples/XrBodyFaceEyeSocial/Projects/Android/gradle.properties @@ -0,0 +1,21 @@ +# Project-wide Gradle settings. +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. More details, visit +# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects +# org.gradle.parallel=true +# AndroidX package structure to make it clearer which packages are bundled with the +# Android operating system, and which are packaged with your app's APK +# https://developer.android.com/topic/libraries/support-library/androidx-rn +android.useAndroidX=true +# Enables namespacing of each library's R class so that its R class includes only the +# resources declared in the library itself and none from the library's dependencies, +# thereby reducing the size of the R class for that library +android.nonTransitiveRClass=true \ No newline at end of file diff --git a/Samples/XrSamples/XrBodyFaceEyeSocial/Projects/Android/gradle/wrapper/gradle-wrapper.jar b/Samples/XrSamples/XrBodyFaceEyeSocial/Projects/Android/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..7454180 Binary files /dev/null and b/Samples/XrSamples/XrBodyFaceEyeSocial/Projects/Android/gradle/wrapper/gradle-wrapper.jar differ diff --git a/Samples/XrSamples/XrBodyFaceEyeSocial/Projects/Android/gradle/wrapper/gradle-wrapper.properties b/Samples/XrSamples/XrBodyFaceEyeSocial/Projects/Android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..ffed3a2 --- /dev/null +++ b/Samples/XrSamples/XrBodyFaceEyeSocial/Projects/Android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/Samples/XrSamples/XrBodyFaceEyeSocial/Projects/Android/gradlew b/Samples/XrSamples/XrBodyFaceEyeSocial/Projects/Android/gradlew new file mode 100755 index 0000000..a69d9cb --- /dev/null +++ b/Samples/XrSamples/XrBodyFaceEyeSocial/Projects/Android/gradlew @@ -0,0 +1,240 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# 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 +# +# https://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. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit + +APP_NAME="Gradle" +APP_BASE_NAME=${0##*/} + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/Samples/XrSamples/XrBodyFaceEyeSocial/Projects/Android/gradlew.bat b/Samples/XrSamples/XrBodyFaceEyeSocial/Projects/Android/gradlew.bat new file mode 100755 index 0000000..f127cfd --- /dev/null +++ b/Samples/XrSamples/XrBodyFaceEyeSocial/Projects/Android/gradlew.bat @@ -0,0 +1,91 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/Samples/XrSamples/XrBodyFaceEyeSocial/Projects/Android/settings.gradle b/Samples/XrSamples/XrBodyFaceEyeSocial/Projects/Android/settings.gradle new file mode 100755 index 0000000..2fb346d --- /dev/null +++ b/Samples/XrSamples/XrBodyFaceEyeSocial/Projects/Android/settings.gradle @@ -0,0 +1 @@ +rootProject.name = "XrBodyFaceEyeSocial" diff --git a/Samples/XrSamples/XrBodyFaceEyeSocial/README.md b/Samples/XrSamples/XrBodyFaceEyeSocial/README.md new file mode 100644 index 0000000..7153efe --- /dev/null +++ b/Samples/XrSamples/XrBodyFaceEyeSocial/README.md @@ -0,0 +1,11 @@ +# OpenXR Body, Face, Eye Social Sample + +## Overview +The extensions XR_FB_body_tracking, XR_FB_eye_tracking_social, and XR_FB_face_tracking2 are designed to work together to support querying devices for information associated with the body, allowing the rendering of a user's avatar. + +XR_FB_body_tracking allows applications to get poses of body joints. XR_FB_face_tracking2 enables applications to capture facial expressions. XR_FB_eye_tracking_social provides applications with eye-tracking information for social/avatar use cases. + +## The Sample +This sample demonstrates how Body Tracking, Eye Tracking, and Face Tracking work together. +![Body Tracking](images/body_tracking.png) The two cones point to the direction the eyes are looking at. The skeleton shows the tracked body movement. +Expressions are detected and displayed in the text box, which defaults to "neutral expression". diff --git a/Samples/XrSamples/XrBodyFaceEyeSocial/Src/main.cpp b/Samples/XrSamples/XrBodyFaceEyeSocial/Src/main.cpp new file mode 100755 index 0000000..9ae60f0 --- /dev/null +++ b/Samples/XrSamples/XrBodyFaceEyeSocial/Src/main.cpp @@ -0,0 +1,577 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/******************************************************************************* + +Filename : Main.cpp +Content : Simple sample app to test body / face / eye social extensions +Created : September 2022 +Authors : John Kearney + +*******************************************************************************/ + +#include +#include +#include + +#include "XrApp.h" + +#include "Input/SkeletonRenderer.h" +#include "Input/ControllerRenderer.h" +#include "Input/TinyUI.h" +#include "Input/AxisRenderer.h" +#include "Render/SimpleBeamRenderer.h" +#include "Render/GeometryRenderer.h" + +class XrBodyFaceEyeSocialApp : public OVRFW::XrApp { + public: + static constexpr std::string_view sampleExplanation = + "OpenXR Body / Face / Eye Social SDK Sample \n" + "\n" + "The extensions XR_FB_body_tracking; XR_FB_eye_tracking_social and \n" + "XR_FB_face_tracking2 are designed to work together to support \n" + "querying devices for information associated with the body to allow\n" + "to render an avatar of the user. \n" + "\n" + "XR_FB_body_tracking allow applications to get poses of body joints.\n" + "XR_FB_face_tracking2 allows applications to get facial expressions.\n" + "XR_FB_eye_tracking_social allows applications to get eye tracking \n" + "information for social / avatar use-cases. \n"; + + XrBodyFaceEyeSocialApp() : OVRFW::XrApp() { + BackgroundColor = OVR::Vector4f(0.60f, 0.95f, 0.4f, 1.0f); + } + + // Returns a list of OpenXr extensions needed for this app + virtual std::vector GetExtensions() override { + std::vector extensions = XrApp::GetExtensions(); + extensions.push_back(XR_FB_BODY_TRACKING_EXTENSION_NAME); + extensions.push_back(XR_FB_EYE_TRACKING_SOCIAL_EXTENSION_NAME); + extensions.push_back(XR_FB_FACE_TRACKING2_EXTENSION_NAME); + return extensions; + } + + // Must return true if the application initializes successfully. + virtual bool AppInit(const xrJava* context) override { + if (false == ui_.Init(context, GetFileSys())) { + ALOG("TinyUI::Init FAILED."); + return false; + } + + XrSystemEyeTrackingPropertiesFB eyeTrackingSystemProperties{ + XR_TYPE_SYSTEM_EYE_TRACKING_PROPERTIES_FB}; + XrSystemBodyTrackingPropertiesFB bodyTrackingSystemProperties{ + XR_TYPE_SYSTEM_BODY_TRACKING_PROPERTIES_FB}; + XrSystemFaceTrackingProperties2FB faceTrackingSystemProperties2 { + XR_TYPE_SYSTEM_FACE_TRACKING_PROPERTIES2_FB}; + + XrSystemProperties systemProperties{XR_TYPE_SYSTEM_PROPERTIES}; + systemProperties.next = &eyeTrackingSystemProperties; + eyeTrackingSystemProperties.next = &bodyTrackingSystemProperties; + bodyTrackingSystemProperties.next = &faceTrackingSystemProperties2; + + OXR(xrGetSystemProperties(GetInstance(), GetSystemId(), &systemProperties)); + + if (!bodyTrackingSystemProperties.supportsBodyTracking) { + // The system does not support body tracking + ALOG("xrGetSystemProperties XR_TYPE_SYSTEM_BODY_TRACKING_PROPERTIES_FB FAILED."); + } else { + ALOG( + "xrGetSystemProperties XR_TYPE_SYSTEM_BODY_TRACKING_PROPERTIES_FB OK - initializing body tracking..."); + /// Hook up extensions for body tracking + OXR(xrGetInstanceProcAddr( + GetInstance(), + "xrCreateBodyTrackerFB", + (PFN_xrVoidFunction*)(&xrCreateBodyTrackerFB_))); + OXR(xrGetInstanceProcAddr( + GetInstance(), + "xrDestroyBodyTrackerFB", + (PFN_xrVoidFunction*)(&xrDestroyBodyTrackerFB_))); + OXR(xrGetInstanceProcAddr( + GetInstance(), + "xrLocateBodyJointsFB", + (PFN_xrVoidFunction*)(&xrLocateBodyJointsFB_))); + OXR(xrGetInstanceProcAddr( + GetInstance(), "xrGetBodySkeletonFB", (PFN_xrVoidFunction*)(&xrGetSkeletonFB_))); + } + + if (!eyeTrackingSystemProperties.supportsEyeTracking) { + // The system does not support eye tracking + ALOG("xrGetSystemProperties XR_TYPE_SYSTEM_EYE_TRACKING_PROPERTIES_FB FAILED."); + } else { + ALOG( + "xrGetSystemProperties XR_TYPE_SYSTEM_EYE_TRACKING_PROPERTIES_FB OK - initializing eye tracking..."); + /// Hook up extensions for eye tracking + OXR(xrGetInstanceProcAddr( + GetInstance(), + "xrCreateEyeTrackerFB", + (PFN_xrVoidFunction*)(&xrCreateEyeTrackerFB_))); + OXR(xrGetInstanceProcAddr( + GetInstance(), + "xrDestroyEyeTrackerFB", + (PFN_xrVoidFunction*)(&xrDestroyEyeTrackerFB_))); + OXR(xrGetInstanceProcAddr( + GetInstance(), "xrGetEyeGazesFB", (PFN_xrVoidFunction*)(&xrGetEyeGazesFB_))); + } + + if (faceTrackingSystemProperties2.supportsAudioFaceTracking || faceTrackingSystemProperties2.supportsVisualFaceTracking) { + ALOG( + "xrGetSystemProperties XR_TYPE_SYSTEM_FACE_TRACKING_PROPERTIES2_FB OK - tongue and audio-driven face tracking are supported."); + /// Hook up extensions for face tracking (v2) + OXR(xrGetInstanceProcAddr( + GetInstance(), + "xrCreateFaceTracker2FB", + (PFN_xrVoidFunction*)(&xrCreateFaceTracker2FB_))); + OXR(xrGetInstanceProcAddr( + GetInstance(), + "xrDestroyFaceTracker2FB", + (PFN_xrVoidFunction*)(&xrDestroyFaceTracker2FB_))); + OXR(xrGetInstanceProcAddr( + GetInstance(), + "xrGetFaceExpressionWeights2FB", + (PFN_xrVoidFunction*)(&xrGetFaceExpressionWeights2FB_))); + } else { + // The system does not support face tracking + ALOG("xrGetSystemProperties XR_TYPE_SYSTEM_FACE_TRACKING_PROPERTIES_FB FAILED."); + } + + return true; + } + + virtual void AppShutdown(const xrJava* context) override { + /// unhook extensions for body tracking + xrCreateBodyTrackerFB_ = nullptr; + xrDestroyBodyTrackerFB_ = nullptr; + xrLocateBodyJointsFB_ = nullptr; + xrGetSkeletonFB_ = nullptr; + + /// unhook extensions for eye tracking + xrCreateEyeTrackerFB_ = nullptr; + xrDestroyEyeTrackerFB_ = nullptr; + xrGetEyeGazesFB_ = nullptr; + + /// unhook extensions for face tracking (v2) + xrCreateFaceTracker2FB_ = nullptr; + xrDestroyFaceTracker2FB_ = nullptr; + xrGetFaceExpressionWeights2FB_ = nullptr; + + OVRFW::XrApp::AppShutdown(context); + ui_.Shutdown(); + } + + virtual bool SessionInit() override { + CreateSampleDescriptionPanel(); + + /// Disable scene navitgation + GetScene().SetFootPos({0.0f, 0.0f, 0.0f}); + this->FreeMove = false; + /// Init session bound objects + if (false == controllerRenderL_.Init(true)) { + ALOG("AppInit::Init L controller renderer FAILED."); + return false; + } + if (false == controllerRenderR_.Init(false)) { + ALOG("AppInit::Init R controller renderer FAILED."); + return false; + } + beamRenderer_.Init(GetFileSys(), nullptr, OVR::Vector4f(1.0f), 1.0f); + + { + // We want to draw our body in front of us so that we can see what we look like + XrQuaternionf bodyOrientation{}; + XrVector3f upAxis{0, 1, 0}; + XrQuaternionf_CreateFromAxisAngle(&bodyOrientation, &upAxis, 120 * MATH_PI / 180); + + XrReferenceSpaceCreateInfo spaceCreateInfo{XR_TYPE_REFERENCE_SPACE_CREATE_INFO}; + spaceCreateInfo.referenceSpaceType = XR_REFERENCE_SPACE_TYPE_STAGE; + spaceCreateInfo.poseInReferenceSpace.position.z = 1.0f; + spaceCreateInfo.poseInReferenceSpace.orientation = bodyOrientation; + OXR(xrCreateReferenceSpace(Session, &spaceCreateInfo, &bodySpace)); + } + + if (xrCreateBodyTrackerFB_) { + XrBodyTrackerCreateInfoFB createInfo{XR_TYPE_BODY_TRACKER_CREATE_INFO_FB}; + createInfo.bodyJointSet = XR_BODY_JOINT_SET_DEFAULT_FB; + OXR(xrCreateBodyTrackerFB_(GetSession(), &createInfo, &bodyTracker_)); + ALOG("xrCreateBodyTrackerFB bodyTracker_=%llx", (long long)bodyTracker_); + } + + if (xrCreateEyeTrackerFB_) { + XrEyeTrackerCreateInfoFB createInfo{XR_TYPE_EYE_TRACKER_CREATE_INFO_FB}; + OXR(xrCreateEyeTrackerFB_(GetSession(), &createInfo, &eyeTracker_)); + ALOG("xrCreateEyeTrackerFB eyeTracker_=%llx", (long long)eyeTracker_); + } + + if (xrCreateFaceTracker2FB_) { + // Request face tracking data from visual or audio data source + XrFaceTrackerCreateInfo2FB createInfo{XR_TYPE_FACE_TRACKER_CREATE_INFO2_FB}; + createInfo.faceExpressionSet = XR_FACE_EXPRESSION_SET2_DEFAULT_FB; + createInfo.requestedDataSourceCount = 2; + XrFaceTrackingDataSource2FB dataSources[2] = { + XR_FACE_TRACKING_DATA_SOURCE2_VISUAL_FB, + XR_FACE_TRACKING_DATA_SOURCE2_AUDIO_FB}; + createInfo.requestedDataSources = dataSources; + OXR(xrCreateFaceTracker2FB_(GetSession(), &createInfo, &faceTracker2_)); + ALOG("xrCreateFaceTracker2FB faceTracker2_=%llx", (long long)faceTracker2_); + } + + /// Body rendering + axisRenderer_.Init(); + + // Skip root and hips + bodySkeletonRenderers.resize(XR_BODY_JOINT_COUNT_FB - 2); + + eyeRenderers.resize(2); + for (auto& gr : eyeRenderers) { + gr.Init(OVRFW::BuildTesselatedConeDescriptor(0.02f, 0.03f, 7, 7, 0.01, 0.01)); + gr.DiffuseColor = eyeColor_; + } + + // Label for mouth + mouthLabel_ = ui_.AddLabel( + EmojiExpressionString[(size_t)emojiExpression_], {2.0f, 0.5f, -1.5f}, {250.0f, 100.0f}); + mouthLabel_->SetLocalRotation( + OVR::Quat::FromRotationVector({0, OVR::DegreeToRad(-45.0f), 0})); + + return true; + } + + virtual void SessionEnd() override { + if (xrDestroyBodyTrackerFB_) { + OXR(xrDestroyBodyTrackerFB_(bodyTracker_)); + } + if (xrDestroyEyeTrackerFB_) { + OXR(xrDestroyEyeTrackerFB_(eyeTracker_)); + } + if (xrDestroyFaceTracker2FB_) { + OXR(xrDestroyFaceTracker2FB_(faceTracker2_)); + } + + controllerRenderL_.Shutdown(); + controllerRenderR_.Shutdown(); + ui_.Shutdown(); + beamRenderer_.Shutdown(); + axisRenderer_.Shutdown(); + + for (auto& gr : bodySkeletonRenderers) { + gr.Shutdown(); + } + for (auto& gr : eyeRenderers) { + gr.Shutdown(); + } + } + + // Update state + virtual void Update(const OVRFW::ovrApplFrameIn& in) override { + auto isValid = [](XrSpaceLocationFlags flags) -> bool { + constexpr XrSpaceLocationFlags isValid = + XR_SPACE_LOCATION_ORIENTATION_VALID_BIT | XR_SPACE_LOCATION_POSITION_VALID_BIT; + return (flags & isValid) != 0; + }; + + ui_.HitTestDevices().clear(); + + /// Body + if (bodyTracker_ != XR_NULL_HANDLE) { + XrBodyJointLocationsFB locations{XR_TYPE_BODY_JOINT_LOCATIONS_FB}; + locations.jointCount = XR_BODY_JOINT_COUNT_FB; + locations.jointLocations = jointLocations_; + + XrBodySkeletonFB skeleton{XR_TYPE_BODY_SKELETON_FB}; + skeleton.jointCount = XR_BODY_JOINT_COUNT_FB; + skeleton.joints = skeletonJoints_; + + XrBodyJointsLocateInfoFB locateInfo{XR_TYPE_BODY_JOINTS_LOCATE_INFO_FB}; + locateInfo.baseSpace = bodySpace; + locateInfo.time = ToXrTime(in.PredictedDisplayTime); + + OXR(xrLocateBodyJointsFB_(bodyTracker_, &locateInfo, &locations)); + + if (locations.skeletonChangedCount != skeletonChangeCount_) { + skeletonChangeCount_ = locations.skeletonChangedCount; + ALOG("BodySkeleton: skeleton proportions have changed."); + + OXR(xrGetSkeletonFB_(bodyTracker_, &skeleton)); + // Skip root and hips + for (int i = 2; i < XR_BODY_JOINT_COUNT_FB; ++i) { + const auto fromJoint = jointLocations_[skeleton.joints[i].parentJoint]; + const auto toJoint = jointLocations_[skeleton.joints[i].joint]; + + // calculation length and orientation between this two points? + const auto p0 = FromXrVector3f(fromJoint.pose.position); + const auto p1 = FromXrVector3f(toJoint.pose.position); + const auto d = (p1 - p0); + + const float h = d.Length(); + + // Skip root and hips + OVRFW::GeometryRenderer& gr = bodySkeletonRenderers[i - 2]; + gr.Shutdown(); + gr.Init(OVRFW::BuildTesselatedCapsuleDescriptor(0.01f, h, 7, 7)); + gr.DiffuseColor = jointColor_; + } + } + + std::vector bodyJoints; + if (locations.isActive) { + // Tracked joints and computed joints can all be valid (or not) + bodyTracked_ = true; + + for (int i = 0; i < XR_BODY_JOINT_COUNT_FB; ++i) { + if (isValid(jointLocations_[i].locationFlags)) { + bodyJoints.push_back(FromXrPosef(jointLocations_[i].pose)); + } + } + + // Display hierarchy + if (skeletonChangeCount_ != 0) { + // Skip root and hips + for (int i = 2; i < XR_BODY_JOINT_COUNT_FB; ++i) { + const auto fromJoint = jointLocations_[skeleton.joints[i].parentJoint]; + const auto toJoint = jointLocations_[skeleton.joints[i].joint]; + + if (isValid(fromJoint.locationFlags) && isValid(toJoint.locationFlags)) { + // calculation length and orientation between this two points? + const auto p0 = FromXrVector3f(fromJoint.pose.position); + const auto p1 = FromXrVector3f(toJoint.pose.position); + const auto d = (p1 - p0); + + const OVR::Quatf look = OVR::Quatf::LookRotation(d.Normalized(), {0, 1, 0}); + /// apply inverse scale here + const float h = d.Length(); + const OVR::Vector3f start = p0 + look.Rotate(OVR::Vector3f(0, 0, -h / 2)); + + // Skip root and hips + OVRFW::GeometryRenderer& gr = bodySkeletonRenderers[i - 2]; + gr.SetScale({1, 1, 1}); + gr.SetPose(OVR::Posef(look, start)); + gr.Update(); + } + } + } + } + + axisRenderer_.Update(bodyJoints); + } + + /// Head position + XrSpaceLocation viewLocation{XR_TYPE_SPACE_LOCATION}; + OXR(xrLocateSpace(HeadSpace, bodySpace, ToXrTime(in.PredictedDisplayTime), &viewLocation)); + + /// Eyes + if (eyeTracker_ != XR_NULL_HANDLE) { + if ((viewLocation.locationFlags & XR_SPACE_LOCATION_POSITION_VALID_BIT) != 0 && + (viewLocation.locationFlags & XR_SPACE_LOCATION_ORIENTATION_VALID_BIT) != 0) { + // NOTE: + // The returned eye poses (position and rotation) in `XrEyeGazesFB` struct may be + // from an older timestamp than the `time` requested in the `XrEyeGazesInfoFB` or + // the latest head/body pose. We can see the timestamp of the (output) eye poses + // in the (output) `time` field of the `XrEyeGazesFB` struct. + // + // The simpliest way of synchronising the eye positions with the head/body poses + // (and therefore avoiding jitter in the eye poses during head/body movements) is + // to request (the eye) poses in the head (VIEW) space and transform the result + // to the head/body space later. + OVR::Posef headPose = FromXrPosef(viewLocation.pose); + + XrEyeGazesFB eyeGazes{XR_TYPE_EYE_GAZES_FB}; + + XrEyeGazesInfoFB gazesInfo{XR_TYPE_EYE_GAZES_INFO_FB}; + gazesInfo.baseSpace = HeadSpace; + gazesInfo.time = ToXrTime(in.PredictedDisplayTime); + + OXR(xrGetEyeGazesFB_(eyeTracker_, &gazesInfo, &eyeGazes)); + + static_assert(std::size(eyeGazes.gaze) == 2); + for (size_t eye = 0; eye < std::size(eyeGazes.gaze); ++eye) { + const OVR::Posef pose = headPose * FromXrPosef(eyeGazes.gaze[eye].gazePose); + OVRFW::GeometryRenderer& gr = eyeRenderers[eye]; + gr.SetScale({1, 1, 1}); + gr.SetPose(pose); + gr.Update(); + } + } + } + + // NOTE - The following logic is used only to showcase how face tracking works. + // It is NOT intended to be used as a reliable mechanism to detect facial expressions. + auto simpleExpression2 = + [](const XrFaceExpressionWeights2FB& expressionWeights) -> EmojiExpression { + // If source is audio and any of the weights are non-zero, then audio-driven expression. + if (expressionWeights.dataSource == XR_FACE_TRACKING_DATA_SOURCE2_AUDIO_FB) { + for (int i = 0; i < XR_FACE_EXPRESSION2_COUNT_FB; ++i) { + if (expressionWeights.weights[i] > 0.01) { + return EmojiExpression::AudioDriven; + } + } + return EmojiExpression::Neutral; + } else if (expressionWeights.weights[XR_FACE_EXPRESSION2_LIP_CORNER_PULLER_L_FB] > 0.5 && + expressionWeights.weights[XR_FACE_EXPRESSION2_LIP_CORNER_PULLER_R_FB] > 0.5) { + return EmojiExpression::Smile; + } else if ( + expressionWeights.weights[XR_FACE_EXPRESSION2_LIP_PUCKER_L_FB] > 0.25 && + expressionWeights.weights[XR_FACE_EXPRESSION2_LIP_PUCKER_R_FB] > 0.25) { + return EmojiExpression::Kiss; + } else if (expressionWeights.weights[XR_FACE_EXPRESSION2_TONGUE_OUT_FB] > 0.5) { + return EmojiExpression::TongueOut; + } else { + return EmojiExpression::Neutral; + } + }; + + /// Face + if (faceTracker2_ != XR_NULL_HANDLE) { + float weights_[XR_FACE_EXPRESSION2_COUNT_FB] = {}; + float confidence_[XR_FACE_CONFIDENCE2_COUNT_FB] = {}; + + XrFaceExpressionWeights2FB expressionWeights{XR_TYPE_FACE_EXPRESSION_WEIGHTS2_FB}; + expressionWeights.next = nullptr; + expressionWeights.weights = weights_; + expressionWeights.confidences = confidence_; + expressionWeights.weightCount = XR_FACE_EXPRESSION2_COUNT_FB; + expressionWeights.confidenceCount = XR_FACE_CONFIDENCE2_COUNT_FB; + + XrFaceExpressionInfo2FB expressionInfo{XR_TYPE_FACE_EXPRESSION_INFO2_FB}; + expressionInfo.time = ToXrTime(in.PredictedDisplayTime); + + OXR(xrGetFaceExpressionWeights2FB_(faceTracker2_, &expressionInfo, &expressionWeights)); + + emojiExpression_ = simpleExpression2(expressionWeights); + + mouthLabel_->SetText(EmojiExpressionString[(size_t)emojiExpression_]); + } + + if (in.LeftRemoteTracked) { + controllerRenderL_.Update(in.LeftRemotePose); + const bool didPinch = in.LeftRemoteIndexTrigger > 0.5f; + ui_.AddHitTestRay(in.LeftRemotePointPose, didPinch); + } + if (in.RightRemoteTracked) { + controllerRenderR_.Update(in.RightRemotePose); + const bool didPinch = in.RightRemoteIndexTrigger > 0.5f; + ui_.AddHitTestRay(in.RightRemotePointPose, didPinch); + } + + ui_.Update(in); + beamRenderer_.Update(in, ui_.HitTestDevices()); + } + + // Render eye buffers while running + virtual void Render(const OVRFW::ovrApplFrameIn& in, OVRFW::ovrRendererOutput& out) override { + /// Render UI + ui_.Render(in, out); + + /// Render controllers + if (in.LeftRemoteTracked) { + controllerRenderL_.Render(out.Surfaces); + } + if (in.RightRemoteTracked) { + controllerRenderR_.Render(out.Surfaces); + } + + /// Render body + if (bodyTracked_) { + axisRenderer_.Render(OVR::Matrix4f(), in, out); + + for (auto& gr : bodySkeletonRenderers) { + gr.Render(out.Surfaces); + } + } + + /// Render eyes + axisRenderer_.Render(OVR::Matrix4f(), in, out); + + for (auto& gr : eyeRenderers) { + gr.Render(out.Surfaces); + } + + /// Render beams + beamRenderer_.Render(in, out); + } + + void CreateSampleDescriptionPanel() { + // Panel to provide sample description to the user for context + auto descriptionLabel = ui_.AddLabel( + static_cast(sampleExplanation), {2.0f, 1.5f, -1.5f}, {950.0f, 600.0f}); + + // Align and size the description text for readability + OVRFW::VRMenuFontParms fontParams{}; + fontParams.Scale = 0.5f; + fontParams.AlignHoriz = OVRFW::HORIZONTAL_LEFT; + descriptionLabel->SetFontParms(fontParams); + descriptionLabel->SetTextLocalPosition({-0.65f, 0, 0}); + + // Tilt the description billboard 45 degrees towards the user + descriptionLabel->SetLocalRotation( + OVR::Quat::FromRotationVector({0, OVR::DegreeToRad(-45.0f), 0})); + } + + public: + /// Body - extension functions + PFN_xrCreateBodyTrackerFB xrCreateBodyTrackerFB_ = nullptr; + PFN_xrDestroyBodyTrackerFB xrDestroyBodyTrackerFB_ = nullptr; + PFN_xrLocateBodyJointsFB xrLocateBodyJointsFB_ = nullptr; + PFN_xrGetBodySkeletonFB xrGetSkeletonFB_ = nullptr; + + /// Body - tracker handle + XrBodyTrackerFB bodyTracker_ = XR_NULL_HANDLE; + + /// Body - data buffers + XrBodyJointLocationFB jointLocations_[XR_BODY_JOINT_COUNT_FB]; + XrBodySkeletonJointFB skeletonJoints_[XR_BODY_JOINT_COUNT_FB]; + XrSpace bodySpace = XR_NULL_HANDLE; + + /// Eyes - extension functions + PFN_xrCreateEyeTrackerFB xrCreateEyeTrackerFB_ = nullptr; + PFN_xrDestroyEyeTrackerFB xrDestroyEyeTrackerFB_ = nullptr; + PFN_xrGetEyeGazesFB xrGetEyeGazesFB_ = nullptr; + + /// Eyes - tracker handle + XrEyeTrackerFB eyeTracker_ = XR_NULL_HANDLE; + + /// Face (v2) - extension functions + PFN_xrCreateFaceTracker2FB xrCreateFaceTracker2FB_ = nullptr; + PFN_xrDestroyFaceTracker2FB xrDestroyFaceTracker2FB_ = nullptr; + PFN_xrGetFaceExpressionWeights2FB xrGetFaceExpressionWeights2FB_ = nullptr; + + /// Face - tracker handle + XrFaceTracker2FB faceTracker2_ = XR_NULL_HANDLE; + + /// Face data + enum class EmojiExpression { + Neutral = 0, + Smile = 1, + Kiss = 2, + TongueOut = 3, + AudioDriven = 4, + COUNT + }; + + std::array(EmojiExpression::COUNT)> EmojiExpressionString{ + "Neutral Expression", + "Smile Expression", + "Kiss Expression", + "Tongue Out Expression", + "Audio-driven Expression", + }; + + EmojiExpression emojiExpression_ = EmojiExpression::Neutral; + + private: + OVRFW::ControllerRenderer controllerRenderL_; + OVRFW::ControllerRenderer controllerRenderR_; + OVRFW::TinyUI ui_; + OVRFW::SimpleBeamRenderer beamRenderer_; + std::vector beams_; + OVRFW::ovrAxisRenderer axisRenderer_; + + OVR::Vector4f jointColor_{0.4, 0.5, 0.2, 0.5}; + OVR::Vector4f eyeColor_{0.3, 0.2, 0.4, 1.}; + std::vector bodySkeletonRenderers; + std::vector eyeRenderers; + OVRFW::VRMenuObject* mouthLabel_; + + bool bodyTracked_ = false; + uint32_t skeletonChangeCount_ = 0; +}; + +ENTRY_POINT(XrBodyFaceEyeSocialApp) diff --git a/Samples/XrSamples/XrBodyFaceEyeSocial/assets/assets.txt b/Samples/XrSamples/XrBodyFaceEyeSocial/assets/assets.txt new file mode 100644 index 0000000..2cc30f7 --- /dev/null +++ b/Samples/XrSamples/XrBodyFaceEyeSocial/assets/assets.txt @@ -0,0 +1 @@ +This file is a placeholder. diff --git a/Samples/XrSamples/XrBodyFaceEyeSocial/assets/panel.ktx b/Samples/XrSamples/XrBodyFaceEyeSocial/assets/panel.ktx new file mode 100644 index 0000000..deb13e8 Binary files /dev/null and b/Samples/XrSamples/XrBodyFaceEyeSocial/assets/panel.ktx differ diff --git a/Samples/XrSamples/XrBodyFaceEyeSocial/java/MainActivity.java b/Samples/XrSamples/XrBodyFaceEyeSocial/java/MainActivity.java new file mode 100644 index 0000000..2ad0286 --- /dev/null +++ b/Samples/XrSamples/XrBodyFaceEyeSocial/java/MainActivity.java @@ -0,0 +1,68 @@ +// Copyright (c) Facebook Technologies, LLC and its affiliates. All Rights reserved. +package com.oculus.xrsamples.xrbodyfaceeyesocial; + +import android.content.pm.PackageManager; +import android.os.Bundle; +import java.util.ArrayList; +import java.util.List; + +/** + * When using NativeActivity, we currently need to handle loading of dependent shared libraries + * manually before a shared library that depends on them is loaded, since there is not currently a + * way to specify a shared library dependency for NativeActivity via the manifest meta-data. + * + *

The simplest method for doing so is to subclass NativeActivity with an empty activity that + * calls System.loadLibrary on the dependent libraries, which is unfortunate when the goal is to + * write a pure native C/C++ only Android activity. + * + *

A native-code only solution is to load the dependent libraries dynamically using dlopen(). + * However, there are a few considerations, see: + * https://groups.google.com/forum/#!msg/android-ndk/l2E2qh17Q6I/wj6s_6HSjaYJ + * + *

1. Only call dlopen() if you're sure it will succeed as the bionic dynamic linker will + * remember if dlopen failed and will not re-try a dlopen on the same lib a second time. + * + *

2. Must remember what libraries have already been loaded to avoid infinitely looping when + * libraries have circular dependencies. + */ +public class MainActivity extends android.app.NativeActivity { + private static final String PERMISSION_EYE_TRACKING = "com.oculus.permission.EYE_TRACKING"; + private static final String PERMISSION_FACE_TRACKING = "com.oculus.permission.FACE_TRACKING"; + private static final String PERMISSION_RECORD_AUDIO = "android.permission.RECORD_AUDIO"; + private static final int REQUEST_CODE_PERMISSION_EYE_AND_FACE_TRACKING = 1; + + static { + System.loadLibrary("openxr_loader"); + System.loadLibrary("xrbodyfaceeyesocial"); + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + // This sample makes use of eye and face tracking data at all times + // so we are requesting it in onCreate. If an app, only requires eye + // and face tracking data deep in their experience it may make sense + // to query this later in the app lifecycle. + requestFaceEyeTrackingPermissionsIfNeeded(); + } + + private void requestFaceEyeTrackingPermissionsIfNeeded() { + List permissionsToRequest = new ArrayList<>(); + if (checkSelfPermission(PERMISSION_EYE_TRACKING) != PackageManager.PERMISSION_GRANTED) { + permissionsToRequest.add(PERMISSION_EYE_TRACKING); + } + if (checkSelfPermission(PERMISSION_FACE_TRACKING) != PackageManager.PERMISSION_GRANTED) { + permissionsToRequest.add(PERMISSION_FACE_TRACKING); + } + if (checkSelfPermission(PERMISSION_RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) { + permissionsToRequest.add(PERMISSION_RECORD_AUDIO); + } + + if (!permissionsToRequest.isEmpty()) { + String[] permissionsAsArray = + permissionsToRequest.toArray(new String[permissionsToRequest.size()]); + requestPermissions(permissionsAsArray, REQUEST_CODE_PERMISSION_EYE_AND_FACE_TRACKING); + } + } +} diff --git a/Samples/XrSamples/XrBodyFaceEyeSocial/res/values/strings.xml b/Samples/XrSamples/XrBodyFaceEyeSocial/res/values/strings.xml new file mode 100644 index 0000000..0aa4c08 --- /dev/null +++ b/Samples/XrSamples/XrBodyFaceEyeSocial/res/values/strings.xml @@ -0,0 +1,4 @@ + + + XrBodyFaceEyeSocial Sample + diff --git a/Samples/XrSamples/XrColorSpaceFB/CMakeLists.txt b/Samples/XrSamples/XrColorSpaceFB/CMakeLists.txt new file mode 100755 index 0000000..ed46757 --- /dev/null +++ b/Samples/XrSamples/XrColorSpaceFB/CMakeLists.txt @@ -0,0 +1,43 @@ +# Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved. + +project(xrsamples_xrcolorspacefb) + +if(NOT TARGET OpenXR::openxr_loader) + find_package(OpenXR REQUIRED) +endif() + +file(GLOB_RECURSE SRC_FILES + Src/*.c + Src/*.cpp +) + +if(ANDROID) + add_library(${PROJECT_NAME} MODULE ${SRC_FILES}) + target_include_directories(${PROJECT_NAME} PUBLIC ${ANDROID_NDK}/sources/android/native_app_glue) + target_link_libraries(${PROJECT_NAME} PRIVATE + android + EGL + GLESv3 + log + ktx + ) + set_target_properties(${PROJECT_NAME} PROPERTIES LINK_FLAGS "-u ANativeActivity_onCreate") +elseif(WIN32) + add_definitions(-D_USE_MATH_DEFINES) + add_executable(${PROJECT_NAME} ${SRC_FILES}) + add_custom_command(TARGET ${PROJECT_NAME} PRE_BUILD + COMMAND "${CMAKE_COMMAND}" -E copy_directory + "${CMAKE_CURRENT_LIST_DIR}/assets" + "$/assets" + VERBATIM) + + add_custom_command(TARGET ${PROJECT_NAME} PRE_BUILD + COMMAND "${CMAKE_COMMAND}" -E copy_directory + "${CMAKE_SOURCE_DIR}/SampleXrFramework/res/raw" + "$/font/res/raw" + VERBATIM) +endif() + +# Common across platforms +target_include_directories(${PROJECT_NAME} PRIVATE Src) +target_link_libraries(${PROJECT_NAME} PRIVATE samplexrframework) diff --git a/Samples/XrSamples/XrColorSpaceFB/Projects/Android/AndroidManifest.xml b/Samples/XrSamples/XrColorSpaceFB/Projects/Android/AndroidManifest.xml new file mode 100755 index 0000000..d1d2df3 --- /dev/null +++ b/Samples/XrSamples/XrColorSpaceFB/Projects/Android/AndroidManifest.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Samples/XrSamples/XrColorSpaceFB/Projects/Android/build.bat b/Samples/XrSamples/XrColorSpaceFB/Projects/Android/build.bat new file mode 100755 index 0000000..facf79f --- /dev/null +++ b/Samples/XrSamples/XrColorSpaceFB/Projects/Android/build.bat @@ -0,0 +1,29 @@ +@rem Only edit the master copy of this file in SDK_ROOT/bin/scripts/build/perproject + +@setlocal enableextensions enabledelayedexpansion + +@if not exist "build.gradle" @echo Build script must be executed from project directory. & goto :Abort + +@set P=.. + +:TryAgain + +@rem @echo P = %P% + +@if exist "%P%\bin\scripts\build\build.py.bat" goto :Found + +@if exist "%P%\bin\scripts\build" @echo "Could not find build.py.bat" & goto :Abort + +@set P=%P%\.. + +@goto :TryAgain + +:Found + +@set P=%P%\bin\scripts\build +@call %P%\build.py.bat %1 %2 %3 %4 %5 +@goto :End + +:Abort + +:End diff --git a/Samples/XrSamples/XrColorSpaceFB/Projects/Android/build.gradle b/Samples/XrSamples/XrColorSpaceFB/Projects/Android/build.gradle new file mode 100755 index 0000000..6b39296 --- /dev/null +++ b/Samples/XrSamples/XrColorSpaceFB/Projects/Android/build.gradle @@ -0,0 +1,78 @@ +// Top-level build file where you can add configuration options common to all sub-projects/modules. +buildscript { + repositories { + google() + mavenCentral() + } + dependencies { + classpath "com.android.tools.build:gradle:7.0.3" + } +} + +repositories { + google() + mavenCentral() +} + +apply plugin: 'com.android.application' + +android { + compileSdk 32 + + defaultConfig { + applicationId "com.oculus.sdk.xrcolorspacefb" + minSdk 26 + targetSdk 32 + versionCode 1 + versionName "1.0" + + // override app plugin abiFilters for 64-bit support + externalNativeBuild { + ndk { + abiFilters 'arm64-v8a' + } + ndkBuild { + abiFilters 'arm64-v8a' + } + cmake { + targets "xrsamples_xrcolorspacefb" + } + } + } + + sourceSets { + main { + manifest.srcFile 'AndroidManifest.xml' + java.srcDirs = ['../../java'] + assets.srcDirs = ['../../assets'] + res.srcDirs = ['../../res'] + } + } + + buildTypes { + debug { + debuggable true + } + + release { + debuggable false + } + } + + externalNativeBuild { + cmake { + path file('../../../../CMakeLists.txt') + } + } + + // Enable prefab support for the OpenXR AAR + buildFeatures { + prefab true + } +} + +dependencies { + // Package/application AndroidManifest.xml properties, plus headers and libraries + // exposed to CMake + implementation 'org.khronos.openxr:openxr_loader_for_android:1.1.36' +} diff --git a/Samples/XrSamples/XrColorSpaceFB/Projects/Android/build.py b/Samples/XrSamples/XrColorSpaceFB/Projects/Android/build.py new file mode 100755 index 0000000..d4b6e58 --- /dev/null +++ b/Samples/XrSamples/XrColorSpaceFB/Projects/Android/build.py @@ -0,0 +1,35 @@ +#!/usr/bin/python +# (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +# This first bit of code is common bootstrapping code +# to determine the SDK root, and to set up the import +# path for additional python code. + +# begin bootstrap +import os +import sys + + +def init(): + root = os.path.realpath(os.path.dirname(os.path.realpath(__file__))) + os.chdir(root) # make sure we are always executing from the project directory + while os.path.isdir(os.path.join(root, "bin/scripts/build")) == False: + root = os.path.realpath(os.path.join(root, "..")) + if ( + len(root) <= 5 + ): # Should catch both Posix and Windows root directories (e.g. '/' and 'C:\') + print("Unable to find SDK root. Exiting.") + sys.exit(1) + root = os.path.abspath(root) + os.environ["OCULUS_SDK_PATH"] = root + sys.path.append(root + "/bin/scripts/build") + + +init() +import ovrbuild + +ovrbuild.init() +# end bootstrap + + +ovrbuild.build() diff --git a/Samples/XrSamples/XrColorSpaceFB/Projects/Android/gradle.properties b/Samples/XrSamples/XrColorSpaceFB/Projects/Android/gradle.properties new file mode 100644 index 0000000..3e927b1 --- /dev/null +++ b/Samples/XrSamples/XrColorSpaceFB/Projects/Android/gradle.properties @@ -0,0 +1,21 @@ +# Project-wide Gradle settings. +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. More details, visit +# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects +# org.gradle.parallel=true +# AndroidX package structure to make it clearer which packages are bundled with the +# Android operating system, and which are packaged with your app's APK +# https://developer.android.com/topic/libraries/support-library/androidx-rn +android.useAndroidX=true +# Enables namespacing of each library's R class so that its R class includes only the +# resources declared in the library itself and none from the library's dependencies, +# thereby reducing the size of the R class for that library +android.nonTransitiveRClass=true \ No newline at end of file diff --git a/Samples/XrSamples/XrColorSpaceFB/Projects/Android/gradle/wrapper/gradle-wrapper.jar b/Samples/XrSamples/XrColorSpaceFB/Projects/Android/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..7454180 Binary files /dev/null and b/Samples/XrSamples/XrColorSpaceFB/Projects/Android/gradle/wrapper/gradle-wrapper.jar differ diff --git a/Samples/XrSamples/XrColorSpaceFB/Projects/Android/gradle/wrapper/gradle-wrapper.properties b/Samples/XrSamples/XrColorSpaceFB/Projects/Android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..ffed3a2 --- /dev/null +++ b/Samples/XrSamples/XrColorSpaceFB/Projects/Android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/Samples/XrSamples/XrColorSpaceFB/Projects/Android/gradlew b/Samples/XrSamples/XrColorSpaceFB/Projects/Android/gradlew new file mode 100755 index 0000000..a69d9cb --- /dev/null +++ b/Samples/XrSamples/XrColorSpaceFB/Projects/Android/gradlew @@ -0,0 +1,240 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# 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 +# +# https://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. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit + +APP_NAME="Gradle" +APP_BASE_NAME=${0##*/} + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/Samples/XrSamples/XrColorSpaceFB/Projects/Android/gradlew.bat b/Samples/XrSamples/XrColorSpaceFB/Projects/Android/gradlew.bat new file mode 100755 index 0000000..f127cfd --- /dev/null +++ b/Samples/XrSamples/XrColorSpaceFB/Projects/Android/gradlew.bat @@ -0,0 +1,91 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/Samples/XrSamples/XrColorSpaceFB/Projects/Android/settings.gradle b/Samples/XrSamples/XrColorSpaceFB/Projects/Android/settings.gradle new file mode 100755 index 0000000..9503fa8 --- /dev/null +++ b/Samples/XrSamples/XrColorSpaceFB/Projects/Android/settings.gradle @@ -0,0 +1 @@ +rootProject.name = "XrColorSpaceFB" diff --git a/Samples/XrSamples/XrColorSpaceFB/README.md b/Samples/XrSamples/XrColorSpaceFB/README.md new file mode 100644 index 0000000..05c2610 --- /dev/null +++ b/Samples/XrSamples/XrColorSpaceFB/README.md @@ -0,0 +1,18 @@ +# OpenXR Color Space Sample + +## Overview +XR devices may use a color space that differs from many monitors used in development. Application developers might want to specify the color space in which they have authored their application, so the appropriate colors are displayed when the application runs on the XR device. + +The extension `XR_FB_color_space` is designed to allow: +* An application to get the native color space of the XR device. +* An application to enumerate the supported color spaces for the session. +* An application to set the color space for the session. + +## What is Color Space? +Color space refers to the range of colors that a device can represent and display. It's a specific organization of colors within a three-dimensional space, which allows for the accurate representation and communication of digital colors. + +For more details and recommendations on color management, see: https://developer.oculus.com/resources/color-management-guide/ + +## The Sample +You can select a color space from the provided list and observe the difference on the color checker. +![Color Space Screen Shot](images/screen_shot.png) diff --git a/Samples/XrSamples/XrColorSpaceFB/Src/main.cpp b/Samples/XrSamples/XrColorSpaceFB/Src/main.cpp new file mode 100755 index 0000000..8f9064a --- /dev/null +++ b/Samples/XrSamples/XrColorSpaceFB/Src/main.cpp @@ -0,0 +1,351 @@ +/******************************************************************************* + +Filename : Main.cpp +Content : Simple test app for XrColorSpaceFB +Created : +Authors : Andreas Selvik +Language : C++ +Copyright: Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved. + +*******************************************************************************/ + +#include +#include +#include + +#include + +#include "GUI/VRMenuObject.h" +#include "Render/BitmapFont.h" +#include "Render/GeometryBuilder.h" +#include "openxr_reflection_macro.h" +#include "XrApp.h" + +#include "OVR_Math.h" +#include "Input/ControllerRenderer.h" +#include "Input/TinyUI.h" +#include "Render/GeometryRenderer.h" +#include "Render/SimpleBeamRenderer.h" + +class XrAppBaseApp : public OVRFW::XrApp { + public: + static constexpr std::string_view sampleExplanation = + "XR devices may display RGB color triplets differently from many \n" + "monitors used in development, that is, they may use a different \n" + "color space than what is common for computer monitors. \n" + "\n" + "XrColorSpaceFB is an extension that allow a developer to specify \n" + "the color space in which they have authored their application so \n" + "the correct colors are shown when the application is running on \n" + "the XR device. \n" + "\n" + "For the majority of developers, the recommendation for Meta Quest \n" + "devices is to author your content in sRGB (XR_COLOR_SPACE_REC709_FB)\n" + "and leave the color space conversion to the default value (which \n" + "is \"XR_COLOR_SPACE_RIFT_CV1_FB\" on Meta Quest 2). \n" + "For more details and recommendations for color management, see: \n" + "https://developer.oculus.com/resources/color-management-guide/ \n"; + + XrAppBaseApp() : OVRFW::XrApp() { + BackgroundColor = OVR::Vector4f(0.55f, 0.35f, 0.1f, 1.0f); + } + + // Returns a list of OpenXR extensions needed for this app + virtual std::vector GetExtensions() override { + std::vector extensions = XrApp::GetExtensions(); + + // Add "XR_FB_color_space" to the list of extensions + // The sample framework will add this to enabledExtensionNames + // when calling xrCreateInstance() + extensions.push_back(XR_FB_COLOR_SPACE_EXTENSION_NAME); + return extensions; + } + + // Before this function, OVRFW::XrApp::Init() calls, among other things; + // - xrInitializeLoaderKHR(...) + // - xrCreateInstance with the extensions from GetExtensions(...), + // - xrSuggestInteractionProfileBindings(...) to set up action bindings + // Before calling this function: + virtual bool AppInit(const xrJava* context) override { + // Init UI system + if (false == ui_.Init(context, GetFileSys())) { + ALOG("TinyUI::Init FAILED."); + return false; + } + + // Bind extension functions so we can call them + OXR(xrGetInstanceProcAddr( + GetInstance(), + "xrEnumerateColorSpacesFB", + reinterpret_cast(&xrEnumerateColorSpacesFB))); + OXR(xrGetInstanceProcAddr( + GetInstance(), + "xrSetColorSpaceFB", + reinterpret_cast(&xrSetColorSpaceFB))); + + return true; + } + + // Before this function, and after AppInit, XrApp::InitSession() calls: + // - xrCreateSession(...) to create our Session + // - xrCreateReferenceSpace(...) for local and stage space + // - Create swapchain with xrCreateSwapchain(...) + // - xrAttachSessionActionSets(...) + // Before calling this function: + virtual bool SessionInit() override { + { + // Get native HMD color space by chaining XrSystemColorSpacePropertiesFB + // to the next pointer of XrSystemProperties + XrSystemColorSpacePropertiesFB colorSpaceProps{ + XR_TYPE_SYSTEM_COLOR_SPACE_PROPERTIES_FB}; + XrSystemProperties sysprops{XR_TYPE_SYSTEM_PROPERTIES}; + sysprops.next = &colorSpaceProps; + OXR(xrGetSystemProperties(GetInstance(), GetSystemId(), &sysprops)); + + // The sample just displays the native HMD color space, + // but an app could use this to load different assets + // and to inform which colorspace it wants to select. + std::string str = + "Native HMD color space: " + std::string(XrEnumStr(colorSpaceProps.colorSpace)); + ui_.AddLabel(str, {-1.0f, 2.4f, -2.0f}, {700.0f, 45.0f}); + } + + // The default source color space is not something that's + // provided by XrColorSpaceFB OpenXR extension, so just initialize + // the label to say [default] + declaredLabel_ = ui_.AddLabel( + "App declared color space: [default]", {-1.0f, 2.3f, -2.0f}, {700.0f, 45.0f}); + + { // Enumerate supported source color spaces with xrEnumerateColorSpaceFB + + // xrEnumerateColorSpacesFB uses the two-call idiom + // First call with nullptr to get the required size: + uint32_t colorSpaceCount{0}; + OXR(xrEnumerateColorSpacesFB(GetSession(), 0, &colorSpaceCount, nullptr)); + + // Allocate array with enough space for the data + auto colorSpaces = std::vector(colorSpaceCount); + + // Second call gets the actual data + OXR(xrEnumerateColorSpacesFB( + GetSession(), (uint32_t)colorSpaces.size(), &colorSpaceCount, colorSpaces.data())); + + // Header + ui_.AddLabel("Compatible color spaces:", {-1.0f, 2.15f, -2.0f}, {700.0f, 45.0f}); + + // Create one button for each supported color space conversion + float posY = 2.0f; + for (auto colorSpace : colorSpaces) { + ui_.AddButton(XrEnumStr(colorSpace), {-1.0f, posY, -2.0f}, {600.0f, 60.0f}, [=]() { + // Set source color space with xrSetColorSpaceFB when button is pressed + // Remember that this is the source colorspace and no the target colorspace + OXR(xrSetColorSpaceFB(GetSession(), colorSpace)); + + declaredColorSpace_ = colorSpace; // For display in the UI + }); + posY -= 0.15f; + } + } + + // Create the rest of the UI + CreateColorCheckerCubes(); + CreateSampleDescriptionPanel(); + + /// Disable scene navitgation + GetScene().SetFootPos({0.0f, 0.0f, 0.0f}); + this->FreeMove = false; + + // Init objects that need OpenXR Session + if (false == controllerRenderL_.Init(true)) { + ALOG("SessionInit::Init L controller renderer FAILED."); + return false; + } + if (false == controllerRenderR_.Init(false)) { + ALOG("SessionInit::Init R controller renderer FAILED."); + return false; + } + cursorBeamRenderer_.Init(GetFileSys(), nullptr, OVR::Vector4f(1.0f), 1.0f); + + return true; + } + + // Called every frame + virtual void Update(const OVRFW::ovrApplFrameIn& in) override { + // Update the UI with the latest declared color space + if (declaredColorSpace_ != XR_COLOR_SPACE_MAX_ENUM_FB) { + std::string label = "App declared color space: "; + label += XrEnumStr(declaredColorSpace_); + declaredLabel_->SetText(label.c_str()); + } + + colorCheckerRenderer_.Update(); + + // Clear the intersection rays from last frame: + ui_.HitTestDevices().clear(); + + if (in.LeftRemoteTracked) { + controllerRenderL_.Update(in.LeftRemotePose); + const bool didTrigger = in.LeftRemoteIndexTrigger > 0.5f; + ui_.AddHitTestRay(in.LeftRemotePointPose, didTrigger); + } + + if (in.RightRemoteTracked) { + controllerRenderR_.Update(in.RightRemotePose); + const bool didTrigger = in.RightRemoteIndexTrigger > 0.5f; + ui_.AddHitTestRay(in.RightRemotePointPose, didTrigger); + } + + ui_.Update(in); + cursorBeamRenderer_.Update(in, ui_.HitTestDevices()); + } + + virtual void Render(const OVRFW::ovrApplFrameIn& in, OVRFW::ovrRendererOutput& out) override { + colorCheckerRenderer_.Render(out.Surfaces); + + ui_.Render(in, out); + + if (in.LeftRemoteTracked) { + controllerRenderL_.Render(out.Surfaces); + } + if (in.RightRemoteTracked) { + controllerRenderR_.Render(out.Surfaces); + } + + /// Render beams last, since they render with transparency + cursorBeamRenderer_.Render(in, out); + } + + virtual void SessionEnd() override { + controllerRenderL_.Shutdown(); + controllerRenderR_.Shutdown(); + cursorBeamRenderer_.Shutdown(); + colorCheckerRenderer_.Shutdown(); + } + + virtual void AppShutdown(const xrJava* context) override { + OVRFW::XrApp::AppShutdown(context); + ui_.Shutdown(); + } + + void CreateColorCheckerCubes() { + // From https://en.wikipedia.org/wiki/ColorChecker + // These values are authored for the sRGB (Rec. 709) color space + std::vector Rec709ColorChecker = { + // clang-format off + "#735244", "#c29682", "#627a9d", "#576c43", "#8580b1", "#67bdaa", + "#d67e2c", "#505ba6", "#c15a63", "#5e3c6c", "#9dbc40", "#e0a32e", + "#383d96", "#469449", "#af363c", "#e7c71f", "#bb5695", "#0885a1", + "#f3f3f2", "#c8c8c8", "#a0a0a0", "#7a7a7a", "#555555", "#343434" + // clang-format on + }; + + auto linearRgbaFromGammaHex = [](std::string_view hex) { + float r = std::stoul(std::string(hex.substr(1, 2)), nullptr, 16) / 256.0f; + float g = std::stoul(std::string(hex.substr(3, 2)), nullptr, 16) / 256.0f; + float b = std::stoul(std::string(hex.substr(5, 2)), nullptr, 16) / 256.0f; + + // Convert colors from gamma 2.2 into linear space for direct shader usage + // Note: If you were to use a texture with these RGB values, and you configure + // the texture as an sRGB texture, this conversion would happen in the GPU + // as your shader reads the texels. But since we are providing the values + // directly into the fragment shader, we need to do this manually. + // Try removing this part and see what happens to the colors :) + r = powf(r, 2.2); + g = powf(g, 2.2); + b = powf(b, 2.2); + + return OVR::Vector4f{r, g, b, 1.0f}; + }; + + OVR::Vector3f chartPose = {0.5, 1.5f, -2.0f}; + ui_.AddLabel( + "Color checker authored for sRGB (XR_COLOR_SPACE_REC709_FB):", + {chartPose.x, chartPose.y + 0.65f, chartPose.z}, + {750.0f, 45.0f}); + + OVRFW::GeometryBuilder colorCheckerCubes; + + constexpr float tileSize = 0.2f; + constexpr float tileGap = 0.04f; + constexpr float chartSizeX = 6 * tileSize + 5 * tileGap; + constexpr float chartSizeY = 4 * tileSize + 3 * tileGap; + constexpr float chartBorder = 0.1f; + + auto transform = OVR::Matrix4f::Translation(chartPose) * + OVR::Matrix4f::Scaling( + chartSizeX + 2 * chartBorder, chartSizeY + 2 * chartBorder, 0.01); + colorCheckerCubes.Add( + OVRFW::BuildUnitCubeDescriptor(), + OVRFW::GeometryBuilder::kInvalidIndex, + {0.0f, 0.0f, 0.0f, 1.0f}, + transform); + + int i = 0; + for (auto tileColor : Rec709ColorChecker) { + int row = i / 6; + int col = i % 6; + i++; + + float tileX = chartPose.x - (chartSizeX - tileSize) / 2.0f + (tileSize + tileGap) * col; + float tileY = chartPose.y + (chartSizeY - tileSize) / 2.0f - (tileSize + tileGap) * row; + float tileZ = chartPose.z + 0.005f; + + transform = OVR::Matrix4f::Translation({tileX, tileY, tileZ}) * + OVR::Matrix4f::Scaling(tileSize, tileSize, 0.1); + + colorCheckerCubes.Add( + OVRFW::BuildUnitCubeDescriptor(), + OVRFW::GeometryBuilder::kInvalidIndex, + linearRgbaFromGammaHex(tileColor), + transform); + + ui_.AddLabel(tileColor, {tileX, tileY, tileZ + 0.07f}, {100.0f, 45.0f}) + ->SetColor({0, 0, 0, 0}); // Set label background to transparent + } + colorCheckerRenderer_.Init(colorCheckerCubes.ToGeometryDescriptor()); + + // Tricks to disable shading and lighting of the color checker + // to ensure the final image contains the exact RGB values we want: + colorCheckerRenderer_.ChannelControl = {0, 1, 0}; // Only enable ambient color + // Pass through vertex color to the final image by using the ambient light + colorCheckerRenderer_.AmbientLightColor = {1, 1, 1}; + } + + void CreateSampleDescriptionPanel() { + // Panel to provide sample description to the user for context + auto descriptionLabel = ui_.AddLabel( + static_cast(sampleExplanation), {2.0f, 1.5f, -1.5f}, {950.0f, 600.0f}); + + // Align and size the description text for readability + OVRFW::VRMenuFontParms fontParams{}; + fontParams.Scale = 0.5f; + fontParams.AlignHoriz = OVRFW::HORIZONTAL_LEFT; + descriptionLabel->SetFontParms(fontParams); + descriptionLabel->SetTextLocalPosition({-0.65f, 0, 0}); + + // Tilt the description billboard 45 degrees towards the user + descriptionLabel->SetLocalRotation( + OVR::Quat::FromRotationVector({0, OVR::DegreeToRad(-45.0f), 0})); + } + + private: + // Function pointers to extension functions + // Remember that you need to call xrGetInstanceProcAddr after + // instance initiation to get these + PFN_xrEnumerateColorSpacesFB xrEnumerateColorSpacesFB{nullptr}; + PFN_xrSetColorSpaceFB xrSetColorSpaceFB{nullptr}; + + XrColorSpaceFB declaredColorSpace_{XR_COLOR_SPACE_MAX_ENUM_FB}; + + OVRFW::VRMenuObject* declaredLabel_; + OVRFW::GeometryRenderer colorCheckerRenderer_; + OVRFW::ControllerRenderer controllerRenderL_; + OVRFW::ControllerRenderer controllerRenderR_; + OVRFW::TinyUI ui_; + + // Renderer that draws the beam from the controller + OVRFW::SimpleBeamRenderer cursorBeamRenderer_; + std::vector beams_; +}; + +ENTRY_POINT(XrAppBaseApp) diff --git a/Samples/XrSamples/XrColorSpaceFB/Src/openxr_reflection_macro.h b/Samples/XrSamples/XrColorSpaceFB/Src/openxr_reflection_macro.h new file mode 100755 index 0000000..7944486 --- /dev/null +++ b/Samples/XrSamples/XrColorSpaceFB/Src/openxr_reflection_macro.h @@ -0,0 +1,19 @@ +#pragma once +#include + +// Macro copied from example in openxr_reflection.h +#define XR_ENUM_CASE_STR(name, val) \ + case name: \ + return #name; + +#define XR_ENUM_STR(enumType) \ + constexpr const char* XrEnumStr(enumType e) { \ + switch (e) { XR_LIST_ENUM_##enumType(XR_ENUM_CASE_STR) default : return "Unknown"; } \ + } + +// Creates overloads of XrEnumStr() function for these enum types +XR_ENUM_STR(XrColorSpaceFB); +XR_ENUM_STR(XrResult); + +#undef XR_ENUM_CASE_STR +#undef XR_ENUM_STR diff --git a/Samples/XrSamples/XrColorSpaceFB/assets/assets.txt b/Samples/XrSamples/XrColorSpaceFB/assets/assets.txt new file mode 100644 index 0000000..2cc30f7 --- /dev/null +++ b/Samples/XrSamples/XrColorSpaceFB/assets/assets.txt @@ -0,0 +1 @@ +This file is a placeholder. diff --git a/Samples/XrSamples/XrColorSpaceFB/assets/panel.ktx b/Samples/XrSamples/XrColorSpaceFB/assets/panel.ktx new file mode 100644 index 0000000..deb13e8 Binary files /dev/null and b/Samples/XrSamples/XrColorSpaceFB/assets/panel.ktx differ diff --git a/Samples/XrSamples/XrColorSpaceFB/java/MainActivity.java b/Samples/XrSamples/XrColorSpaceFB/java/MainActivity.java new file mode 100644 index 0000000..cce8548 --- /dev/null +++ b/Samples/XrSamples/XrColorSpaceFB/java/MainActivity.java @@ -0,0 +1,28 @@ +// Copyright (c) Facebook Technologies, LLC and its affiliates. All Rights reserved. +package com.oculus.sdk.xrcolorspacefb; + +/** + * When using NativeActivity, we currently need to handle loading of dependent shared libraries + * manually before a shared library that depends on them is loaded, since there is not currently a + * way to specify a shared library dependency for NativeActivity via the manifest meta-data. + * + *

The simplest method for doing so is to subclass NativeActivity with an empty activity that + * calls System.loadLibrary on the dependent libraries, which is unfortunate when the goal is to + * write a pure native C/C++ only Android activity. + * + *

A native-code only solution is to load the dependent libraries dynamically using dlopen(). + * However, there are a few considerations, see: + * https://groups.google.com/forum/#!msg/android-ndk/l2E2qh17Q6I/wj6s_6HSjaYJ + * + *

1. Only call dlopen() if you're sure it will succeed as the bionic dynamic linker will + * remember if dlopen failed and will not re-try a dlopen on the same lib a second time. + * + *

2. Must remember what libraries have already been loaded to avoid infinitely looping when + * libraries have circular dependencies. + */ +public class MainActivity extends android.app.NativeActivity { + static { + System.loadLibrary("openxr_loader"); + System.loadLibrary("xrsamples_xrcolorspacefb"); + } +} diff --git a/Samples/XrSamples/XrColorSpaceFB/res/values/strings.xml b/Samples/XrSamples/XrColorSpaceFB/res/values/strings.xml new file mode 100644 index 0000000..ad5df76 --- /dev/null +++ b/Samples/XrSamples/XrColorSpaceFB/res/values/strings.xml @@ -0,0 +1,4 @@ + + + XrColorSpaceFB Sample + diff --git a/Samples/XrSamples/XrCompositor_NativeActivity/CMakeLists.txt b/Samples/XrSamples/XrCompositor_NativeActivity/CMakeLists.txt new file mode 100755 index 0000000..e00ce87 --- /dev/null +++ b/Samples/XrSamples/XrCompositor_NativeActivity/CMakeLists.txt @@ -0,0 +1,21 @@ +# Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved. + +project(xrcompositor) + +if(NOT TARGET OpenXR::openxr_loader) + find_package(OpenXR REQUIRED) +endif() + +if(ANDROID) + file(GLOB_RECURSE SRC_FILES + Src/*.c + Src/*.cpp + ) + + add_library(${PROJECT_NAME} MODULE ${SRC_FILES}) + target_link_libraries(${PROJECT_NAME} PRIVATE native_activity_framework) + set_target_properties(${PROJECT_NAME} PROPERTIES LINK_FLAGS "-u ANativeActivity_onCreate") + + # Common across platforms + target_include_directories(${PROJECT_NAME} PRIVATE Src) +endif() diff --git a/Samples/XrSamples/XrCompositor_NativeActivity/Projects/Android/AndroidManifest.xml b/Samples/XrSamples/XrCompositor_NativeActivity/Projects/Android/AndroidManifest.xml new file mode 100755 index 0000000..ee3ae88 --- /dev/null +++ b/Samples/XrSamples/XrCompositor_NativeActivity/Projects/Android/AndroidManifest.xml @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Samples/XrSamples/XrCompositor_NativeActivity/Projects/Android/build.bat b/Samples/XrSamples/XrCompositor_NativeActivity/Projects/Android/build.bat new file mode 100755 index 0000000..facf79f --- /dev/null +++ b/Samples/XrSamples/XrCompositor_NativeActivity/Projects/Android/build.bat @@ -0,0 +1,29 @@ +@rem Only edit the master copy of this file in SDK_ROOT/bin/scripts/build/perproject + +@setlocal enableextensions enabledelayedexpansion + +@if not exist "build.gradle" @echo Build script must be executed from project directory. & goto :Abort + +@set P=.. + +:TryAgain + +@rem @echo P = %P% + +@if exist "%P%\bin\scripts\build\build.py.bat" goto :Found + +@if exist "%P%\bin\scripts\build" @echo "Could not find build.py.bat" & goto :Abort + +@set P=%P%\.. + +@goto :TryAgain + +:Found + +@set P=%P%\bin\scripts\build +@call %P%\build.py.bat %1 %2 %3 %4 %5 +@goto :End + +:Abort + +:End diff --git a/Samples/XrSamples/XrCompositor_NativeActivity/Projects/Android/build.gradle b/Samples/XrSamples/XrCompositor_NativeActivity/Projects/Android/build.gradle new file mode 100755 index 0000000..229d5e8 --- /dev/null +++ b/Samples/XrSamples/XrCompositor_NativeActivity/Projects/Android/build.gradle @@ -0,0 +1,78 @@ +// Top-level build file where you can add configuration options common to all sub-projects/modules. +buildscript { + repositories { + google() + mavenCentral() + } + dependencies { + classpath "com.android.tools.build:gradle:7.0.3" + } +} + +repositories { + google() + mavenCentral() +} + +apply plugin: 'com.android.application' + +android { + compileSdk 32 + + defaultConfig { + applicationId "com.oculus.sdk.xrcompositor" + minSdk 26 + targetSdk 32 + versionCode 1 + versionName "1.0" + + // override app plugin abiFilters for 64-bit support + externalNativeBuild { + ndk { + abiFilters 'arm64-v8a' + } + ndkBuild { + abiFilters 'arm64-v8a' + } + cmake { + targets "xrcompositor" + } + } + } + + sourceSets { + main { + manifest.srcFile 'AndroidManifest.xml' + java.srcDirs = ['../../java'] + assets.srcDirs = ['../../assets'] + res.srcDirs = ['../../res'] + } + } + + buildTypes { + debug { + debuggable true + } + + release { + debuggable false + } + } + + externalNativeBuild { + cmake { + path file('../../../../CMakeLists.txt') + } + } + + // Enable prefab support for the OpenXR AAR + buildFeatures { + prefab true + } +} + +dependencies { + // Package/application AndroidManifest.xml properties, plus headers and libraries + // exposed to CMake + implementation 'org.khronos.openxr:openxr_loader_for_android:1.1.36' +} diff --git a/Samples/XrSamples/XrCompositor_NativeActivity/Projects/Android/build.py b/Samples/XrSamples/XrCompositor_NativeActivity/Projects/Android/build.py new file mode 100755 index 0000000..d4b6e58 --- /dev/null +++ b/Samples/XrSamples/XrCompositor_NativeActivity/Projects/Android/build.py @@ -0,0 +1,35 @@ +#!/usr/bin/python +# (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +# This first bit of code is common bootstrapping code +# to determine the SDK root, and to set up the import +# path for additional python code. + +# begin bootstrap +import os +import sys + + +def init(): + root = os.path.realpath(os.path.dirname(os.path.realpath(__file__))) + os.chdir(root) # make sure we are always executing from the project directory + while os.path.isdir(os.path.join(root, "bin/scripts/build")) == False: + root = os.path.realpath(os.path.join(root, "..")) + if ( + len(root) <= 5 + ): # Should catch both Posix and Windows root directories (e.g. '/' and 'C:\') + print("Unable to find SDK root. Exiting.") + sys.exit(1) + root = os.path.abspath(root) + os.environ["OCULUS_SDK_PATH"] = root + sys.path.append(root + "/bin/scripts/build") + + +init() +import ovrbuild + +ovrbuild.init() +# end bootstrap + + +ovrbuild.build() diff --git a/Samples/XrSamples/XrCompositor_NativeActivity/Projects/Android/gradle.properties b/Samples/XrSamples/XrCompositor_NativeActivity/Projects/Android/gradle.properties new file mode 100644 index 0000000..3e927b1 --- /dev/null +++ b/Samples/XrSamples/XrCompositor_NativeActivity/Projects/Android/gradle.properties @@ -0,0 +1,21 @@ +# Project-wide Gradle settings. +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. More details, visit +# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects +# org.gradle.parallel=true +# AndroidX package structure to make it clearer which packages are bundled with the +# Android operating system, and which are packaged with your app's APK +# https://developer.android.com/topic/libraries/support-library/androidx-rn +android.useAndroidX=true +# Enables namespacing of each library's R class so that its R class includes only the +# resources declared in the library itself and none from the library's dependencies, +# thereby reducing the size of the R class for that library +android.nonTransitiveRClass=true \ No newline at end of file diff --git a/Samples/XrSamples/XrCompositor_NativeActivity/Projects/Android/gradle/wrapper/gradle-wrapper.jar b/Samples/XrSamples/XrCompositor_NativeActivity/Projects/Android/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..7454180 Binary files /dev/null and b/Samples/XrSamples/XrCompositor_NativeActivity/Projects/Android/gradle/wrapper/gradle-wrapper.jar differ diff --git a/Samples/XrSamples/XrCompositor_NativeActivity/Projects/Android/gradle/wrapper/gradle-wrapper.properties b/Samples/XrSamples/XrCompositor_NativeActivity/Projects/Android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..ffed3a2 --- /dev/null +++ b/Samples/XrSamples/XrCompositor_NativeActivity/Projects/Android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/Samples/XrSamples/XrCompositor_NativeActivity/Projects/Android/gradlew b/Samples/XrSamples/XrCompositor_NativeActivity/Projects/Android/gradlew new file mode 100755 index 0000000..a69d9cb --- /dev/null +++ b/Samples/XrSamples/XrCompositor_NativeActivity/Projects/Android/gradlew @@ -0,0 +1,240 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# 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 +# +# https://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. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit + +APP_NAME="Gradle" +APP_BASE_NAME=${0##*/} + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/Samples/XrSamples/XrCompositor_NativeActivity/Projects/Android/gradlew.bat b/Samples/XrSamples/XrCompositor_NativeActivity/Projects/Android/gradlew.bat new file mode 100755 index 0000000..f127cfd --- /dev/null +++ b/Samples/XrSamples/XrCompositor_NativeActivity/Projects/Android/gradlew.bat @@ -0,0 +1,91 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/Samples/XrSamples/XrCompositor_NativeActivity/Projects/Android/settings.gradle b/Samples/XrSamples/XrCompositor_NativeActivity/Projects/Android/settings.gradle new file mode 100755 index 0000000..44708c6 --- /dev/null +++ b/Samples/XrSamples/XrCompositor_NativeActivity/Projects/Android/settings.gradle @@ -0,0 +1 @@ +rootProject.name = "XrCompositor_NativeActivity" diff --git a/Samples/XrSamples/XrCompositor_NativeActivity/README.md b/Samples/XrSamples/XrCompositor_NativeActivity/README.md new file mode 100644 index 0000000..dd8e74c --- /dev/null +++ b/Samples/XrSamples/XrCompositor_NativeActivity/README.md @@ -0,0 +1,12 @@ +# OpenXR Compositor Sample + +## Overview +The extensions: `XR_KHR_composition_layer_cube`, `XR_KHR_composition_layer_cylinder`, and `XR_KHR_composition_layer_equirect2` add additional layer types that allow mapping textures from swapchains onto various types of surfaces. + +* `XR_KHR_composition_layer_cube` adds an additional layer type that enables direct sampling from cubemaps. +* `XR_KHR_composition_layer_cylinder` adds an additional layer type that enables mapping a texture stemming from a swapchain onto the inside of a cylinder section. +* `XR_KHR_composition_layer_equirect2` adds an additional layer type that enables mapping an equirectangular coded image stemming from a swapchain onto the inside of a sphere. + +## The Sample +The sample is a single `C` file that shows how to create a native app using OpenXR from beginning to end. The sample also demonstrates how to create multiple layers and compose them all together with the compositor in a depth-consistent order. +![screenshot](images/screen_shot.png) diff --git a/Samples/XrSamples/XrCompositor_NativeActivity/Src/XrCompositor_NativeActivity.c b/Samples/XrSamples/XrCompositor_NativeActivity/Src/XrCompositor_NativeActivity.c new file mode 100755 index 0000000..fa844ce --- /dev/null +++ b/Samples/XrSamples/XrCompositor_NativeActivity/Src/XrCompositor_NativeActivity.c @@ -0,0 +1,3856 @@ +/************************************************************************************ + +Filename : XrCompositor_NativeActivity.c +Content : This sample uses the Android NativeActivity class. +Created : +Authors : + +Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved. + +*************************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +/* +This example demonstrates the use of the supported compositor layer types: projection, +cylinder, quad, cubemap, and equirect. + +Compositor layers provide advantages such as increased resolution and better sampling +qualities over rendering to the eye buffers. Layers are composited strictly in order, +with no depth interaction, so it is the applications responsibility to not place the +layers such that they would be occluded by any world geometry, which can result in +eye straining depth paradoxes. + +Layer rendering may break down at extreme grazing angles, and as such, they should be +faded out or converted to normal surfaces which render to the eye buffers if the viewer +can get too close. + +All texture levels must have a 0 alpha border to avoid edge smear. +*/ + +#if !defined(EGL_OPENGL_ES3_BIT_KHR) +#define EGL_OPENGL_ES3_BIT_KHR 0x0040 +#endif + +// EXT_texture_border_clamp +#ifndef GL_CLAMP_TO_BORDER +#define GL_CLAMP_TO_BORDER 0x812D +#endif + +#ifndef GL_TEXTURE_BORDER_COLOR +#define GL_TEXTURE_BORDER_COLOR 0x1004 +#endif + +#if !defined(GL_EXT_multisampled_render_to_texture) +typedef void(GL_APIENTRY* PFNGLRENDERBUFFERSTORAGEMULTISAMPLEEXTPROC)( + GLenum target, + GLsizei samples, + GLenum internalformat, + GLsizei width, + GLsizei height); +typedef void(GL_APIENTRY* PFNGLFRAMEBUFFERTEXTURE2DMULTISAMPLEEXTPROC)( + GLenum target, + GLenum attachment, + GLenum textarget, + GLuint texture, + GLint level, + GLsizei samples); +#endif + +// GL_EXT_texture_cube_map_array +#if !defined(GL_TEXTURE_CUBE_MAP_ARRAY) +#define GL_TEXTURE_CUBE_MAP_ARRAY 0x9009 +#endif + +#define XR_USE_GRAPHICS_API_OPENGL_ES 1 +#define XR_USE_PLATFORM_ANDROID 1 +#include +#include + +#include + +#define MATH_PI 3.14159265358979323846f + +#define DEBUG 1 +#define OVR_LOG_TAG "XrCompositor_NativeActivity" + +#define ALOGE(...) __android_log_print(ANDROID_LOG_ERROR, OVR_LOG_TAG, __VA_ARGS__) +#define ALOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, OVR_LOG_TAG, __VA_ARGS__) +#define ALOGD(...) __android_log_print(ANDROID_LOG_DEBUG, OVR_LOG_TAG, __VA_ARGS__) + +static const int CPU_LEVEL = 2; +static const int GPU_LEVEL = 3; +static const int NUM_MULTI_SAMPLES = 4; + +typedef union { + XrCompositionLayerProjection Projection; + XrCompositionLayerQuad Quad; + XrCompositionLayerCylinderKHR Cylinder; + XrCompositionLayerCubeKHR Cube; + XrCompositionLayerEquirect2KHR Equirect2; +} ovrCompositorLayer_Union; + +enum { ovrMaxLayerCount = 16 }; +enum { ovrMaxNumEyes = 2 }; + +// Forward declarations +XrInstance ovrApp_GetInstance(); + +/* +================================================================================ + +OpenXR Utility Functions + +================================================================================ +*/ + +#if defined(DEBUG) +static void +OXR_CheckErrors(XrInstance instance, XrResult result, const char* function, bool failOnError) { + if (XR_FAILED(result)) { + char errorBuffer[XR_MAX_RESULT_STRING_SIZE]; + xrResultToString(instance, result, errorBuffer); + if (failOnError) { + ALOGE("OpenXR error: %s: %s\n", function, errorBuffer); + } else { + ALOGV("OpenXR error: %s: %s\n", function, errorBuffer); + } + } +} +#endif + +#if defined(DEBUG) +#define OXR(func) OXR_CheckErrors(ovrApp_GetInstance(), func, #func, true); +#else +#define OXR(func) OXR_CheckErrors(ovrApp_GetInstance(), func, #func, false); +#endif + +/* +================================================================================ + +OpenGL-ES Utility Functions + +================================================================================ +*/ + +static const char* EglErrorString(const EGLint error) { + switch (error) { + case EGL_SUCCESS: + return "EGL_SUCCESS"; + case EGL_NOT_INITIALIZED: + return "EGL_NOT_INITIALIZED"; + case EGL_BAD_ACCESS: + return "EGL_BAD_ACCESS"; + case EGL_BAD_ALLOC: + return "EGL_BAD_ALLOC"; + case EGL_BAD_ATTRIBUTE: + return "EGL_BAD_ATTRIBUTE"; + case EGL_BAD_CONTEXT: + return "EGL_BAD_CONTEXT"; + case EGL_BAD_CONFIG: + return "EGL_BAD_CONFIG"; + case EGL_BAD_CURRENT_SURFACE: + return "EGL_BAD_CURRENT_SURFACE"; + case EGL_BAD_DISPLAY: + return "EGL_BAD_DISPLAY"; + case EGL_BAD_SURFACE: + return "EGL_BAD_SURFACE"; + case EGL_BAD_MATCH: + return "EGL_BAD_MATCH"; + case EGL_BAD_PARAMETER: + return "EGL_BAD_PARAMETER"; + case EGL_BAD_NATIVE_PIXMAP: + return "EGL_BAD_NATIVE_PIXMAP"; + case EGL_BAD_NATIVE_WINDOW: + return "EGL_BAD_NATIVE_WINDOW"; + case EGL_CONTEXT_LOST: + return "EGL_CONTEXT_LOST"; + default: + return "unknown"; + } +} + +static const char* GlFrameBufferStatusString(GLenum status) { + switch (status) { + case GL_FRAMEBUFFER_UNDEFINED: + return "GL_FRAMEBUFFER_UNDEFINED"; + case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: + return "GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT"; + case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: + return "GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT"; + case GL_FRAMEBUFFER_UNSUPPORTED: + return "GL_FRAMEBUFFER_UNSUPPORTED"; + case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE: + return "GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE"; + default: + return "unknown"; + } +} + +#ifdef CHECK_GL_ERRORS + +static const char* GlErrorString(GLenum error) { + switch (error) { + case GL_NO_ERROR: + return "GL_NO_ERROR"; + case GL_INVALID_ENUM: + return "GL_INVALID_ENUM"; + case GL_INVALID_VALUE: + return "GL_INVALID_VALUE"; + case GL_INVALID_OPERATION: + return "GL_INVALID_OPERATION"; + case GL_INVALID_FRAMEBUFFER_OPERATION: + return "GL_INVALID_FRAMEBUFFER_OPERATION"; + case GL_OUT_OF_MEMORY: + return "GL_OUT_OF_MEMORY"; + default: + return "unknown"; + } +} + +static void GLCheckErrors(int line) { + for (int i = 0; i < 10; i++) { + const GLenum error = glGetError(); + if (error == GL_NO_ERROR) { + break; + } + ALOGE("GL error on line %d: %s", line, GlErrorString(error)); + } +} + +#define GL(func) \ + func; \ + GLCheckErrors(__LINE__); + +#else // CHECK_GL_ERRORS + +#define GL(func) func; + +#endif // CHECK_GL_ERRORS + +/* +================================================================================ + +ovrEgl + +================================================================================ +*/ + +typedef struct { + EGLint MajorVersion; + EGLint MinorVersion; + EGLDisplay Display; + EGLConfig Config; + EGLSurface TinySurface; + EGLSurface MainSurface; + EGLContext Context; +} ovrEgl; + +static void ovrEgl_Clear(ovrEgl* egl) { + egl->MajorVersion = 0; + egl->MinorVersion = 0; + egl->Display = 0; + egl->Config = 0; + egl->TinySurface = EGL_NO_SURFACE; + egl->MainSurface = EGL_NO_SURFACE; + egl->Context = EGL_NO_CONTEXT; +} + +/** + * Checks if a given flag @flag is present in the list of flags @flags, and + * logs an error if it isn't. + * @humanName is the human-readable name of the list of flags being checked, + * used for logging. + */ +static bool checkFlagAndLog(EGLint flags, EGLint flag, const char* humanName) { + const bool present = (flags & flag) == flag; + if (!present) + { + ALOGD(" Skipping EGL config because %s %d doesn't have %d flag", humanName, flags, flag); + } + return present; +} + +static void ovrEgl_CreateContext(ovrEgl* egl, const ovrEgl* shareEgl) { + if (egl->Display != 0) { + return; + } + + egl->Display = eglGetDisplay(EGL_DEFAULT_DISPLAY); + ALOGV(" eglInitialize( Display, &MajorVersion, &MinorVersion )"); + eglInitialize(egl->Display, &egl->MajorVersion, &egl->MinorVersion); + // Do NOT use eglChooseConfig, because the Android EGL code pushes in multisample + // flags in eglChooseConfig if the user has selected the "force 4x MSAA" option in + // settings, and that is completely wasted for our warp target. + enum { MAX_CONFIGS = 1024 }; + EGLConfig configs[MAX_CONFIGS]; + EGLint numConfigs = 0; + if (eglGetConfigs(egl->Display, configs, MAX_CONFIGS, &numConfigs) == EGL_FALSE) { + ALOGE(" eglGetConfigs() failed: %s", EglErrorString(eglGetError())); + return; + } + const EGLint configAttribs[] = { + EGL_RED_SIZE, + 8, + EGL_GREEN_SIZE, + 8, + EGL_BLUE_SIZE, + 8, + EGL_ALPHA_SIZE, + 8, // need alpha for the multi-pass timewarp compositor + EGL_DEPTH_SIZE, + 0, + EGL_STENCIL_SIZE, + 0, + EGL_SAMPLES, + 0, + EGL_NONE}; + egl->Config = 0; + ALOGD(" Queried %d EGL configs, evaluating ...", numConfigs); + for (int i = 0; i < numConfigs; i++) { + EGLint value = 0; + + ALOGD(" Evaluating EGL config %d.", i); + eglGetConfigAttrib(egl->Display, configs[i], EGL_RENDERABLE_TYPE, &value); + if (!checkFlagAndLog(value, EGL_OPENGL_ES3_BIT_KHR, "renderable type")) + { + continue; + } + + // The pbuffer config also needs to be compatible with normal window rendering + // so it can share textures with the window context. + eglGetConfigAttrib(egl->Display, configs[i], EGL_SURFACE_TYPE, &value); + if (!checkFlagAndLog(value, EGL_WINDOW_BIT | EGL_PBUFFER_BIT, "surface type")) + { + continue; + } + + int j = 0; + ALOGD(" Checking EGL config attributes to make sure they match what we want..."); + for (; configAttribs[j] != EGL_NONE; j += 2) { + eglGetConfigAttrib(egl->Display, configs[i], configAttribs[j], &value); + if (value != configAttribs[j + 1]) { + ALOGD(" Skipping EGL config due to mismatch in config attribute %d: expected %d, got %d", j / 2, configAttribs[j + 1], value); + break; + } + } + if (configAttribs[j] == EGL_NONE) { + ALOGD(" Successfully picked EGL config %d!", i); + egl->Config = configs[i]; + break; + } + } + if (egl->Config == 0) { + ALOGE(" Failed to pick EGL config!"); + return; + } + EGLint contextAttribs[] = {EGL_CONTEXT_CLIENT_VERSION, 3, EGL_NONE}; + ALOGV(" Context = eglCreateContext( Display, Config, EGL_NO_CONTEXT, contextAttribs )"); + egl->Context = eglCreateContext( + egl->Display, + egl->Config, + (shareEgl != NULL) ? shareEgl->Context : EGL_NO_CONTEXT, + contextAttribs); + if (egl->Context == EGL_NO_CONTEXT) { + ALOGE(" eglCreateContext() failed: %s", EglErrorString(eglGetError())); + return; + } + const EGLint surfaceAttribs[] = {EGL_WIDTH, 16, EGL_HEIGHT, 16, EGL_NONE}; + ALOGV(" TinySurface = eglCreatePbufferSurface( Display, Config, surfaceAttribs )"); + egl->TinySurface = eglCreatePbufferSurface(egl->Display, egl->Config, surfaceAttribs); + if (egl->TinySurface == EGL_NO_SURFACE) { + ALOGE(" eglCreatePbufferSurface() failed: %s", EglErrorString(eglGetError())); + eglDestroyContext(egl->Display, egl->Context); + egl->Context = EGL_NO_CONTEXT; + return; + } + ALOGV(" eglMakeCurrent( Display, TinySurface, TinySurface, Context )"); + if (eglMakeCurrent(egl->Display, egl->TinySurface, egl->TinySurface, egl->Context) == + EGL_FALSE) { + ALOGE(" eglMakeCurrent() failed: %s", EglErrorString(eglGetError())); + eglDestroySurface(egl->Display, egl->TinySurface); + eglDestroyContext(egl->Display, egl->Context); + egl->Context = EGL_NO_CONTEXT; + return; + } +} + +static void ovrEgl_DestroyContext(ovrEgl* egl) { + if (egl->Display != 0) { + ALOGE(" eglMakeCurrent( Display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT )"); + if (eglMakeCurrent(egl->Display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT) == + EGL_FALSE) { + ALOGE(" eglMakeCurrent() failed: %s", EglErrorString(eglGetError())); + } + } + if (egl->Context != EGL_NO_CONTEXT) { + ALOGE(" eglDestroyContext( Display, Context )"); + if (eglDestroyContext(egl->Display, egl->Context) == EGL_FALSE) { + ALOGE(" eglDestroyContext() failed: %s", EglErrorString(eglGetError())); + } + egl->Context = EGL_NO_CONTEXT; + } + if (egl->TinySurface != EGL_NO_SURFACE) { + ALOGE(" eglDestroySurface( Display, TinySurface )"); + if (eglDestroySurface(egl->Display, egl->TinySurface) == EGL_FALSE) { + ALOGE(" eglDestroySurface() failed: %s", EglErrorString(eglGetError())); + } + egl->TinySurface = EGL_NO_SURFACE; + } + if (egl->Display != 0) { + ALOGE(" eglTerminate( Display )"); + if (eglTerminate(egl->Display) == EGL_FALSE) { + ALOGE(" eglTerminate() failed: %s", EglErrorString(eglGetError())); + } + egl->Display = 0; + } +} + +/* +================================================================================ + +ovrGeometry + +================================================================================ +*/ + +typedef struct { + GLint Index; + GLint Size; + GLenum Type; + GLboolean Normalized; + GLsizei Stride; + const GLvoid* Pointer; +} ovrVertexAttribPointer; + +#define MAX_VERTEX_ATTRIB_POINTERS 3 + +typedef struct { + GLuint VertexBuffer; + GLuint IndexBuffer; + GLuint VertexArrayObject; + int VertexCount; + int IndexCount; + ovrVertexAttribPointer VertexAttribs[MAX_VERTEX_ATTRIB_POINTERS]; +} ovrGeometry; + +enum VertexAttributeLocation { + VERTEX_ATTRIBUTE_LOCATION_POSITION, + VERTEX_ATTRIBUTE_LOCATION_COLOR, + VERTEX_ATTRIBUTE_LOCATION_UV, + VERTEX_ATTRIBUTE_LOCATION_TRANSFORM +}; + +typedef struct { + enum VertexAttributeLocation location; + const char* name; +} ovrVertexAttribute; + +static ovrVertexAttribute ProgramVertexAttributes[] = { + {VERTEX_ATTRIBUTE_LOCATION_POSITION, "vertexPosition"}, + {VERTEX_ATTRIBUTE_LOCATION_COLOR, "vertexColor"}, + {VERTEX_ATTRIBUTE_LOCATION_UV, "vertexUv"}, +}; + +static void ovrGeometry_Clear(ovrGeometry* geometry) { + geometry->VertexBuffer = 0; + geometry->IndexBuffer = 0; + geometry->VertexArrayObject = 0; + geometry->VertexCount = 0; + geometry->IndexCount = 0; + for (int i = 0; i < MAX_VERTEX_ATTRIB_POINTERS; i++) { + memset(&geometry->VertexAttribs[i], 0, sizeof(geometry->VertexAttribs[i])); + geometry->VertexAttribs[i].Index = -1; + } +} + +static void ovrGeometry_CreateGroundPlane(ovrGeometry* geometry) { + typedef struct { + float positions[12][4]; + unsigned char colors[12][4]; + } ovrCubeVertices; + + static const ovrCubeVertices cubeVertices = { + // positions + {{4.5f, 0.0f, 4.5f, 1.0f}, + {4.5f, 0.0f, -4.5f, 1.0f}, + {-4.5f, 0.0f, -4.5f, 1.0f}, + {-4.5f, 0.0f, 4.5f, 1.0f}, + + {4.5f, -10.0f, 4.5f, 1.0f}, + {4.5f, -10.0f, -4.5f, 1.0f}, + {-4.5f, -10.0f, -4.5f, 1.0f}, + {-4.5f, -10.0f, 4.5f, 1.0f}, + + {4.5f, 10.0f, 4.5f, 1.0f}, + {4.5f, 10.0f, -4.5f, 1.0f}, + {-4.5f, 10.0f, -4.5f, 1.0f}, + {-4.5f, 10.0f, 4.5f, 1.0f}}, + // colors + {{255, 0, 0, 255}, + {0, 255, 0, 255}, + {0, 0, 255, 255}, + {255, 255, 0, 255}, + + {255, 128, 0, 255}, + {0, 255, 255, 255}, + {0, 0, 255, 255}, + {255, 0, 255, 255}, + + {255, 128, 128, 255}, + {128, 255, 128, 255}, + {128, 128, 255, 255}, + {255, 255, 128, 255}}, + }; + + static const unsigned short cubeIndices[18] = { + 0, + 1, + 2, + 0, + 2, + 3, + + 4, + 5, + 6, + 4, + 6, + 7, + + 8, + 9, + 10, + 8, + 10, + 11, + }; + + geometry->VertexCount = 12; + geometry->IndexCount = 18; + + geometry->VertexAttribs[0].Index = VERTEX_ATTRIBUTE_LOCATION_POSITION; + geometry->VertexAttribs[0].Size = 4; + geometry->VertexAttribs[0].Type = GL_FLOAT; + geometry->VertexAttribs[0].Normalized = false; + geometry->VertexAttribs[0].Stride = sizeof(cubeVertices.positions[0]); + geometry->VertexAttribs[0].Pointer = (const GLvoid*)offsetof(ovrCubeVertices, positions); + + geometry->VertexAttribs[1].Index = VERTEX_ATTRIBUTE_LOCATION_COLOR; + geometry->VertexAttribs[1].Size = 4; + geometry->VertexAttribs[1].Type = GL_UNSIGNED_BYTE; + geometry->VertexAttribs[1].Normalized = true; + geometry->VertexAttribs[1].Stride = sizeof(cubeVertices.colors[0]); + geometry->VertexAttribs[1].Pointer = (const GLvoid*)offsetof(ovrCubeVertices, colors); + + GL(glGenBuffers(1, &geometry->VertexBuffer)); + GL(glBindBuffer(GL_ARRAY_BUFFER, geometry->VertexBuffer)); + GL(glBufferData(GL_ARRAY_BUFFER, sizeof(cubeVertices), &cubeVertices, GL_STATIC_DRAW)); + GL(glBindBuffer(GL_ARRAY_BUFFER, 0)); + + GL(glGenBuffers(1, &geometry->IndexBuffer)); + GL(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, geometry->IndexBuffer)); + GL(glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(cubeIndices), cubeIndices, GL_STATIC_DRAW)); + GL(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)); +} + +static void ovrGeometry_CreateStagePlane( + ovrGeometry* geometry, + float minx, + float minz, + float maxx, + float maxz) { + typedef struct { + float positions[12][4]; + unsigned char colors[12][4]; + } ovrCubeVertices; + + const ovrCubeVertices cubeVertices = { + // positions + {{maxx, 0.0f, maxz, 1.0f}, + {maxx, 0.0f, minz, 1.0f}, + {minx, 0.0f, minz, 1.0f}, + {minx, 0.0f, maxz, 1.0f}, + + {maxx, -3.0f, maxz, 1.0f}, + {maxx, -3.0f, minz, 1.0f}, + {minx, -3.0f, minz, 1.0f}, + {minx, -3.0f, maxz, 1.0f}, + + {maxx, 3.0f, maxz, 1.0f}, + {maxx, 3.0f, minz, 1.0f}, + {minx, 3.0f, minz, 1.0f}, + {minx, 3.0f, maxz, 1.0f}}, + // colors + {{128, 0, 0, 255}, + {0, 128, 0, 255}, + {0, 0, 128, 255}, + {128, 128, 0, 255}, + + {128, 64, 0, 255}, + {0, 128, 128, 255}, + {0, 0, 128, 255}, + {128, 0, 128, 255}, + + {128, 64, 64, 255}, + {64, 128, 64, 255}, + {64, 64, 128, 255}, + {128, 128, 64, 255}}, + }; + + static const unsigned short cubeIndices[18] = { + 0, + 1, + 2, + 0, + 2, + 3, + + 4, + 5, + 6, + 4, + 6, + 7, + + 8, + 9, + 10, + 8, + 10, + 11, + }; + + geometry->VertexCount = 12; + geometry->IndexCount = 18; + + geometry->VertexAttribs[0].Index = VERTEX_ATTRIBUTE_LOCATION_POSITION; + geometry->VertexAttribs[0].Size = 4; + geometry->VertexAttribs[0].Type = GL_FLOAT; + geometry->VertexAttribs[0].Normalized = false; + geometry->VertexAttribs[0].Stride = sizeof(cubeVertices.positions[0]); + geometry->VertexAttribs[0].Pointer = (const GLvoid*)offsetof(ovrCubeVertices, positions); + + geometry->VertexAttribs[1].Index = VERTEX_ATTRIBUTE_LOCATION_COLOR; + geometry->VertexAttribs[1].Size = 4; + geometry->VertexAttribs[1].Type = GL_UNSIGNED_BYTE; + geometry->VertexAttribs[1].Normalized = true; + geometry->VertexAttribs[1].Stride = sizeof(cubeVertices.colors[0]); + geometry->VertexAttribs[1].Pointer = (const GLvoid*)offsetof(ovrCubeVertices, colors); + + GL(glGenBuffers(1, &geometry->VertexBuffer)); + GL(glBindBuffer(GL_ARRAY_BUFFER, geometry->VertexBuffer)); + GL(glBufferData(GL_ARRAY_BUFFER, sizeof(cubeVertices), &cubeVertices, GL_STATIC_DRAW)); + GL(glBindBuffer(GL_ARRAY_BUFFER, 0)); + + GL(glGenBuffers(1, &geometry->IndexBuffer)); + GL(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, geometry->IndexBuffer)); + GL(glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(cubeIndices), cubeIndices, GL_STATIC_DRAW)); + GL(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)); +} + +static void ovrGeometry_CreateBox(ovrGeometry* geometry) { + typedef struct { + float positions[8][4]; + unsigned char colors[8][4]; + } ovrCubeVertices; + + static const ovrCubeVertices cubeVertices = { + // positions + {{-1.0f, -1.0f, -1.0f, 1.0f}, + {1.0f, -1.0f, -1.0f, 1.0f}, + {-1.0f, 1.0f, -1.0f, 1.0f}, + {1.0f, 1.0f, -1.0f, 1.0f}, + + {-1.0f, -1.0f, 1.0f, 1.0f}, + {1.0f, -1.0f, 1.0f, 1.0f}, + {-1.0f, 1.0f, 1.0f, 1.0f}, + {1.0f, 1.0f, 1.0f, 1.0f}}, + // colors + { + {255, 0, 0, 255}, + {250, 255, 0, 255}, + {250, 0, 255, 255}, + {255, 255, 0, 255}, + {255, 0, 0, 255}, + {250, 255, 0, 255}, + {250, 0, 255, 255}, + {255, 255, 0, 255}, + }, + }; + + // 6------7 + // /| /| + // 2-+----3 | + // | | | | + // | 4----+-5 + // |/ |/ + // 0------1 + + static const unsigned short cubeIndices[36] = {0, 1, 3, 0, 3, 2, + + 5, 4, 6, 5, 6, 7, + + 4, 0, 2, 4, 2, 6, + + 1, 5, 7, 1, 7, 3, + + 4, 5, 1, 4, 1, 0, + + 2, 3, 7, 2, 7, 6}; + + geometry->VertexCount = 8; + geometry->IndexCount = 36; + + geometry->VertexAttribs[0].Index = VERTEX_ATTRIBUTE_LOCATION_POSITION; + geometry->VertexAttribs[0].Size = 4; + geometry->VertexAttribs[0].Type = GL_FLOAT; + geometry->VertexAttribs[0].Normalized = false; + geometry->VertexAttribs[0].Stride = sizeof(cubeVertices.positions[0]); + geometry->VertexAttribs[0].Pointer = (const GLvoid*)offsetof(ovrCubeVertices, positions); + + geometry->VertexAttribs[1].Index = VERTEX_ATTRIBUTE_LOCATION_COLOR; + geometry->VertexAttribs[1].Size = 4; + geometry->VertexAttribs[1].Type = GL_UNSIGNED_BYTE; + geometry->VertexAttribs[1].Normalized = true; + geometry->VertexAttribs[1].Stride = sizeof(cubeVertices.colors[0]); + geometry->VertexAttribs[1].Pointer = (const GLvoid*)offsetof(ovrCubeVertices, colors); + + GL(glGenBuffers(1, &geometry->VertexBuffer)); + GL(glBindBuffer(GL_ARRAY_BUFFER, geometry->VertexBuffer)); + GL(glBufferData(GL_ARRAY_BUFFER, sizeof(cubeVertices), &cubeVertices, GL_STATIC_DRAW)); + GL(glBindBuffer(GL_ARRAY_BUFFER, 0)); + + GL(glGenBuffers(1, &geometry->IndexBuffer)); + GL(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, geometry->IndexBuffer)); + GL(glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(cubeIndices), cubeIndices, GL_STATIC_DRAW)); + GL(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)); +} + +static void ovrGeometry_Destroy(ovrGeometry* geometry) { + GL(glDeleteBuffers(1, &geometry->IndexBuffer)); + GL(glDeleteBuffers(1, &geometry->VertexBuffer)); + + ovrGeometry_Clear(geometry); +} + +static void ovrGeometry_CreateVAO(ovrGeometry* geometry) { + GL(glGenVertexArrays(1, &geometry->VertexArrayObject)); + GL(glBindVertexArray(geometry->VertexArrayObject)); + + GL(glBindBuffer(GL_ARRAY_BUFFER, geometry->VertexBuffer)); + + for (int i = 0; i < MAX_VERTEX_ATTRIB_POINTERS; i++) { + if (geometry->VertexAttribs[i].Index != -1) { + GL(glEnableVertexAttribArray(geometry->VertexAttribs[i].Index)); + GL(glVertexAttribPointer( + geometry->VertexAttribs[i].Index, + geometry->VertexAttribs[i].Size, + geometry->VertexAttribs[i].Type, + geometry->VertexAttribs[i].Normalized, + geometry->VertexAttribs[i].Stride, + geometry->VertexAttribs[i].Pointer)); + } + } + + GL(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, geometry->IndexBuffer)); + + GL(glBindVertexArray(0)); +} + +static void ovrGeometry_DestroyVAO(ovrGeometry* geometry) { + GL(glDeleteVertexArrays(1, &geometry->VertexArrayObject)); +} + +/* +================================================================================ + +ovrProgram + +================================================================================ +*/ + +#define MAX_PROGRAM_UNIFORMS 8 +#define MAX_PROGRAM_TEXTURES 8 + +typedef struct { + GLuint Program; + GLuint VertexShader; + GLuint FragmentShader; + // These will be -1 if not used by the program. + GLint UniformLocation[MAX_PROGRAM_UNIFORMS]; // ProgramUniforms[].name + GLint UniformBinding[MAX_PROGRAM_UNIFORMS]; // ProgramUniforms[].name + GLint Textures[MAX_PROGRAM_TEXTURES]; // Texture%i +} ovrProgram; + +typedef enum ovrUniformIndex { + MODEL_MATRIX, + VIEW_PROJ_MATRIX, + SCENE_MATRICES, +} ovrUniformIndex; + +typedef enum ovrUniformType { + VECTOR4, + MATRIX4X4, + INTEGER, + BUFFER, +} ovrUniformType; + +typedef struct { + ovrUniformIndex index; + ovrUniformType type; + const char* name; +} ovrUniform; + +static ovrUniform ProgramUniforms[] = { + {MODEL_MATRIX, MATRIX4X4, "modelMatrix"}, + {VIEW_PROJ_MATRIX, MATRIX4X4, "viewProjectionMatrix"}, +}; + +static void ovrProgram_Clear(ovrProgram* program) { + program->Program = 0; + program->VertexShader = 0; + program->FragmentShader = 0; + memset(program->UniformLocation, 0, sizeof(program->UniformLocation)); + memset(program->UniformBinding, 0, sizeof(program->UniformBinding)); + memset(program->Textures, 0, sizeof(program->Textures)); +} + +static bool +ovrProgram_Create(ovrProgram* program, const char* vertexSource, const char* fragmentSource) { + GLint r; + + GL(program->VertexShader = glCreateShader(GL_VERTEX_SHADER)); + + GL(glShaderSource(program->VertexShader, 1, &vertexSource, 0)); + GL(glCompileShader(program->VertexShader)); + GL(glGetShaderiv(program->VertexShader, GL_COMPILE_STATUS, &r)); + if (r == GL_FALSE) { + GLchar msg[4096]; + GL(glGetShaderInfoLog(program->VertexShader, sizeof(msg), 0, msg)); + ALOGE("%s\n%s\n", vertexSource, msg); + return false; + } + + GL(program->FragmentShader = glCreateShader(GL_FRAGMENT_SHADER)); + GL(glShaderSource(program->FragmentShader, 1, &fragmentSource, 0)); + GL(glCompileShader(program->FragmentShader)); + GL(glGetShaderiv(program->FragmentShader, GL_COMPILE_STATUS, &r)); + if (r == GL_FALSE) { + GLchar msg[4096]; + GL(glGetShaderInfoLog(program->FragmentShader, sizeof(msg), 0, msg)); + ALOGE("%s\n%s\n", fragmentSource, msg); + return false; + } + + GL(program->Program = glCreateProgram()); + GL(glAttachShader(program->Program, program->VertexShader)); + GL(glAttachShader(program->Program, program->FragmentShader)); + + // Bind the vertex attribute locations. + for (size_t i = 0; i < sizeof(ProgramVertexAttributes) / sizeof(ProgramVertexAttributes[0]); + i++) { + GL(glBindAttribLocation( + program->Program, + ProgramVertexAttributes[i].location, + ProgramVertexAttributes[i].name)); + } + + GL(glLinkProgram(program->Program)); + GL(glGetProgramiv(program->Program, GL_LINK_STATUS, &r)); + if (r == GL_FALSE) { + GLchar msg[4096]; + GL(glGetProgramInfoLog(program->Program, sizeof(msg), 0, msg)); + ALOGE("Linking program failed: %s\n", msg); + return false; + } + + int numBufferBindings = 0; + + // Get the uniform locations. + memset(program->UniformLocation, -1, sizeof(program->UniformLocation)); + for (size_t i = 0; i < sizeof(ProgramUniforms) / sizeof(ProgramUniforms[0]); i++) { + const int uniformIndex = ProgramUniforms[i].index; + if (ProgramUniforms[i].type == BUFFER) { + GL(program->UniformLocation[uniformIndex] = + glGetUniformBlockIndex(program->Program, ProgramUniforms[i].name)); + program->UniformBinding[uniformIndex] = numBufferBindings++; + GL(glUniformBlockBinding( + program->Program, + program->UniformLocation[uniformIndex], + program->UniformBinding[uniformIndex])); + } else { + GL(program->UniformLocation[uniformIndex] = + glGetUniformLocation(program->Program, ProgramUniforms[i].name)); + program->UniformBinding[uniformIndex] = program->UniformLocation[uniformIndex]; + } + } + + GL(glUseProgram(program->Program)); + + // Get the texture locations. + for (int i = 0; i < MAX_PROGRAM_TEXTURES; i++) { + char name[32]; + sprintf(name, "Texture%i", i); + program->Textures[i] = glGetUniformLocation(program->Program, name); + if (program->Textures[i] != -1) { + GL(glUniform1i(program->Textures[i], i)); + } + } + + GL(glUseProgram(0)); + + return true; +} + +static void ovrProgram_Destroy(ovrProgram* program) { + if (program->Program != 0) { + GL(glDeleteProgram(program->Program)); + program->Program = 0; + } + if (program->VertexShader != 0) { + GL(glDeleteShader(program->VertexShader)); + program->VertexShader = 0; + } + if (program->FragmentShader != 0) { + GL(glDeleteShader(program->FragmentShader)); + program->FragmentShader = 0; + } +} + +static const char VERTEX_SHADER[] = + "#version 300 es\n" + "in vec3 vertexPosition;\n" + "in vec4 vertexColor;\n" + "uniform mat4 viewProjectionMatrix;\n" + "uniform mat4 modelMatrix;\n" + "out vec4 fragmentColor;\n" + "void main()\n" + "{\n" + " gl_Position = viewProjectionMatrix * ( modelMatrix * vec4( vertexPosition, 1.0 ) );\n" + " fragmentColor = vertexColor;\n" + "}\n"; + +static const char FRAGMENT_SHADER[] = + "#version 300 es\n" + "in lowp vec4 fragmentColor;\n" + "out lowp vec4 outColor;\n" + "void main()\n" + "{\n" + " outColor = fragmentColor;\n" + "}\n"; + +typedef struct { + XrSwapchain Handle; + uint32_t Width; + uint32_t Height; +} ovrSwapChain; + +/* +================================================================================ + +ovrFramebuffer + +================================================================================ +*/ + +typedef struct { + int Width; + int Height; + int Multisamples; + uint32_t TextureSwapChainLength; + uint32_t TextureSwapChainIndex; + ovrSwapChain ColorSwapChain; + XrSwapchainImageOpenGLESKHR* ColorSwapChainImage; + GLuint* DepthBuffers; + GLuint* FrameBuffers; +} ovrFramebuffer; + +static void ovrFramebuffer_Clear(ovrFramebuffer* frameBuffer) { + frameBuffer->Width = 0; + frameBuffer->Height = 0; + frameBuffer->Multisamples = 0; + frameBuffer->TextureSwapChainLength = 0; + frameBuffer->TextureSwapChainIndex = 0; + frameBuffer->ColorSwapChain.Handle = XR_NULL_HANDLE; + frameBuffer->ColorSwapChain.Width = 0; + frameBuffer->ColorSwapChain.Height = 0; + frameBuffer->ColorSwapChainImage = NULL; + frameBuffer->DepthBuffers = NULL; + frameBuffer->FrameBuffers = NULL; +} + +static bool ovrFramebuffer_Create( + XrSession session, + ovrFramebuffer* frameBuffer, + const GLenum colorFormat, + const int width, + const int height, + const int multisamples) { + PFNGLRENDERBUFFERSTORAGEMULTISAMPLEEXTPROC glRenderbufferStorageMultisampleEXT = + (PFNGLRENDERBUFFERSTORAGEMULTISAMPLEEXTPROC)eglGetProcAddress( + "glRenderbufferStorageMultisampleEXT"); + PFNGLFRAMEBUFFERTEXTURE2DMULTISAMPLEEXTPROC glFramebufferTexture2DMultisampleEXT = + (PFNGLFRAMEBUFFERTEXTURE2DMULTISAMPLEEXTPROC)eglGetProcAddress( + "glFramebufferTexture2DMultisampleEXT"); + + frameBuffer->Width = width; + frameBuffer->Height = height; + frameBuffer->Multisamples = multisamples; + + GLenum requestedGLFormat = colorFormat; + + // Get the number of supported formats. + uint32_t numInputFormats = 0; + uint32_t numOutputFormats = 0; + OXR(xrEnumerateSwapchainFormats(session, numInputFormats, &numOutputFormats, NULL)); + + // Allocate an array large enough to contain the supported formats. + numInputFormats = numOutputFormats; + int64_t* supportedFormats = (int64_t*)malloc(numOutputFormats * sizeof(int64_t)); + if (supportedFormats != NULL) { + OXR(xrEnumerateSwapchainFormats( + session, numInputFormats, &numOutputFormats, supportedFormats)); + } + + // Verify the requested format is supported. + uint64_t selectedFormat = 0; + for (uint32_t i = 0; i < numOutputFormats; i++) { + if (supportedFormats[i] == requestedGLFormat) { + selectedFormat = supportedFormats[i]; + break; + } + } + + free(supportedFormats); + + if (selectedFormat == 0) { + ALOGE("Format not supported"); + } + + XrSwapchainCreateInfo swapChainCreateInfo = {XR_TYPE_SWAPCHAIN_CREATE_INFO}; + swapChainCreateInfo.usageFlags = + XR_SWAPCHAIN_USAGE_SAMPLED_BIT | XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT; + swapChainCreateInfo.format = selectedFormat; + swapChainCreateInfo.sampleCount = 1; + swapChainCreateInfo.width = width; + swapChainCreateInfo.height = height; + swapChainCreateInfo.faceCount = 1; + swapChainCreateInfo.arraySize = 1; + swapChainCreateInfo.mipCount = 1; + + // Enable Foveation on this swapchain + XrSwapchainCreateInfoFoveationFB swapChainFoveationCreateInfo = {XR_TYPE_SWAPCHAIN_CREATE_INFO_FOVEATION_FB}; + swapChainCreateInfo.next = &swapChainFoveationCreateInfo; + + frameBuffer->ColorSwapChain.Width = swapChainCreateInfo.width; + frameBuffer->ColorSwapChain.Height = swapChainCreateInfo.height; + + // Create the swapchain. + OXR(xrCreateSwapchain(session, &swapChainCreateInfo, &frameBuffer->ColorSwapChain.Handle)); + // Get the number of swapchain images. + OXR(xrEnumerateSwapchainImages( + frameBuffer->ColorSwapChain.Handle, 0, &frameBuffer->TextureSwapChainLength, NULL)); + // Allocate the swapchain images array. + frameBuffer->ColorSwapChainImage = (XrSwapchainImageOpenGLESKHR*)malloc( + frameBuffer->TextureSwapChainLength * sizeof(XrSwapchainImageOpenGLESKHR)); + + // Populate the swapchain image array. + for (uint32_t i = 0; i < frameBuffer->TextureSwapChainLength; i++) { + frameBuffer->ColorSwapChainImage[i].type = XR_TYPE_SWAPCHAIN_IMAGE_OPENGL_ES_KHR; + frameBuffer->ColorSwapChainImage[i].next = NULL; + } + OXR(xrEnumerateSwapchainImages( + frameBuffer->ColorSwapChain.Handle, + frameBuffer->TextureSwapChainLength, + &frameBuffer->TextureSwapChainLength, + (XrSwapchainImageBaseHeader*)frameBuffer->ColorSwapChainImage)); + + frameBuffer->DepthBuffers = + (GLuint*)malloc(frameBuffer->TextureSwapChainLength * sizeof(GLuint)); + frameBuffer->FrameBuffers = + (GLuint*)malloc(frameBuffer->TextureSwapChainLength * sizeof(GLuint)); + + for (uint32_t i = 0; i < frameBuffer->TextureSwapChainLength; i++) { + // Create the color buffer texture. + const GLuint colorTexture = frameBuffer->ColorSwapChainImage[i].image; + + GLenum colorTextureTarget = GL_TEXTURE_2D; + GL(glBindTexture(colorTextureTarget, colorTexture)); + GL(glTexParameteri(colorTextureTarget, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)); + GL(glTexParameteri(colorTextureTarget, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)); + GL(glTexParameteri(colorTextureTarget, GL_TEXTURE_MIN_FILTER, GL_LINEAR)); + GL(glTexParameteri(colorTextureTarget, GL_TEXTURE_MAG_FILTER, GL_LINEAR)); + GL(glBindTexture(colorTextureTarget, 0)); + + if (multisamples > 1 && glRenderbufferStorageMultisampleEXT != NULL && + glFramebufferTexture2DMultisampleEXT != NULL) { + // Create multisampled depth buffer. + GL(glGenRenderbuffers(1, &frameBuffer->DepthBuffers[i])); + GL(glBindRenderbuffer(GL_RENDERBUFFER, frameBuffer->DepthBuffers[i])); + GL(glRenderbufferStorageMultisampleEXT( + GL_RENDERBUFFER, multisamples, GL_DEPTH_COMPONENT24, width, height)); + GL(glBindRenderbuffer(GL_RENDERBUFFER, 0)); + + // Create the frame buffer. + // NOTE: glFramebufferTexture2DMultisampleEXT only works with GL_FRAMEBUFFER. + GL(glGenFramebuffers(1, &frameBuffer->FrameBuffers[i])); + GL(glBindFramebuffer(GL_FRAMEBUFFER, frameBuffer->FrameBuffers[i])); + GL(glFramebufferTexture2DMultisampleEXT( + GL_FRAMEBUFFER, + GL_COLOR_ATTACHMENT0, + GL_TEXTURE_2D, + colorTexture, + 0, + multisamples)); + GL(glFramebufferRenderbuffer( + GL_FRAMEBUFFER, + GL_DEPTH_ATTACHMENT, + GL_RENDERBUFFER, + frameBuffer->DepthBuffers[i])); + GL(GLenum renderFramebufferStatus = glCheckFramebufferStatus(GL_FRAMEBUFFER)); + GL(glBindFramebuffer(GL_FRAMEBUFFER, 0)); + if (renderFramebufferStatus != GL_FRAMEBUFFER_COMPLETE) { + ALOGE( + "Incomplete frame buffer object: %s", + GlFrameBufferStatusString(renderFramebufferStatus)); + return false; + } + } else { + // Create depth buffer. + GL(glGenRenderbuffers(1, &frameBuffer->DepthBuffers[i])); + GL(glBindRenderbuffer(GL_RENDERBUFFER, frameBuffer->DepthBuffers[i])); + GL(glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, width, height)); + GL(glBindRenderbuffer(GL_RENDERBUFFER, 0)); + + // Create the frame buffer. + GL(glGenFramebuffers(1, &frameBuffer->FrameBuffers[i])); + GL(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, frameBuffer->FrameBuffers[i])); + GL(glFramebufferRenderbuffer( + GL_DRAW_FRAMEBUFFER, + GL_DEPTH_ATTACHMENT, + GL_RENDERBUFFER, + frameBuffer->DepthBuffers[i])); + GL(glFramebufferTexture2D( + GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, colorTexture, 0)); + GL(GLenum renderFramebufferStatus = glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER)); + GL(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0)); + if (renderFramebufferStatus != GL_FRAMEBUFFER_COMPLETE) { + ALOGE( + "Incomplete frame buffer object: %s", + GlFrameBufferStatusString(renderFramebufferStatus)); + return false; + } + } + } + + return true; +} + +static void ovrFramebuffer_Destroy(ovrFramebuffer* frameBuffer) { + GL(glDeleteFramebuffers(frameBuffer->TextureSwapChainLength, frameBuffer->FrameBuffers)); + GL(glDeleteRenderbuffers(frameBuffer->TextureSwapChainLength, frameBuffer->DepthBuffers)); + OXR(xrDestroySwapchain(frameBuffer->ColorSwapChain.Handle)); + free(frameBuffer->ColorSwapChainImage); + + free(frameBuffer->DepthBuffers); + free(frameBuffer->FrameBuffers); + + ovrFramebuffer_Clear(frameBuffer); +} + +static void ovrFramebuffer_SetCurrent(ovrFramebuffer* frameBuffer) { + GL(glBindFramebuffer( + GL_DRAW_FRAMEBUFFER, frameBuffer->FrameBuffers[frameBuffer->TextureSwapChainIndex])); +} + +static void ovrFramebuffer_SetNone() { + GL(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0)); +} + +static void ovrFramebuffer_Resolve(ovrFramebuffer* frameBuffer) { + // Discard the depth buffer, so the tiler won't need to write it back out to memory. + const GLenum depthAttachment[1] = {GL_DEPTH_ATTACHMENT}; + glInvalidateFramebuffer(GL_DRAW_FRAMEBUFFER, 1, depthAttachment); + + // We now let the resolve happen implicitly. +} + +static void ovrFramebuffer_Acquire(ovrFramebuffer* frameBuffer) { + // Acquire the swapchain image + XrSwapchainImageAcquireInfo acquireInfo = {XR_TYPE_SWAPCHAIN_IMAGE_ACQUIRE_INFO}; + OXR(xrAcquireSwapchainImage( + frameBuffer->ColorSwapChain.Handle, &acquireInfo, &frameBuffer->TextureSwapChainIndex)); + + XrSwapchainImageWaitInfo waitInfo = {XR_TYPE_SWAPCHAIN_IMAGE_WAIT_INFO}; + waitInfo.timeout = 1000000000; /* timeout in nanoseconds */ + XrResult res = xrWaitSwapchainImage(frameBuffer->ColorSwapChain.Handle, &waitInfo); + int i = 0; + while (res == XR_TIMEOUT_EXPIRED) { + res = xrWaitSwapchainImage(frameBuffer->ColorSwapChain.Handle, &waitInfo); + i++; + ALOGV( + " Retry xrWaitSwapchainImage %d times due to XR_TIMEOUT_EXPIRED (duration %f seconds)", + i, + waitInfo.timeout * (1E-9)); + } +} + +static void ovrFramebuffer_Release(ovrFramebuffer* frameBuffer) { + XrSwapchainImageReleaseInfo releaseInfo = {XR_TYPE_SWAPCHAIN_IMAGE_RELEASE_INFO}; + OXR(xrReleaseSwapchainImage(frameBuffer->ColorSwapChain.Handle, &releaseInfo)); +} + +/* +================================================================================ + +KTX Loading + +================================================================================ +*/ + +typedef struct { + int width; + int height; + int depth; + GLenum internalFormat; + GLenum target; + int numberOfArrayElements; + int numberOfFaces; + int numberOfMipmapLevels; + bool mipSizeStored; + + const void* data; // caller responsible for freeing memory + int dataOffset; + int dataSize; +} ktxImageInfo_t; + +bool LoadImageDataFromKTXFile(AAssetManager* amgr, const char* assetName, ktxImageInfo_t* outInfo) { +#pragma pack(1) + typedef struct { + unsigned char identifier[12]; + unsigned int endianness; + unsigned int glType; + unsigned int glTypeSize; + unsigned int glFormat; + unsigned int glInternalFormat; + unsigned int glBaseInternalFormat; + unsigned int pixelWidth; + unsigned int pixelHeight; + unsigned int pixelDepth; + unsigned int numberOfArrayElements; + unsigned int numberOfFaces; + unsigned int numberOfMipmapLevels; + unsigned int bytesOfKeyValueData; + } GlHeaderKTX_t; +#pragma pack() + + AAsset* asset = AAssetManager_open(amgr, assetName, AASSET_MODE_BUFFER); + if (asset == NULL) { + ALOGE("Failed to open %s", assetName); + return false; + } + const void* buffer = AAsset_getBuffer(asset); + if (buffer == NULL) { + ALOGE("Failed to read %s", assetName); + return false; + } + size_t bufferSize = AAsset_getLength(asset); + + if (bufferSize < sizeof(GlHeaderKTX_t)) { + ALOGE("%s: Invalid KTX file", assetName); + return false; + } + + const unsigned char fileIdentifier[12] = { + (unsigned char)'\xAB', + 'K', + 'T', + 'X', + ' ', + '1', + '1', + (unsigned char)'\xBB', + '\r', + '\n', + '\x1A', + '\n'}; + + const GlHeaderKTX_t* header = (GlHeaderKTX_t*)buffer; + if (memcmp(header->identifier, fileIdentifier, sizeof(fileIdentifier)) != 0) { + ALOGE("%s: Invalid KTX file", assetName); + return false; + } + // only support little endian + if (header->endianness != 0x04030201) { + ALOGE("%s: KTX file has wrong endianness", assetName); + return false; + } + // only support unsigned byte + if (header->glType != GL_UNSIGNED_BYTE) { + ALOGE("%s: KTX file has unsupported glType %d", assetName, header->glType); + return false; + } + // skip the key value data + const size_t startTex = sizeof(GlHeaderKTX_t) + header->bytesOfKeyValueData; + if ((startTex < sizeof(GlHeaderKTX_t)) || (startTex >= bufferSize)) { + ALOGE("%s: Invalid KTX header sizes", assetName); + return false; + } + + outInfo->width = header->pixelWidth; + outInfo->height = header->pixelHeight; + outInfo->depth = header->pixelDepth; + outInfo->internalFormat = header->glInternalFormat; + outInfo->numberOfArrayElements = + (header->numberOfArrayElements >= 1) ? header->numberOfArrayElements : 1; + outInfo->numberOfFaces = (header->numberOfFaces >= 1) ? header->numberOfFaces : 1; + outInfo->numberOfMipmapLevels = header->numberOfMipmapLevels; + outInfo->mipSizeStored = true; + outInfo->data = buffer; + outInfo->dataOffset = startTex; + outInfo->dataSize = bufferSize; + + outInfo->target = + ((outInfo->depth > 1) + ? GL_TEXTURE_3D + : ((outInfo->numberOfFaces > 1) + ? ((outInfo->numberOfArrayElements > 1) ? GL_TEXTURE_CUBE_MAP_ARRAY + : GL_TEXTURE_CUBE_MAP) + : ((outInfo->numberOfArrayElements > 1) ? GL_TEXTURE_2D_ARRAY + : GL_TEXTURE_2D))); + + return true; +} + +bool LoadTextureFromKTXImageMemory(const ktxImageInfo_t* info, const int textureId) { + if (info == NULL) { + return false; + } + + if (info->data != NULL) { + GL(glBindTexture(info->target, textureId)); + + const int numDataLevels = info->numberOfMipmapLevels; + const unsigned char* levelData = (const unsigned char*)(info->data) + info->dataOffset; + const unsigned char* endOfBuffer = levelData + (info->dataSize - info->dataOffset); + + for (int i = 0; i < numDataLevels; i++) { + const int w = (info->width >> i) >= 1 ? (info->width >> i) : 1; + const int h = (info->height >> i) >= 1 ? (info->height >> i) : 1; + const int d = (info->depth >> i) >= 1 ? (info->depth >> i) : 1; + + size_t mipSize = 0; + bool compressed = false; + GLenum glFormat = GL_RGBA; + GLenum glDataType = GL_UNSIGNED_BYTE; + switch (info->internalFormat) { + case GL_R8: { + mipSize = w * h * d * 1 * sizeof(unsigned char); + glFormat = GL_RED; + glDataType = GL_UNSIGNED_BYTE; + break; + } + case GL_RG8: { + mipSize = w * h * d * 2 * sizeof(unsigned char); + glFormat = GL_RG; + glDataType = GL_UNSIGNED_BYTE; + break; + } + case GL_RGB8: { + mipSize = w * h * d * 3 * sizeof(unsigned char); + glFormat = GL_RGB; + glDataType = GL_UNSIGNED_BYTE; + break; + } + case GL_RGBA8: { + mipSize = w * h * d * 4 * sizeof(unsigned char); + glFormat = GL_RGBA; + glDataType = GL_UNSIGNED_BYTE; + break; + } + } + if (mipSize == 0) { + ALOGE("Unsupported image format %d", info->internalFormat); + GL(glBindTexture(info->target, 0)); + return false; + } + + if (info->numberOfArrayElements > 1) { + mipSize = mipSize * info->numberOfArrayElements * info->numberOfFaces; + } + + // ALOGV( "mipSize%d = %zu (%d)", i, mipSize, info->numberOfMipmapLevels ); + + if (info->mipSizeStored) { + if (levelData + 4 > endOfBuffer) { + ALOGE("Image data exceeds buffer size"); + GL(glBindTexture(info->target, 0)); + return false; + } + const size_t storedMipSize = (size_t) * (const unsigned int*)levelData; + // ALOGV( "storedMipSize = %zu", storedMipSize ); + mipSize = storedMipSize; + levelData += 4; + } + + if (info->depth <= 1 && info->numberOfArrayElements <= 1) { + for (int face = 0; face < info->numberOfFaces; face++) { + if (mipSize <= 0 || mipSize > (size_t)(endOfBuffer - levelData)) { + ALOGE( + "Mip %d data exceeds buffer size (%zu > %zu)", + i, + mipSize, + (endOfBuffer - levelData)); + GL(glBindTexture(info->target, 0)); + return false; + } + + const GLenum uploadTarget = (info->target == GL_TEXTURE_CUBE_MAP) + ? GL_TEXTURE_CUBE_MAP_POSITIVE_X + : GL_TEXTURE_2D; + if (compressed) { + GL(glCompressedTexSubImage2D( + uploadTarget + face, + i, + 0, + 0, + w, + h, + info->internalFormat, + (GLsizei)mipSize, + levelData)); + } else { + GL(glTexSubImage2D( + uploadTarget + face, i, 0, 0, w, h, glFormat, glDataType, levelData)); + } + + levelData += mipSize; + + if (info->mipSizeStored) { + levelData += 3 - ((mipSize + 3) % 4); + if (levelData > endOfBuffer) { + ALOGE("Image data exceeds buffer size"); + GL(glBindTexture(info->target, 0)); + return false; + } + } + } + } else { + if (mipSize <= 0 || mipSize > (size_t)(endOfBuffer - levelData)) { + ALOGE( + "Mip %d data exceeds buffer size (%zu > %zu)", + i, + mipSize, + (endOfBuffer - levelData)); + GL(glBindTexture(info->target, 0)); + return false; + } + + if (compressed) { + GL(glCompressedTexSubImage3D( + info->target, + i, + 0, + 0, + 0, + w, + h, + d * info->numberOfArrayElements, + info->internalFormat, + (GLsizei)mipSize, + levelData)); + } else { + GL(glTexSubImage3D( + info->target, + i, + 0, + 0, + 0, + w, + h, + d * info->numberOfArrayElements, + glFormat, + glDataType, + levelData)); + } + + levelData += mipSize; + + if (info->mipSizeStored) { + levelData += 3 - ((mipSize + 3) % 4); + if (levelData > endOfBuffer) { + ALOGE("Image data exceeds buffer size"); + GL(glBindTexture(info->target, 0)); + return false; + } + } + } + } + + GL(glTexParameteri( + info->target, + GL_TEXTURE_MIN_FILTER, + (info->numberOfMipmapLevels > 1) ? GL_LINEAR_MIPMAP_LINEAR : GL_LINEAR)); + GL(glTexParameteri(info->target, GL_TEXTURE_MAG_FILTER, GL_LINEAR)); + + GL(glBindTexture(info->target, 0)); + } + + return true; +} + +/* +================================================================================ + +ovrTrackedController + +================================================================================ +*/ + +typedef struct { + bool Active; + XrPosef Pose; +} ovrTrackedController; + +static void ovrTrackedController_Clear(ovrTrackedController* controller) { + controller->Active = false; + XrPosef_CreateIdentity(&controller->Pose); +} + +/* +================================================================================ + +ovrScene + +================================================================================ +*/ + +enum ovrBackgroundType { + BACKGROUND_NONE, + BACKGROUND_CUBEMAP, + BACKGROUND_EQUIRECT, + MAX_BACKGROUND_TYPES +}; + +typedef struct { + bool CreatedScene; + bool CreatedVAOs; + ovrProgram Program; + ovrGeometry GroundPlane; + ovrGeometry Box; + ovrTrackedController TrackedController[4]; // left aim, left grip, right aim, right grip + + ovrSwapChain CubeMapSwapChain; + XrSwapchainImageOpenGLESKHR* CubeMapSwapChainImage; + ovrSwapChain EquirectSwapChain; + XrSwapchainImageOpenGLESKHR* EquirectSwapChainImage; + ovrSwapChain CylinderSwapChain; + XrSwapchainImageOpenGLESKHR* CylinderSwapChainImage; + ovrSwapChain QuadSwapChain; + XrSwapchainImageOpenGLESKHR* QuadSwapChainImage; + + enum ovrBackgroundType BackGroundType; +} ovrScene; + +static void ovrScene_Clear(ovrScene* scene) { + scene->CreatedScene = false; + scene->CreatedVAOs = false; + ovrProgram_Clear(&scene->Program); + ovrGeometry_Clear(&scene->GroundPlane); + ovrGeometry_Clear(&scene->Box); + for (int i = 0; i < 4; i++) { + ovrTrackedController_Clear(&scene->TrackedController[i]); + } + + scene->CubeMapSwapChain.Handle = XR_NULL_HANDLE; + scene->CubeMapSwapChain.Width = 0; + scene->CubeMapSwapChain.Height = 0; + scene->CubeMapSwapChainImage = NULL; + scene->EquirectSwapChain.Handle = XR_NULL_HANDLE; + scene->EquirectSwapChain.Width = 0; + scene->EquirectSwapChain.Height = 0; + scene->EquirectSwapChainImage = NULL; + scene->CylinderSwapChain.Handle = XR_NULL_HANDLE; + scene->CylinderSwapChain.Width = 0; + scene->CylinderSwapChain.Height = 0; + scene->CylinderSwapChainImage = NULL; + scene->QuadSwapChain.Handle = XR_NULL_HANDLE; + scene->QuadSwapChain.Width = 0; + scene->QuadSwapChain.Height = 0; + scene->QuadSwapChainImage = NULL; + + scene->BackGroundType = BACKGROUND_EQUIRECT; +} + +static bool ovrScene_IsCreated(ovrScene* scene) { + return scene->CreatedScene; +} + +static void ovrScene_CreateVAOs(ovrScene* scene) { + if (!scene->CreatedVAOs) { + ovrGeometry_CreateVAO(&scene->GroundPlane); + ovrGeometry_CreateVAO(&scene->Box); + scene->CreatedVAOs = true; + } +} + +static void ovrScene_DestroyVAOs(ovrScene* scene) { + if (scene->CreatedVAOs) { + ovrGeometry_DestroyVAO(&scene->GroundPlane); + ovrGeometry_DestroyVAO(&scene->Box); + scene->CreatedVAOs = false; + } +} + +static void +ovrScene_Create(AAssetManager* amgr, XrInstance instance, XrSession session, ovrScene* scene) { + // Simple ground plane and box geometry. + { + ovrProgram_Create(&scene->Program, VERTEX_SHADER, FRAGMENT_SHADER); + ovrGeometry_CreateGroundPlane(&scene->GroundPlane); + ovrGeometry_CreateBox(&scene->Box); + + ovrScene_CreateVAOs(scene); + } + + // Simple cubemap loaded from ktx file on the sdcard. NOTE: Currently only + // handles texture2d or cubemap types. + { + ktxImageInfo_t ktxImageInfo; + memset(&ktxImageInfo, 0, sizeof(ktxImageInfo_t)); + if (LoadImageDataFromKTXFile(amgr, "cubemap256.ktx", &ktxImageInfo) == true) { + int swapChainTexId = 0; + XrSwapchainCreateInfo swapChainCreateInfo = {XR_TYPE_SWAPCHAIN_CREATE_INFO}; + swapChainCreateInfo.createFlags = XR_SWAPCHAIN_CREATE_STATIC_IMAGE_BIT; + swapChainCreateInfo.usageFlags = + XR_SWAPCHAIN_USAGE_SAMPLED_BIT | XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT; + swapChainCreateInfo.format = ktxImageInfo.internalFormat; + swapChainCreateInfo.sampleCount = 1; + swapChainCreateInfo.width = ktxImageInfo.width; + swapChainCreateInfo.height = ktxImageInfo.height; + swapChainCreateInfo.faceCount = ktxImageInfo.numberOfFaces; + swapChainCreateInfo.arraySize = ktxImageInfo.numberOfArrayElements; + swapChainCreateInfo.mipCount = ktxImageInfo.numberOfMipmapLevels; + + scene->CubeMapSwapChain.Width = swapChainCreateInfo.width; + scene->CubeMapSwapChain.Height = swapChainCreateInfo.height; + + // Create the swapchain. + OXR(xrCreateSwapchain(session, &swapChainCreateInfo, &scene->CubeMapSwapChain.Handle)); + // Get the number of swapchain images. + uint32_t length; + OXR(xrEnumerateSwapchainImages(scene->CubeMapSwapChain.Handle, 0, &length, NULL)); + scene->CubeMapSwapChainImage = + (XrSwapchainImageOpenGLESKHR*)malloc(length * sizeof(XrSwapchainImageOpenGLESKHR)); + for (uint32_t i = 0; i < length; i++) { + scene->CubeMapSwapChainImage[i].type = XR_TYPE_SWAPCHAIN_IMAGE_OPENGL_ES_KHR; + scene->CubeMapSwapChainImage[i].next = NULL; + } + OXR(xrEnumerateSwapchainImages( + scene->CubeMapSwapChain.Handle, + length, + &length, + (XrSwapchainImageBaseHeader*)scene->CubeMapSwapChainImage)); + + swapChainTexId = scene->CubeMapSwapChainImage[0].image; + + if (LoadTextureFromKTXImageMemory(&ktxImageInfo, swapChainTexId) == false) { + ALOGE("Failed to load texture from KTX image"); + } + + uint32_t index = 0; + XrSwapchainImageAcquireInfo acquireInfo = {XR_TYPE_SWAPCHAIN_IMAGE_ACQUIRE_INFO}; + OXR(xrAcquireSwapchainImage(scene->CubeMapSwapChain.Handle, &acquireInfo, &index)); + + XrSwapchainImageWaitInfo waitInfo = {XR_TYPE_SWAPCHAIN_IMAGE_WAIT_INFO}; + OXR(xrWaitSwapchainImage(scene->CubeMapSwapChain.Handle, &waitInfo)); + + XrSwapchainImageReleaseInfo releaseInfo = {XR_TYPE_SWAPCHAIN_IMAGE_RELEASE_INFO}; + OXR(xrReleaseSwapchainImage(scene->CubeMapSwapChain.Handle, &releaseInfo)); + } else { + ALOGE("Failed to load KTX image - generating procedural cubemap"); + XrSwapchainCreateInfo swapChainCreateInfo = {XR_TYPE_SWAPCHAIN_CREATE_INFO}; + swapChainCreateInfo.createFlags = XR_SWAPCHAIN_CREATE_STATIC_IMAGE_BIT; + swapChainCreateInfo.usageFlags = + XR_SWAPCHAIN_USAGE_SAMPLED_BIT | XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT; + swapChainCreateInfo.format = GL_RGBA8; + swapChainCreateInfo.sampleCount = 1; + int w = 512; + swapChainCreateInfo.width = w; + swapChainCreateInfo.height = w; + swapChainCreateInfo.faceCount = 6; + swapChainCreateInfo.arraySize = 1; + swapChainCreateInfo.mipCount = 1; + + scene->CubeMapSwapChain.Width = w; + scene->CubeMapSwapChain.Height = w; + + // Create the swapchain. + OXR(xrCreateSwapchain(session, &swapChainCreateInfo, &scene->CubeMapSwapChain.Handle)); + // Get the number of swapchain images. + uint32_t length; + OXR(xrEnumerateSwapchainImages(scene->CubeMapSwapChain.Handle, 0, &length, NULL)); + scene->CubeMapSwapChainImage = + (XrSwapchainImageOpenGLESKHR*)malloc(length * sizeof(XrSwapchainImageOpenGLESKHR)); + for (uint32_t i = 0; i < length; i++) { + scene->CubeMapSwapChainImage[i].type = XR_TYPE_SWAPCHAIN_IMAGE_OPENGL_ES_KHR; + scene->CubeMapSwapChainImage[i].next = NULL; + } + OXR(xrEnumerateSwapchainImages( + scene->CubeMapSwapChain.Handle, + length, + &length, + (XrSwapchainImageBaseHeader*)scene->CubeMapSwapChainImage)); + + uint32_t* img = (uint32_t*)malloc(w * w * sizeof(uint32_t)); + for (int j = 0; j < w; j++) { + for (int i = 0; i < w; i++) { + img[j * w + i] = 0xff000000 + (((j * 256) / w) << 8) + (((i * 256) / w) << 0); + } + } + GL(glBindTexture(GL_TEXTURE_CUBE_MAP, scene->CubeMapSwapChainImage[0].image)); + const int start = 3 * w / 8; + const int stop = 5 * w / 8; + for (int j = start; j < stop; j++) { + for (int i = start; i < stop; i++) { + img[j * w + i] = 0xff0000ff; + } + } + GL(glTexSubImage2D( + GL_TEXTURE_CUBE_MAP_POSITIVE_X, 0, 0, 0, w, w, GL_RGBA, GL_UNSIGNED_BYTE, img)); + for (int j = start; j < stop; j++) { + for (int i = start; i < stop; i++) { + img[j * w + i] = 0xffffff00; + } + } + GL(glTexSubImage2D( + GL_TEXTURE_CUBE_MAP_NEGATIVE_X, 0, 0, 0, w, w, GL_RGBA, GL_UNSIGNED_BYTE, img)); + for (int j = start; j < stop; j++) { + for (int i = start; i < stop; i++) { + img[j * w + i] = 0xff00ff00; + } + } + GL(glTexSubImage2D( + GL_TEXTURE_CUBE_MAP_POSITIVE_Y, 0, 0, 0, w, w, GL_RGBA, GL_UNSIGNED_BYTE, img)); + for (int j = start; j < stop; j++) { + for (int i = start; i < stop; i++) { + img[j * w + i] = 0xffff00ff; + } + } + GL(glTexSubImage2D( + GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, 0, 0, 0, w, w, GL_RGBA, GL_UNSIGNED_BYTE, img)); + for (int j = start; j < stop; j++) { + for (int i = start; i < stop; i++) { + img[j * w + i] = 0xffff0000; + } + } + GL(glTexSubImage2D( + GL_TEXTURE_CUBE_MAP_POSITIVE_Z, 0, 0, 0, w, w, GL_RGBA, GL_UNSIGNED_BYTE, img)); + for (int j = start; j < stop; j++) { + for (int i = start; i < stop; i++) { + img[j * w + i] = 0xff00ffff; + } + } + GL(glTexSubImage2D( + GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, 0, 0, 0, w, w, GL_RGBA, GL_UNSIGNED_BYTE, img)); + + free(img); + + uint32_t index = 0; + XrSwapchainImageAcquireInfo acquireInfo = {XR_TYPE_SWAPCHAIN_IMAGE_ACQUIRE_INFO}; + OXR(xrAcquireSwapchainImage(scene->CubeMapSwapChain.Handle, &acquireInfo, &index)); + + XrSwapchainImageWaitInfo waitInfo = {XR_TYPE_SWAPCHAIN_IMAGE_WAIT_INFO}; + OXR(xrWaitSwapchainImage(scene->CubeMapSwapChain.Handle, &waitInfo)); + + XrSwapchainImageReleaseInfo releaseInfo = {XR_TYPE_SWAPCHAIN_IMAGE_RELEASE_INFO}; + OXR(xrReleaseSwapchainImage(scene->CubeMapSwapChain.Handle, &releaseInfo)); + } + } + + // Simple checkerboard pattern. + { + const int width = 512; + const int height = 256; + XrSwapchainCreateInfo swapChainCreateInfo = {XR_TYPE_SWAPCHAIN_CREATE_INFO}; + swapChainCreateInfo.createFlags = XR_SWAPCHAIN_CREATE_STATIC_IMAGE_BIT; + swapChainCreateInfo.usageFlags = + XR_SWAPCHAIN_USAGE_SAMPLED_BIT | XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT; + swapChainCreateInfo.format = GL_SRGB8_ALPHA8; + swapChainCreateInfo.sampleCount = 1; + swapChainCreateInfo.width = width; + swapChainCreateInfo.height = height; + swapChainCreateInfo.faceCount = 1; + swapChainCreateInfo.arraySize = 1; + swapChainCreateInfo.mipCount = 1; + + scene->EquirectSwapChain.Width = swapChainCreateInfo.width; + scene->EquirectSwapChain.Height = swapChainCreateInfo.height; + + // Create the swapchain. + OXR(xrCreateSwapchain(session, &swapChainCreateInfo, &scene->EquirectSwapChain.Handle)); + // Get the number of swapchain images. + uint32_t length; + OXR(xrEnumerateSwapchainImages(scene->EquirectSwapChain.Handle, 0, &length, NULL)); + scene->EquirectSwapChainImage = + (XrSwapchainImageOpenGLESKHR*)malloc(length * sizeof(XrSwapchainImageOpenGLESKHR)); + + for (uint32_t i = 0; i < length; i++) { + scene->EquirectSwapChainImage[i].type = XR_TYPE_SWAPCHAIN_IMAGE_OPENGL_ES_KHR; + scene->EquirectSwapChainImage[i].next = NULL; + } + OXR(xrEnumerateSwapchainImages( + scene->EquirectSwapChain.Handle, + length, + &length, + (XrSwapchainImageBaseHeader*)scene->EquirectSwapChainImage)); + + uint32_t* texData = (uint32_t*)malloc(width * height * sizeof(uint32_t)); + + for (int y = 0; y < height; y++) { + float greenfrac = y / (height - 1.0f); + uint32_t green = ((uint32_t)(255 * greenfrac)) << 8; + for (int x = 0; x < width; x++) { + float redfrac = x / (width - 1.0f); + uint32_t red = ((uint32_t)(255 * redfrac)); + texData[y * width + x] = (x ^ y) & 16 ? 0xFFFFFFFF : (0xFF000000 | green | red); + } + } + + const int texId = scene->EquirectSwapChainImage[0].image; + glBindTexture(GL_TEXTURE_2D, texId); + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, texData); + glBindTexture(GL_TEXTURE_2D, 0); + free(texData); + + uint32_t index = 0; + XrSwapchainImageAcquireInfo acquireInfo = {XR_TYPE_SWAPCHAIN_IMAGE_ACQUIRE_INFO}; + OXR(xrAcquireSwapchainImage(scene->EquirectSwapChain.Handle, &acquireInfo, &index)); + + XrSwapchainImageWaitInfo waitInfo = {XR_TYPE_SWAPCHAIN_IMAGE_WAIT_INFO}; + OXR(xrWaitSwapchainImage(scene->EquirectSwapChain.Handle, &waitInfo)); + + XrSwapchainImageReleaseInfo releaseInfo = {XR_TYPE_SWAPCHAIN_IMAGE_RELEASE_INFO}; + OXR(xrReleaseSwapchainImage(scene->EquirectSwapChain.Handle, &releaseInfo)); + } + + // Simple checkerboard pattern. + { + static const int CYLINDER_WIDTH = 512; + static const int CYLINDER_HEIGHT = 128; + + XrSwapchainCreateInfo swapChainCreateInfo = {XR_TYPE_SWAPCHAIN_CREATE_INFO}; + swapChainCreateInfo.createFlags = XR_SWAPCHAIN_CREATE_STATIC_IMAGE_BIT; + swapChainCreateInfo.usageFlags = + XR_SWAPCHAIN_USAGE_SAMPLED_BIT | XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT; + swapChainCreateInfo.format = GL_SRGB8_ALPHA8; + swapChainCreateInfo.sampleCount = 1; + swapChainCreateInfo.width = CYLINDER_WIDTH; + swapChainCreateInfo.height = CYLINDER_HEIGHT; + swapChainCreateInfo.faceCount = 1; + swapChainCreateInfo.arraySize = 1; + swapChainCreateInfo.mipCount = 1; + + scene->CylinderSwapChain.Width = swapChainCreateInfo.width; + scene->CylinderSwapChain.Height = swapChainCreateInfo.height; + + // Create the swapchain. + OXR(xrCreateSwapchain(session, &swapChainCreateInfo, &scene->CylinderSwapChain.Handle)); + // Get the number of swapchain images. + uint32_t length; + OXR(xrEnumerateSwapchainImages(scene->CylinderSwapChain.Handle, 0, &length, NULL)); + scene->CylinderSwapChainImage = + (XrSwapchainImageOpenGLESKHR*)malloc(length * sizeof(XrSwapchainImageOpenGLESKHR)); + for (uint32_t i = 0; i < length; i++) { + scene->CylinderSwapChainImage[i].type = XR_TYPE_SWAPCHAIN_IMAGE_OPENGL_ES_KHR; + scene->CylinderSwapChainImage[i].next = NULL; + } + OXR(xrEnumerateSwapchainImages( + scene->CylinderSwapChain.Handle, + length, + &length, + (XrSwapchainImageBaseHeader*)scene->CylinderSwapChainImage)); + + uint32_t* texData = (uint32_t*)malloc(CYLINDER_WIDTH * CYLINDER_HEIGHT * sizeof(uint32_t)); + + for (int y = 0; y < CYLINDER_HEIGHT; y++) { + for (int x = 0; x < CYLINDER_WIDTH; x++) { + texData[y * CYLINDER_WIDTH + x] = (x ^ y) & 64 ? 0xFF6464F0 : 0xFFF06464; + } + } + for (int y = 0; y < CYLINDER_HEIGHT; y++) { + int g = 255.0f * (y / (CYLINDER_HEIGHT - 1.0f)); + texData[y * CYLINDER_WIDTH] = 0xff000000 | (g << 8); + } + for (int x = 0; x < CYLINDER_WIDTH; x++) { + int r = 255.0f * (x / (CYLINDER_WIDTH - 1.0f)); + texData[x] = 0xff000000 | r; + } + + const int texId = scene->CylinderSwapChainImage[0].image; + + glBindTexture(GL_TEXTURE_2D, texId); + glTexSubImage2D( + GL_TEXTURE_2D, + 0, + 0, + 0, + CYLINDER_WIDTH, + CYLINDER_HEIGHT, + GL_RGBA, + GL_UNSIGNED_BYTE, + texData); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); + GLfloat borderColor[] = {0.0f, 0.0f, 0.0f, 0.0f}; + glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor); + + glBindTexture(GL_TEXTURE_2D, 0); + + free(texData); + + uint32_t index = 0; + XrSwapchainImageAcquireInfo acquireInfo = {XR_TYPE_SWAPCHAIN_IMAGE_ACQUIRE_INFO}; + OXR(xrAcquireSwapchainImage(scene->CylinderSwapChain.Handle, &acquireInfo, &index)); + + XrSwapchainImageWaitInfo waitInfo = {XR_TYPE_SWAPCHAIN_IMAGE_WAIT_INFO}; + OXR(xrWaitSwapchainImage(scene->CylinderSwapChain.Handle, &waitInfo)); + + XrSwapchainImageReleaseInfo releaseInfo = {XR_TYPE_SWAPCHAIN_IMAGE_RELEASE_INFO}; + OXR(xrReleaseSwapchainImage(scene->CylinderSwapChain.Handle, &releaseInfo)); + } + + // Simple checkerboard pattern. + { + static const int QUAD_WIDTH = 256; + static const int QUAD_HEIGHT = 256; + + XrSwapchainCreateInfo swapChainCreateInfo = {XR_TYPE_SWAPCHAIN_CREATE_INFO}; + swapChainCreateInfo.createFlags = XR_SWAPCHAIN_CREATE_STATIC_IMAGE_BIT; + swapChainCreateInfo.usageFlags = + XR_SWAPCHAIN_USAGE_SAMPLED_BIT | XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT; + swapChainCreateInfo.format = GL_SRGB8_ALPHA8; + swapChainCreateInfo.sampleCount = 1; + swapChainCreateInfo.width = QUAD_WIDTH; + swapChainCreateInfo.height = QUAD_HEIGHT; + swapChainCreateInfo.faceCount = 1; + swapChainCreateInfo.arraySize = 1; + swapChainCreateInfo.mipCount = 1; + + scene->QuadSwapChain.Width = swapChainCreateInfo.width; + scene->QuadSwapChain.Height = swapChainCreateInfo.height; + + // Create the swapchain. + OXR(xrCreateSwapchain(session, &swapChainCreateInfo, &scene->QuadSwapChain.Handle)); + // Get the number of swapchain images. + uint32_t length; + OXR(xrEnumerateSwapchainImages(scene->QuadSwapChain.Handle, 0, &length, NULL)); + scene->QuadSwapChainImage = + (XrSwapchainImageOpenGLESKHR*)malloc(length * sizeof(XrSwapchainImageOpenGLESKHR)); + for (uint32_t i = 0; i < length; i++) { + scene->QuadSwapChainImage[i].type = XR_TYPE_SWAPCHAIN_IMAGE_OPENGL_ES_KHR; + scene->QuadSwapChainImage[i].next = NULL; + } + OXR(xrEnumerateSwapchainImages( + scene->QuadSwapChain.Handle, + length, + &length, + (XrSwapchainImageBaseHeader*)scene->QuadSwapChainImage)); + + uint32_t* texData = (uint32_t*)malloc(QUAD_WIDTH * QUAD_HEIGHT * sizeof(uint32_t)); + + for (int y = 0; y < QUAD_HEIGHT; y++) { + for (int x = 0; x < QUAD_WIDTH; x++) { + uint32_t gray = ((x ^ (x >> 1)) ^ (y ^ (y >> 1))) & 0xff; + uint32_t r = gray + 255.0f * ((1.0f - (gray / 255.0f)) * (x / (QUAD_WIDTH - 1.0f))); + uint32_t g = gray + 255.0f * ((1.0f - (gray / 255.0f)) * (y / (QUAD_HEIGHT - 1.0f))); + uint32_t rgba = (0xffu << 24) | (gray << 16) | (g << 8) | r; + texData[y * QUAD_WIDTH + x] = rgba; + } + } + + const int texId = scene->QuadSwapChainImage[0].image; + + glBindTexture(GL_TEXTURE_2D, texId); + glTexSubImage2D( + GL_TEXTURE_2D, 0, 0, 0, QUAD_WIDTH, QUAD_HEIGHT, GL_RGBA, GL_UNSIGNED_BYTE, texData); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); + GLfloat borderColor[] = {0.0f, 0.0f, 0.0f, 0.0f}; + glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor); + + glBindTexture(GL_TEXTURE_2D, 0); + + free(texData); + + uint32_t index = 0; + XrSwapchainImageAcquireInfo acquireInfo = {XR_TYPE_SWAPCHAIN_IMAGE_ACQUIRE_INFO}; + OXR(xrAcquireSwapchainImage(scene->QuadSwapChain.Handle, &acquireInfo, &index)); + + XrSwapchainImageWaitInfo waitInfo = {XR_TYPE_SWAPCHAIN_IMAGE_WAIT_INFO}; + OXR(xrWaitSwapchainImage(scene->QuadSwapChain.Handle, &waitInfo)); + + XrSwapchainImageReleaseInfo releaseInfo = {XR_TYPE_SWAPCHAIN_IMAGE_RELEASE_INFO}; + OXR(xrReleaseSwapchainImage(scene->QuadSwapChain.Handle, &releaseInfo)); + } + + scene->CreatedScene = true; +} + +static void ovrScene_Destroy(ovrScene* scene) { + ovrScene_DestroyVAOs(scene); + + ovrProgram_Destroy(&scene->Program); + ovrGeometry_Destroy(&scene->GroundPlane); + ovrGeometry_Destroy(&scene->Box); + + // Cubemap is optional + if (scene->CubeMapSwapChain.Handle != XR_NULL_HANDLE) { + OXR(xrDestroySwapchain(scene->CubeMapSwapChain.Handle)); + } + if (scene->CubeMapSwapChainImage != NULL) { + free(scene->CubeMapSwapChainImage); + } + OXR(xrDestroySwapchain(scene->EquirectSwapChain.Handle)); + free(scene->EquirectSwapChainImage); + OXR(xrDestroySwapchain(scene->CylinderSwapChain.Handle)); + free(scene->CylinderSwapChainImage); + OXR(xrDestroySwapchain(scene->QuadSwapChain.Handle)); + free(scene->QuadSwapChainImage); + + scene->CreatedScene = false; +} + +/* +================================================================================ + +ovrRenderer + +================================================================================ +*/ + +typedef struct { + ovrFramebuffer FrameBuffer[ovrMaxNumEyes]; +} ovrRenderer; + +static void ovrRenderer_Clear(ovrRenderer* renderer) { + for (int eye = 0; eye < ovrMaxNumEyes; eye++) { + ovrFramebuffer_Clear(&renderer->FrameBuffer[eye]); + } +} + +static void ovrRenderer_Create( + XrSession session, + ovrRenderer* renderer, + int suggestedEyeTextureWidth, + int suggestedEyeTextureHeight) { + // Create the frame buffers. + for (int eye = 0; eye < ovrMaxNumEyes; eye++) { + ovrFramebuffer_Create( + session, + &renderer->FrameBuffer[eye], + GL_SRGB8_ALPHA8, + suggestedEyeTextureWidth, + suggestedEyeTextureHeight, + NUM_MULTI_SAMPLES); + } +} + +static void ovrRenderer_Destroy(ovrRenderer* renderer) { + for (int eye = 0; eye < ovrMaxNumEyes; eye++) { + ovrFramebuffer_Destroy(&renderer->FrameBuffer[eye]); + } +} + +typedef struct { + XrMatrix4x4f ViewMatrix[ovrMaxNumEyes]; + XrMatrix4x4f ProjectionMatrix[ovrMaxNumEyes]; +} ovrSceneMatrices; + +static void ovrRenderer_RenderFrame( + ovrRenderer* renderer, + const ovrScene* scene, + const ovrSceneMatrices* sceneMatrices) { + // Let the background layer show through if one is present. + float clearAlpha = 1.0f; + if (scene->BackGroundType != BACKGROUND_NONE) { + clearAlpha = 0.0f; + } + + for (int eye = 0; eye < ovrMaxNumEyes; eye++) { + ovrFramebuffer* frameBuffer = &renderer->FrameBuffer[eye]; + + ovrFramebuffer_Acquire(frameBuffer); + + // Set the current framebuffer. + ovrFramebuffer_SetCurrent(frameBuffer); + + GL(glUseProgram(scene->Program.Program)); + + XrMatrix4x4f modelMatrix; + XrMatrix4x4f_CreateIdentity(&modelMatrix); + glUniformMatrix4fv( + scene->Program.UniformLocation[MODEL_MATRIX], 1, GL_FALSE, &modelMatrix.m[0]); + XrMatrix4x4f viewProjMatrix; + XrMatrix4x4f_Multiply( + &viewProjMatrix, + &sceneMatrices->ProjectionMatrix[eye], + &sceneMatrices->ViewMatrix[eye]); + glUniformMatrix4fv( + scene->Program.UniformLocation[VIEW_PROJ_MATRIX], 1, GL_FALSE, &viewProjMatrix.m[0]); + + GL(glEnable(GL_SCISSOR_TEST)); + GL(glDepthMask(GL_TRUE)); + GL(glEnable(GL_DEPTH_TEST)); + GL(glDepthFunc(GL_LEQUAL)); + GL(glDisable(GL_CULL_FACE)); + // GL( glCullFace( GL_BACK ) ); + GL(glViewport(0, 0, frameBuffer->Width, frameBuffer->Height)); + GL(glScissor(0, 0, frameBuffer->Width, frameBuffer->Height)); + GL(glClearColor(0.0f, 0.0f, 0.0f, clearAlpha)); + GL(glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)); + GL(glBindVertexArray(scene->GroundPlane.VertexArrayObject)); + GL(glDrawElements(GL_TRIANGLES, scene->GroundPlane.IndexCount, GL_UNSIGNED_SHORT, NULL)); + + for (int i = 0; i < 4; i++) { + if (scene->TrackedController[i].Active == false) { + continue; + } + XrMatrix4x4f pose; + XrMatrix4x4f_CreateFromRigidTransform(&pose, &scene->TrackedController[i].Pose); + XrMatrix4x4f scale; + if (i & 1) { + XrMatrix4x4f_CreateScale(&scale, 0.03f, 0.03f, 0.03f); + } else { + XrMatrix4x4f_CreateScale(&scale, 0.02f, 0.02f, 0.06f); + } + XrMatrix4x4f model; + XrMatrix4x4f_Multiply(&model, &pose, &scale); + glUniformMatrix4fv( + scene->Program.UniformLocation[MODEL_MATRIX], 1, GL_FALSE, &model.m[0]); + GL(glBindVertexArray(scene->Box.VertexArrayObject)); + GL(glDrawElements(GL_TRIANGLES, scene->Box.IndexCount, GL_UNSIGNED_SHORT, NULL)); + } + glUniformMatrix4fv( + scene->Program.UniformLocation[MODEL_MATRIX], 1, GL_FALSE, &modelMatrix.m[0]); + + GL(glBindVertexArray(0)); + GL(glUseProgram(0)); + + ovrFramebuffer_Resolve(frameBuffer); + + ovrFramebuffer_Release(frameBuffer); + } + + ovrFramebuffer_SetNone(); +} + +static void ovrRenderer_SetFoveation( + XrInstance* instance, + XrSession* session, + ovrRenderer* renderer, + XrFoveationLevelFB level, + float verticalOffset, + XrFoveationDynamicFB dynamic) { + PFN_xrCreateFoveationProfileFB pfnCreateFoveationProfileFB; + OXR(xrGetInstanceProcAddr( + *instance, + "xrCreateFoveationProfileFB", + (PFN_xrVoidFunction*)(&pfnCreateFoveationProfileFB))); + + PFN_xrDestroyFoveationProfileFB pfnDestroyFoveationProfileFB; + OXR(xrGetInstanceProcAddr( + *instance, + "xrDestroyFoveationProfileFB", + (PFN_xrVoidFunction*)(&pfnDestroyFoveationProfileFB))); + + PFN_xrUpdateSwapchainFB pfnUpdateSwapchainFB; + OXR(xrGetInstanceProcAddr( + *instance, "xrUpdateSwapchainFB", (PFN_xrVoidFunction*)(&pfnUpdateSwapchainFB))); + + for (int eye = 0; eye < ovrMaxNumEyes; eye++) { + XrFoveationLevelProfileCreateInfoFB levelProfileCreateInfo = {XR_TYPE_FOVEATION_LEVEL_PROFILE_CREATE_INFO_FB}; + levelProfileCreateInfo.level = level; + levelProfileCreateInfo.verticalOffset = verticalOffset; + levelProfileCreateInfo.dynamic = dynamic; + + XrFoveationProfileCreateInfoFB profileCreateInfo = {XR_TYPE_FOVEATION_PROFILE_CREATE_INFO_FB}; + profileCreateInfo.next = &levelProfileCreateInfo; + + XrFoveationProfileFB foveationProfile; + + pfnCreateFoveationProfileFB(*session, &profileCreateInfo, &foveationProfile); + + XrSwapchainStateFoveationFB foveationUpdateState = {XR_TYPE_SWAPCHAIN_STATE_FOVEATION_FB}; + foveationUpdateState.profile = foveationProfile; + + pfnUpdateSwapchainFB( + renderer->FrameBuffer[eye].ColorSwapChain.Handle, + (XrSwapchainStateBaseHeaderFB*)(&foveationUpdateState)); + + pfnDestroyFoveationProfileFB(foveationProfile); + } +} + +/* +================================================================================ + +ovrApp + +================================================================================ +*/ + +typedef struct { + ovrEgl Egl; + bool Resumed; + bool Focused; + + XrInstance Instance; + XrSession Session; + XrViewConfigurationProperties ViewportConfig; + XrViewConfigurationView ViewConfigurationView[ovrMaxNumEyes]; + XrSystemId SystemId; + XrSpace HeadSpace; + XrSpace LocalSpace; + XrSpace StageSpace; + XrSpace FakeStageSpace; + XrSpace CurrentSpace; + bool SessionActive; + + ovrScene Scene; + + float* SupportedDisplayRefreshRates; + uint32_t RequestedDisplayRefreshRateIndex; + uint32_t NumSupportedDisplayRefreshRates; + PFN_xrGetDisplayRefreshRateFB pfnGetDisplayRefreshRate; + PFN_xrRequestDisplayRefreshRateFB pfnRequestDisplayRefreshRate; + + int SwapInterval; + int CpuLevel; + int GpuLevel; + // These threads will be marked as performance threads. + int MainThreadTid; + int RenderThreadTid; + ovrCompositorLayer_Union Layers[ovrMaxLayerCount]; + int LayerCount; + + bool TouchPadDownLastFrame; + ovrRenderer Renderer; +} ovrApp; + +static void ovrApp_Clear(ovrApp* app) { + app->Resumed = false; + app->Focused = false; + app->Instance = XR_NULL_HANDLE; + app->Session = XR_NULL_HANDLE; + memset(&app->ViewportConfig, 0, sizeof(XrViewConfigurationProperties)); + memset(&app->ViewConfigurationView, 0, ovrMaxNumEyes * sizeof(XrViewConfigurationView)); + app->SystemId = XR_NULL_SYSTEM_ID; + app->HeadSpace = XR_NULL_HANDLE; + app->LocalSpace = XR_NULL_HANDLE; + app->StageSpace = XR_NULL_HANDLE; + app->FakeStageSpace = XR_NULL_HANDLE; + app->CurrentSpace = XR_NULL_HANDLE; + app->SessionActive = false; + app->SupportedDisplayRefreshRates = NULL; + app->RequestedDisplayRefreshRateIndex = 0; + app->NumSupportedDisplayRefreshRates = 0; + app->pfnGetDisplayRefreshRate = NULL; + app->pfnRequestDisplayRefreshRate = NULL; + app->SwapInterval = 1; + memset(app->Layers, 0, sizeof(ovrCompositorLayer_Union) * ovrMaxLayerCount); + app->LayerCount = 0; + app->CpuLevel = 2; + app->GpuLevel = 2; + app->MainThreadTid = 0; + app->RenderThreadTid = 0; + app->TouchPadDownLastFrame = false; + + ovrEgl_Clear(&app->Egl); + ovrScene_Clear(&app->Scene); + ovrRenderer_Clear(&app->Renderer); +} + +static void ovrApp_Destroy(ovrApp* app) { + if (app->SupportedDisplayRefreshRates != NULL) { + free(app->SupportedDisplayRefreshRates); + } + + ovrApp_Clear(app); +} + +static void ovrApp_HandleSessionStateChanges(ovrApp* app, XrSessionState state) { + if (state == XR_SESSION_STATE_READY) { + assert(app->Resumed); + assert(app->SessionActive == false); + + XrSessionBeginInfo sessionBeginInfo = {XR_TYPE_SESSION_BEGIN_INFO}; + sessionBeginInfo.primaryViewConfigurationType = app->ViewportConfig.viewConfigurationType; + + XrResult result; + OXR(result = xrBeginSession(app->Session, &sessionBeginInfo)); + + app->SessionActive = (result == XR_SUCCESS); + + // Set session state once we have entered VR mode and have a valid session object. + if (app->SessionActive) { + XrPerfSettingsLevelEXT cpuPerfLevel = XR_PERF_SETTINGS_LEVEL_SUSTAINED_HIGH_EXT; + switch (app->CpuLevel) { + case 0: + cpuPerfLevel = XR_PERF_SETTINGS_LEVEL_POWER_SAVINGS_EXT; + break; + case 1: + cpuPerfLevel = XR_PERF_SETTINGS_LEVEL_SUSTAINED_LOW_EXT; + break; + case 2: + cpuPerfLevel = XR_PERF_SETTINGS_LEVEL_SUSTAINED_HIGH_EXT; + break; + case 3: + cpuPerfLevel = XR_PERF_SETTINGS_LEVEL_BOOST_EXT; + break; + default: + ALOGE("Invalid CPU level %d", app->CpuLevel); + break; + } + + XrPerfSettingsLevelEXT gpuPerfLevel = XR_PERF_SETTINGS_LEVEL_SUSTAINED_HIGH_EXT; + switch (app->GpuLevel) { + case 0: + gpuPerfLevel = XR_PERF_SETTINGS_LEVEL_POWER_SAVINGS_EXT; + break; + case 1: + gpuPerfLevel = XR_PERF_SETTINGS_LEVEL_SUSTAINED_LOW_EXT; + break; + case 2: + gpuPerfLevel = XR_PERF_SETTINGS_LEVEL_SUSTAINED_HIGH_EXT; + break; + case 3: + gpuPerfLevel = XR_PERF_SETTINGS_LEVEL_BOOST_EXT; + break; + default: + ALOGE("Invalid GPU level %d", app->GpuLevel); + break; + } + + PFN_xrPerfSettingsSetPerformanceLevelEXT pfnPerfSettingsSetPerformanceLevelEXT = NULL; + OXR(xrGetInstanceProcAddr( + app->Instance, + "xrPerfSettingsSetPerformanceLevelEXT", + (PFN_xrVoidFunction*)(&pfnPerfSettingsSetPerformanceLevelEXT))); + + OXR(pfnPerfSettingsSetPerformanceLevelEXT( + app->Session, XR_PERF_SETTINGS_DOMAIN_CPU_EXT, cpuPerfLevel)); + OXR(pfnPerfSettingsSetPerformanceLevelEXT( + app->Session, XR_PERF_SETTINGS_DOMAIN_GPU_EXT, gpuPerfLevel)); + + PFN_xrSetAndroidApplicationThreadKHR pfnSetAndroidApplicationThreadKHR = NULL; + OXR(xrGetInstanceProcAddr( + app->Instance, + "xrSetAndroidApplicationThreadKHR", + (PFN_xrVoidFunction*)(&pfnSetAndroidApplicationThreadKHR))); + + OXR(pfnSetAndroidApplicationThreadKHR( + app->Session, XR_ANDROID_THREAD_TYPE_APPLICATION_MAIN_KHR, app->MainThreadTid)); + OXR(pfnSetAndroidApplicationThreadKHR( + app->Session, XR_ANDROID_THREAD_TYPE_RENDERER_MAIN_KHR, app->RenderThreadTid)); + } + } else if (state == XR_SESSION_STATE_STOPPING) { + assert(app->Resumed == false); + assert(app->SessionActive); + + OXR(xrEndSession(app->Session)); + app->SessionActive = false; + } +} + +static void ovrApp_HandleXrEvents(ovrApp* app) { + XrEventDataBuffer eventDataBuffer = {XR_TYPE_EVENT_DATA_BUFFER}; + + // Poll for events + for (;;) { + XrEventDataBaseHeader* baseEventHeader = (XrEventDataBaseHeader*)(&eventDataBuffer); + baseEventHeader->type = XR_TYPE_EVENT_DATA_BUFFER; + baseEventHeader->next = NULL; + XrResult r; + OXR(r = xrPollEvent(app->Instance, &eventDataBuffer)); + if (r != XR_SUCCESS) { + break; + } + + switch (baseEventHeader->type) { + case XR_TYPE_EVENT_DATA_EVENTS_LOST: + ALOGV("xrPollEvent: received XR_TYPE_EVENT_DATA_EVENTS_LOST event"); + break; + case XR_TYPE_EVENT_DATA_INSTANCE_LOSS_PENDING: { + const XrEventDataInstanceLossPending* instance_loss_pending_event = + (XrEventDataInstanceLossPending*)(baseEventHeader); + ALOGV( + "xrPollEvent: received XR_TYPE_EVENT_DATA_INSTANCE_LOSS_PENDING event: time %f", + FromXrTime(instance_loss_pending_event->lossTime)); + } break; + case XR_TYPE_EVENT_DATA_INTERACTION_PROFILE_CHANGED: + ALOGV("xrPollEvent: received XR_TYPE_EVENT_DATA_INTERACTION_PROFILE_CHANGED event"); + break; + case XR_TYPE_EVENT_DATA_PERF_SETTINGS_EXT: { + const XrEventDataPerfSettingsEXT* perf_settings_event = + (XrEventDataPerfSettingsEXT*)(baseEventHeader); + ALOGV( + "xrPollEvent: received XR_TYPE_EVENT_DATA_PERF_SETTINGS_EXT event: type %d subdomain %d : level %d -> level %d", + perf_settings_event->type, + perf_settings_event->subDomain, + perf_settings_event->fromLevel, + perf_settings_event->toLevel); + } break; + case XR_TYPE_EVENT_DATA_DISPLAY_REFRESH_RATE_CHANGED_FB: { + const XrEventDataDisplayRefreshRateChangedFB* refresh_rate_changed_event = + (XrEventDataDisplayRefreshRateChangedFB*)(baseEventHeader); + ALOGV( + "xrPollEvent: received XR_TYPE_EVENT_DATA_DISPLAY_REFRESH_RATE_CHANGED_FB event: fromRate %f -> toRate %f", + refresh_rate_changed_event->fromDisplayRefreshRate, + refresh_rate_changed_event->toDisplayRefreshRate); + } break; + case XR_TYPE_EVENT_DATA_REFERENCE_SPACE_CHANGE_PENDING: { + XrEventDataReferenceSpaceChangePending* ref_space_change_event = + (XrEventDataReferenceSpaceChangePending*)(baseEventHeader); + ALOGV( + "xrPollEvent: received XR_TYPE_EVENT_DATA_REFERENCE_SPACE_CHANGE_PENDING event: changed space: %d for session %p at time %f", + ref_space_change_event->referenceSpaceType, + (void*)ref_space_change_event->session, + FromXrTime(ref_space_change_event->changeTime)); + } break; + case XR_TYPE_EVENT_DATA_SESSION_STATE_CHANGED: { + const XrEventDataSessionStateChanged* session_state_changed_event = + (XrEventDataSessionStateChanged*)(baseEventHeader); + ALOGV( + "xrPollEvent: received XR_TYPE_EVENT_DATA_SESSION_STATE_CHANGED: %d for session %p at time %f", + session_state_changed_event->state, + (void*)session_state_changed_event->session, + FromXrTime(session_state_changed_event->time)); + + switch (session_state_changed_event->state) { + case XR_SESSION_STATE_FOCUSED: + app->Focused = true; + break; + case XR_SESSION_STATE_VISIBLE: + app->Focused = false; + break; + case XR_SESSION_STATE_READY: + case XR_SESSION_STATE_STOPPING: + ovrApp_HandleSessionStateChanges(app, session_state_changed_event->state); + break; + default: + break; + } + } break; + default: + ALOGV("xrPollEvent: Unknown event"); + break; + } + } +} + +/* +================================================================================ + +Native Activity + +================================================================================ +*/ + +/** + * Process the next main command. + */ +static void app_handle_cmd(struct android_app* app, int32_t cmd) { + ovrApp* appState = (ovrApp*)app->userData; + + switch (cmd) { + // There is no APP_CMD_CREATE. The ANativeActivity creates the + // application thread from onCreate(). The application thread + // then calls android_main(). + case APP_CMD_START: { + ALOGV("onStart()"); + ALOGV(" APP_CMD_START"); + break; + } + case APP_CMD_RESUME: { + ALOGV("onResume()"); + ALOGV(" APP_CMD_RESUME"); + appState->Resumed = true; + break; + } + case APP_CMD_PAUSE: { + ALOGV("onPause()"); + ALOGV(" APP_CMD_PAUSE"); + appState->Resumed = false; + break; + } + case APP_CMD_STOP: { + ALOGV("onStop()"); + ALOGV(" APP_CMD_STOP"); + break; + } + case APP_CMD_DESTROY: { + ALOGV("onDestroy()"); + ALOGV(" APP_CMD_DESTROY"); + ovrApp_Clear(appState); + break; + } + case APP_CMD_INIT_WINDOW: { + ALOGV("surfaceCreated()"); + ALOGV(" APP_CMD_INIT_WINDOW"); + break; + } + case APP_CMD_TERM_WINDOW: { + ALOGV("surfaceDestroyed()"); + ALOGV(" APP_CMD_TERM_WINDOW"); + break; + } + } +} + +void UpdateStageBounds(ovrApp* pappState) { + XrExtent2Df stageBounds = {0}; + + XrResult result; + OXR(result = xrGetReferenceSpaceBoundsRect( + pappState->Session, XR_REFERENCE_SPACE_TYPE_STAGE, &stageBounds)); + if (result != XR_SUCCESS) { + ALOGV("Stage bounds query failed: using small defaults"); + stageBounds.width = 1.0f; + stageBounds.height = 1.0f; + + pappState->CurrentSpace = pappState->FakeStageSpace; + } + + ALOGV("Stage bounds: width = %f, depth %f", stageBounds.width, stageBounds.height); + + const float halfWidth = stageBounds.width * 0.5f; + const float halfDepth = stageBounds.height * 0.5f; + + ovrGeometry_Destroy(&pappState->Scene.GroundPlane); + ovrGeometry_DestroyVAO(&pappState->Scene.GroundPlane); + ovrGeometry_CreateStagePlane( + &pappState->Scene.GroundPlane, -halfWidth, -halfDepth, halfWidth, halfDepth); + ovrGeometry_CreateVAO(&pappState->Scene.GroundPlane); +} + +ovrApp appState; + +XrInstance ovrApp_GetInstance() { + return appState.Instance; +} + +static XrActionSet CreateActionSet(int priority, const char* name, const char* localizedName) { + XrActionSetCreateInfo asci = {XR_TYPE_ACTION_SET_CREATE_INFO}; + asci.priority = priority; + strcpy(asci.actionSetName, name); + strcpy(asci.localizedActionSetName, localizedName); + XrActionSet actionSet = XR_NULL_HANDLE; + OXR(xrCreateActionSet(appState.Instance, &asci, &actionSet)); + return actionSet; +} + +static XrAction CreateAction( + XrActionSet actionSet, + XrActionType type, + const char* actionName, + const char* localizedName, + int countSubactionPaths, + XrPath* subactionPaths) { + ALOGV("CreateAction %s, %" PRIi32, actionName, countSubactionPaths); + + XrActionCreateInfo aci = {XR_TYPE_ACTION_CREATE_INFO}; + aci.actionType = type; + if (countSubactionPaths > 0) { + aci.countSubactionPaths = countSubactionPaths; + aci.subactionPaths = subactionPaths; + } + strcpy(aci.actionName, actionName); + strcpy(aci.localizedActionName, localizedName ? localizedName : actionName); + XrAction action = XR_NULL_HANDLE; + OXR(xrCreateAction(actionSet, &aci, &action)); + return action; +} + +static XrActionSuggestedBinding ActionSuggestedBinding(XrAction action, const char* bindingString) { + XrActionSuggestedBinding asb; + asb.action = action; + XrPath bindingPath; + OXR(xrStringToPath(appState.Instance, bindingString, &bindingPath)); + asb.binding = bindingPath; + return asb; +} + +static XrSpace CreateActionSpace(XrAction poseAction, XrPath subactionPath) { + XrActionSpaceCreateInfo asci = {XR_TYPE_ACTION_SPACE_CREATE_INFO}; + asci.action = poseAction; + asci.poseInActionSpace.orientation.w = 1.0f; + asci.subactionPath = subactionPath; + XrSpace actionSpace = XR_NULL_HANDLE; + OXR(xrCreateActionSpace(appState.Session, &asci, &actionSpace)); + return actionSpace; +} + +static XrActionStateBoolean GetActionStateBoolean(XrAction action) { + XrActionStateGetInfo getInfo = {XR_TYPE_ACTION_STATE_GET_INFO}; + getInfo.action = action; + + XrActionStateBoolean state = {XR_TYPE_ACTION_STATE_BOOLEAN}; + + OXR(xrGetActionStateBoolean(appState.Session, &getInfo, &state)); + return state; +} + +static XrActionStateFloat GetActionStateFloat(XrAction action) { + XrActionStateGetInfo getInfo = {XR_TYPE_ACTION_STATE_GET_INFO}; + getInfo.action = action; + + XrActionStateFloat state = {XR_TYPE_ACTION_STATE_FLOAT}; + + OXR(xrGetActionStateFloat(appState.Session, &getInfo, &state)); + return state; +} + +static XrActionStateVector2f GetActionStateVector2(XrAction action) { + XrActionStateGetInfo getInfo = {XR_TYPE_ACTION_STATE_GET_INFO}; + getInfo.action = action; + + XrActionStateVector2f state = {XR_TYPE_ACTION_STATE_VECTOR2F}; + + OXR(xrGetActionStateVector2f(appState.Session, &getInfo, &state)); + return state; +} + +static bool ActionPoseIsActive(XrAction action, XrPath subactionPath) { + XrActionStateGetInfo getInfo = {XR_TYPE_ACTION_STATE_GET_INFO}; + getInfo.action = action; + getInfo.subactionPath = subactionPath; + + XrActionStatePose state = {XR_TYPE_ACTION_STATE_POSE}; + OXR(xrGetActionStatePose(appState.Session, &getInfo, &state)); + return state.isActive != XR_FALSE; +} + +typedef struct { + XrSpaceLocation loc; + XrSpaceVelocity vel; +} LocVel; + +static LocVel GetSpaceLocVel(XrSpace space, XrTime time) { + LocVel lv = {{XR_TYPE_SPACE_LOCATION}, {XR_TYPE_SPACE_VELOCITY}}; + lv.loc.next = &lv.vel; + OXR(xrLocateSpace(space, appState.CurrentSpace, time, &lv.loc)); + lv.loc.next = NULL; // pointer no longer valid or necessary + return lv; +} + +/** + * This is the main entry point of a native application that is using + * android_native_app_glue. It runs in its own thread, with its own + * event loop for receiving input events and doing other things. + */ +void android_main(struct android_app* app) { + ALOGV("----------------------------------------------------------------"); + ALOGV("android_app_entry()"); + ALOGV(" android_main()"); + + JNIEnv* Env; +#if defined(__cplusplus) + app->activity->vm->AttachCurrentThread(&Env, nullptr); +#else + (*app->activity->vm)->AttachCurrentThread(app->activity->vm, &Env, NULL); +#endif + + // Note that AttachCurrentThread will reset the thread name. + prctl(PR_SET_NAME, (long)"OVR::Main", 0, 0, 0); + + ovrApp_Clear(&appState); + + PFN_xrInitializeLoaderKHR xrInitializeLoaderKHR; + xrGetInstanceProcAddr( + XR_NULL_HANDLE, "xrInitializeLoaderKHR", (PFN_xrVoidFunction*)&xrInitializeLoaderKHR); + if (xrInitializeLoaderKHR != NULL) { + XrLoaderInitInfoAndroidKHR loaderInitializeInfoAndroid = {XR_TYPE_LOADER_INIT_INFO_ANDROID_KHR}; + loaderInitializeInfoAndroid.applicationVM = app->activity->vm; + loaderInitializeInfoAndroid.applicationContext = app->activity->clazz; + xrInitializeLoaderKHR((XrLoaderInitInfoBaseHeaderKHR*)&loaderInitializeInfoAndroid); + } + + // Log available layers. + { + uint32_t numLayers = 0; + OXR(xrEnumerateApiLayerProperties(0, &numLayers, NULL)); + + XrApiLayerProperties* layerProperties = + (XrApiLayerProperties*)malloc(numLayers * sizeof(XrApiLayerProperties)); + + for (uint32_t i = 0; i < numLayers; i++) { + layerProperties[i].type = XR_TYPE_API_LAYER_PROPERTIES; + layerProperties[i].next = NULL; + } + + OXR(xrEnumerateApiLayerProperties(numLayers, &numLayers, layerProperties)); + + for (uint32_t i = 0; i < numLayers; i++) { + ALOGV("Found layer %s", layerProperties[i].layerName); + } + + free(layerProperties); + } + + // Check that the extensions required are present. + const char* const requiredExtensionNames[] = { + XR_KHR_OPENGL_ES_ENABLE_EXTENSION_NAME, + XR_EXT_PERFORMANCE_SETTINGS_EXTENSION_NAME, + XR_KHR_ANDROID_THREAD_SETTINGS_EXTENSION_NAME, + XR_KHR_COMPOSITION_LAYER_CUBE_EXTENSION_NAME, + XR_KHR_COMPOSITION_LAYER_CYLINDER_EXTENSION_NAME, + XR_KHR_COMPOSITION_LAYER_EQUIRECT2_EXTENSION_NAME, + XR_FB_DISPLAY_REFRESH_RATE_EXTENSION_NAME, + XR_FB_COLOR_SPACE_EXTENSION_NAME, + XR_FB_SWAPCHAIN_UPDATE_STATE_EXTENSION_NAME, + XR_FB_SWAPCHAIN_UPDATE_STATE_OPENGL_ES_EXTENSION_NAME, + XR_FB_FOVEATION_EXTENSION_NAME, + XR_FB_FOVEATION_CONFIGURATION_EXTENSION_NAME}; + const uint32_t numRequiredExtensions = + sizeof(requiredExtensionNames) / sizeof(requiredExtensionNames[0]); + + // Check the list of required extensions against what is supported by the runtime. + { + uint32_t numExtensions = 0; + OXR(xrEnumerateInstanceExtensionProperties( + NULL, 0, &numExtensions, NULL)); + ALOGV("xrEnumerateInstanceExtensionProperties found %u extension(s).", numExtensions); + + XrExtensionProperties* extensionProperties = + (XrExtensionProperties*)malloc(numExtensions * sizeof(XrExtensionProperties)); + + for (uint32_t i = 0; i < numExtensions; i++) { + extensionProperties[i].type = XR_TYPE_EXTENSION_PROPERTIES; + extensionProperties[i].next = NULL; + } + + OXR(xrEnumerateInstanceExtensionProperties( + NULL, numExtensions, &numExtensions, extensionProperties)); + for (uint32_t i = 0; i < numExtensions; i++) { + ALOGV("Extension #%d = '%s'.", i, extensionProperties[i].extensionName); + } + + for (uint32_t i = 0; i < numRequiredExtensions; i++) { + bool found = false; + for (uint32_t j = 0; j < numExtensions; j++) { + if (!strcmp(requiredExtensionNames[i], extensionProperties[j].extensionName)) { + ALOGV("Found required extension %s", requiredExtensionNames[i]); + found = true; + break; + } + } + if (!found) { + ALOGE("Failed to find required extension %s", requiredExtensionNames[i]); + exit(1); + } + } + + free(extensionProperties); + } + + // Create the OpenXR instance. + XrApplicationInfo appInfo; + memset(&appInfo, 0, sizeof(appInfo)); + strcpy(appInfo.applicationName, "OpenXR_NativeActivity"); + appInfo.applicationVersion = 0; + strcpy(appInfo.engineName, "Oculus Mobile Sample"); + appInfo.engineVersion = 0; + appInfo.apiVersion = XR_MAKE_VERSION(1, 0, 34); + + XrInstanceCreateInfo instanceCreateInfo = {XR_TYPE_INSTANCE_CREATE_INFO}; + instanceCreateInfo.createFlags = 0; + instanceCreateInfo.applicationInfo = appInfo; + instanceCreateInfo.enabledApiLayerCount = 0; + instanceCreateInfo.enabledApiLayerNames = NULL; + instanceCreateInfo.enabledExtensionCount = numRequiredExtensions; + instanceCreateInfo.enabledExtensionNames = requiredExtensionNames; + + XrResult initResult; + OXR(initResult = xrCreateInstance(&instanceCreateInfo, &appState.Instance)); + if (initResult != XR_SUCCESS) { + ALOGE("Failed to create XR instance: %d.", initResult); + exit(1); + } + + XrInstanceProperties instanceInfo = {XR_TYPE_INSTANCE_PROPERTIES}; + OXR(xrGetInstanceProperties(appState.Instance, &instanceInfo)); + ALOGV( + "Runtime %s: Version : %u.%u.%u", + instanceInfo.runtimeName, + XR_VERSION_MAJOR(instanceInfo.runtimeVersion), + XR_VERSION_MINOR(instanceInfo.runtimeVersion), + XR_VERSION_PATCH(instanceInfo.runtimeVersion)); + + XrSystemGetInfo systemGetInfo = {XR_TYPE_SYSTEM_GET_INFO}; + systemGetInfo.formFactor = XR_FORM_FACTOR_HEAD_MOUNTED_DISPLAY; + + XrSystemId systemId; + OXR(initResult = xrGetSystem(appState.Instance, &systemGetInfo, &systemId)); + if (initResult != XR_SUCCESS) { + if (initResult == XR_ERROR_FORM_FACTOR_UNAVAILABLE) { + ALOGE("Failed to get system; the specified form factor is not available. Is your headset connected?"); + } else { + ALOGE("xrGetSystem failed, error %d", initResult); + } + exit(1); + } + + XrSystemColorSpacePropertiesFB colorSpacePropertiesFB = {XR_TYPE_SYSTEM_COLOR_SPACE_PROPERTIES_FB}; + + XrSystemProperties systemProperties = {XR_TYPE_SYSTEM_PROPERTIES}; + systemProperties.next = &colorSpacePropertiesFB; + OXR(xrGetSystemProperties(appState.Instance, systemId, &systemProperties)); + + ALOGV( + "System Properties: Name=%s VendorId=%x", + systemProperties.systemName, + systemProperties.vendorId); + ALOGV( + "System Graphics Properties: MaxWidth=%d MaxHeight=%d MaxLayers=%d", + systemProperties.graphicsProperties.maxSwapchainImageWidth, + systemProperties.graphicsProperties.maxSwapchainImageHeight, + systemProperties.graphicsProperties.maxLayerCount); + ALOGV( + "System Tracking Properties: OrientationTracking=%s PositionTracking=%s", + systemProperties.trackingProperties.orientationTracking ? "True" : "False", + systemProperties.trackingProperties.positionTracking ? "True" : "False"); + + ALOGV("System Color Space Properties: colorspace=%d", colorSpacePropertiesFB.colorSpace); + + assert(ovrMaxLayerCount <= systemProperties.graphicsProperties.maxLayerCount); + + // Get the graphics requirements. + PFN_xrGetOpenGLESGraphicsRequirementsKHR pfnGetOpenGLESGraphicsRequirementsKHR = NULL; + OXR(xrGetInstanceProcAddr( + appState.Instance, + "xrGetOpenGLESGraphicsRequirementsKHR", + (PFN_xrVoidFunction*)(&pfnGetOpenGLESGraphicsRequirementsKHR))); + + XrGraphicsRequirementsOpenGLESKHR graphicsRequirements = {XR_TYPE_GRAPHICS_REQUIREMENTS_OPENGL_ES_KHR}; + OXR(pfnGetOpenGLESGraphicsRequirementsKHR(appState.Instance, systemId, &graphicsRequirements)); + + // Create the EGL Context + ovrEgl_CreateContext(&appState.Egl, NULL); + + // Check the graphics requirements. + int eglMajor = 0; + int eglMinor = 0; + glGetIntegerv(GL_MAJOR_VERSION, &eglMajor); + glGetIntegerv(GL_MINOR_VERSION, &eglMinor); + const XrVersion eglVersion = XR_MAKE_VERSION(eglMajor, eglMinor, 0); + if (eglVersion < graphicsRequirements.minApiVersionSupported || + eglVersion > graphicsRequirements.maxApiVersionSupported) { + ALOGE("GLES version %d.%d not supported", eglMajor, eglMinor); + exit(0); + } + + appState.CpuLevel = CPU_LEVEL; + appState.GpuLevel = GPU_LEVEL; + appState.MainThreadTid = gettid(); + + appState.SystemId = systemId; + + // Create the OpenXR Session. + XrGraphicsBindingOpenGLESAndroidKHR graphicsBindingAndroidGLES = {XR_TYPE_GRAPHICS_BINDING_OPENGL_ES_ANDROID_KHR}; + graphicsBindingAndroidGLES.display = appState.Egl.Display; + graphicsBindingAndroidGLES.config = appState.Egl.Config; + graphicsBindingAndroidGLES.context = appState.Egl.Context; + + XrSessionCreateInfo sessionCreateInfo = {XR_TYPE_SESSION_CREATE_INFO}; + sessionCreateInfo.next = &graphicsBindingAndroidGLES; + sessionCreateInfo.createFlags = 0; + sessionCreateInfo.systemId = appState.SystemId; + + OXR(initResult = xrCreateSession(appState.Instance, &sessionCreateInfo, &appState.Session)); + if (initResult != XR_SUCCESS) { + ALOGE("Failed to create XR session: %d.", initResult); + exit(1); + } + + // App only supports the primary stereo view config. + const XrViewConfigurationType supportedViewConfigType = + XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO; + + // Enumerate the viewport configurations. + uint32_t viewportConfigTypeCount = 0; + OXR(xrEnumerateViewConfigurations( + appState.Instance, appState.SystemId, 0, &viewportConfigTypeCount, NULL)); + + XrViewConfigurationType* viewportConfigurationTypes = + (XrViewConfigurationType*)malloc(viewportConfigTypeCount * sizeof(XrViewConfigurationType)); + + OXR(xrEnumerateViewConfigurations( + appState.Instance, + appState.SystemId, + viewportConfigTypeCount, + &viewportConfigTypeCount, + viewportConfigurationTypes)); + + ALOGV("Available Viewport Configuration Types: %d", viewportConfigTypeCount); + + for (uint32_t i = 0; i < viewportConfigTypeCount; i++) { + const XrViewConfigurationType viewportConfigType = viewportConfigurationTypes[i]; + + ALOGV( + "Viewport configuration type %d : %s", + viewportConfigType, + viewportConfigType == supportedViewConfigType ? "Selected" : ""); + + XrViewConfigurationProperties viewportConfig = {XR_TYPE_VIEW_CONFIGURATION_PROPERTIES}; + OXR(xrGetViewConfigurationProperties( + appState.Instance, appState.SystemId, viewportConfigType, &viewportConfig)); + ALOGV( + "FovMutable=%s ConfigurationType %d", + viewportConfig.fovMutable ? "true" : "false", + viewportConfig.viewConfigurationType); + + uint32_t viewCount; + OXR(xrEnumerateViewConfigurationViews( + appState.Instance, appState.SystemId, viewportConfigType, 0, &viewCount, NULL)); + + if (viewCount > 0) { + XrViewConfigurationView* elements = + (XrViewConfigurationView*)malloc(viewCount * sizeof(XrViewConfigurationView)); + + for (uint32_t e = 0; e < viewCount; e++) { + elements[e].type = XR_TYPE_VIEW_CONFIGURATION_VIEW; + elements[e].next = NULL; + } + + OXR(xrEnumerateViewConfigurationViews( + appState.Instance, + appState.SystemId, + viewportConfigType, + viewCount, + &viewCount, + elements)); + + // Log the view config info for each view type for debugging purposes. + for (uint32_t e = 0; e < viewCount; e++) { + const XrViewConfigurationView* element = &elements[e]; + + ALOGV( + "Viewport [%d]: Recommended Width=%d Height=%d SampleCount=%d", + e, + element->recommendedImageRectWidth, + element->recommendedImageRectHeight, + element->recommendedSwapchainSampleCount); + + ALOGV( + "Viewport [%d]: Max Width=%d Height=%d SampleCount=%d", + e, + element->maxImageRectWidth, + element->maxImageRectHeight, + element->maxSwapchainSampleCount); + } + + // Cache the view config properties for the selected config type. + if (viewportConfigType == supportedViewConfigType) { + assert(viewCount == ovrMaxNumEyes); + for (uint32_t e = 0; e < viewCount; e++) { + appState.ViewConfigurationView[e] = elements[e]; + } + } + + free(elements); + } else { + ALOGE("Empty viewport configuration type: %d", viewCount); + } + } + + free(viewportConfigurationTypes); + + // Get the viewport configuration info for the chosen viewport configuration type. + appState.ViewportConfig.type = XR_TYPE_VIEW_CONFIGURATION_PROPERTIES; + + OXR(xrGetViewConfigurationProperties( + appState.Instance, appState.SystemId, supportedViewConfigType, &appState.ViewportConfig)); + + // Enumerate the supported color space options for the system. + { + PFN_xrEnumerateColorSpacesFB pfnxrEnumerateColorSpacesFB = NULL; + OXR(xrGetInstanceProcAddr( + appState.Instance, + "xrEnumerateColorSpacesFB", + (PFN_xrVoidFunction*)(&pfnxrEnumerateColorSpacesFB))); + + uint32_t colorSpaceCountOutput = 0; + OXR(pfnxrEnumerateColorSpacesFB(appState.Session, 0, &colorSpaceCountOutput, NULL)); + + XrColorSpaceFB* colorSpaces = + (XrColorSpaceFB*)malloc(colorSpaceCountOutput * sizeof(XrColorSpaceFB)); + + OXR(pfnxrEnumerateColorSpacesFB( + appState.Session, colorSpaceCountOutput, &colorSpaceCountOutput, colorSpaces)); + ALOGV("Supported ColorSpaces:"); + + for (uint32_t i = 0; i < colorSpaceCountOutput; i++) { + ALOGV("%d:%d", i, colorSpaces[i]); + } + + const XrColorSpaceFB requestColorSpace = XR_COLOR_SPACE_REC2020_FB; + + PFN_xrSetColorSpaceFB pfnxrSetColorSpaceFB = NULL; + OXR(xrGetInstanceProcAddr( + appState.Instance, "xrSetColorSpaceFB", (PFN_xrVoidFunction*)(&pfnxrSetColorSpaceFB))); + + OXR(pfnxrSetColorSpaceFB(appState.Session, requestColorSpace)); + + free(colorSpaces); + } + + // Get the supported display refresh rates for the system. + { + PFN_xrEnumerateDisplayRefreshRatesFB pfnxrEnumerateDisplayRefreshRatesFB = NULL; + OXR(xrGetInstanceProcAddr( + appState.Instance, + "xrEnumerateDisplayRefreshRatesFB", + (PFN_xrVoidFunction*)(&pfnxrEnumerateDisplayRefreshRatesFB))); + + OXR(pfnxrEnumerateDisplayRefreshRatesFB( + appState.Session, 0, &appState.NumSupportedDisplayRefreshRates, NULL)); + + appState.SupportedDisplayRefreshRates = + (float*)malloc(appState.NumSupportedDisplayRefreshRates * sizeof(float)); + OXR(pfnxrEnumerateDisplayRefreshRatesFB( + appState.Session, + appState.NumSupportedDisplayRefreshRates, + &appState.NumSupportedDisplayRefreshRates, + appState.SupportedDisplayRefreshRates)); + ALOGV("Supported Refresh Rates:"); + for (uint32_t i = 0; i < appState.NumSupportedDisplayRefreshRates; i++) { + ALOGV("%d:%f", i, appState.SupportedDisplayRefreshRates[i]); + } + + OXR(xrGetInstanceProcAddr( + appState.Instance, + "xrGetDisplayRefreshRateFB", + (PFN_xrVoidFunction*)(&appState.pfnGetDisplayRefreshRate))); + + float currentDisplayRefreshRate = 0.0f; + OXR(appState.pfnGetDisplayRefreshRate(appState.Session, ¤tDisplayRefreshRate)); + ALOGV("Current System Display Refresh Rate: %f", currentDisplayRefreshRate); + + OXR(xrGetInstanceProcAddr( + appState.Instance, + "xrRequestDisplayRefreshRateFB", + (PFN_xrVoidFunction*)(&appState.pfnRequestDisplayRefreshRate))); + + // Test requesting the system default. + OXR(appState.pfnRequestDisplayRefreshRate(appState.Session, 0.0f)); + ALOGV("Requesting system default display refresh rate"); + } + + bool stageSupported = false; + + uint32_t numOutputSpaces = 0; + OXR(xrEnumerateReferenceSpaces(appState.Session, 0, &numOutputSpaces, NULL)); + + XrReferenceSpaceType* referenceSpaces = + (XrReferenceSpaceType*)malloc(numOutputSpaces * sizeof(XrReferenceSpaceType)); + + OXR(xrEnumerateReferenceSpaces( + appState.Session, numOutputSpaces, &numOutputSpaces, referenceSpaces)); + + for (uint32_t i = 0; i < numOutputSpaces; i++) { + if (referenceSpaces[i] == XR_REFERENCE_SPACE_TYPE_STAGE) { + stageSupported = true; + break; + } + } + + free(referenceSpaces); + + // Create a space to the first path + XrReferenceSpaceCreateInfo spaceCreateInfo = {XR_TYPE_REFERENCE_SPACE_CREATE_INFO}; + spaceCreateInfo.referenceSpaceType = XR_REFERENCE_SPACE_TYPE_VIEW; + spaceCreateInfo.poseInReferenceSpace.orientation.w = 1.0f; + OXR(xrCreateReferenceSpace(appState.Session, &spaceCreateInfo, &appState.HeadSpace)); + + spaceCreateInfo.referenceSpaceType = XR_REFERENCE_SPACE_TYPE_LOCAL; + OXR(xrCreateReferenceSpace(appState.Session, &spaceCreateInfo, &appState.LocalSpace)); + + // Create a default stage space to use if SPACE_TYPE_STAGE is not + // supported, or calls to xrGetReferenceSpaceBoundsRect fail. + { + spaceCreateInfo.referenceSpaceType = XR_REFERENCE_SPACE_TYPE_LOCAL; + spaceCreateInfo.poseInReferenceSpace.position.y = -1.6750f; + OXR(xrCreateReferenceSpace(appState.Session, &spaceCreateInfo, &appState.FakeStageSpace)); + ALOGV("Created fake stage space from local space with offset"); + appState.CurrentSpace = appState.FakeStageSpace; + } + + if (stageSupported) { + spaceCreateInfo.referenceSpaceType = XR_REFERENCE_SPACE_TYPE_STAGE; + spaceCreateInfo.poseInReferenceSpace.position.y = 0.0f; + OXR(xrCreateReferenceSpace(appState.Session, &spaceCreateInfo, &appState.StageSpace)); + ALOGV("Created stage space"); + appState.CurrentSpace = appState.StageSpace; + } + + XrView* projections = (XrView*)(malloc(ovrMaxNumEyes * sizeof(XrView))); + for (int eye = 0; eye < ovrMaxNumEyes; eye++) { + memset(&projections[eye], 0, sizeof(XrView)); + projections[eye].type = XR_TYPE_VIEW; + } + + // Actions + XrActionSet runningActionSet = + CreateActionSet(1, "running_action_set", "Action Set used on main loop"); + XrAction toggleAction = + CreateAction(runningActionSet, XR_ACTION_TYPE_BOOLEAN_INPUT, "toggle", "Toggle", 0, NULL); + XrAction moveOnXAction = CreateAction( + runningActionSet, XR_ACTION_TYPE_FLOAT_INPUT, "move_on_x", "Move on X", 0, NULL); + XrAction moveOnYAction = CreateAction( + runningActionSet, XR_ACTION_TYPE_FLOAT_INPUT, "move_on_y", "Move on Y", 0, NULL); + XrAction moveOnJoystickAction = CreateAction( + runningActionSet, XR_ACTION_TYPE_VECTOR2F_INPUT, "move_on_joy", "Move on Joy", 0, NULL); + XrAction thumbstickClickAction = CreateAction( + runningActionSet, + XR_ACTION_TYPE_BOOLEAN_INPUT, + "thumbstick_click", + "Thumbstick Click", + 0, + NULL); + + XrAction vibrateLeftToggle = CreateAction( + runningActionSet, + XR_ACTION_TYPE_BOOLEAN_INPUT, + "vibrate_left", + "Vibrate Left Controller", + 0, + NULL); + XrAction vibrateRightToggle = CreateAction( + runningActionSet, + XR_ACTION_TYPE_BOOLEAN_INPUT, + "vibrate_right", + "Vibrate Right Controller", + 0, + NULL); + XrAction vibrateLeftFeedback = CreateAction( + runningActionSet, + XR_ACTION_TYPE_VIBRATION_OUTPUT, + "vibrate_left_feedback", + "Vibrate Left Controller Feedback", + 0, + NULL); + XrAction vibrateRightFeedback = CreateAction( + runningActionSet, + XR_ACTION_TYPE_VIBRATION_OUTPUT, + "vibrate_right_feedback", + "Vibrate Right Controller Feedback", + 0, + NULL); + + XrPath leftHandPath; + OXR(xrStringToPath(appState.Instance, "/user/hand/left", &leftHandPath)); + XrPath rightHandPath; + OXR(xrStringToPath(appState.Instance, "/user/hand/right", &rightHandPath)); + XrPath handSubactionPaths[2] = {leftHandPath, rightHandPath}; + + XrAction aimPoseAction = CreateAction( + runningActionSet, XR_ACTION_TYPE_POSE_INPUT, "aim_pose", NULL, 2, handSubactionPaths); + XrAction gripPoseAction = CreateAction( + runningActionSet, XR_ACTION_TYPE_POSE_INPUT, "grip_pose", NULL, 2, handSubactionPaths); + + XrPath interactionProfilePath = XR_NULL_PATH; + XrPath interactionProfilePathTouch = XR_NULL_PATH; + XrPath interactionProfilePathKHRSimple = XR_NULL_PATH; + + OXR(xrStringToPath( + appState.Instance, + "/interaction_profiles/oculus/touch_controller", + &interactionProfilePathTouch)); + OXR(xrStringToPath( + appState.Instance, + "/interaction_profiles/khr/simple_controller", + &interactionProfilePathKHRSimple)); + + // Toggle this to force simple as a first choice, otherwise use it as a last resort + bool useSimpleProfile = false; /// true; + if (useSimpleProfile) { + ALOGV("xrSuggestInteractionProfileBindings found bindings for Khronos SIMPLE controller"); + interactionProfilePath = interactionProfilePathKHRSimple; + } else { + // Query Set + XrActionSet queryActionSet = + CreateActionSet(1, "query_action_set", "Action Set used to query device caps"); + XrAction dummyAction = CreateAction( + queryActionSet, XR_ACTION_TYPE_BOOLEAN_INPUT, "dummy_action", "Dummy Action", 0, NULL); + + // Map bindings + XrActionSuggestedBinding bindings[1]; + int currBinding = 0; + bindings[currBinding++] = + ActionSuggestedBinding(dummyAction, "/user/hand/right/input/system/click"); + + XrInteractionProfileSuggestedBinding suggestedBindings = {XR_TYPE_INTERACTION_PROFILE_SUGGESTED_BINDING}; + suggestedBindings.suggestedBindings = bindings; + suggestedBindings.countSuggestedBindings = currBinding; + + // Try all + suggestedBindings.interactionProfile = interactionProfilePathTouch; + XrResult suggestTouchResult = + xrSuggestInteractionProfileBindings(appState.Instance, &suggestedBindings); + OXR(suggestTouchResult); + + if (XR_SUCCESS == suggestTouchResult) { + ALOGV("xrSuggestInteractionProfileBindings found bindings for QUEST controller"); + interactionProfilePath = interactionProfilePathTouch; + } + + if (interactionProfilePath == XR_NULL_PATH) { + // Simple as a fallback + bindings[0] = + ActionSuggestedBinding(dummyAction, "/user/hand/right/input/select/click"); + suggestedBindings.interactionProfile = interactionProfilePathKHRSimple; + XrResult suggestKHRSimpleResult = + xrSuggestInteractionProfileBindings(appState.Instance, &suggestedBindings); + OXR(suggestKHRSimpleResult); + if (XR_SUCCESS == suggestKHRSimpleResult) { + ALOGV( + "xrSuggestInteractionProfileBindings found bindings for Khronos SIMPLE controller"); + interactionProfilePath = interactionProfilePathKHRSimple; + } else { + ALOGE("xrSuggestInteractionProfileBindings did NOT find any bindings."); + assert(false); + } + } + } + + // Action creation + { + // Map bindings + + XrActionSuggestedBinding bindings[22]; // large enough for all profiles + int currBinding = 0; + + { + if (interactionProfilePath == interactionProfilePathTouch) { + bindings[currBinding++] = + ActionSuggestedBinding(toggleAction, "/user/hand/left/input/trigger"); + bindings[currBinding++] = + ActionSuggestedBinding(toggleAction, "/user/hand/right/input/trigger"); + bindings[currBinding++] = + ActionSuggestedBinding(toggleAction, "/user/hand/left/input/x/click"); + bindings[currBinding++] = + ActionSuggestedBinding(toggleAction, "/user/hand/right/input/a/click"); + bindings[currBinding++] = + ActionSuggestedBinding(moveOnXAction, "/user/hand/left/input/squeeze/value"); + bindings[currBinding++] = + ActionSuggestedBinding(moveOnXAction, "/user/hand/right/input/squeeze/value"); + bindings[currBinding++] = + ActionSuggestedBinding(moveOnYAction, "/user/hand/left/input/trigger/value"); + bindings[currBinding++] = + ActionSuggestedBinding(moveOnYAction, "/user/hand/right/input/trigger/value"); + bindings[currBinding++] = ActionSuggestedBinding( + moveOnJoystickAction, "/user/hand/left/input/thumbstick"); + bindings[currBinding++] = ActionSuggestedBinding( + moveOnJoystickAction, "/user/hand/right/input/thumbstick"); + bindings[currBinding++] = ActionSuggestedBinding( + thumbstickClickAction, "/user/hand/left/input/thumbstick/click"); + bindings[currBinding++] = ActionSuggestedBinding( + thumbstickClickAction, "/user/hand/right/input/thumbstick/click"); + bindings[currBinding++] = + ActionSuggestedBinding(vibrateLeftToggle, "/user/hand/left/input/y/click"); + bindings[currBinding++] = + ActionSuggestedBinding(vibrateRightToggle, "/user/hand/right/input/b/click"); + bindings[currBinding++] = + ActionSuggestedBinding(vibrateLeftFeedback, "/user/hand/left/output/haptic"); + bindings[currBinding++] = + ActionSuggestedBinding(vibrateRightFeedback, "/user/hand/right/output/haptic"); + bindings[currBinding++] = + ActionSuggestedBinding(aimPoseAction, "/user/hand/left/input/aim/pose"); + bindings[currBinding++] = + ActionSuggestedBinding(aimPoseAction, "/user/hand/right/input/aim/pose"); + bindings[currBinding++] = + ActionSuggestedBinding(gripPoseAction, "/user/hand/left/input/grip/pose"); + bindings[currBinding++] = + ActionSuggestedBinding(gripPoseAction, "/user/hand/right/input/grip/pose"); + } + + if (interactionProfilePath == interactionProfilePathKHRSimple) { + bindings[currBinding++] = + ActionSuggestedBinding(toggleAction, "/user/hand/left/input/select/click"); + bindings[currBinding++] = + ActionSuggestedBinding(toggleAction, "/user/hand/right/input/select/click"); + bindings[currBinding++] = + ActionSuggestedBinding(vibrateLeftToggle, "/user/hand/left/input/menu/click"); + bindings[currBinding++] = + ActionSuggestedBinding(vibrateRightToggle, "/user/hand/right/input/menu/click"); + bindings[currBinding++] = + ActionSuggestedBinding(vibrateLeftFeedback, "/user/hand/left/output/haptic"); + bindings[currBinding++] = + ActionSuggestedBinding(vibrateRightFeedback, "/user/hand/right/output/haptic"); + bindings[currBinding++] = + ActionSuggestedBinding(aimPoseAction, "/user/hand/left/input/aim/pose"); + bindings[currBinding++] = + ActionSuggestedBinding(aimPoseAction, "/user/hand/right/input/aim/pose"); + bindings[currBinding++] = + ActionSuggestedBinding(gripPoseAction, "/user/hand/left/input/grip/pose"); + bindings[currBinding++] = + ActionSuggestedBinding(gripPoseAction, "/user/hand/right/input/grip/pose"); + } + } + + XrInteractionProfileSuggestedBinding suggestedBindings = {XR_TYPE_INTERACTION_PROFILE_SUGGESTED_BINDING}; + suggestedBindings.interactionProfile = interactionProfilePath; + suggestedBindings.suggestedBindings = bindings; + suggestedBindings.countSuggestedBindings = currBinding; + OXR(xrSuggestInteractionProfileBindings(appState.Instance, &suggestedBindings)); + + // Attach to session + XrSessionActionSetsAttachInfo attachInfo = {XR_TYPE_SESSION_ACTION_SETS_ATTACH_INFO}; + attachInfo.countActionSets = 1; + attachInfo.actionSets = &runningActionSet; + OXR(xrAttachSessionActionSets(appState.Session, &attachInfo)); + + // Enumerate actions + XrPath actionPathsBuffer[16]; + char stringBuffer[256]; + XrAction actionsToEnumerate[] = { + toggleAction, + moveOnXAction, + moveOnYAction, + moveOnJoystickAction, + thumbstickClickAction, + vibrateLeftToggle, + vibrateRightToggle, + vibrateLeftFeedback, + vibrateRightFeedback, + aimPoseAction, + gripPoseAction, + }; + for (size_t i = 0; i < sizeof(actionsToEnumerate) / sizeof(actionsToEnumerate[0]); ++i) { + XrBoundSourcesForActionEnumerateInfo enumerateInfo = {XR_TYPE_BOUND_SOURCES_FOR_ACTION_ENUMERATE_INFO}; + enumerateInfo.action = actionsToEnumerate[i]; + + // Get Count + uint32_t countOutput = 0; + OXR(xrEnumerateBoundSourcesForAction( + appState.Session, &enumerateInfo, 0 /* request size */, &countOutput, NULL)); + ALOGV( + "xrEnumerateBoundSourcesForAction action=%lld count=%u", + (long long)enumerateInfo.action, + countOutput); + + if (countOutput < 16) { + OXR(xrEnumerateBoundSourcesForAction( + appState.Session, &enumerateInfo, 16, &countOutput, actionPathsBuffer)); + for (uint32_t a = 0; a < countOutput; ++a) { + XrInputSourceLocalizedNameGetInfo nameGetInfo = {XR_TYPE_INPUT_SOURCE_LOCALIZED_NAME_GET_INFO}; + nameGetInfo.sourcePath = actionPathsBuffer[a]; + nameGetInfo.whichComponents = XR_INPUT_SOURCE_LOCALIZED_NAME_USER_PATH_BIT | + XR_INPUT_SOURCE_LOCALIZED_NAME_INTERACTION_PROFILE_BIT | + XR_INPUT_SOURCE_LOCALIZED_NAME_COMPONENT_BIT; + + uint32_t stringCount = 0u; + OXR(xrGetInputSourceLocalizedName( + appState.Session, &nameGetInfo, 0, &stringCount, NULL)); + if (stringCount < 256) { + OXR(xrGetInputSourceLocalizedName( + appState.Session, &nameGetInfo, 256, &stringCount, stringBuffer)); + char pathStr[256]; + uint32_t strLen = 0; + OXR(xrPathToString( + appState.Instance, + actionPathsBuffer[a], + (uint32_t)sizeof(pathStr), + &strLen, + pathStr)); + ALOGV( + " -> path = %lld `%s` -> `%s`", + (long long)actionPathsBuffer[a], + pathStr, + stringBuffer); + } + } + } + } + } + + XrSpace leftControllerAimSpace = XR_NULL_HANDLE; + XrSpace rightControllerAimSpace = XR_NULL_HANDLE; + XrSpace leftControllerGripSpace = XR_NULL_HANDLE; + XrSpace rightControllerGripSpace = XR_NULL_HANDLE; + + ovrRenderer_Create( + appState.Session, + &appState.Renderer, + appState.ViewConfigurationView[0].recommendedImageRectWidth, + appState.ViewConfigurationView[0].recommendedImageRectHeight); + + ovrRenderer_SetFoveation( + &appState.Instance, + &appState.Session, + &appState.Renderer, + XR_FOVEATION_LEVEL_HIGH_FB, + 0, + XR_FOVEATION_DYNAMIC_DISABLED_FB); + + app->userData = &appState; + app->onAppCmd = app_handle_cmd; + + bool stageBoundsDirty = true; + + // App-specific input + float appQuadPositionX = 0.0f; + float appQuadPositionY = 0.0f; + float appCylPositionX = 0.0f; + float appCylPositionY = 0.0f; + + while (app->destroyRequested == 0) { + // Read all pending events. + for (;;) { + int events; + struct android_poll_source* source; + // If the timeout is zero, returns immediately without blocking. + // If the timeout is negative, waits indefinitely until an event appears. + const int timeoutMilliseconds = + (appState.Resumed == false && appState.SessionActive == false && + app->destroyRequested == 0) + ? -1 + : 0; + if (ALooper_pollAll(timeoutMilliseconds, NULL, &events, (void**)&source) < 0) { + break; + } + + // Process this event. + if (source != NULL) { + source->process(app, source); + } + } + + ovrApp_HandleXrEvents(&appState); + + if (appState.SessionActive == false) { + continue; + } + + if (leftControllerAimSpace == XR_NULL_HANDLE) { + leftControllerAimSpace = CreateActionSpace(aimPoseAction, leftHandPath); + } + if (rightControllerAimSpace == XR_NULL_HANDLE) { + rightControllerAimSpace = CreateActionSpace(aimPoseAction, rightHandPath); + } + if (leftControllerGripSpace == XR_NULL_HANDLE) { + leftControllerGripSpace = CreateActionSpace(gripPoseAction, leftHandPath); + } + if (rightControllerGripSpace == XR_NULL_HANDLE) { + rightControllerGripSpace = CreateActionSpace(gripPoseAction, rightHandPath); + } + + // Create the scene if not yet created. + // The scene is created here to be able to show a loading icon. + if (!ovrScene_IsCreated(&appState.Scene)) { + ovrScene_Create( + app->activity->assetManager, appState.Instance, appState.Session, &appState.Scene); + } + + if (stageBoundsDirty) { + UpdateStageBounds(&appState); + stageBoundsDirty = false; + } + + // NOTE: OpenXR does not use the concept of frame indices. Instead, + // XrWaitFrame returns the predicted display time. + XrFrameWaitInfo waitFrameInfo = {XR_TYPE_FRAME_WAIT_INFO}; + XrFrameState frameState = {XR_TYPE_FRAME_STATE}; + + OXR(xrWaitFrame(appState.Session, &waitFrameInfo, &frameState)); + + // Get the HMD pose, predicted for the middle of the time period during which + // the new eye images will be displayed. The number of frames predicted ahead + // depends on the pipeline depth of the engine and the synthesis rate. + // The better the prediction, the less black will be pulled in at the edges. + XrFrameBeginInfo beginFrameDesc = {XR_TYPE_FRAME_BEGIN_INFO}; + OXR(xrBeginFrame(appState.Session, &beginFrameDesc)); + + XrSpaceLocation loc = {XR_TYPE_SPACE_LOCATION}; + OXR(xrLocateSpace( + appState.HeadSpace, appState.CurrentSpace, frameState.predictedDisplayTime, &loc)); + XrPosef xfStageFromHead = loc.pose; + OXR(xrLocateSpace( + appState.HeadSpace, appState.LocalSpace, frameState.predictedDisplayTime, &loc)); + + XrViewLocateInfo projectionInfo = {XR_TYPE_VIEW_LOCATE_INFO}; + projectionInfo.viewConfigurationType = appState.ViewportConfig.viewConfigurationType; + projectionInfo.displayTime = frameState.predictedDisplayTime; + projectionInfo.space = appState.HeadSpace; + + XrViewState viewState = {XR_TYPE_VIEW_STATE}; + + uint32_t projectionCapacityInput = ovrMaxNumEyes; + uint32_t projectionCountOutput = projectionCapacityInput; + + OXR(xrLocateViews( + appState.Session, + &projectionInfo, + &viewState, + projectionCapacityInput, + &projectionCountOutput, + projections)); + // + + ovrSceneMatrices sceneMatrices; + XrPosef viewTransform[2]; + + for (int eye = 0; eye < ovrMaxNumEyes; eye++) { + XrPosef xfHeadFromEye = projections[eye].pose; + XrPosef xfStageFromEye; + XrPosef_Multiply(&xfStageFromEye, &xfStageFromHead, &xfHeadFromEye); + XrPosef_Invert(&viewTransform[eye], &xfStageFromEye); + + XrMatrix4x4f_CreateFromRigidTransform(&sceneMatrices.ViewMatrix[eye], &viewTransform[eye]); + + const XrFovf fov = projections[eye].fov; + XrMatrix4x4f_CreateProjectionFov( + &sceneMatrices.ProjectionMatrix[eye], GRAPHICS_OPENGL_ES, fov, 0.1f, 0.0f); + } + + // update input information + XrAction controller[] = {aimPoseAction, gripPoseAction, aimPoseAction, gripPoseAction}; + XrPath subactionPath[] = {leftHandPath, leftHandPath, rightHandPath, rightHandPath}; + XrSpace controllerSpace[] = { + leftControllerAimSpace, + leftControllerGripSpace, + rightControllerAimSpace, + rightControllerGripSpace, + }; + for (int i = 0; i < 4; i++) { + if (ActionPoseIsActive(controller[i], subactionPath[i])) { + LocVel lv = GetSpaceLocVel(controllerSpace[i], frameState.predictedDisplayTime); + appState.Scene.TrackedController[i].Active = + (lv.loc.locationFlags & XR_SPACE_LOCATION_POSITION_VALID_BIT) != 0; + appState.Scene.TrackedController[i].Pose = lv.loc.pose; + for (int j = 0; j < 3; j++) { + float dt = 0.01f; // use 0.2f for for testing velocity vectors + (&appState.Scene.TrackedController[i].Pose.position.x)[j] += + (&lv.vel.linearVelocity.x)[j] * dt; + } + } else { + ovrTrackedController_Clear(&appState.Scene.TrackedController[i]); + } + } + + // OpenXR input + { + // sync action data + XrActiveActionSet activeActionSet = {0}; + activeActionSet.actionSet = runningActionSet; + activeActionSet.subactionPath = XR_NULL_PATH; + + XrActionsSyncInfo syncInfo = {XR_TYPE_ACTIONS_SYNC_INFO}; + syncInfo.countActiveActionSets = 1; + syncInfo.activeActionSets = &activeActionSet; + OXR(xrSyncActions(appState.Session, &syncInfo)); + + // query input action states + XrActionStateGetInfo getInfo = {XR_TYPE_ACTION_STATE_GET_INFO}; + getInfo.subactionPath = XR_NULL_PATH; + + XrActionStateBoolean toggleState = GetActionStateBoolean(toggleAction); + XrActionStateBoolean vibrateLeftState = GetActionStateBoolean(vibrateLeftToggle); + XrActionStateBoolean vibrateRightState = GetActionStateBoolean(vibrateRightToggle); + XrActionStateBoolean thumbstickClickState = + GetActionStateBoolean(thumbstickClickAction); + + // Update app logic based on input + if (toggleState.changedSinceLastSync) { + // Also stop haptics + XrHapticActionInfo hapticActionInfo = {XR_TYPE_HAPTIC_ACTION_INFO}; + hapticActionInfo.action = vibrateLeftFeedback; + OXR(xrStopHapticFeedback(appState.Session, &hapticActionInfo)); + hapticActionInfo.action = vibrateRightFeedback; + OXR(xrStopHapticFeedback(appState.Session, &hapticActionInfo)); + } + + if (thumbstickClickState.changedSinceLastSync && + thumbstickClickState.currentState == XR_TRUE) { + float currentRefreshRate = 0.0f; + OXR(appState.pfnGetDisplayRefreshRate(appState.Session, ¤tRefreshRate)); + ALOGV("Current Display Refresh Rate: %f", currentRefreshRate); + + const int requestedRateIndex = appState.RequestedDisplayRefreshRateIndex++ % + appState.NumSupportedDisplayRefreshRates; + + const float requestRefreshRate = + appState.SupportedDisplayRefreshRates[requestedRateIndex]; + ALOGV("Requesting Display Refresh Rate: %f", requestRefreshRate); + OXR(appState.pfnRequestDisplayRefreshRate(appState.Session, requestRefreshRate)); + } + + // The KHR simple profile doesn't have these actions, so the getters will fail + // and flood the log with errors. + if (useSimpleProfile == false) { + XrActionStateFloat moveXState = GetActionStateFloat(moveOnXAction); + XrActionStateFloat moveYState = GetActionStateFloat(moveOnYAction); + if (moveXState.changedSinceLastSync) { + appQuadPositionX = moveXState.currentState; + } + if (moveYState.changedSinceLastSync) { + appQuadPositionY = moveYState.currentState; + } + + XrActionStateVector2f moveJoystickState = + GetActionStateVector2(moveOnJoystickAction); + if (moveJoystickState.changedSinceLastSync) { + appCylPositionX = moveJoystickState.currentState.x; + appCylPositionY = moveJoystickState.currentState.y; + } + } + + // Haptics + // NOTE: using the values from the example in the spec + if (vibrateLeftState.changedSinceLastSync && vibrateLeftState.currentState) { + ALOGV("Firing Haptics on L ... "); + // fire haptics using output action + XrHapticVibration vibration = {XR_TYPE_HAPTIC_VIBRATION}; + vibration.amplitude = 0.5; + vibration.duration = ToXrTime(0.5); // half a second + vibration.frequency = 3000; + XrHapticActionInfo hapticActionInfo = {XR_TYPE_HAPTIC_ACTION_INFO}; + hapticActionInfo.action = vibrateLeftFeedback; + OXR(xrApplyHapticFeedback( + appState.Session, &hapticActionInfo, (const XrHapticBaseHeader*)&vibration)); + } + if (vibrateRightState.changedSinceLastSync && vibrateRightState.currentState) { + ALOGV("Firing Haptics on R ... "); + // fire haptics using output action + XrHapticVibration vibration = {XR_TYPE_HAPTIC_VIBRATION}; + vibration.amplitude = 0.5; + vibration.duration = XR_MIN_HAPTIC_DURATION; + vibration.frequency = 3000; + XrHapticActionInfo hapticActionInfo = {XR_TYPE_HAPTIC_ACTION_INFO}; + hapticActionInfo.action = vibrateRightFeedback; + OXR(xrApplyHapticFeedback( + appState.Session, &hapticActionInfo, (const XrHapticBaseHeader*)&vibration)); + } + } + + // Set-up the compositor layers for this frame. + // NOTE: Multiple independent layers are allowed, but they need to be added + // in a depth consistent order. + + XrCompositionLayerProjectionView projection_layer_elements[2] = { + {XR_TYPE_COMPOSITION_LAYER_PROJECTION_VIEW}, {XR_TYPE_COMPOSITION_LAYER_PROJECTION_VIEW}}; + + appState.LayerCount = 0; + memset(appState.Layers, 0, sizeof(ovrCompositorLayer_Union) * ovrMaxLayerCount); + bool shouldRenderWorldLayer = true; + bool hasCubeMapBackground = appState.Scene.CubeMapSwapChain.Handle != XR_NULL_HANDLE; + + // Add a background Layer + if (appState.Scene.BackGroundType == BACKGROUND_CUBEMAP && + hasCubeMapBackground /* data loaded from sdcard */) { + XrCompositionLayerCubeKHR cube_layer = {XR_TYPE_COMPOSITION_LAYER_CUBE_KHR}; + cube_layer.layerFlags = 0; + cube_layer.space = appState.CurrentSpace; + cube_layer.eyeVisibility = XR_EYE_VISIBILITY_BOTH; + cube_layer.swapchain = appState.Scene.CubeMapSwapChain.Handle; + XrQuaternionf_CreateIdentity(&cube_layer.orientation); + + appState.Layers[appState.LayerCount++].Cube = cube_layer; + shouldRenderWorldLayer = false; + } else if (appState.Scene.BackGroundType == BACKGROUND_EQUIRECT) { + XrCompositionLayerEquirect2KHR equirect_layer = {XR_TYPE_COMPOSITION_LAYER_EQUIRECT2_KHR}; + equirect_layer.layerFlags = 0; + equirect_layer.space = appState.CurrentSpace; + equirect_layer.eyeVisibility = XR_EYE_VISIBILITY_BOTH; + memset(&equirect_layer.subImage, 0, sizeof(XrSwapchainSubImage)); + equirect_layer.subImage.swapchain = appState.Scene.EquirectSwapChain.Handle; + equirect_layer.subImage.imageRect.offset.x = 0; + equirect_layer.subImage.imageRect.offset.y = 0; + equirect_layer.subImage.imageRect.extent.width = appState.Scene.EquirectSwapChain.Width; + equirect_layer.subImage.imageRect.extent.height = + appState.Scene.EquirectSwapChain.Height; + equirect_layer.subImage.imageArrayIndex = 0; + XrPosef_CreateIdentity(&equirect_layer.pose); + equirect_layer.radius = 10.0f; + const float centralHorizontalAngle = (2.0f * MATH_PI) / 3.0f; + const float upperVerticalAngle = + (MATH_PI / 2.0f) * (2.0f / 3.0f); // 60 degrees north latitude + const float lowerVerticalAngle = 0.0f; // equator + equirect_layer.centralHorizontalAngle = centralHorizontalAngle; + equirect_layer.upperVerticalAngle = upperVerticalAngle; + equirect_layer.lowerVerticalAngle = lowerVerticalAngle; + + appState.Layers[appState.LayerCount++].Equirect2 = equirect_layer; + } + + // Render the world-view layer (simple ground plane) + if (shouldRenderWorldLayer) { + ovrRenderer_RenderFrame(&appState.Renderer, &appState.Scene, &sceneMatrices); + + XrCompositionLayerProjection projection_layer = {XR_TYPE_COMPOSITION_LAYER_PROJECTION}; + projection_layer.layerFlags = XR_COMPOSITION_LAYER_BLEND_TEXTURE_SOURCE_ALPHA_BIT; + projection_layer.layerFlags |= XR_COMPOSITION_LAYER_CORRECT_CHROMATIC_ABERRATION_BIT; + projection_layer.space = appState.CurrentSpace; + projection_layer.viewCount = ovrMaxNumEyes; + projection_layer.views = projection_layer_elements; + + for (int eye = 0; eye < ovrMaxNumEyes; eye++) { + ovrFramebuffer* frameBuffer = &appState.Renderer.FrameBuffer[eye]; + + memset( + &projection_layer_elements[eye], 0, sizeof(XrCompositionLayerProjectionView)); + projection_layer_elements[eye].type = XR_TYPE_COMPOSITION_LAYER_PROJECTION_VIEW; + + XrPosef_Invert(&projection_layer_elements[eye].pose, &viewTransform[eye]); + projection_layer_elements[eye].fov = projections[eye].fov; + + memset(&projection_layer_elements[eye].subImage, 0, sizeof(XrSwapchainSubImage)); + projection_layer_elements[eye].subImage.swapchain = + frameBuffer->ColorSwapChain.Handle; + projection_layer_elements[eye].subImage.imageRect.offset.x = 0; + projection_layer_elements[eye].subImage.imageRect.offset.y = 0; + projection_layer_elements[eye].subImage.imageRect.extent.width = + frameBuffer->ColorSwapChain.Width; + projection_layer_elements[eye].subImage.imageRect.extent.height = + frameBuffer->ColorSwapChain.Height; + projection_layer_elements[eye].subImage.imageArrayIndex = 0; + } + + appState.Layers[appState.LayerCount++].Projection = projection_layer; + } + + // Build the cylinder layer + { + XrCompositionLayerCylinderKHR cylinder_layer = {XR_TYPE_COMPOSITION_LAYER_CYLINDER_KHR}; + cylinder_layer.layerFlags = XR_COMPOSITION_LAYER_BLEND_TEXTURE_SOURCE_ALPHA_BIT; + cylinder_layer.space = appState.LocalSpace; + cylinder_layer.eyeVisibility = XR_EYE_VISIBILITY_BOTH; + memset(&cylinder_layer.subImage, 0, sizeof(XrSwapchainSubImage)); + cylinder_layer.subImage.swapchain = appState.Scene.CylinderSwapChain.Handle; + cylinder_layer.subImage.imageRect.offset.x = 0; + cylinder_layer.subImage.imageRect.offset.y = 0; + cylinder_layer.subImage.imageRect.extent.width = appState.Scene.CylinderSwapChain.Width; + cylinder_layer.subImage.imageRect.extent.height = + appState.Scene.CylinderSwapChain.Height; + cylinder_layer.subImage.imageArrayIndex = 0; + const XrVector3f axis = {0.0f, 1.0f, 0.0f}; + const XrVector3f pos = {appCylPositionX, appCylPositionY, 0.0f}; + XrQuaternionf_CreateFromAxisAngle(&cylinder_layer.pose.orientation, &axis, -45.0f * MATH_PI / 180.0f); + cylinder_layer.pose.position = pos; + cylinder_layer.radius = 2.0f; + + cylinder_layer.centralAngle = MATH_PI / 4.0; + cylinder_layer.aspectRatio = 2.0f; + + appState.Layers[appState.LayerCount++].Cylinder = cylinder_layer; + } + + // Build the quad layer + { + const XrVector3f axis = {0.0f, 1.0f, 0.0f}; + XrVector3f pos = { + -2.0f * (1.0f - appQuadPositionX), 2.0f * (1.0f - appQuadPositionY), -2.0f}; + XrExtent2Df size = {1.0f, 1.0f}; + + XrCompositionLayerQuad quad_layer_left = {XR_TYPE_COMPOSITION_LAYER_QUAD}; + quad_layer_left.layerFlags = XR_COMPOSITION_LAYER_BLEND_TEXTURE_SOURCE_ALPHA_BIT; + quad_layer_left.space = appState.CurrentSpace; + quad_layer_left.eyeVisibility = XR_EYE_VISIBILITY_LEFT; + memset(&quad_layer_left.subImage, 0, sizeof(XrSwapchainSubImage)); + quad_layer_left.subImage.swapchain = appState.Scene.QuadSwapChain.Handle; + quad_layer_left.subImage.imageRect.offset.x = 0; + quad_layer_left.subImage.imageRect.offset.y = 0; + quad_layer_left.subImage.imageRect.extent.width = appState.Scene.QuadSwapChain.Width; + quad_layer_left.subImage.imageRect.extent.height = appState.Scene.QuadSwapChain.Height; + quad_layer_left.subImage.imageArrayIndex = 0; + XrPosef_CreateIdentity(&quad_layer_left.pose); + XrQuaternionf_CreateFromAxisAngle(&quad_layer_left.pose.orientation, &axis, 45.0f * MATH_PI / 180.0f); + quad_layer_left.pose.position = pos; + quad_layer_left.size = size; + + appState.Layers[appState.LayerCount++].Quad = quad_layer_left; + + XrCompositionLayerQuad quad_layer_right = {XR_TYPE_COMPOSITION_LAYER_QUAD}; + quad_layer_right.layerFlags = XR_COMPOSITION_LAYER_BLEND_TEXTURE_SOURCE_ALPHA_BIT; + quad_layer_right.space = appState.CurrentSpace; + quad_layer_right.eyeVisibility = XR_EYE_VISIBILITY_RIGHT; + memset(&quad_layer_right.subImage, 0, sizeof(XrSwapchainSubImage)); + quad_layer_right.subImage.swapchain = appState.Scene.QuadSwapChain.Handle; + quad_layer_right.subImage.imageRect.offset.x = 0; + quad_layer_right.subImage.imageRect.offset.y = 0; + quad_layer_right.subImage.imageRect.extent.width = appState.Scene.QuadSwapChain.Width; + quad_layer_right.subImage.imageRect.extent.height = appState.Scene.QuadSwapChain.Height; + quad_layer_right.subImage.imageArrayIndex = 0; + XrPosef_CreateIdentity(&quad_layer_right.pose); + XrQuaternionf_CreateFromAxisAngle(&quad_layer_right.pose.orientation, &axis, 45.0f * MATH_PI / 180.0f); + quad_layer_right.pose.position = pos; + quad_layer_right.size = size; + + appState.Layers[appState.LayerCount++].Quad = quad_layer_right; + } + + // Compose the layers for this frame. + const XrCompositionLayerBaseHeader* layers[ovrMaxLayerCount] = {0}; + for (int i = 0; i < appState.LayerCount; i++) { + layers[i] = (const XrCompositionLayerBaseHeader*)&appState.Layers[i]; + } + + XrFrameEndInfo endFrameInfo = {XR_TYPE_FRAME_END_INFO}; + endFrameInfo.displayTime = frameState.predictedDisplayTime; + endFrameInfo.environmentBlendMode = XR_ENVIRONMENT_BLEND_MODE_OPAQUE; + endFrameInfo.layerCount = appState.LayerCount; + endFrameInfo.layers = layers; + + OXR(xrEndFrame(appState.Session, &endFrameInfo)); + } + + ovrRenderer_Destroy(&appState.Renderer); + + + free(projections); + + ovrScene_Destroy(&appState.Scene); + ovrEgl_DestroyContext(&appState.Egl); + + OXR(xrDestroySpace(appState.HeadSpace)); + OXR(xrDestroySpace(appState.LocalSpace)); + // StageSpace is optional. + if (appState.StageSpace != XR_NULL_HANDLE) { + OXR(xrDestroySpace(appState.StageSpace)); + } + OXR(xrDestroySpace(appState.FakeStageSpace)); + appState.CurrentSpace = XR_NULL_HANDLE; + OXR(xrDestroySession(appState.Session)); + OXR(xrDestroyInstance(appState.Instance)); + + ovrApp_Destroy(&appState); + +#if defined(__cplusplus) + app->activity->vm->DetachCurrentThread(); +#else + (*app->activity->vm)->DetachCurrentThread(app->activity->vm); +#endif +} diff --git a/Samples/XrSamples/XrCompositor_NativeActivity/assets/cubemap256.ktx b/Samples/XrSamples/XrCompositor_NativeActivity/assets/cubemap256.ktx new file mode 100644 index 0000000..ca3f505 Binary files /dev/null and b/Samples/XrSamples/XrCompositor_NativeActivity/assets/cubemap256.ktx differ diff --git a/Samples/XrSamples/XrCompositor_NativeActivity/assets/donotdelete.txt b/Samples/XrSamples/XrCompositor_NativeActivity/assets/donotdelete.txt new file mode 100644 index 0000000..e69de29 diff --git a/Samples/XrSamples/XrControllers/CMakeLists.txt b/Samples/XrSamples/XrControllers/CMakeLists.txt new file mode 100755 index 0000000..c487a98 --- /dev/null +++ b/Samples/XrSamples/XrControllers/CMakeLists.txt @@ -0,0 +1,43 @@ +# Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved. + +project(xrcontrollers) + +if(NOT TARGET OpenXR::openxr_loader) + find_package(OpenXR REQUIRED) +endif() + +file(GLOB_RECURSE SRC_FILES + Src/*.c + Src/*.cpp +) + +if(ANDROID) + add_library(${PROJECT_NAME} MODULE ${SRC_FILES}) + target_include_directories(${PROJECT_NAME} PUBLIC ${ANDROID_NDK}/sources/android/native_app_glue) + target_link_libraries(${PROJECT_NAME} PRIVATE + android + EGL + GLESv3 + log + ktx + ) + set_target_properties(${PROJECT_NAME} PROPERTIES LINK_FLAGS "-u ANativeActivity_onCreate") +elseif(WIN32) + add_definitions(-D_USE_MATH_DEFINES) + add_executable(${PROJECT_NAME} ${SRC_FILES}) + add_custom_command(TARGET ${PROJECT_NAME} PRE_BUILD + COMMAND "${CMAKE_COMMAND}" -E copy_directory + "${CMAKE_CURRENT_LIST_DIR}/assets" + "$/assets" + VERBATIM) + + add_custom_command(TARGET ${PROJECT_NAME} PRE_BUILD + COMMAND "${CMAKE_COMMAND}" -E copy_directory + "${CMAKE_SOURCE_DIR}/SampleXrFramework/res/raw" + "$/font/res/raw" + VERBATIM) +endif() + +# Common across platforms +target_include_directories(${PROJECT_NAME} PRIVATE Src) +target_link_libraries(${PROJECT_NAME} PRIVATE samplexrframework) diff --git a/Samples/XrSamples/XrControllers/Projects/Android/AndroidManifest-ControllerEmulator.xml b/Samples/XrSamples/XrControllers/Projects/Android/AndroidManifest-ControllerEmulator.xml new file mode 100644 index 0000000..0a0e8dc --- /dev/null +++ b/Samples/XrSamples/XrControllers/Projects/Android/AndroidManifest-ControllerEmulator.xml @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Samples/XrSamples/XrControllers/Projects/Android/AndroidManifest.xml b/Samples/XrSamples/XrControllers/Projects/Android/AndroidManifest.xml new file mode 100755 index 0000000..9424c6c --- /dev/null +++ b/Samples/XrSamples/XrControllers/Projects/Android/AndroidManifest.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Samples/XrSamples/XrControllers/Projects/Android/build.bat b/Samples/XrSamples/XrControllers/Projects/Android/build.bat new file mode 100755 index 0000000..facf79f --- /dev/null +++ b/Samples/XrSamples/XrControllers/Projects/Android/build.bat @@ -0,0 +1,29 @@ +@rem Only edit the master copy of this file in SDK_ROOT/bin/scripts/build/perproject + +@setlocal enableextensions enabledelayedexpansion + +@if not exist "build.gradle" @echo Build script must be executed from project directory. & goto :Abort + +@set P=.. + +:TryAgain + +@rem @echo P = %P% + +@if exist "%P%\bin\scripts\build\build.py.bat" goto :Found + +@if exist "%P%\bin\scripts\build" @echo "Could not find build.py.bat" & goto :Abort + +@set P=%P%\.. + +@goto :TryAgain + +:Found + +@set P=%P%\bin\scripts\build +@call %P%\build.py.bat %1 %2 %3 %4 %5 +@goto :End + +:Abort + +:End diff --git a/Samples/XrSamples/XrControllers/Projects/Android/build.gradle b/Samples/XrSamples/XrControllers/Projects/Android/build.gradle new file mode 100755 index 0000000..17cf246 --- /dev/null +++ b/Samples/XrSamples/XrControllers/Projects/Android/build.gradle @@ -0,0 +1,78 @@ +// Top-level build file where you can add configuration options common to all sub-projects/modules. +buildscript { + repositories { + google() + mavenCentral() + } + dependencies { + classpath "com.android.tools.build:gradle:7.0.3" + } +} + +repositories { + google() + mavenCentral() +} + +apply plugin: 'com.android.application' + +android { + compileSdk 32 + + defaultConfig { + applicationId "com.oculus.sdk.xrcontrollers" + minSdk 26 + targetSdk 32 + versionCode 1 + versionName "1.0" + + // override app plugin abiFilters for 64-bit support + externalNativeBuild { + ndk { + abiFilters 'arm64-v8a' + } + ndkBuild { + abiFilters 'arm64-v8a' + } + cmake { + targets "xrcontrollers" + } + } + } + + sourceSets { + main { + manifest.srcFile 'AndroidManifest.xml' + java.srcDirs = ['../../java'] + assets.srcDirs = ['../../assets'] + res.srcDirs = ['../../res'] + } + } + + buildTypes { + debug { + debuggable true + } + + release { + debuggable false + } + } + + externalNativeBuild { + cmake { + path file('../../../../CMakeLists.txt') + } + } + + // Enable prefab support for the OpenXR AAR + buildFeatures { + prefab true + } +} + +dependencies { + // Package/application AndroidManifest.xml properties, plus headers and libraries + // exposed to CMake + implementation 'org.khronos.openxr:openxr_loader_for_android:1.1.36' +} diff --git a/Samples/XrSamples/XrControllers/Projects/Android/build.py b/Samples/XrSamples/XrControllers/Projects/Android/build.py new file mode 100755 index 0000000..d4b6e58 --- /dev/null +++ b/Samples/XrSamples/XrControllers/Projects/Android/build.py @@ -0,0 +1,35 @@ +#!/usr/bin/python +# (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +# This first bit of code is common bootstrapping code +# to determine the SDK root, and to set up the import +# path for additional python code. + +# begin bootstrap +import os +import sys + + +def init(): + root = os.path.realpath(os.path.dirname(os.path.realpath(__file__))) + os.chdir(root) # make sure we are always executing from the project directory + while os.path.isdir(os.path.join(root, "bin/scripts/build")) == False: + root = os.path.realpath(os.path.join(root, "..")) + if ( + len(root) <= 5 + ): # Should catch both Posix and Windows root directories (e.g. '/' and 'C:\') + print("Unable to find SDK root. Exiting.") + sys.exit(1) + root = os.path.abspath(root) + os.environ["OCULUS_SDK_PATH"] = root + sys.path.append(root + "/bin/scripts/build") + + +init() +import ovrbuild + +ovrbuild.init() +# end bootstrap + + +ovrbuild.build() diff --git a/Samples/XrSamples/XrControllers/Projects/Android/gradle.properties b/Samples/XrSamples/XrControllers/Projects/Android/gradle.properties new file mode 100644 index 0000000..3e927b1 --- /dev/null +++ b/Samples/XrSamples/XrControllers/Projects/Android/gradle.properties @@ -0,0 +1,21 @@ +# Project-wide Gradle settings. +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. More details, visit +# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects +# org.gradle.parallel=true +# AndroidX package structure to make it clearer which packages are bundled with the +# Android operating system, and which are packaged with your app's APK +# https://developer.android.com/topic/libraries/support-library/androidx-rn +android.useAndroidX=true +# Enables namespacing of each library's R class so that its R class includes only the +# resources declared in the library itself and none from the library's dependencies, +# thereby reducing the size of the R class for that library +android.nonTransitiveRClass=true \ No newline at end of file diff --git a/Samples/XrSamples/XrControllers/Projects/Android/gradle/wrapper/gradle-wrapper.jar b/Samples/XrSamples/XrControllers/Projects/Android/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..7454180 Binary files /dev/null and b/Samples/XrSamples/XrControllers/Projects/Android/gradle/wrapper/gradle-wrapper.jar differ diff --git a/Samples/XrSamples/XrControllers/Projects/Android/gradle/wrapper/gradle-wrapper.properties b/Samples/XrSamples/XrControllers/Projects/Android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..ffed3a2 --- /dev/null +++ b/Samples/XrSamples/XrControllers/Projects/Android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/Samples/XrSamples/XrControllers/Projects/Android/gradlew b/Samples/XrSamples/XrControllers/Projects/Android/gradlew new file mode 100755 index 0000000..a69d9cb --- /dev/null +++ b/Samples/XrSamples/XrControllers/Projects/Android/gradlew @@ -0,0 +1,240 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# 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 +# +# https://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. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit + +APP_NAME="Gradle" +APP_BASE_NAME=${0##*/} + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/Samples/XrSamples/XrControllers/Projects/Android/gradlew.bat b/Samples/XrSamples/XrControllers/Projects/Android/gradlew.bat new file mode 100755 index 0000000..f127cfd --- /dev/null +++ b/Samples/XrSamples/XrControllers/Projects/Android/gradlew.bat @@ -0,0 +1,91 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/Samples/XrSamples/XrControllers/Projects/Android/settings.gradle b/Samples/XrSamples/XrControllers/Projects/Android/settings.gradle new file mode 100755 index 0000000..9f85691 --- /dev/null +++ b/Samples/XrSamples/XrControllers/Projects/Android/settings.gradle @@ -0,0 +1 @@ +rootProject.name = "XrControllers" diff --git a/Samples/XrSamples/XrControllers/README.md b/Samples/XrSamples/XrControllers/README.md new file mode 100644 index 0000000..582e6d8 --- /dev/null +++ b/Samples/XrSamples/XrControllers/README.md @@ -0,0 +1,8 @@ +# OpenXR Controller And Haptic Sample + +## Overview +Meta Quest controllers enable VR developers to build their games with extensive interactions. This native sample showcases how to obtain values from each component of the controllers, as well as the different types of haptic feedback the controller supports for all controllers Meta provides. + +## The Sample +This sample contains a panel which includes all parts of the controller. Users can observe the value change when they interact with their controller. +![screen_shot](images/screen_shot.png) diff --git a/Samples/XrSamples/XrControllers/Src/main.cpp b/Samples/XrSamples/XrControllers/Src/main.cpp new file mode 100755 index 0000000..74012b5 --- /dev/null +++ b/Samples/XrSamples/XrControllers/Src/main.cpp @@ -0,0 +1,1320 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +/******************************************************************************* + +Filename : Main.cpp +Content : OpenXR sample app to showcase the use of controller and haptics extensions +Created : Dec 2020 +Authors : - +Language : C++ + +*******************************************************************************/ + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "XrApp.h" + +/// Haptic effects +const float kScrollBuffer[]{1, 0.9, 0.8, 0.7, 0.6, 0.5, 0.4, 0.3, 0.2, 0.1}; +float reducingIntensity[]{1, 0.9, 0.8, 0.7, 0.6, 0.5, 0.4, 0.3, 0.2, 0.1}; +float increasingIntensity[]{0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1}; +float constantIntensity[]{0.5}; + +// PCM Haptic API +PFN_xrGetDeviceSampleRateFB xrGetDeviceSampleRateFB = nullptr; + +#include "Input/SkeletonRenderer.h" +#include "Input/ControllerRenderer.h" +#include "Input/TinyUI.h" +#include "Input/AxisRenderer.h" +#include "Render/SimpleBeamRenderer.h" + + +class XrControllersApp : public OVRFW::XrApp { + public: + XrControllersApp() : OVRFW::XrApp() { + BackgroundColor = OVR::Vector4f(0.60f, 0.95f, 0.4f, 1.0f); + } + + /** +Function to create PCM samples from an array of amplitudes, frequency and duration +**/ + std::vector + createPCMSamples(float freq, int amplitudeCount, float* amplitudes, float durationSecs) { + int numSamples = static_cast( + 2000 * FromXrTime(durationSecs)); // samples consumed per sec * duration in secs + + // Initialize result + std::vector result(numSamples); + + // Init data for sample loop + float srcSample = 0; + float srcStep = static_cast(amplitudeCount) / numSamples; + float currentCycle = 0.0; + float dt = 1.0 / 2000; + + // Compute samples + for (int i = 0; i < numSamples; i++) { + float intPart; // trash + const float cycleTime = std::modf(currentCycle, &intPart); + int srcIdx = static_cast(srcSample); + float srcAmplitude = amplitudes[srcIdx]; + + // Compute sample value + float base = std::sin(cycleTime * M_PI * 2.0); + float sample = base * srcAmplitude; + result[i] = sample; + + // Step + currentCycle += freq * dt; + srcSample += srcStep; + } + return result; + } + + // Returns a list of OpenXr extensions needed for this app + virtual std::vector GetExtensions() override { + std::vector extensions = XrApp::GetExtensions(); + extensions.push_back(XR_FB_TOUCH_CONTROLLER_PRO_EXTENSION_NAME); + extensions.push_back(XR_META_TOUCH_CONTROLLER_PLUS_EXTENSION_NAME); + extensions.push_back(XR_FB_HAPTIC_AMPLITUDE_ENVELOPE_EXTENSION_NAME); + extensions.push_back(XR_FB_HAPTIC_PCM_EXTENSION_NAME); + extensions.push_back(XR_FB_TOUCH_CONTROLLER_PROXIMITY_EXTENSION_NAME); + return extensions; + } + + // Returns a map from interaction profile paths to vectors of suggested bindings. + // xrSuggestInteractionProfileBindings() is called once for each interaction profile path in the + // returned map. + // Apps are encouraged to suggest bindings for every device/interaction profile they support. + // Overridden to add support for the touch_pro interaction profile + std::unordered_map> GetSuggestedBindings( + XrInstance instance) override { + // …/input/trackpad/x + // …/input/trackpad/y + // …/input/trackpad/force + // …/input/stylus/force + // …/input/trigger/curl + // …/input/trigger/slide + // …/output/trigger_haptic + // …/output/thumb_haptic + + XrPath handSubactionPaths[2] = {LeftHandPath, RightHandPath}; + + trackpadForceAction_ = CreateAction( + BaseActionSet, + XR_ACTION_TYPE_FLOAT_INPUT, + "the_trackpad_force", + nullptr, + 2, + handSubactionPaths); + triggerForceAction_ = CreateAction( + BaseActionSet, + XR_ACTION_TYPE_FLOAT_INPUT, + "trigger_force", + nullptr, + 2, + handSubactionPaths); + stylusForceAction_ = CreateAction( + BaseActionSet, + XR_ACTION_TYPE_FLOAT_INPUT, + "the_stylus_force", + nullptr, + 2, + handSubactionPaths); + triggerCurlAction_ = CreateAction( + BaseActionSet, + XR_ACTION_TYPE_FLOAT_INPUT, + "the_trigger_curl", + nullptr, + 2, + handSubactionPaths); + triggerSlideAction_ = CreateAction( + BaseActionSet, + XR_ACTION_TYPE_FLOAT_INPUT, + "the_trigger_slide", + nullptr, + 2, + handSubactionPaths); + + /// haptics + mainHapticAction_ = CreateAction( + BaseActionSet, + XR_ACTION_TYPE_VIBRATION_OUTPUT, + "the_main_haptic", + nullptr, + 2, + handSubactionPaths); + triggerHapticAction_ = CreateAction( + BaseActionSet, + XR_ACTION_TYPE_VIBRATION_OUTPUT, + "the_trigger_haptic", + nullptr, + 2, + handSubactionPaths); + thumbHapticAction_ = CreateAction( + BaseActionSet, + XR_ACTION_TYPE_VIBRATION_OUTPUT, + "the_thumb_haptic", + nullptr, + 2, + handSubactionPaths); + + // Proximity + triggerProxAction_ = CreateAction( + BaseActionSet, + XR_ACTION_TYPE_BOOLEAN_INPUT, + "trigger_prox", + nullptr, + 2, + handSubactionPaths); + thumbFbProxAction_ = CreateAction( + BaseActionSet, + XR_ACTION_TYPE_BOOLEAN_INPUT, + "thumb_fb_prox", + nullptr, + 2, + handSubactionPaths); + thumbMetaProxAction_ = CreateAction( + BaseActionSet, + XR_ACTION_TYPE_BOOLEAN_INPUT, + "thumb_meta_prox", + nullptr, + 2, + handSubactionPaths); + + // Trigger Value + triggerValueAction_ = CreateAction( + BaseActionSet, + XR_ACTION_TYPE_BOOLEAN_INPUT, + "trigger_value", + nullptr, + 2, + handSubactionPaths); + + // Trigger Touch + triggerTouchAction_ = CreateAction( + BaseActionSet, + XR_ACTION_TYPE_BOOLEAN_INPUT, + "trigger_touch", + nullptr, + 2, + handSubactionPaths); + + // Squeeze Value + squeezeValueAction_ = CreateAction( + BaseActionSet, + XR_ACTION_TYPE_BOOLEAN_INPUT, + "squeeze_value", + nullptr, + 2, + handSubactionPaths); + + XrPath touchInteractionProfile = XR_NULL_PATH; + OXR(xrStringToPath( + instance, "/interaction_profiles/oculus/touch_controller", &touchInteractionProfile)); + + XrPath touchProInteractionProfile = XR_NULL_PATH; + OXR(xrStringToPath( + instance, + "/interaction_profiles/facebook/touch_controller_pro", + &touchProInteractionProfile)); + XrPath touchPlusInteractionProfile = XR_NULL_PATH; + OXR(xrStringToPath( + instance, + "/interaction_profiles/meta/touch_controller_plus", + &touchPlusInteractionProfile)); + + auto baseSuggestedBindings = XrApp::GetSuggestedBindings(instance); + + if (baseSuggestedBindings.find(touchProInteractionProfile) != baseSuggestedBindings.end()) { + // Base app now has touch pro bindings, so we don't need to add custom ones + // If we start hitting this case it might be worth removing this whole override function + return baseSuggestedBindings; + } + + // We know that the parent class generates bindings for oculus/touch_controller so this is a + // safe thing to do + std::vector baseTouchBindings = + baseSuggestedBindings[touchInteractionProfile]; + baseTouchBindings.emplace_back( + ActionSuggestedBinding(mainHapticAction_, "/user/hand/left/output/haptic")); + baseTouchBindings.emplace_back( + ActionSuggestedBinding(mainHapticAction_, "/user/hand/right/output/haptic")); + + // Proximity + baseTouchBindings.emplace_back( + ActionSuggestedBinding(thumbFbProxAction_, "/user/hand/left/input/thumb_fb/proximity_fb")); + baseTouchBindings.emplace_back(ActionSuggestedBinding( + thumbFbProxAction_, "/user/hand/right/input/thumb_fb/proximity_fb")); + + // Trigger Value + baseTouchBindings.emplace_back(ActionSuggestedBinding( + triggerValueAction_, "/user/hand/left/input/trigger/value")); + baseTouchBindings.emplace_back(ActionSuggestedBinding( + triggerValueAction_, "/user/hand/right/input/trigger/value")); + + // Trigger Touch + baseTouchBindings.emplace_back(ActionSuggestedBinding( + triggerTouchAction_, "/user/hand/left/input/trigger/touch")); + baseTouchBindings.emplace_back(ActionSuggestedBinding( + triggerTouchAction_, "/user/hand/right/input/trigger/touch")); + + // Squeeze Value + baseTouchBindings.emplace_back(ActionSuggestedBinding( + squeezeValueAction_, "/user/hand/left/input/squeeze/value")); + baseTouchBindings.emplace_back(ActionSuggestedBinding( + squeezeValueAction_, "/user/hand/right/input/squeeze/value")); + + // Copy(construct) base paths since these interaction profiles are similar + std::vector touchProBindings(baseTouchBindings); + + // We are assuming that every touch binding exists for touch pro here + // if that is not the case they need to be removed from the vector + + touchProBindings.emplace_back( + ActionSuggestedBinding(trackpadForceAction_, "/user/hand/left/input/thumbrest/force")); + touchProBindings.emplace_back( + ActionSuggestedBinding(trackpadForceAction_, "/user/hand/right/input/thumbrest/force")); + touchProBindings.emplace_back( + ActionSuggestedBinding(stylusForceAction_, "/user/hand/left/input/stylus_fb/force")); + touchProBindings.emplace_back( + ActionSuggestedBinding(stylusForceAction_, "/user/hand/right/input/stylus_fb/force")); + touchProBindings.emplace_back(ActionSuggestedBinding( + triggerProxAction_, "/user/hand/left/input/trigger/proximity_fb")); + touchProBindings.emplace_back(ActionSuggestedBinding( + triggerProxAction_, "/user/hand/right/input/trigger/proximity_fb")); + touchProBindings.emplace_back( + ActionSuggestedBinding(triggerCurlAction_, "/user/hand/left/input/trigger/curl_fb")); + touchProBindings.emplace_back( + ActionSuggestedBinding(triggerCurlAction_, "/user/hand/right/input/trigger/curl_fb")); + touchProBindings.emplace_back( + ActionSuggestedBinding(triggerSlideAction_, "/user/hand/left/input/trigger/slide_fb")); + touchProBindings.emplace_back( + ActionSuggestedBinding(triggerSlideAction_, "/user/hand/right/input/trigger/slide_fb")); + touchProBindings.emplace_back(ActionSuggestedBinding( + triggerHapticAction_, "/user/hand/left/output/trigger_haptic_fb")); + touchProBindings.emplace_back(ActionSuggestedBinding( + triggerHapticAction_, "/user/hand/right/output/trigger_haptic_fb")); + touchProBindings.emplace_back( + ActionSuggestedBinding(thumbHapticAction_, "/user/hand/left/output/thumb_haptic_fb")); + touchProBindings.emplace_back( + ActionSuggestedBinding(thumbHapticAction_, "/user/hand/right/output/thumb_haptic_fb")); + + std::vector touchPlusBindings(baseTouchBindings); + touchPlusBindings.emplace_back(ActionSuggestedBinding( + triggerProxAction_, "/user/hand/left/input/trigger/proximity_meta")); + touchPlusBindings.emplace_back(ActionSuggestedBinding( + triggerProxAction_, "/user/hand/right/input/trigger/proximity_meta")); + touchPlusBindings.emplace_back(ActionSuggestedBinding( + thumbMetaProxAction_, "/user/hand/left/input/thumb_meta/proximity_meta")); + touchPlusBindings.emplace_back(ActionSuggestedBinding( + thumbMetaProxAction_, "/user/hand/right/input/thumb_meta/proximity_meta")); + touchPlusBindings.emplace_back( + ActionSuggestedBinding(triggerForceAction_, "/user/hand/left/input/trigger/force")); + touchPlusBindings.emplace_back( + ActionSuggestedBinding(triggerForceAction_, "/user/hand/right/input/trigger/force")); + touchPlusBindings.emplace_back( + ActionSuggestedBinding(triggerCurlAction_, "/user/hand/left/input/trigger/curl_meta")); + touchPlusBindings.emplace_back( + ActionSuggestedBinding(triggerCurlAction_, "/user/hand/right/input/trigger/curl_meta")); + touchPlusBindings.emplace_back( + ActionSuggestedBinding(triggerSlideAction_, "/user/hand/left/input/trigger/slide_meta")); + touchPlusBindings.emplace_back( + ActionSuggestedBinding(triggerSlideAction_, "/user/hand/right/input/trigger/slide_meta")); + + // Now that we are done using these for pro and plus, add these to the base touch as well + baseTouchBindings.emplace_back(ActionSuggestedBinding( + triggerProxAction_, "/user/hand/left/input/trigger/proximity_fb")); + baseTouchBindings.emplace_back(ActionSuggestedBinding( + triggerProxAction_, "/user/hand/right/input/trigger/proximity_fb")); + + std::unordered_map> allSuggestedBindings; + allSuggestedBindings[touchInteractionProfile] = baseTouchBindings; + allSuggestedBindings[touchProInteractionProfile] = touchProBindings; + allSuggestedBindings[touchPlusInteractionProfile] = touchPlusBindings; + return allSuggestedBindings; + } + + // Must return true if the application initializes successfully. + virtual bool AppInit(const xrJava* context) override { + if (false == ui_.Init(context, GetFileSys())) { + ALOG("TinyUI::Init FAILED."); + return false; + } + + /// Hook up extensions + + /// Build UI + bigText_ = ui_.AddLabel( + "OpenXR Controllers Sample", {0.0f, -0.8f, -1.9f}, {1300.0f, 100.0f}); + + OVR::Vector2f size = {200.0f, 100.0f}; + OVR::Vector3f position = {+0.0f, 0.5f, -1.9f}; + OVR::Vector3f positionL = {-0.4f, 0.5f, -1.9f}; + OVR::Vector3f positionR = {+0.4f, 0.5f, -1.9f}; + const float dh = 0.2f; + position.y += dh; + positionL.y += dh; + positionR.y += dh; + ui_.AddLabel("Trigger Force", position, size); + triggerForceLText_ = ui_.AddLabel("trf L 0.0", positionL, size); + triggerForceRText_ = ui_.AddLabel("trf R 0.0", positionR, size); + position.y += dh; + positionL.y += dh; + positionR.y += dh; + ui_.AddLabel("Track Force", position, size); + trackpadForceLText_ = ui_.AddLabel("tf L 0.0", positionL, size); + trackpadForceRText_ = ui_.AddLabel("tf R 0.0", positionR, size); + position.y += dh; + positionL.y += dh; + positionR.y += dh; + ui_.AddLabel("Stylus Force", position, size); + stylusForceLText_ = ui_.AddLabel("tf L 0.0", positionL, size); + stylusForceRText_ = ui_.AddLabel("tf R 0.0", positionR, size); + position.y += dh; + positionL.y += dh; + positionR.y += dh; + ui_.AddLabel("Trigger Curl", position, size); + triggerCurlLText_ = ui_.AddLabel("tf L 0.0", positionL, size); + triggerCurlRText_ = ui_.AddLabel("tf R 0.0", positionR, size); + position.y += dh; + positionL.y += dh; + positionR.y += dh; + ui_.AddLabel("Trigger Slide", position, size); + squeezeCurlLText_ = ui_.AddLabel("tf L 0.0", positionL, size); + squeezeCurlRText_ = ui_.AddLabel("tf R 0.0", positionR, size); + // Proximity + positionL.y += dh; + positionR.y += dh; + position.y += dh; + ui_.AddLabel("Trigger Prox", position, size); + triggerProxLText_ = ui_.AddLabel("trProx L 0.0", positionL, size); + triggerProxRText_ = ui_.AddLabel("trProx R 0.0", positionR, size); + + positionL.y += dh * 3/4.0f; + positionR.y += dh * 3/4.0f; + position.y += dh; + ui_.AddLabel("Thumb Prox", position, size); + const OVR::Vector2f halfSize{size.x, size.y / 2.0f}; + thumbFBProxLText_ = ui_.AddLabel("_FB: 0", positionL, halfSize); + thumbFBProxRText_ = ui_.AddLabel("_FB: 0", positionR, halfSize); + positionL.y += dh / 2.0; + positionR.y += dh / 2.0; + thumbMetaProxLText_ = ui_.AddLabel("_META: 0", positionL, halfSize); + thumbMetaProxRText_ = ui_.AddLabel("_META: 0", positionR, halfSize); + + positionL.y += dh * 3 / 4.0f; + positionR.y += dh * 3 / 4.0f; + position.y += dh; + ui_.AddLabel("Trigger Value", position, size); + triggerValueLText_ = ui_.AddLabel("trVal L 0.0", positionL, size); + triggerValueRText_ = ui_.AddLabel("trVal R 0.0", positionR, size); + + positionL.y += dh; + positionR.y += dh; + position.y += dh; + ui_.AddLabel("Trigger Touch", position, size); + triggerTouchLText_ = ui_.AddLabel("trTouch L 0.0", positionL, size); + triggerTouchRText_ = ui_.AddLabel("trTouch R 0.0", positionR, size); + + positionL.y += dh; + positionR.y += dh; + position.y += dh; + ui_.AddLabel("Squeeze Value", position, size); + squeezeValueLText_ = ui_.AddLabel("sqVal L 0.0", positionL, size); + squeezeValueRText_ = ui_.AddLabel("sqVal R 0.0", positionR, size); + + ipText_ = ui_.AddLabel( + "Interaction Profiles", {0.0f, 0.5f, -1.9f}, {600.0f, 100.0f}); + + ui_.AddButton("Haptic Main S", {-0.8f, 0.5f, -1.9f}, size, [=]() { + VibrateController(mainHapticAction_, LeftHandPath, XR_MIN_HAPTIC_DURATION, 157.0f, 1.0f); + }); + ui_.AddButton("Haptic Main S", {+0.8f, 0.5f, -1.9f}, size, [=]() { + VibrateController( + mainHapticAction_, RightHandPath, XR_MIN_HAPTIC_DURATION, 157.0f, 1.0f); + }); + ui_.AddButton("Haptic Main M", {-1.2f, 0.5f, -1.9f}, size, [=]() { + VibrateController(mainHapticAction_, LeftHandPath, 0.1f, 157.0f, 1.0f); + }); + ui_.AddButton("Haptic Main M", {+1.2f, 0.5f, -1.9f}, size, [=]() { + VibrateController(mainHapticAction_, RightHandPath, 0.1f, 157.0f, 1.0f); + }); + ui_.AddButton("Haptic Main L", {-1.6f, 0.5f, -1.9f}, size, [=]() { + VibrateController(mainHapticAction_, LeftHandPath, 1.0f, 157.0f, 1.0f); + }); + ui_.AddButton("Haptic Main L", {+1.6f, 0.5f, -1.9f}, size, [=]() { + VibrateController(mainHapticAction_, RightHandPath, 1.0f, 157.0f, 1.0f); + }); + ui_.AddButton("Haptic Trigger", {-0.8f, 0.7f, -1.9f}, size, [=]() { + VibrateController(triggerHapticAction_, LeftHandPath, 0.1f, 157.0f, 1.0f); + }); + ui_.AddButton("Haptic Trigger", {+0.8f, 0.7f, -1.9f}, size, [=]() { + VibrateController(triggerHapticAction_, RightHandPath, 0.1f, 157.0f, 1.0f); + }); + ui_.AddButton("Haptic Thumb", {-0.8f, 0.9f, -1.9f}, size, [=]() { + VibrateController(thumbHapticAction_, LeftHandPath, 0.1f, 157.0f, 1.0f); + }); + ui_.AddButton("Haptic Thumb", {+0.8f, 0.9f, -1.9f}, size, [=]() { + VibrateController(thumbHapticAction_, RightHandPath, 0.1f, 157.0f, 1.0f); + }); + position.y += dh; + ui_.AddToggleButton("Lag On", "Lag Off", &delayUI_, position, size); + + // Left Hand + const float sampleDurationBuffered = 0.002f; // 2ms + position = {-1.2f, 0.7f, -1.9f}; + ui_.AddButton("AE Scroll", position, size, [=]() { + VibrateControllerAmplitude( + mainHapticAction_, + LeftHandPath, + kScrollBuffer, + std::size(kScrollBuffer), + sampleDurationBuffered * std::size(kScrollBuffer)); + }); + position.x -= 0.4f; + float aeBufferSimple[500]; // 1sec + for (int i = 0; i < 500; i++) { + aeBufferSimple[i] = 0.1; + } + ui_.AddButton("AE 1s", position, size, [=]() { + VibrateControllerAmplitude( + mainHapticAction_, + LeftHandPath, + aeBufferSimple, + std::size(aeBufferSimple), + sampleDurationBuffered * std::size(aeBufferSimple)); + }); + position.x -= 0.4f; + ui_.AddButton("AE 0.5s\n(Downsample)", position, size, [=]() { + VibrateControllerAmplitude( + mainHapticAction_, LeftHandPath, aeBufferSimple, std::size(aeBufferSimple), 0.5f); + }); + + // Right Hand + position = {+1.2f, 0.7f, -1.9f}; + ui_.AddButton("AE Scroll", position, size, [=]() { + VibrateControllerAmplitude( + mainHapticAction_, + RightHandPath, + kScrollBuffer, + std::size(kScrollBuffer), + sampleDurationBuffered * std::size(kScrollBuffer)); + }); + position.x += 0.4f; + float aeBufferSingle[2] = {1, 0.5f}; + ui_.AddButton("AE 1s (Upsample)", position, size, [=]() { + VibrateControllerAmplitude( + mainHapticAction_, RightHandPath, aeBufferSingle, std::size(aeBufferSingle), 1.0f); + }); + + position.x += 0.4f; + ui_.AddButton("AE Fail:\nexceeding\nmax samples", position, size, [=]() { + VibrateControllerAmplitude( + mainHapticAction_, RightHandPath, aeBufferSingle, std::size(aeBufferSingle), 10.0f); + }); + + position = {+0.0f, 0.5f, -1.9f}; + position.y -= dh; + pcmHapticText_ = ui_.AddLabel("PCM Haptic\n[SR: 0.0]", position, size); + + float pcmBufferSimple[5000]; + for (int i = 0; i < 5000; i++) { + if (i < 2500) { + pcmBufferSimple[i] = 0.8f; + } else { + pcmBufferSimple[i] = -0.8f; + } + } + // Right Controller + position.x += 0.4f; + float sampleRate = 2000.0f; + std::vector decayingSineWave = + createPCMSamples(40, std::size(reducingIntensity), reducingIntensity, ToXrTime(2)); + + ui_.AddButton("Decaying sine\nwave", position, size, [=]() { + VibrateControllerPCM( + mainHapticAction_, + RightHandPath, + decayingSineWave.data(), + decayingSineWave.size(), + sampleRate); + }); + + auto copyReducingSineWave = + createPCMSamples(40, std::size(reducingIntensity), reducingIntensity, ToXrTime(1)); + auto copyIncreasingSineWave = + createPCMSamples(40, std::size(increasingIntensity), increasingIntensity, ToXrTime(1)); + + std::vector decayingSineWaveLong; + for (int i = 0; i < 5; ++i) { + decayingSineWaveLong.insert( + decayingSineWaveLong.end(), + copyReducingSineWave.begin(), + copyReducingSineWave.end()); + decayingSineWaveLong.insert( + decayingSineWaveLong.end(), + copyIncreasingSineWave.begin(), + copyIncreasingSineWave.end()); + } + + position.x += 0.4f; + ui_.AddButton("Long wave (10s)", position, size, [=]() { + VibrateControllerPCM( + mainHapticAction_, + RightHandPath, + decayingSineWaveLong.data(), + decayingSineWaveLong.size(), + sampleRate); + }); + + std::vector sineWave = + createPCMSamples(157, std::size(constantIntensity), constantIntensity, ToXrTime(1)); + + position.x += 0.4f; + ui_.AddButton("Wave 1s", position, size, [=]() { + VibrateControllerPCM( + mainHapticAction_, RightHandPath, sineWave.data(), sineWave.size(), sampleRate); + }); + + position.x += 0.4f; + sampleRate = 1000.0f; + ui_.AddButton("Upsampled Wave\n2s", position, size, [=]() { + VibrateControllerPCM( + mainHapticAction_, RightHandPath, sineWave.data(), sineWave.size(), sampleRate); + }); + + position.x += 0.4f; + sampleRate = 4000.0f; + ui_.AddButton("Downsampled\nWave\n0.5s", position, size, [=]() { + VibrateControllerPCM( + mainHapticAction_, RightHandPath, sineWave.data(), sineWave.size(), sampleRate); + }); + + // Left Controller + position.x -= 2.4f; + sampleRate = 2000.0f; + + ui_.AddButton("Decaying sine\nwave 1s", position, size, [=]() { + VibrateControllerPCM( + mainHapticAction_, + LeftHandPath, + decayingSineWave.data(), + decayingSineWave.size(), + sampleRate); + }); + + position.x -= 0.4f; + ui_.AddButton("Long wave (10s)", position, size, [=]() { + VibrateControllerPCM( + mainHapticAction_, + LeftHandPath, + decayingSineWaveLong.data(), + decayingSineWaveLong.size(), + sampleRate); + }); + + position.x -= 0.4f; + // 2s Sine wave + float singleIntensity[]{1}; + sineWave = createPCMSamples(157, std::size(singleIntensity), singleIntensity, ToXrTime(2)); + + ui_.AddButton("Wave 2s", position, size, [=]() { + VibrateControllerPCM( + mainHapticAction_, LeftHandPath, sineWave.data(), sineWave.size(), sampleRate); + }); + + position.x -= 0.4f; + sampleRate = 1500.0f; + ui_.AddButton("Upsampled Wave\n2.67s", position, size, [=]() { + VibrateControllerPCM( + mainHapticAction_, LeftHandPath, sineWave.data(), sineWave.size(), sampleRate); + }); + + position.x -= 0.4f; + sampleRate = 3000.0f; + ui_.AddButton("Downsampled\nWave\n1.3s", position, size, [=]() { + VibrateControllerPCM( + mainHapticAction_, LeftHandPath, sineWave.data(), sineWave.size(), sampleRate); + }); + + // Playing haptics on both controllers by passing XR_NULL_PATH in subActionPath + position = {+0.0f, +0.1f, -1.9f}; + ui_.AddButton("Haptic Main (both)", position, size, [=]() { + VibrateController(mainHapticAction_, XR_NULL_PATH, 1.0f, 157.0f, 0.5f); + }); + OXR(xrGetInstanceProcAddr( + GetInstance(), + "xrGetDeviceSampleRateFB", + (PFN_xrVoidFunction*)(&xrGetDeviceSampleRateFB))); + + // both triggers + position.x -= 0.4f; + ui_.AddButton("Thumb (2s, both)", position, size, [=]() { + VibrateController(thumbHapticAction_, XR_NULL_PATH, 2.0f, 157.0f, 0.25f); + }); + // both grips + position.x -= 0.4f; + ui_.AddButton("Trigger (2s, both)", position, size, [=]() { + VibrateController(triggerHapticAction_, XR_NULL_PATH, 2.0f, 157.0f, 0.25f); + }); + position = {+0.0f, 0.1f, -1.9f}; + position.x += 0.4f; + ui_.AddButton("Thumb (2s, right)", position, size, [=]() { + VibrateController(thumbHapticAction_, RightHandPath, 2.0f, 157.0f, 0.25f); + }); + // both grips + position.x += 0.4f; + ui_.AddButton("Trigger (2s, right)", position, size, [=]() { + VibrateController(triggerHapticAction_, RightHandPath, 2.0f, 157.0f, 0.25f); + }); + + position = {+0.0f, -0.1f, -1.9f}; + ui_.AddButton("Stop BOTH Main", position, size, [=]() { + StopHapticEffect(mainHapticAction_, XR_NULL_PATH); + }); + + position.x -= 0.4f; + ui_.AddButton("Stop Left Main", position, size, [=]() { + StopHapticEffect(mainHapticAction_, LeftHandPath); + }); + + position.x += 2*0.4f; + ui_.AddButton("Stop Right Main", position, size, [=]() { + StopHapticEffect(mainHapticAction_, RightHandPath); + }); + + position = {+0.0f, -0.3f, -1.9f}; + ui_.AddButton("Stop BOTH Thumb", position, size, [=]() { + StopHapticEffect(thumbHapticAction_, XR_NULL_PATH); + }); + + position.x -= 0.4f; + ui_.AddButton("Stop Left Thumb", position, size, [=]() { + StopHapticEffect(thumbHapticAction_, LeftHandPath); + }); + + position.x += 2 * 0.4f; + ui_.AddButton("Stop Right Thumb", position, size, [=]() { + StopHapticEffect(thumbHapticAction_, RightHandPath); + }); + + position = {+0.0f, -0.5f, -1.9f}; + ui_.AddButton("Stop BOTH Trigger", position, size, [=]() { + StopHapticEffect(triggerHapticAction_, XR_NULL_PATH); + }); + + position.x -= 0.4f; + ui_.AddButton("Stop Left Trigger", position, size, [=]() { + StopHapticEffect(triggerHapticAction_, LeftHandPath); + }); + + position.x += 2*0.4f; + ui_.AddButton("Stop Right Trigger", position, size, [=]() { + StopHapticEffect(triggerHapticAction_, RightHandPath); + }); + + return true; + } + + virtual void AppShutdown(const xrJava* context) override { + /// unhook extensions + + OVRFW::XrApp::AppShutdown(context); + ui_.Shutdown(); + } + + virtual bool SessionInit() override { + /// Use LocalSpace instead of Stage Space. + CurrentSpace = LocalSpace; + /// Disable scene navitgation + GetScene().SetFootPos({0.0f, 0.0f, 0.0f}); + this->FreeMove = false; + /// Init session bound objects + if (false == controllerRenderL_.Init(true)) { + ALOG("AppInit::Init L controller renderer FAILED."); + return false; + } + if (false == controllerRenderR_.Init(false)) { + ALOG("AppInit::Init R controller renderer FAILED."); + return false; + } + beamRenderer_.Init(GetFileSys(), nullptr, OVR::Vector4f(1.0f), 1.0f); + + /// enumerate all actions + EnumerateActions(); + + return true; + } + + virtual void SessionEnd() override { + controllerRenderL_.Shutdown(); + controllerRenderR_.Shutdown(); + beamRenderer_.Shutdown(); + } + + // Update state + virtual void Update(const OVRFW::ovrApplFrameIn& in) override { + /// Update Input + { + /// Trigger Force + triggerForceL_ = GetActionStateFloat(triggerForceAction_, LeftHandPath).currentState; + triggerForceR_ = GetActionStateFloat(triggerForceAction_, RightHandPath).currentState; + /// TrackPad Force + trackpadForceL_ = GetActionStateFloat(trackpadForceAction_, LeftHandPath).currentState; + trackpadForceR_ = GetActionStateFloat(trackpadForceAction_, RightHandPath).currentState; + /// Stylus Force + stylusForceL_ = GetActionStateFloat(stylusForceAction_, LeftHandPath).currentState; + stylusForceR_ = GetActionStateFloat(stylusForceAction_, RightHandPath).currentState; + /// Trigger Curl + triggerCurlL_ = GetActionStateFloat(triggerCurlAction_, LeftHandPath).currentState; + triggerCurlR_ = GetActionStateFloat(triggerCurlAction_, RightHandPath).currentState; + /// Squeeze Curl + squeezeCurlL_ = GetActionStateFloat(triggerSlideAction_, LeftHandPath).currentState; + squeezeCurlR_ = GetActionStateFloat(triggerSlideAction_, RightHandPath).currentState; + /// Proximity + triggerProxL_ = GetActionStateBoolean(triggerProxAction_, LeftHandPath).currentState; + triggerProxR_ = GetActionStateBoolean(triggerProxAction_, RightHandPath).currentState; + thumbFBProxL_ = GetActionStateBoolean(thumbFbProxAction_, LeftHandPath).currentState; + thumbFBProxR_ = GetActionStateBoolean(thumbFbProxAction_, RightHandPath).currentState; + // same as above on touch plus + thumbMetaProxL_ = GetActionStateBoolean(thumbMetaProxAction_, LeftHandPath).currentState; + thumbMetaProxR_ = GetActionStateBoolean(thumbMetaProxAction_, RightHandPath).currentState; + /// Trigger Value + triggerValueL_ = GetActionStateBoolean(triggerValueAction_, LeftHandPath).currentState; + triggerValueR_ = GetActionStateBoolean(triggerValueAction_, RightHandPath).currentState; + + /// Trigger Touch + triggerTouchL_ = GetActionStateBoolean(triggerTouchAction_, LeftHandPath).currentState; + triggerTouchR_ = GetActionStateBoolean(triggerTouchAction_, RightHandPath).currentState; + /// Squeeze Value + squeezeValueL_ = GetActionStateBoolean(squeezeValueAction_, LeftHandPath).currentState; + squeezeValueR_ = GetActionStateBoolean(squeezeValueAction_, RightHandPath).currentState; + } + + // we can only request haptic sample rate when the session is in focus + if (Focused) { + XrHapticActionInfo hai = {XR_TYPE_HAPTIC_ACTION_INFO, nullptr}; + hai.action = mainHapticAction_; + hai.subactionPath = LeftHandPath; + OXR(xrGetDeviceSampleRateFB(Session, &hai, &leftDeviceSampleRate_)); + + hai.action = mainHapticAction_; + hai.subactionPath = RightHandPath; + OXR(xrGetDeviceSampleRateFB(Session, &hai, &rightDeviceSampleRate_)); + } + + // once per A button press + const auto buttonA = GetActionStateBoolean(ButtonAAction); + if (buttonA.currentState == XR_TRUE && buttonA.changedSinceLastSync == XR_TRUE) { + // Trigger PCM haptics: simple sine wave + std::vector sineWave = + createPCMSamples(157, std::size(constantIntensity), constantIntensity, ToXrTime(1)); + VibrateControllerPCM( + mainHapticAction_, RightHandPath, sineWave.data(), sineWave.size(), 2000.0f); + } + + // once per B button press + const auto buttonB = GetActionStateBoolean(ButtonBAction); + if (buttonB.currentState == XR_TRUE && buttonB.changedSinceLastSync == XR_TRUE) { + // Trigger AE haptics + float aeBufferSimple[500]; // 1sec + for (int i = 0; i < 500; i++) { + aeBufferSimple[i] = 0.1; + } + + VibrateControllerAmplitude( + mainHapticAction_, + RightHandPath, + aeBufferSimple, + std::size(aeBufferSimple), + 0.002f * std::size(aeBufferSimple)); + } + + // once per X button press + const auto buttonX = GetActionStateBoolean(ButtonXAction); + if (buttonX.currentState == XR_TRUE && buttonX.changedSinceLastSync == XR_TRUE) { + // Trigger Localized(thumb) haptics + VibrateController(thumbHapticAction_, LeftHandPath, 0.1f, 157.0f, 1.0f); + } + + // once per Y button press + const auto buttonY = GetActionStateBoolean(ButtonYAction); + if (buttonY.currentState == XR_TRUE && buttonY.changedSinceLastSync == XR_TRUE) { + // Trigger Localized(trigger) haptics + VibrateController(triggerHapticAction_, LeftHandPath, 0.1f, 157.0f, 1.0f); + } + + ui_.HitTestDevices().clear(); + + if (in.LeftRemoteTracked) { + controllerRenderL_.Update(in.LeftRemotePose); + const bool didPinch = in.LeftRemoteIndexTrigger > 0.25f; + ui_.AddHitTestRay(in.LeftRemotePointPose, didPinch); + } + if (in.RightRemoteTracked) { + controllerRenderR_.Update(in.RightRemotePose); + const bool didPinch = in.RightRemoteIndexTrigger > 0.25f; + ui_.AddHitTestRay(in.RightRemotePointPose, didPinch); + } + + /// Update labels + { + XrInteractionProfileState lIpState {XR_TYPE_INTERACTION_PROFILE_STATE}; + OXR(xrGetCurrentInteractionProfile(Session, LeftHandPath, &lIpState)); + XrInteractionProfileState rIpState {XR_TYPE_INTERACTION_PROFILE_STATE}; + OXR(xrGetCurrentInteractionProfile(Session, LeftHandPath, &rIpState)); + + char lBuf[XR_MAX_PATH_LENGTH]; + uint32_t written = 0; + if(lIpState.interactionProfile != XR_NULL_PATH) { + OXR(xrPathToString(Instance, lIpState.interactionProfile, XR_MAX_PATH_LENGTH, &written, lBuf)); + } + if(written == 0) { + strcpy(lBuf, ""); + } + + char rBuf[XR_MAX_PATH_LENGTH]; + written = 0; + if(rIpState.interactionProfile != XR_NULL_PATH) { + OXR(xrPathToString(Instance, rIpState.interactionProfile, XR_MAX_PATH_LENGTH, &written, rBuf)); + } + if(written == 0) { + strcpy(rBuf, ""); + } + + std::stringstream ss; + ss << "Left IP: " << lBuf << std::endl; + ss << "Right IP: " << rBuf; + ipText_->SetText(ss.str().c_str()); + } + { + std::stringstream ss; + ss << std::setprecision(4) << std::fixed; + ss << triggerForceL_; + triggerForceLText_->SetText(ss.str().c_str()); + } + { + std::stringstream ss; + ss << std::setprecision(4) << std::fixed; + ss << triggerForceR_; + triggerForceRText_->SetText(ss.str().c_str()); + } + { + std::stringstream ss; + ss << std::setprecision(4) << std::fixed; + ss << trackpadForceL_; + trackpadForceLText_->SetText(ss.str().c_str()); + } + { + std::stringstream ss; + ss << std::setprecision(4) << std::fixed; + ss << trackpadForceR_; + trackpadForceRText_->SetText(ss.str().c_str()); + } + { + std::stringstream ss; + ss << std::setprecision(4) << std::fixed; + ss << stylusForceL_; + stylusForceLText_->SetText(ss.str().c_str()); + } + { + std::stringstream ss; + ss << std::setprecision(4) << std::fixed; + ss << stylusForceR_; + stylusForceRText_->SetText(ss.str().c_str()); + } + { + std::stringstream ss; + ss << std::setprecision(4) << std::fixed; + ss << triggerCurlL_; + triggerCurlLText_->SetText(ss.str().c_str()); + } + { + std::stringstream ss; + ss << std::setprecision(4) << std::fixed; + ss << triggerCurlR_; + triggerCurlRText_->SetText(ss.str().c_str()); + } + { + std::stringstream ss; + ss << std::setprecision(4) << std::fixed; + ss << squeezeCurlL_; + squeezeCurlLText_->SetText(ss.str().c_str()); + } + { + std::stringstream ss; + ss << std::setprecision(4) << std::fixed; + ss << squeezeCurlR_; + squeezeCurlRText_->SetText(ss.str().c_str()); + } + { + std::stringstream ss; + ss << "PCM Haptic\n[SR: "; + ss << std::setprecision(1) << std::fixed; + ss << leftDeviceSampleRate_.sampleRate << ", "; + ss << rightDeviceSampleRate_.sampleRate << "]"; + pcmHapticText_->SetText(ss.str().c_str()); + } + { + std::stringstream ss; + ss << std::setprecision(4) << std::fixed; + ss << triggerProxL_; + triggerProxLText_->SetText(ss.str().c_str()); + } + { + std::stringstream ss; + ss << std::setprecision(4) << std::fixed; + ss << triggerProxR_; + triggerProxRText_->SetText(ss.str().c_str()); + } + { + std::stringstream ss; + ss << "_FB: " << thumbFBProxL_; + thumbFBProxLText_->SetText(ss.str().c_str()); + } + { + std::stringstream ss; + ss << "_FB: " << thumbFBProxR_; + thumbFBProxRText_->SetText(ss.str().c_str()); + } + { + std::stringstream ss; + ss << "_META: " << thumbMetaProxL_; + thumbMetaProxLText_->SetText(ss.str().c_str()); + } + { + std::stringstream ss; + ss << "_META: " << thumbMetaProxR_; + thumbMetaProxRText_->SetText(ss.str().c_str()); + } + { + std::stringstream ss; + ss << std::setprecision(4) << std::fixed; + ss << triggerValueL_; + triggerValueLText_->SetText(ss.str().c_str()); + } + { + std::stringstream ss; + ss << std::setprecision(4) << std::fixed; + ss << triggerValueR_; + triggerValueRText_->SetText(ss.str().c_str()); + } + { + std::stringstream ss; + ss << std::setprecision(4) << std::fixed; + ss << triggerTouchL_; + triggerTouchLText_->SetText(ss.str().c_str()); + } + { + std::stringstream ss; + ss << std::setprecision(4) << std::fixed; + ss << triggerTouchR_; + triggerTouchRText_->SetText(ss.str().c_str()); + } + { + std::stringstream ss; + ss << std::setprecision(4) << std::fixed; + ss << squeezeValueL_; + squeezeValueLText_->SetText(ss.str().c_str()); + } + { + std::stringstream ss; + ss << std::setprecision(4) << std::fixed; + ss << squeezeValueR_; + squeezeValueRText_->SetText(ss.str().c_str()); + } + + /* + */ + + ui_.Update(in); + beamRenderer_.Update(in, ui_.HitTestDevices()); + + /// Add some deliberate lag to the app + if (delayUI_) { + std::this_thread::sleep_for(std::chrono::milliseconds(150)); + } + } + + // Render eye buffers while running + virtual void Render(const OVRFW::ovrApplFrameIn& in, OVRFW::ovrRendererOutput& out) override { + /// Render UI + ui_.Render(in, out); + + /// Render controllers + if (in.LeftRemoteTracked) { + controllerRenderL_.Render(out.Surfaces); + } + if (in.RightRemoteTracked) { + controllerRenderR_.Render(out.Surfaces); + } + + /// Render beams + beamRenderer_.Render(in, out); + } + + void EnumerateActions() { + // Enumerate actions + XrPath actionPathsBuffer[16]; + char stringBuffer[256]; + XrAction actionsToEnumerate[] = { + /// new actions + triggerForceAction_, + thumbMetaProxAction_, + trackpadForceAction_, + stylusForceAction_, + triggerCurlAction_, + triggerSlideAction_, + /// existing actions form base class + IndexTriggerAction, + GripTriggerAction, + triggerProxAction_, + thumbFbProxAction_, + triggerValueAction_, + triggerTouchAction_, + squeezeValueAction_, + }; + for (size_t i = 0; i < sizeof(actionsToEnumerate) / sizeof(actionsToEnumerate[0]); ++i) { + XrBoundSourcesForActionEnumerateInfo enumerateInfo = {XR_TYPE_BOUND_SOURCES_FOR_ACTION_ENUMERATE_INFO}; + enumerateInfo.action = actionsToEnumerate[i]; + + // Get Count + uint32_t countOutput = 0; + OXR(xrEnumerateBoundSourcesForAction( + Session, &enumerateInfo, 0 /* request size */, &countOutput, nullptr)); + ALOGV( + "xrEnumerateBoundSourcesForAction action=%lld count=%u", + (long long)enumerateInfo.action, + countOutput); + + if (countOutput < 16) { + OXR(xrEnumerateBoundSourcesForAction( + Session, &enumerateInfo, 16, &countOutput, actionPathsBuffer)); + for (uint32_t a = 0; a < countOutput; ++a) { + XrInputSourceLocalizedNameGetInfo nameGetInfo = {XR_TYPE_INPUT_SOURCE_LOCALIZED_NAME_GET_INFO}; + nameGetInfo.sourcePath = actionPathsBuffer[a]; + nameGetInfo.whichComponents = XR_INPUT_SOURCE_LOCALIZED_NAME_USER_PATH_BIT | + XR_INPUT_SOURCE_LOCALIZED_NAME_INTERACTION_PROFILE_BIT | + XR_INPUT_SOURCE_LOCALIZED_NAME_COMPONENT_BIT; + + uint32_t stringCount = 0u; + OXR(xrGetInputSourceLocalizedName( + Session, &nameGetInfo, 0, &stringCount, nullptr)); + if (stringCount < 256) { + OXR(xrGetInputSourceLocalizedName( + Session, &nameGetInfo, 256, &stringCount, stringBuffer)); + char pathStr[256]; + uint32_t strLen = 0; + OXR(xrPathToString( + Instance, + actionPathsBuffer[a], + (uint32_t)sizeof(pathStr), + &strLen, + pathStr)); + ALOGV( + "Xr## -> path = %lld `%s` -> `%s`", + (long long)actionPathsBuffer[a], + pathStr, + stringBuffer); + } + } + } + } + } + + void VibrateController( + const XrAction& action, + const XrPath& subactionPath, + float duration, + float frequency, + float amplitude) { + // fire haptics using output action + XrHapticVibration v{XR_TYPE_HAPTIC_VIBRATION, nullptr}; + v.amplitude = amplitude; + v.duration = ToXrTime(duration); + v.frequency = frequency; + XrHapticActionInfo hai = {XR_TYPE_HAPTIC_ACTION_INFO, nullptr}; + hai.action = action; + hai.subactionPath = subactionPath; + OXR(xrApplyHapticFeedback(Session, &hai, (const XrHapticBaseHeader*)&v)); + } + + void VibrateControllerAmplitude( + const XrAction& action, + const XrPath& subactionPath, + const float* envelope, + const size_t envelopeSize, + const float durationSecs) { + /// fill in the amplitude buffer + std::vector amplitudes(envelope, envelope + envelopeSize); + // fire haptics using output action + XrHapticAmplitudeEnvelopeVibrationFB v{ + XR_TYPE_HAPTIC_AMPLITUDE_ENVELOPE_VIBRATION_FB, nullptr}; + v.duration = ToXrTime(durationSecs); + v.amplitudeCount = (uint32_t)envelopeSize; + v.amplitudes = amplitudes.data(); + XrHapticActionInfo hai = {XR_TYPE_HAPTIC_ACTION_INFO, nullptr}; + hai.action = action; + hai.subactionPath = subactionPath; + OXR(xrApplyHapticFeedback(Session, &hai, (const XrHapticBaseHeader*)&v)); + } + + void VibrateControllerPCM( + const XrAction& action, + const XrPath& subactionPath, + const float* buffer, + const size_t bufferSize, + float sampleRate) { + /// fill in the amplitude buffer + std::vector pcmBuffer(bufferSize); + for (size_t i = 0; i < bufferSize; ++i) { + pcmBuffer[i] = buffer[i]; + } + // fire haptics using output action + XrHapticPcmVibrationFB v{XR_TYPE_HAPTIC_PCM_VIBRATION_FB, nullptr}; + v.sampleRate = sampleRate; + v.bufferSize = bufferSize; + v.buffer = pcmBuffer.data(); + uint32_t samplesUsed = 0; + v.samplesConsumed = &samplesUsed; + v.append = XR_FALSE; + XrHapticActionInfo hai = {XR_TYPE_HAPTIC_ACTION_INFO, nullptr}; + hai.action = action; + hai.subactionPath = subactionPath; + OXR(xrApplyHapticFeedback(Session, &hai, (const XrHapticBaseHeader*)&v)); + samplesUsed = *(v.samplesConsumed); + ALOG("Initial Haptics PCM Buffer Count Output: %d", samplesUsed); + uint32_t totalSamplesUsed = samplesUsed; + while (totalSamplesUsed < bufferSize) { + ALOG("TotalSamplesUsed: %d", totalSamplesUsed); + std::this_thread::sleep_for(std::chrono::milliseconds(1000)); + int newBufferSize = bufferSize - totalSamplesUsed; + pcmBuffer.resize(newBufferSize); + for (int i = 0; i < newBufferSize; ++i) { + pcmBuffer[i] = buffer[i + totalSamplesUsed]; + } + v.bufferSize = newBufferSize; + v.buffer = pcmBuffer.data(); + v.append = XR_TRUE; + OXR(xrApplyHapticFeedback(Session, &hai, (const XrHapticBaseHeader*)&v)); + samplesUsed = *(v.samplesConsumed); + if (samplesUsed == 0) { + ALOG("No samples used; stopping logging."); + break; + } + totalSamplesUsed += samplesUsed; + ALOG("Haptics PCM Buffer Count Output: %d", *(v.samplesConsumed)); + } + } + + void StopHapticEffect(const XrAction& action, const XrPath& subactionPath) { + XrHapticActionInfo hai = {XR_TYPE_HAPTIC_ACTION_INFO, nullptr}; + hai.action = action; + hai.subactionPath = subactionPath; + OXR(xrStopHapticFeedback(Session, &hai)); + } + + public: + private: + OVRFW::ControllerRenderer controllerRenderL_; + OVRFW::ControllerRenderer controllerRenderR_; + OVRFW::TinyUI ui_; + OVRFW::SimpleBeamRenderer beamRenderer_; + std::vector beams_; + + OVRFW::VRMenuObject* bigText_ = nullptr; + OVRFW::VRMenuObject* ipText_ = nullptr; + XrAction triggerForceAction_ = XR_NULL_HANDLE; + float triggerForceL_ = 0.0f; + float triggerForceR_ = 0.0f; + OVRFW::VRMenuObject* triggerForceLText_ = nullptr; + OVRFW::VRMenuObject* triggerForceRText_ = nullptr; + + XrAction trackpadForceAction_ = XR_NULL_HANDLE; + float trackpadForceL_ = 0.0f; + float trackpadForceR_ = 0.0f; + OVRFW::VRMenuObject* trackpadForceLText_ = nullptr; + OVRFW::VRMenuObject* trackpadForceRText_ = nullptr; + + XrAction stylusForceAction_ = XR_NULL_HANDLE; + float stylusForceL_ = 0.0f; + float stylusForceR_ = 0.0f; + OVRFW::VRMenuObject* stylusForceLText_ = nullptr; + OVRFW::VRMenuObject* stylusForceRText_ = nullptr; + + XrAction triggerCurlAction_ = XR_NULL_HANDLE; + float triggerCurlL_ = 0.0f; + float triggerCurlR_ = 0.0f; + OVRFW::VRMenuObject* triggerCurlLText_ = nullptr; + OVRFW::VRMenuObject* triggerCurlRText_ = nullptr; + + XrAction triggerSlideAction_ = XR_NULL_HANDLE; + float squeezeCurlL_ = 0.0f; + float squeezeCurlR_ = 0.0f; + OVRFW::VRMenuObject* squeezeCurlLText_ = nullptr; + OVRFW::VRMenuObject* squeezeCurlRText_ = nullptr; + + XrDevicePcmSampleRateGetInfoFB rightDeviceSampleRate_{ + XR_TYPE_DEVICE_PCM_SAMPLE_RATE_GET_INFO_FB}; + XrDevicePcmSampleRateGetInfoFB leftDeviceSampleRate_{ + XR_TYPE_DEVICE_PCM_SAMPLE_RATE_GET_INFO_FB}; + OVRFW::VRMenuObject* pcmHapticText_ = nullptr; + + XrAction mainHapticAction_ = XR_NULL_HANDLE; + XrAction triggerHapticAction_ = XR_NULL_HANDLE; + XrAction thumbHapticAction_ = XR_NULL_HANDLE; + + // Proximity + XrAction triggerProxAction_ = XR_NULL_HANDLE; + bool triggerProxL_ = false; + bool triggerProxR_ = false; + OVRFW::VRMenuObject* triggerProxLText_ = nullptr; + OVRFW::VRMenuObject* triggerProxRText_ = nullptr; + + XrAction thumbFbProxAction_ = XR_NULL_HANDLE; + bool thumbFBProxL_ = false; + bool thumbFBProxR_ = false; + OVRFW::VRMenuObject* thumbFBProxLText_ = nullptr; + OVRFW::VRMenuObject* thumbFBProxRText_ = nullptr; + XrAction thumbMetaProxAction_ = XR_NULL_HANDLE; + bool thumbMetaProxL_ = false; + bool thumbMetaProxR_ = false; + OVRFW::VRMenuObject* thumbMetaProxLText_ = nullptr; + OVRFW::VRMenuObject* thumbMetaProxRText_ = nullptr; + + // Trigger Value + XrAction triggerValueAction_ = XR_NULL_HANDLE; + bool triggerValueL_ = false; + bool triggerValueR_ = false; + OVRFW::VRMenuObject* triggerValueLText_ = nullptr; + OVRFW::VRMenuObject* triggerValueRText_ = nullptr; + + // Trigger Touch + XrAction triggerTouchAction_ = XR_NULL_HANDLE; + bool triggerTouchL_ = false; + bool triggerTouchR_ = false; + OVRFW::VRMenuObject* triggerTouchLText_ = nullptr; + OVRFW::VRMenuObject* triggerTouchRText_ = nullptr; + + // Squeeze Value + XrAction squeezeValueAction_ = XR_NULL_HANDLE; + bool squeezeValueL_ = false; + bool squeezeValueR_ = false; + OVRFW::VRMenuObject* squeezeValueLText_ = nullptr; + OVRFW::VRMenuObject* squeezeValueRText_ = nullptr; + + /// UI lag + bool delayUI_ = false; +}; + +ENTRY_POINT(XrControllersApp) diff --git a/Samples/XrSamples/XrControllers/assets/assets.txt b/Samples/XrSamples/XrControllers/assets/assets.txt new file mode 100644 index 0000000..2cc30f7 --- /dev/null +++ b/Samples/XrSamples/XrControllers/assets/assets.txt @@ -0,0 +1 @@ +This file is a placeholder. diff --git a/Samples/XrSamples/XrControllers/assets/panel.ktx b/Samples/XrSamples/XrControllers/assets/panel.ktx new file mode 100644 index 0000000..deb13e8 Binary files /dev/null and b/Samples/XrSamples/XrControllers/assets/panel.ktx differ diff --git a/Samples/XrSamples/XrControllers/java/MainActivity.java b/Samples/XrSamples/XrControllers/java/MainActivity.java new file mode 100644 index 0000000..669a0f0 --- /dev/null +++ b/Samples/XrSamples/XrControllers/java/MainActivity.java @@ -0,0 +1,29 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +package com.oculus.sdk.xrcontrollers; + +/** + * When using NativeActivity, we currently need to handle loading of dependent shared libraries + * manually before a shared library that depends on them is loaded, since there is not currently a + * way to specify a shared library dependency for NativeActivity via the manifest meta-data. + * + *

The simplest method for doing so is to subclass NativeActivity with an empty activity that + * calls System.loadLibrary on the dependent libraries, which is unfortunate when the goal is to + * write a pure native C/C++ only Android activity. + * + *

A native-code only solution is to load the dependent libraries dynamically using dlopen(). + * However, there are a few considerations, see: + * https://groups.google.com/forum/#!msg/android-ndk/l2E2qh17Q6I/wj6s_6HSjaYJ + * + *

1. Only call dlopen() if you're sure it will succeed as the bionic dynamic linker will + * remember if dlopen failed and will not re-try a dlopen on the same lib a second time. + * + *

2. Must remember what libraries have already been loaded to avoid infinitely looping when + * libraries have circular dependencies. + */ +public class MainActivity extends android.app.NativeActivity { + static { + System.loadLibrary("openxr_loader"); + System.loadLibrary("xrcontrollers"); + } +} diff --git a/Samples/XrSamples/XrControllers/res/values/strings.xml b/Samples/XrSamples/XrControllers/res/values/strings.xml new file mode 100644 index 0000000..ed323ce --- /dev/null +++ b/Samples/XrSamples/XrControllers/res/values/strings.xml @@ -0,0 +1,4 @@ + + + Xr Controllers + diff --git a/Samples/XrSamples/XrHandDataSource/CMakeLists.txt b/Samples/XrSamples/XrHandDataSource/CMakeLists.txt new file mode 100755 index 0000000..36b936a --- /dev/null +++ b/Samples/XrSamples/XrHandDataSource/CMakeLists.txt @@ -0,0 +1,43 @@ +# Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved. + +project(xrhanddatasource) + +if(NOT TARGET OpenXR::openxr_loader) + find_package(OpenXR REQUIRED) +endif() + +file(GLOB_RECURSE SRC_FILES + Src/*.c + Src/*.cpp +) + +if(ANDROID) + add_library(${PROJECT_NAME} MODULE ${SRC_FILES}) + target_include_directories(${PROJECT_NAME} PUBLIC ${ANDROID_NDK}/sources/android/native_app_glue) + target_link_libraries(${PROJECT_NAME} PRIVATE + android + EGL + GLESv3 + log + ktx + ) + set_target_properties(${PROJECT_NAME} PROPERTIES LINK_FLAGS "-u ANativeActivity_onCreate") +elseif(WIN32) + add_definitions(-D_USE_MATH_DEFINES) + add_executable(${PROJECT_NAME} ${SRC_FILES}) + add_custom_command(TARGET ${PROJECT_NAME} PRE_BUILD + COMMAND "${CMAKE_COMMAND}" -E copy_directory + "${CMAKE_CURRENT_LIST_DIR}/assets" + "$/assets" + VERBATIM) + + add_custom_command(TARGET ${PROJECT_NAME} PRE_BUILD + COMMAND "${CMAKE_COMMAND}" -E copy_directory + "${CMAKE_SOURCE_DIR}/SampleXrFramework/res/raw" + "$/font/res/raw" + VERBATIM) +endif() + +# Common across platforms +target_include_directories(${PROJECT_NAME} PRIVATE Src) +target_link_libraries(${PROJECT_NAME} PRIVATE samplexrframework) diff --git a/Samples/XrSamples/XrHandDataSource/Projects/Android/AndroidManifest.xml b/Samples/XrSamples/XrHandDataSource/Projects/Android/AndroidManifest.xml new file mode 100755 index 0000000..38bde58 --- /dev/null +++ b/Samples/XrSamples/XrHandDataSource/Projects/Android/AndroidManifest.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Samples/XrSamples/XrHandDataSource/Projects/Android/build.bat b/Samples/XrSamples/XrHandDataSource/Projects/Android/build.bat new file mode 100755 index 0000000..facf79f --- /dev/null +++ b/Samples/XrSamples/XrHandDataSource/Projects/Android/build.bat @@ -0,0 +1,29 @@ +@rem Only edit the master copy of this file in SDK_ROOT/bin/scripts/build/perproject + +@setlocal enableextensions enabledelayedexpansion + +@if not exist "build.gradle" @echo Build script must be executed from project directory. & goto :Abort + +@set P=.. + +:TryAgain + +@rem @echo P = %P% + +@if exist "%P%\bin\scripts\build\build.py.bat" goto :Found + +@if exist "%P%\bin\scripts\build" @echo "Could not find build.py.bat" & goto :Abort + +@set P=%P%\.. + +@goto :TryAgain + +:Found + +@set P=%P%\bin\scripts\build +@call %P%\build.py.bat %1 %2 %3 %4 %5 +@goto :End + +:Abort + +:End diff --git a/Samples/XrSamples/XrHandDataSource/Projects/Android/build.gradle b/Samples/XrSamples/XrHandDataSource/Projects/Android/build.gradle new file mode 100755 index 0000000..cff5f32 --- /dev/null +++ b/Samples/XrSamples/XrHandDataSource/Projects/Android/build.gradle @@ -0,0 +1,78 @@ +// Top-level build file where you can add configuration options common to all sub-projects/modules. +buildscript { + repositories { + google() + mavenCentral() + } + dependencies { + classpath "com.android.tools.build:gradle:7.0.3" + } +} + +repositories { + google() + mavenCentral() +} + +apply plugin: 'com.android.application' + +android { + compileSdk 32 + + defaultConfig { + applicationId "com.oculus.sdk.xrhanddatasource" + minSdk 26 + targetSdk 32 + versionCode 1 + versionName "1.0" + + // override app plugin abiFilters for 64-bit support + externalNativeBuild { + ndk { + abiFilters 'arm64-v8a' + } + ndkBuild { + abiFilters 'arm64-v8a' + } + cmake { + targets "xrhanddatasource" + } + } + } + + sourceSets { + main { + manifest.srcFile 'AndroidManifest.xml' + java.srcDirs = ['../../java'] + assets.srcDirs = ['../../assets'] + res.srcDirs = ['../../res'] + } + } + + buildTypes { + debug { + debuggable true + } + + release { + debuggable false + } + } + + externalNativeBuild { + cmake { + path file('../../../../CMakeLists.txt') + } + } + + // Enable prefab support for the OpenXR AAR + buildFeatures { + prefab true + } +} + +dependencies { + // Package/application AndroidManifest.xml properties, plus headers and libraries + // exposed to CMake + implementation 'org.khronos.openxr:openxr_loader_for_android:1.1.36' +} diff --git a/Samples/XrSamples/XrHandDataSource/Projects/Android/build.py b/Samples/XrSamples/XrHandDataSource/Projects/Android/build.py new file mode 100755 index 0000000..d4b6e58 --- /dev/null +++ b/Samples/XrSamples/XrHandDataSource/Projects/Android/build.py @@ -0,0 +1,35 @@ +#!/usr/bin/python +# (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +# This first bit of code is common bootstrapping code +# to determine the SDK root, and to set up the import +# path for additional python code. + +# begin bootstrap +import os +import sys + + +def init(): + root = os.path.realpath(os.path.dirname(os.path.realpath(__file__))) + os.chdir(root) # make sure we are always executing from the project directory + while os.path.isdir(os.path.join(root, "bin/scripts/build")) == False: + root = os.path.realpath(os.path.join(root, "..")) + if ( + len(root) <= 5 + ): # Should catch both Posix and Windows root directories (e.g. '/' and 'C:\') + print("Unable to find SDK root. Exiting.") + sys.exit(1) + root = os.path.abspath(root) + os.environ["OCULUS_SDK_PATH"] = root + sys.path.append(root + "/bin/scripts/build") + + +init() +import ovrbuild + +ovrbuild.init() +# end bootstrap + + +ovrbuild.build() diff --git a/Samples/XrSamples/XrHandDataSource/Projects/Android/gradle.properties b/Samples/XrSamples/XrHandDataSource/Projects/Android/gradle.properties new file mode 100644 index 0000000..3e927b1 --- /dev/null +++ b/Samples/XrSamples/XrHandDataSource/Projects/Android/gradle.properties @@ -0,0 +1,21 @@ +# Project-wide Gradle settings. +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. More details, visit +# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects +# org.gradle.parallel=true +# AndroidX package structure to make it clearer which packages are bundled with the +# Android operating system, and which are packaged with your app's APK +# https://developer.android.com/topic/libraries/support-library/androidx-rn +android.useAndroidX=true +# Enables namespacing of each library's R class so that its R class includes only the +# resources declared in the library itself and none from the library's dependencies, +# thereby reducing the size of the R class for that library +android.nonTransitiveRClass=true \ No newline at end of file diff --git a/Samples/XrSamples/XrHandDataSource/Projects/Android/gradle/wrapper/gradle-wrapper.jar b/Samples/XrSamples/XrHandDataSource/Projects/Android/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..7454180 Binary files /dev/null and b/Samples/XrSamples/XrHandDataSource/Projects/Android/gradle/wrapper/gradle-wrapper.jar differ diff --git a/Samples/XrSamples/XrHandDataSource/Projects/Android/gradle/wrapper/gradle-wrapper.properties b/Samples/XrSamples/XrHandDataSource/Projects/Android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..ffed3a2 --- /dev/null +++ b/Samples/XrSamples/XrHandDataSource/Projects/Android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/Samples/XrSamples/XrHandDataSource/Projects/Android/gradlew b/Samples/XrSamples/XrHandDataSource/Projects/Android/gradlew new file mode 100755 index 0000000..a69d9cb --- /dev/null +++ b/Samples/XrSamples/XrHandDataSource/Projects/Android/gradlew @@ -0,0 +1,240 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# 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 +# +# https://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. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit + +APP_NAME="Gradle" +APP_BASE_NAME=${0##*/} + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/Samples/XrSamples/XrHandDataSource/Projects/Android/gradlew.bat b/Samples/XrSamples/XrHandDataSource/Projects/Android/gradlew.bat new file mode 100755 index 0000000..f127cfd --- /dev/null +++ b/Samples/XrSamples/XrHandDataSource/Projects/Android/gradlew.bat @@ -0,0 +1,91 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/Samples/XrSamples/XrHandDataSource/Projects/Android/settings.gradle b/Samples/XrSamples/XrHandDataSource/Projects/Android/settings.gradle new file mode 100755 index 0000000..1bc19eb --- /dev/null +++ b/Samples/XrSamples/XrHandDataSource/Projects/Android/settings.gradle @@ -0,0 +1 @@ +rootProject.name = "XrHandDataSource" diff --git a/Samples/XrSamples/XrHandDataSource/README.md b/Samples/XrSamples/XrHandDataSource/README.md new file mode 100644 index 0000000..0222f76 --- /dev/null +++ b/Samples/XrSamples/XrHandDataSource/README.md @@ -0,0 +1,29 @@ +# OpenXR Hybrid Hands Sample + +## Overview +The `XR_EXT_hand_tracking_data_source` extension augments the `XR_EXT_hand_tracking` extension. Runtimes may support a variety of data sources for hand joint data for `XR_EXT_hand_tracking`, and some runtimes and devices may use joint data from multiple sources. This app showcases hybrid hands functionality, which can derive hand joint data poses from controller data. + +## The Sample +There are two buttons in the sample: +* The top one controls whether we present the hands as wrapped around the controller, or if we present them in a natural state. The application starts in the state where hands are wrapped around the controller. +* The one below that controls whether we render the controller model or not. In a real scenario, the developer would usually render the controller model in the controller-wrapped state and not in the natural state. The application starts with the controller rendering turned off. +![screen_shot](images/screen_shot.png) + +#### main.cpp +Main logic of the sample, handles responding to button presses and setting up the scene. + +#### xr_hand_helper.h +Helper class to handle the OpenXR logic of hand tracking. + +## Enabling Hybrid Hands +When a hand tracker is created it is passed a XrHandTrackingDataSourceEXT array parameter. +XR_HAND_TRACKING_DATA_SOURCE_UNOBSTRUCTED means the runtime can return hand poses generated by cameras. +XR_HAND_TRACKING_DATA_SOURCE_CONTROLLER_EXT means the runtime can return hand poses generated by controller data. + +## Natural vs Controller +The xrHandJointsLocateInfoEXT has a parameter for handJointsMotionRange. +XR_HANDS_JOINTS_MOTION_RANGE_UNOBSTRUCTED_XT is for natural poses. +XR_HAND_JOINTS_MOTION_RANGE_CONFORMING_TO_CONTROLLER_EXT is for hands wrapped around the controller. + +## Note +When the hand skeleton is in Natural Mode, in order for a pinch to register the thumb has to be down on the controller as well as the index trigger pulled. diff --git a/Samples/XrSamples/XrHandDataSource/Src/EnvironmentRenderer.cpp b/Samples/XrSamples/XrHandDataSource/Src/EnvironmentRenderer.cpp new file mode 100755 index 0000000..df9530a --- /dev/null +++ b/Samples/XrSamples/XrHandDataSource/Src/EnvironmentRenderer.cpp @@ -0,0 +1,223 @@ +/************************************************************************************************ +Filename : EnvironmentRenderer.cpp +Content : A variant of ModelRenderer suited for rendering gltf scenes with vertex color based fog +Created : July 2023 +Authors : Alexander Borsboom +Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved. +************************************************************************************************/ +#include "EnvironmentRenderer.h" +#include "Model/ModelFile.h" +#include "Model/ModelFileLoading.h" +#include "XrApp.h" +#include + +using OVR::Matrix4f; +using OVR::Posef; +using OVR::Quatf; +using OVR::Vector3f; +using OVR::Vector4f; + +namespace OVRFW { +namespace EnvironmentShaders { + +/// clang-format off +static const char* VertexShaderSrc = R"glsl( +attribute highp vec4 Position; +attribute highp vec3 Normal; +attribute highp vec2 TexCoord; +attribute lowp vec4 VertexColor; + +varying lowp vec3 oEye; +varying lowp vec3 oNormal; +varying lowp vec2 oTexCoord; +varying lowp vec4 oVertexColor; + +vec3 multiply( mat4 m, vec3 v ) +{ + return vec3( + m[0].x * v.x + m[1].x * v.y + m[2].x * v.z, + m[0].y * v.x + m[1].y * v.y + m[2].y * v.z, + m[0].z * v.x + m[1].z * v.y + m[2].z * v.z ); +} + +vec3 transposeMultiply( mat4 m, vec3 v ) +{ + return vec3( + m[0].x * v.x + m[0].y * v.y + m[0].z * v.z, + m[1].x * v.x + m[1].y * v.y + m[1].z * v.z, + m[2].x * v.x + m[2].y * v.y + m[2].z * v.z ); +} + +void main() +{ + gl_Position = TransformVertex( Position ); + vec3 eye = transposeMultiply( sm.ViewMatrix[VIEW_ID], -vec3( sm.ViewMatrix[VIEW_ID][3] ) ); + oEye = eye - vec3( ModelMatrix * Position ); + vec3 iNormal = Normal * 100.0f; + oNormal = multiply( ModelMatrix, iNormal ); + oTexCoord = TexCoord; + oVertexColor = VertexColor; +} +)glsl"; + +/// This shader uses vertex color.r for a fog, fading to a fog color as vertex color decreases to 0. +/// This gives behaviour consistent with our unity samples. +static const char* FragmentShaderSrc = R"glsl( +precision lowp float; + +uniform sampler2D Texture0; +uniform sampler2D Texture1; +uniform lowp vec3 SpecularLightDirection; +uniform lowp vec3 SpecularLightColor; +uniform lowp vec3 AmbientLightColor; +uniform lowp float FogStrength; +uniform lowp vec3 FogColor; + +varying lowp vec3 oEye; +varying lowp vec3 oNormal; +varying lowp vec2 oTexCoord; +varying lowp vec4 oVertexColor; + +lowp vec3 multiply( lowp mat3 m, lowp vec3 v ) +{ + return vec3( + m[0].x * v.x + m[1].x * v.y + m[2].x * v.z, + m[0].y * v.x + m[1].y * v.y + m[2].y * v.z, + m[0].z * v.x + m[1].z * v.y + m[2].z * v.z ); +} + +void main() +{ + lowp vec3 eyeDir = normalize( oEye.xyz ); + lowp vec3 Normal = normalize( oNormal ); + + lowp vec3 reflectionDir = dot( eyeDir, Normal ) * 2.0 * Normal - eyeDir; + lowp vec4 diffuse = texture2D( Texture0, oTexCoord ); + lowp vec4 detail = texture2D( Texture1, oTexCoord * 20.0 ); + lowp vec4 res = 0.5 * (diffuse + detail); + lowp vec3 ambientValue = res.xyz * AmbientLightColor; + + lowp float nDotL = max( dot( Normal , SpecularLightDirection ), 0.0 ); + lowp vec3 diffuseValue = res.xyz * SpecularLightColor * nDotL; + + lowp float specularPower = 1.0f - res.a; + specularPower = specularPower * specularPower; + + lowp vec3 H = normalize( SpecularLightDirection + eyeDir ); + lowp float nDotH = max( dot( Normal, H ), 0.0 ); + lowp float specularIntensity = pow( nDotH, 64.0f * ( specularPower ) ) * specularPower; + lowp vec3 specularValue = specularIntensity * SpecularLightColor; + + lowp vec3 controllerColor = diffuseValue + ambientValue + specularValue; + + lowp float fog = FogStrength * (1.0 - oVertexColor.r); + controllerColor = fog * FogColor + (1.0 - fog) * controllerColor; + + gl_FragColor.w = 1.0; + gl_FragColor.xyz = controllerColor; +} +)glsl"; + +/// clang-format on + +} // namespace EnvironmentRenderer + +bool EnvironmentRenderer::Init(std::string modelPath, OVRFW::ovrFileSys* fileSys) { + /// Shader + ovrProgramParm UniformParms[] = { + {"Texture0", ovrProgramParmType::TEXTURE_SAMPLED}, + {"Texture1", ovrProgramParmType::TEXTURE_SAMPLED}, // An optional detail texture. + {"SpecularLightDirection", ovrProgramParmType::FLOAT_VECTOR3}, + {"SpecularLightColor", ovrProgramParmType::FLOAT_VECTOR3}, + {"AmbientLightColor", ovrProgramParmType::FLOAT_VECTOR3}, + {"FogStrength", ovrProgramParmType::FLOAT}, + {"FogColor", ovrProgramParmType::FLOAT_VECTOR3}, + }; + ProgRenderModel = GlProgram::Build( + "", + EnvironmentShaders::VertexShaderSrc, + "", + EnvironmentShaders::FragmentShaderSrc, + UniformParms, + sizeof(UniformParms) / sizeof(ovrProgramParm)); + + MaterialParms materials = {}; + ModelGlPrograms programs = {}; + programs.ProgSingleTexture = &ProgRenderModel; + programs.ProgBaseColorPBR = &ProgRenderModel; + programs.ProgSkinnedBaseColorPBR = &ProgRenderModel; + programs.ProgLightMapped = &ProgRenderModel; + programs.ProgBaseColorEmissivePBR = &ProgRenderModel; + programs.ProgSkinnedBaseColorEmissivePBR = &ProgRenderModel; + programs.ProgSimplePBR = &ProgRenderModel; + programs.ProgSkinnedSimplePBR = &ProgRenderModel; + + if( fileSys ) { + OVRFW::ovrFileSys& fs = *fileSys; + RenderModel = LoadModelFile(fs, modelPath.c_str(), programs, materials); + } else { + ALOGE("Couldn't load model, we didn't get a valid filesystem"); + return false; + } + + if (RenderModel == nullptr || static_cast(RenderModel->Models.size()) < 1) { + ALOGE("Couldn't load modelrenderer model!"); + return false; + } + + FogStrengths = new OVR::Size[RenderModel->Models.size()]; + int modelIndex = 0; + for (auto& model : RenderModel->Models) { + auto& gc = model.surfaces[0].surfaceDef.graphicsCommand; + gc.UniformData[0].Data = &gc.Textures[0]; + gc.UniformData[1].Data = &gc.Textures[1]; + gc.UniformData[2].Data = &SpecularLightDirection; + gc.UniformData[3].Data = &SpecularLightColor; + gc.UniformData[4].Data = &AmbientLightColor; + FogStrengths[modelIndex] = OVR::Size(gc.GpuState.blendEnable == ovrGpuState::BLEND_ENABLE ? 1.0f : 0.0f); + gc.UniformData[5].Data = &FogStrengths[modelIndex]; + gc.UniformData[6].Data = &FogColor; + gc.GpuState.depthEnable = gc.GpuState.depthMaskEnable = true; + modelIndex++; + } + + /// Set defaults + SpecularLightDirection = Vector3f(1.0f, 1.0f, 0.0f); + SpecularLightColor = Vector3f(1.0f, 0.95f, 0.8f) * 0.75f; + AmbientLightColor = Vector3f(1.0f, 1.0f, 1.0f) * 0.15f; + FogColor = Vector3f(0.3372549f, 0.345098f, 0.3686275f); + + /// all good + Initialized = true; + return true; +} + +void EnvironmentRenderer::Shutdown() { + OVRFW::GlProgram::Free(ProgRenderModel); + if (RenderModel != nullptr) { + delete RenderModel; + RenderModel = nullptr; + } + if(FogStrengths != nullptr) { + delete FogStrengths; + FogStrengths = nullptr; + } +} + +void EnvironmentRenderer::Render(std::vector& surfaceList) { + /// toggle alpha override + if (RenderModel != nullptr) { + for( int i=0; i < static_cast(RenderModel->Models.size()); i++ ) { + auto& model = RenderModel->Models[i]; + auto& node = RenderModel->Nodes[i+1]; + ovrDrawSurface controllerSurface; + for( int j=0; j < static_cast(model.surfaces.size()); j++ ) { + controllerSurface.surface = &(model.surfaces[j].surfaceDef); + controllerSurface.modelMatrix = node.GetGlobalTransform(); + surfaceList.push_back(controllerSurface); + } + } + } +} + +} // namespace OVRFW diff --git a/Samples/XrSamples/XrHandDataSource/Src/EnvironmentRenderer.h b/Samples/XrSamples/XrHandDataSource/Src/EnvironmentRenderer.h new file mode 100755 index 0000000..0bbcfbb --- /dev/null +++ b/Samples/XrSamples/XrHandDataSource/Src/EnvironmentRenderer.h @@ -0,0 +1,63 @@ +/************************************************************************************************ +Filename : EnvironmentRenderer.h +Content : A variant of ModelRenderer suited for rendering gltf scenes with vertex color based fog +Created : July 2023 +Authors : Alexander Borsboom +Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved. +************************************************************************************************/ +#pragma once + +#include +#include +#include + +/// Sample Framework +#include "Misc/Log.h" +#include "Model/SceneView.h" +#include "Render/GlProgram.h" +#include "Render/SurfaceRender.h" +#include "OVR_FileSys.h" +#include "OVR_Math.h" + +#if defined(ANDROID) +#define XR_USE_GRAPHICS_API_OPENGL_ES 1 +#define XR_USE_PLATFORM_ANDROID 1 +#else +#include "unknwn.h" +#define XR_USE_GRAPHICS_API_OPENGL 1 +#define XR_USE_PLATFORM_WIN32 1 +#endif + +#include +#include +#include + +namespace OVRFW { + +class EnvironmentRenderer { + public: + EnvironmentRenderer() = default; + ~EnvironmentRenderer() = default; + + bool Init(std::vector& modelBuffer); + bool Init(std::string modelPath, OVRFW::ovrFileSys* fileSys); + void Shutdown(); + void Render(std::vector& surfaceList); + bool IsInitialized() const { return Initialized;} + + public: + OVR::Vector3f SpecularLightDirection; + OVR::Vector3f SpecularLightColor; + OVR::Vector3f AmbientLightColor; + OVR::Vector3f FogColor; + + private: + bool Initialized = false; + GlProgram ProgRenderModel; + ModelFile* RenderModel = nullptr; + GlTexture RenderModelTextureSolid; + OVR::Matrix4f Transform; + OVR::Size* FogStrengths; +}; + +} // namespace OVRFW diff --git a/Samples/XrSamples/XrHandDataSource/Src/SkyboxRenderer.cpp b/Samples/XrSamples/XrHandDataSource/Src/SkyboxRenderer.cpp new file mode 100755 index 0000000..5bb2348 --- /dev/null +++ b/Samples/XrSamples/XrHandDataSource/Src/SkyboxRenderer.cpp @@ -0,0 +1,176 @@ +/************************************************************************************************ +Filename : SkyboxRenderer.cpp +Content : A renderer suited for gradient skyboxes. +Created : July 2023 +Authors : Alex Borsboom +Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved. +************************************************************************************************/ +#include "SkyboxRenderer.h" +#include "Model/ModelFile.h" +#include "Model/ModelFileLoading.h" +#include "XrApp.h" +#include + +using OVR::Matrix4f; +using OVR::Posef; +using OVR::Quatf; +using OVR::Vector3f; +using OVR::Vector4f; + +namespace OVRFW { +namespace SkyboxShaders { + +/// clang-format off +static const char* VertexShaderSrc = R"glsl( +attribute highp vec4 Position; +attribute highp vec3 Normal; +attribute highp vec2 TexCoord; + +varying lowp vec3 oEye; +varying lowp vec3 oNormal; +varying lowp vec2 oTexCoord; + +vec3 multiply( mat4 m, vec3 v ) +{ + return vec3( + m[0].x * v.x + m[1].x * v.y + m[2].x * v.z, + m[0].y * v.x + m[1].y * v.y + m[2].y * v.z, + m[0].z * v.x + m[1].z * v.y + m[2].z * v.z ); +} + +vec3 transposeMultiply( mat4 m, vec3 v ) +{ + return vec3( + m[0].x * v.x + m[0].y * v.y + m[0].z * v.z, + m[1].x * v.x + m[1].y * v.y + m[1].z * v.z, + m[2].x * v.x + m[2].y * v.y + m[2].z * v.z ); +} + +void main() +{ + gl_Position = TransformVertex( Position ); + oTexCoord = TexCoord; +} +)glsl"; + +static const char* FragmentShaderSrc = R"glsl( +precision lowp float; + +uniform lowp vec3 TopColor; +uniform lowp vec3 MiddleColor; +uniform lowp vec3 BottomColor; + +varying lowp vec2 oTexCoord; + +lowp vec3 multiply( lowp mat3 m, lowp vec3 v ) +{ + return vec3( + m[0].x * v.x + m[1].x * v.y + m[2].x * v.z, + m[0].y * v.x + m[1].y * v.y + m[2].y * v.z, + m[0].z * v.x + m[1].z * v.y + m[2].z * v.z ); +} + +lowp float saturate(lowp float v) { + return clamp(v, 0.0f, 1.0f); +} + +void main() +{ + lowp float val = oTexCoord.y; + lowp float topVal = saturate(-3.0 + ( 4.0 * val )); + lowp float middleVal = saturate( 1.0 - 4.0 * abs(0.75 - val )); + lowp float bottomVal = saturate( 4.0 * ( 0.75 - val)); + + lowp vec3 finalColor = BottomColor.rgb * bottomVal + MiddleColor.rgb * middleVal + TopColor.rgb * topVal; + + gl_FragColor.w = 1.0f; + gl_FragColor.xyz = finalColor; +} +)glsl"; + +/// clang-format on + +} // namespace SkyboxShaders + +bool SkyboxRenderer::Init(std::string modelPath, OVRFW::ovrFileSys* fileSys) { + /// Shader + ovrProgramParm UniformParms[] = { + {"TopColor", ovrProgramParmType::FLOAT_VECTOR3}, + {"MiddleColor", ovrProgramParmType::FLOAT_VECTOR3}, + {"BottomColor", ovrProgramParmType::FLOAT_VECTOR3}, + }; + ProgRenderModel = GlProgram::Build( + "", + SkyboxShaders::VertexShaderSrc, + "", + SkyboxShaders::FragmentShaderSrc, + UniformParms, + sizeof(UniformParms) / sizeof(ovrProgramParm)); + + MaterialParms materials = {}; + ModelGlPrograms programs = {}; + programs.ProgSingleTexture = &ProgRenderModel; + programs.ProgBaseColorPBR = &ProgRenderModel; + programs.ProgSkinnedBaseColorPBR = &ProgRenderModel; + programs.ProgLightMapped = &ProgRenderModel; + programs.ProgBaseColorEmissivePBR = &ProgRenderModel; + programs.ProgSkinnedBaseColorEmissivePBR = &ProgRenderModel; + programs.ProgSimplePBR = &ProgRenderModel; + programs.ProgSkinnedSimplePBR = &ProgRenderModel; + + if( fileSys ) { + OVRFW::ovrFileSys& fs = *fileSys; + RenderModel = LoadModelFile(fs, modelPath.c_str(), programs, materials); + } else { + ALOGE("Couldn't load model, we didn't get a valid filesystem"); + return false; + } + + if (RenderModel == nullptr || static_cast(RenderModel->Models.size()) < 1) { + ALOGE("Couldn't load modelrenderer model!"); + return false; + } + + TopColor = OVR::Vector3f(0.937f, 0.9236477f, 0.883591f); + MiddleColor = OVR::Vector3f(0.6705883f, 0.6909091f, 0.7450981f); + BottomColor = OVR::Vector3f(0.3372549f, 0.345098f, 0.3686275f); + + for (auto& model : RenderModel->Models) { + auto& gc = model.surfaces[0].surfaceDef.graphicsCommand; + gc.UniformData[0].Data = &TopColor; + gc.UniformData[1].Data = &MiddleColor; + gc.UniformData[2].Data = &BottomColor; + gc.GpuState.depthMaskEnable = false; + gc.GpuState.depthEnable = false; + gc.GpuState.blendEnable = ovrGpuState::BLEND_DISABLE; + } + + /// all good + Initialized = true; + return true; +} + +void SkyboxRenderer::Shutdown() { + OVRFW::GlProgram::Free(ProgRenderModel); + if (RenderModel != nullptr) { + delete RenderModel; + RenderModel = nullptr; + } +} + +void SkyboxRenderer::Render(std::vector& surfaceList) { + if (RenderModel != nullptr) { + for( int i=0; i < static_cast(RenderModel->Models.size()); i++ ) { + auto& model = RenderModel->Models[i]; + auto& node = RenderModel->Nodes[i]; + ovrDrawSurface controllerSurface; + for( int j=0; j < static_cast(model.surfaces.size()); j++ ) { + controllerSurface.surface = &(model.surfaces[j].surfaceDef); + controllerSurface.modelMatrix = node.GetGlobalTransform(); + surfaceList.push_back(controllerSurface); + } + } + } +} + +} // namespace OVRFW diff --git a/Samples/XrSamples/XrHandDataSource/Src/SkyboxRenderer.h b/Samples/XrSamples/XrHandDataSource/Src/SkyboxRenderer.h new file mode 100755 index 0000000..5f99ef6 --- /dev/null +++ b/Samples/XrSamples/XrHandDataSource/Src/SkyboxRenderer.h @@ -0,0 +1,61 @@ +/************************************************************************************************ +Filename : SkyboxRenderer.h +Content : A renderer which displays gradient skyboxes. +Created : July 2023 +Authors : Alexander Borsboom +Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved. +************************************************************************************************/ +#pragma once + +#include +#include +#include + +/// Sample Framework +#include "Misc/Log.h" +#include "Model/SceneView.h" +#include "Render/GlProgram.h" +#include "Render/SurfaceRender.h" +#include "OVR_FileSys.h" +#include "OVR_Math.h" + +#if defined(ANDROID) +#define XR_USE_GRAPHICS_API_OPENGL_ES 1 +#define XR_USE_PLATFORM_ANDROID 1 +#else +#include "unknwn.h" +#define XR_USE_GRAPHICS_API_OPENGL 1 +#define XR_USE_PLATFORM_WIN32 1 +#endif + +#include +#include +#include + +namespace OVRFW { + +class SkyboxRenderer { + public: + SkyboxRenderer() = default; + ~SkyboxRenderer() = default; + + bool Init(std::string modelPath, OVRFW::ovrFileSys* fileSys); + void Shutdown(); + void Render(std::vector& surfaceList); + bool IsInitialized() const { return Initialized;} + + public: + OVR::Vector3f TopColor; + OVR::Vector3f MiddleColor; + OVR::Vector3f BottomColor; + OVR::Vector3f Direction; + + private: + bool Initialized = false; + GlProgram ProgRenderModel; + ModelFile* RenderModel = nullptr; + GlTexture RenderModelTextureSolid; + OVR::Matrix4f Transform; +}; + +} // namespace OVRFW diff --git a/Samples/XrSamples/XrHandDataSource/Src/main.cpp b/Samples/XrSamples/XrHandDataSource/Src/main.cpp new file mode 100755 index 0000000..d4126db --- /dev/null +++ b/Samples/XrSamples/XrHandDataSource/Src/main.cpp @@ -0,0 +1,352 @@ +/******************************************************************************* + +Filename : Main.cpp +Content : Simple test app to test openxr hand tracking usage extension +Language : C++ +Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved. + +*******************************************************************************/ + +#include +#include +#include +#include +#include + +#include "Model/ModelDef.h" +#include "Model/ModelFile.h" +#include "Model/ModelFileLoading.h" +#include "OVR_FileSys.h" +#include "XrApp.h" + +#include "Input/HandRenderer.h" +#include "Input/TinyUI.h" +#include "EnvironmentRenderer.h" +#include "SkyboxRenderer.h" +#include "Render/GeometryRenderer.h" +#include "Render/SimpleGlbRenderer.h" +#include "Render/SimpleBeamRenderer.h" + +/// add logging +#define XRLOG ALOG + +#include "xr_hand_helper.h" +#include "xr_render_model_helper.h" + +std::unique_ptr fileSys = nullptr; + +class XrHandDataSourceApp : public OVRFW::XrApp { + private: + static constexpr std::string_view kSampleExplanation = + "Hand poses can be generated either from controller data or actual tracked hands. \n" + "In this sample when the controllers are active and held, the hand poses are \n" + "generated from the controller inputs. \n" + " \n" + "The 'Set Hand Type to Controller' / 'Set Hand Type to Natural' button toggles between \n" + "a hand pose that is open, and one that is generated wrapped around the controller. \n" + " \n" + "The 'Render / Stop Rendering Controllers' button toggles displaying the \n" + "controller, Usually a controller will not be drawn when the hands are in natural \n" + "mode. \n" + " \n" + "Note: In Natural mode, the thumb must be on the controller when the index trigger \n" + "is pulled, in order for the button to be pressed. \n"; + + public: + XrHandDataSourceApp() : OVRFW::XrApp() { + BackgroundColor = OVR::Vector4f(0.3372549f, 0.345098f, 0.4f, 0.3686274f); + } + + // Returns a list of OpenXr extensions needed for this app + virtual std::vector GetExtensions() override { + std::vector extensions = XrApp::GetExtensions(); + /// add hand extensions + for (const auto& handExtension : XrHandHelper::RequiredExtensionNames()) { + extensions.push_back(handExtension); + } + /// add render model extensions + for (const auto& renderModelExtension : XrRenderModelHelper::RequiredExtensionNames()) { + extensions.push_back(renderModelExtension); + } + + /// add composition alpha blend + extensions.push_back(XR_FB_COMPOSITION_LAYER_ALPHA_BLEND_EXTENSION_NAME); + + /// log all extensions + ALOG("XrHandDataSourceApp requesting extensions:"); + for (const auto& e : extensions) { + ALOG(" --> %s", e); + } + + return extensions; + } + + // Must return true if the application initializes successfully. + virtual bool AppInit(const xrJava* context) override { + if (false == ui_.Init(context, GetFileSys())) { + ALOG("TinyUI::Init FAILED."); + return false; + } + + /// hand tracking + handL_ = std::make_unique(GetInstance(), true); + OXR(handL_->GetLastError()); + handR_ = std::make_unique(GetInstance(), false); + OXR(handR_->GetLastError()); + + /// render model + renderModelLeft_ = std::make_unique(GetInstance()); + OXR(renderModelLeft_->GetLastError()); + renderModelRight_ = std::make_unique(GetInstance()); + OXR(renderModelRight_->GetLastError()); + + /// Build UI + CreateSampleDescriptionPanel(); + bigText_ = ui_.AddLabel( + "OpenXR Hand Data Source Sample", {0.1f, 0.9f, -2.0f}, {1300.0f, 100.0f}); + bigText_->SetSurfaceColor(0 ,{0.0f, 0.0f, 1.0f, 1.0f}); + + renderTrackedRemoteButton_ = + ui_.AddButton("Render Controllers", {-0.5f, -0.15f, -2.0f}, {500.0f, 100.0f}, [=]() { + renderTrackedRemotes_ = !renderTrackedRemotes_; + if (renderTrackedRemotes_) { + renderTrackedRemoteButton_->SetText("Stop Rendering Controllers"); + } else { + renderTrackedRemoteButton_->SetText("Render Controllers"); + } + }); + + controllerHandDataTypeButton_ = + ui_.AddButton("Set Hand Type to Natural", {0.75f, -0.15f, -2.0f}, {500.0f, 100.0f}, [=]() { + handDataTypeNatural_ = !handDataTypeNatural_; + if (handDataTypeNatural_) { + controllerHandDataTypeButton_->SetText("Set Hand Type to Controller"); + } else { + controllerHandDataTypeButton_->SetText("Set Hand Type to Natural"); + } + }); + + fileSys = std::unique_ptr(OVRFW::ovrFileSys::Create(*context)); + + if (fileSys) { + std::string environmentPath = "apk:///assets/SmallRoom.gltf.ovrscene"; + environmentRenderer_.Init(environmentPath, fileSys.get()); + std::string skyboxPath = "apk:///assets/Skybox.gltf.ovrscene"; + skyboxRenderer.Init(skyboxPath, fileSys.get()); + } + + return true; + } + + void CreateSampleDescriptionPanel() { + // Panel to provide sample description to the user for context + auto descriptionLabel = ui_.AddLabel( + static_cast(kSampleExplanation), {0.125f, 0.4f, -2.0f}, {1200.0f, 380.0f}); + + // Align and size the description text for readability + OVRFW::VRMenuFontParms fontParams{}; + fontParams.Scale = 0.5f; + fontParams.AlignHoriz = OVRFW::HORIZONTAL_LEFT; + descriptionLabel->SetFontParms(fontParams); + descriptionLabel->SetTextLocalPosition({-0.7f, 0, 0}); + descriptionLabel->SetSurfaceColor(0 ,{0.0f, 0.0f, 1.0f, 1.0f}); + } + + virtual void AppShutdown(const xrJava* context) override { + handL_ = nullptr; + handR_ = nullptr; + renderModelLeft_ = nullptr; + renderModelRight_ = nullptr; + + OVRFW::XrApp::AppShutdown(context); + ui_.Shutdown(); + } + + virtual bool SessionInit() override { + /// Use LocalSpace instead of Stage Space. + CurrentSpace = LocalSpace; + /// Disable scene navigation + GetScene().SetFootPos({0.0f, 0.0f, 0.0f}); + this->FreeMove = false; + beamRenderer_.Init(GetFileSys(), nullptr, OVR::Vector4f(1.0f), 1.0f); + + /// hands + handL_->SessionInit(GetSession()); + handR_->SessionInit(GetSession()); + /// render model + renderModelLeft_->SessionInit(GetSession()); + renderModelRight_->SessionInit(GetSession()); + + /// rendering; + handRendererL_.Init(&handL_->Mesh(), handL_->IsLeft()); + handRendererR_.Init(&handR_->Mesh(), handR_->IsLeft()); + + return true; + } + + virtual void SessionEnd() override { + /// hands + handL_->SessionEnd(); + handR_->SessionEnd(); + /// render model + renderModelLeft_->SessionEnd(); + renderModelRight_->SessionEnd(); + + controllerRenderL_.Shutdown(); + controllerRenderR_.Shutdown(); + skyboxRenderer.Shutdown(); + environmentRenderer_.Shutdown(); + beamRenderer_.Shutdown(); + handRendererL_.Shutdown(); + handRendererR_.Shutdown(); + } + + // Update state + virtual void Update(const OVRFW::ovrApplFrameIn& in) override { + XrSpace currentSpace = GetCurrentSpace(); + XrTime predictedDisplayTime = ToXrTime(in.PredictedDisplayTime); + + /// render model + renderModelLeft_->Update(currentSpace, predictedDisplayTime); + renderModelRight_->Update(currentSpace, predictedDisplayTime); + /// hands + handL_->SetHandDataTypeNatural(handDataTypeNatural_); + handL_->Update(currentSpace, predictedDisplayTime); + handR_->SetHandDataTypeNatural(handDataTypeNatural_); + handR_->Update(currentSpace, predictedDisplayTime); + + bool renderLeftController = renderTrackedRemotes_; + if (handL_->AreLocationsActive()) { + handRendererL_.Update(handL_->Joints(), handL_->RenderScale()); + if (!handL_->OnController()) { + renderLeftController = false; + } + } + if (renderLeftController) { + if (controllerRenderL_.IsInitialized()) { + controllerRenderL_.Update(in.LeftRemotePose); + } else { + std::vector controllerBufferl; + std::string strToCheck = "/model_fb/controller/left"; + controllerBufferl = renderModelLeft_->LoadRenderModel(strToCheck); + if (controllerBufferl.size() > 0) { + ALOG( + "### Left Controller Render Model Size: %u", + (uint32_t)controllerBufferl.size()); + controllerRenderL_.Init(controllerBufferl); + controllerRenderL_.UseSolidTexture = true; + controllerRenderL_.Opacity = 1.0f; + } else { + ALOG("### Failed to Load Left Controller Render Model"); + } + } + } + + bool renderRightController = renderTrackedRemotes_; + if (handR_->AreLocationsActive()) { + handRendererR_.Update(handR_->Joints(), handR_->RenderScale()); + if (!handR_->OnController()) { + renderRightController = false; + } + } + if (renderRightController) { + if (controllerRenderR_.IsInitialized()) { + controllerRenderR_.Update(in.RightRemotePose); + } else { + std::vector controllerBufferr; + std::string strToCheck = "/model_fb/controller/right"; + controllerBufferr = renderModelRight_->LoadRenderModel(strToCheck); + if (controllerBufferr.size() > 0) { + controllerRenderR_.Init(controllerBufferr); + controllerRenderR_.UseSolidTexture = true; + controllerRenderR_.Opacity = 1.0f; + } + } + } + + /// UI + ui_.HitTestDevices().clear(); + if (handDataTypeNatural_ && handR_->AreLocationsActive()) { + ui_.AddHitTestRay(FromXrPosef(handR_->AimPose()), handR_->IndexPinching()); + } else if (in.RightRemoteTracked) { + const bool didPinch = in.RightRemoteIndexTrigger > 0.25f; + ui_.AddHitTestRay(in.RightRemotePointPose, didPinch); + } + + if (handDataTypeNatural_ && handL_->AreLocationsActive()) { + ui_.AddHitTestRay(FromXrPosef(handL_->AimPose()), handL_->IndexPinching()); + } else if (in.LeftRemoteTracked) { + const bool didPinch = in.LeftRemoteIndexTrigger > 0.25f; + ui_.AddHitTestRay(in.LeftRemotePointPose, didPinch); + } + + ui_.Update(in); + beamRenderer_.Update(in, ui_.HitTestDevices()); + } + + // Render eye buffers while running + virtual void Render(const OVRFW::ovrApplFrameIn& in, OVRFW::ovrRendererOutput& out) override { + // Render the environment first, to place behind all other surfaces. + skyboxRenderer.Render(out.Surfaces); + environmentRenderer_.Render(out.Surfaces); + + /// Render UI + ui_.Render(in, out); + + /// Render beams + beamRenderer_.Render(in, out); + + if (handL_->AreLocationsActive() && handL_->IsPositionValid()) { + /// Render solid Hands + handRendererL_.Solidity = 1.0f; + handRendererL_.Render(out.Surfaces); + } + + if (in.LeftRemoteTracked && renderTrackedRemotes_ && handL_->OnController()) { + controllerRenderL_.Render(out.Surfaces); + } + + if (handR_->AreLocationsActive() && handR_->IsPositionValid()) { + /// Render solid Hands + handRendererR_.Solidity = 1.0f; + handRendererR_.Render(out.Surfaces); + } + + if (in.RightRemoteTracked && renderTrackedRemotes_ && handR_->OnController()) { + controllerRenderR_.Render(out.Surfaces); + } + } + + public: + private: + OVRFW::SimpleGlbRenderer controllerRenderL_; + OVRFW::SimpleGlbRenderer controllerRenderR_; + OVRFW::EnvironmentRenderer environmentRenderer_; + OVRFW::SkyboxRenderer skyboxRenderer; + + OVRFW::TinyUI ui_; + OVRFW::SimpleBeamRenderer beamRenderer_; + std::vector beams_; + + /// hands - xr interface + std::unique_ptr handL_; + std::unique_ptr handR_; + /// hands - rendering + OVRFW::HandRenderer handRendererL_; + OVRFW::HandRenderer handRendererR_; + + /// render model - xr interface + std::unique_ptr renderModelLeft_; + std::unique_ptr renderModelRight_; + + // gui and state + bool renderTrackedRemotes_ = false; + bool handDataTypeNatural_ = false; + OVRFW::VRMenuObject* renderTrackedRemoteButton_ = nullptr; + OVRFW::VRMenuObject* controllerHandDataTypeButton_ = nullptr; + + // info text; + OVRFW::VRMenuObject* bigText_ = nullptr; +}; + +ENTRY_POINT(XrHandDataSourceApp) diff --git a/Samples/XrSamples/XrHandDataSource/Src/xr_hand_helper.h b/Samples/XrSamples/XrHandDataSource/Src/xr_hand_helper.h new file mode 100755 index 0000000..117d046 --- /dev/null +++ b/Samples/XrSamples/XrHandDataSource/Src/xr_hand_helper.h @@ -0,0 +1,253 @@ +/************************************************************************************************ +Filename : xr_hand_helper.h +Content : Helper Inteface for openxr hand extensions +Created : April 2021 +Authors : Federico Schliemann +Language : C++ +Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved. +************************************************************************************************/ +#pragma once + + +#include "xr_helper.h" + +#include + +class XrHandHelper : public XrHelper { + public: + XrHandHelper(XrInstance instance, bool isLeft) : XrHelper(instance), isLeft_(isLeft) { + /// Hook up extensions for hand tracking + oxr(xrGetInstanceProcAddr( + instance, "xrCreateHandTrackerEXT", (PFN_xrVoidFunction*)(&xrCreateHandTrackerEXT_))); + oxr(xrGetInstanceProcAddr( + instance, "xrDestroyHandTrackerEXT", (PFN_xrVoidFunction*)(&xrDestroyHandTrackerEXT_))); + oxr(xrGetInstanceProcAddr( + instance, "xrLocateHandJointsEXT", (PFN_xrVoidFunction*)(&xrLocateHandJointsEXT_))); + /// Hook up extensions for hand rendering + oxr(xrGetInstanceProcAddr( + instance, "xrGetHandMeshFB", (PFN_xrVoidFunction*)(&xrGetHandMeshFB_))); + } + + ~XrHandHelper() override { + xrCreateHandTrackerEXT_ = nullptr; + xrDestroyHandTrackerEXT_ = nullptr; + xrLocateHandJointsEXT_ = nullptr; + xrGetHandMeshFB_ = nullptr; + } + + /// XrHelper Interface + virtual bool SessionInit(XrSession session) override { + if (xrCreateHandTrackerEXT_) { + XrHandTrackerCreateInfoEXT createInfo{XR_TYPE_HAND_TRACKER_CREATE_INFO_EXT}; + createInfo.handJointSet = XR_HAND_JOINT_SET_DEFAULT_EXT; + createInfo.hand = isLeft_ ? XR_HAND_LEFT_EXT : XR_HAND_RIGHT_EXT; + + std::array dataSources = { + XR_HAND_TRACKING_DATA_SOURCE_UNOBSTRUCTED_EXT, + XR_HAND_TRACKING_DATA_SOURCE_CONTROLLER_EXT, + }; + + XrHandTrackingDataSourceInfoEXT usageInfo{ + XR_TYPE_HAND_TRACKING_DATA_SOURCE_INFO_EXT}; + usageInfo.requestedDataSourceCount = (uint32_t)dataSources.size(); + usageInfo.requestedDataSources = dataSources.data(); + + createInfo.next = &usageInfo; + + if (!oxr(xrCreateHandTrackerEXT_(session, &createInfo, &handTracker_))) { + return false; + } + + if (xrGetHandMeshFB_) { + mesh_.type = XR_TYPE_HAND_TRACKING_MESH_FB; + mesh_.next = nullptr; + mesh_.jointCapacityInput = 0; + mesh_.jointCountOutput = 0; + mesh_.vertexCapacityInput = 0; + mesh_.vertexCountOutput = 0; + mesh_.indexCapacityInput = 0; + mesh_.indexCountOutput = 0; + if (!oxr(xrGetHandMeshFB_(handTracker_, &mesh_))) { + return false; + } + /// update sizes + mesh_.jointCapacityInput = mesh_.jointCountOutput; + mesh_.vertexCapacityInput = mesh_.vertexCountOutput; + mesh_.indexCapacityInput = mesh_.indexCountOutput; + vertexPositions_.resize(mesh_.vertexCapacityInput); + vertexNormals_.resize(mesh_.vertexCapacityInput); + vertexUVs_.resize(mesh_.vertexCapacityInput); + vertexBlendIndices_.resize(mesh_.vertexCapacityInput); + vertexBlendWeights_.resize(mesh_.vertexCapacityInput); + indices_.resize(mesh_.indexCapacityInput); + + /// skeleton + mesh_.jointBindPoses = jointBindPoses_; + mesh_.jointParents = jointParents_; + mesh_.jointRadii = jointRadii_; + /// mesh + mesh_.vertexPositions = vertexPositions_.data(); + mesh_.vertexNormals = vertexNormals_.data(); + mesh_.vertexUVs = vertexUVs_.data(); + mesh_.vertexBlendIndices = vertexBlendIndices_.data(); + mesh_.vertexBlendWeights = vertexBlendWeights_.data(); + mesh_.indices = indices_.data(); + /// get mesh + if (!oxr(xrGetHandMeshFB_(handTracker_, &mesh_))) { + return false; + } + } + return true; + } + return false; + } + + virtual bool SessionEnd() override { + if (xrDestroyHandTrackerEXT_) { + return oxr(xrDestroyHandTrackerEXT_(handTracker_)); + } + return false; + } + + virtual bool Update(XrSpace currentSpace, XrTime predictedDisplayTime) override { + if (xrLocateHandJointsEXT_) { + /// data source (hands or controllers) + XrHandTrackingDataSourceStateEXT dataSourceState_{ + XR_TYPE_HAND_TRACKING_DATA_SOURCE_STATE_EXT}; + dataSourceState_.next = nullptr; + /// aim + aimState_.next = &dataSourceState_; + /// scale + scale_.next = &aimState_; + scale_.sensorOutput = 1.0f; + scale_.currentOutput = 1.0f; + scale_.overrideHandScale = XR_TRUE; + scale_.overrideValueInput = 1.00f; + /// locations + locations_.next = &scale_; + locations_.jointCount = XR_HAND_JOINT_COUNT_EXT; + locations_.jointLocations = jointLocations_; + + XrHandJointsLocateInfoEXT locateInfo{XR_TYPE_HAND_JOINTS_LOCATE_INFO_EXT}; + locateInfo.baseSpace = currentSpace; + locateInfo.time = predictedDisplayTime; + XrHandJointsMotionRangeInfoEXT motionRangeInfo{ + XR_TYPE_HAND_JOINTS_MOTION_RANGE_INFO_EXT}; + if (handDataTypeNatural_) { + motionRangeInfo.handJointsMotionRange = + XR_HAND_JOINTS_MOTION_RANGE_UNOBSTRUCTED_EXT; + } else { + motionRangeInfo.handJointsMotionRange = + XR_HAND_JOINTS_MOTION_RANGE_CONFORMING_TO_CONTROLLER_EXT; + } + locateInfo.next = &motionRangeInfo; + + if (oxr(xrLocateHandJointsEXT_(handTracker_, &locateInfo, &locations_))) { + onController_ = false; + + if (dataSourceState_.dataSource == XR_HAND_TRACKING_DATA_SOURCE_CONTROLLER_EXT) { + onController_ = true; + } + return true; + } else { + return false; + } + + } + return false; + } + + static std::vector RequiredExtensionNames() { + return { + XR_EXT_HAND_TRACKING_EXTENSION_NAME, + XR_FB_HAND_TRACKING_MESH_EXTENSION_NAME, + XR_FB_HAND_TRACKING_AIM_EXTENSION_NAME, + XR_EXT_HAND_JOINTS_MOTION_RANGE_EXTENSION_NAME, + XR_EXT_HAND_TRACKING_DATA_SOURCE_EXTENSION_NAME}; + } + + public: + /// Own interface + bool IsLeft() const { + return isLeft_; + } + bool OnController() const { + return onController_; + } + const XrHandTrackingMeshFB& Mesh() const { + return mesh_; + } + const XrHandTrackingScaleFB& Scale() const { + return scale_; + } + const XrPosef* JointBindPoses() const { + return jointBindPoses_; + } + const XrHandJointEXT* JointParents() const { + return jointParents_; + } + const XrHandJointLocationEXT* Joints() const { + return jointLocations_; + } + float RenderScale() const { + return scale_.sensorOutput; + } + bool IsTracking() const { + return (handTracker_ != XR_NULL_HANDLE); + } + bool AreLocationsActive() const { + return IsTracking() && (locations_.isActive); + } + bool IsPositionValid() const { + return jointLocations_[XR_HAND_JOINT_PALM_EXT].locationFlags & + XR_SPACE_LOCATION_POSITION_VALID_BIT; + } + const XrPosef& AimPose() const { + return aimState_.aimPose; + } + bool IndexPinching() const { + return (aimState_.status & XR_HAND_TRACKING_AIM_INDEX_PINCHING_BIT_FB) != 0; + } + bool IsDominant() const { + return (aimState_.status & XR_HAND_TRACKING_AIM_DOMINANT_HAND_BIT_FB) != 0; + } + const XrHandTrackingAimStateFB& AimState() const { + return aimState_; + } + void SetHandDataTypeNatural(bool handDataTypeNatural) { + handDataTypeNatural_ = handDataTypeNatural; + } + + private: + bool isLeft_; + bool onController_ = false; + /// Hands - extension functions + PFN_xrCreateHandTrackerEXT xrCreateHandTrackerEXT_ = nullptr; + PFN_xrDestroyHandTrackerEXT xrDestroyHandTrackerEXT_ = nullptr; + PFN_xrLocateHandJointsEXT xrLocateHandJointsEXT_ = nullptr; + /// Hands - FB mesh rendering extensions + PFN_xrGetHandMeshFB xrGetHandMeshFB_ = nullptr; + /// Hands - tracker handles + XrHandTrackerEXT handTracker_ = XR_NULL_HANDLE; + /// Hands - data buffers + XrHandJointLocationEXT jointLocations_[XR_HAND_JOINT_COUNT_EXT]; + XrPosef jointBindPoses_[XR_HAND_JOINT_COUNT_EXT]; + XrHandJointEXT jointParents_[XR_HAND_JOINT_COUNT_EXT]; + float jointRadii_[XR_HAND_JOINT_COUNT_EXT]; + /// mesh storage + XrHandTrackingMeshFB mesh_{XR_TYPE_HAND_TRACKING_MESH_FB}; + std::vector vertexPositions_; + std::vector vertexNormals_; + std::vector vertexUVs_; + std::vector vertexBlendIndices_; + std::vector vertexBlendWeights_; + std::vector indices_; + /// extension nodes + /// scale + XrHandTrackingScaleFB scale_{XR_TYPE_HAND_TRACKING_SCALE_FB}; + /// aim + XrHandTrackingAimStateFB aimState_{XR_TYPE_HAND_TRACKING_AIM_STATE_FB}; + /// location + XrHandJointLocationsEXT locations_{XR_TYPE_HAND_JOINT_LOCATIONS_EXT}; + bool handDataTypeNatural_ = true; +}; diff --git a/Samples/XrSamples/XrHandDataSource/Src/xr_helper.h b/Samples/XrSamples/XrHandDataSource/Src/xr_helper.h new file mode 100755 index 0000000..58556ac --- /dev/null +++ b/Samples/XrSamples/XrHandDataSource/Src/xr_helper.h @@ -0,0 +1,67 @@ +/************************************************************************************************ +Filename : xr_helper.h +Content : Base interface to wrap openxr extension functionality +Created : April 2021 +Authors : Federico Schliemann +Language : C++ +Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved. +************************************************************************************************/ +#pragma once + +#if defined(ANDROID) +#define XR_USE_GRAPHICS_API_OPENGL_ES 1 +#define XR_USE_PLATFORM_ANDROID 1 +#else +#include "unknwn.h" +#define XR_USE_GRAPHICS_API_OPENGL 1 +#define XR_USE_PLATFORM_WIN32 1 +#endif + +#include +#include + +#include +#include +#include +#include +#include +#include + +#if !defined(XRLOG) +#define XRLOG(...) +#endif + +class XrHelper { + public: + XrHelper(XrInstance instance) : instance_(instance), lastError_(XR_SUCCESS) {} + virtual ~XrHelper() = default; + virtual bool SessionInit(XrSession session) = 0; + virtual bool SessionEnd() = 0; + virtual bool Update(XrSpace currentSpace, XrTime predictedDisplayTime) = 0; + XrResult GetLastError() const { + return lastError_; + } + + protected: + bool _oxr(XrResult xr, const char* func) { + if (XR_FAILED(xr)) { + char errorBuffer[XR_MAX_RESULT_STRING_SIZE]; + xrResultToString(instance_, xr, errorBuffer); + XRLOG("XR FAIL: `%s` -> `%s` 0x%08X", func, errorBuffer, (uint64_t)xr); + lastError_ = xr; + return false; + } + return true; + } + XrInstance GetInstance() const { + return instance_; + } + + private: + XrInstance instance_; + XrResult lastError_; +}; + +#if !defined(oxr) +#define oxr(func) _oxr(func, #func) +#endif diff --git a/Samples/XrSamples/XrHandDataSource/Src/xr_render_model_helper.h b/Samples/XrSamples/XrHandDataSource/Src/xr_render_model_helper.h new file mode 100755 index 0000000..752b320 --- /dev/null +++ b/Samples/XrSamples/XrHandDataSource/Src/xr_render_model_helper.h @@ -0,0 +1,172 @@ +/************************************************************************************************ +Filename : xr_render_model_helper.h +Content : Helper Inteface for openxr render_model extensions +Created : April 2021 +Authors : Federico Schliemann +Language : C++ +Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved. +************************************************************************************************/ +#pragma once + +#include "xr_helper.h" + +class XrRenderModelHelper : public XrHelper { + public: + explicit XrRenderModelHelper(XrInstance instance) : XrHelper(instance) { + /// Hook up extensions for device settings + oxr(xrGetInstanceProcAddr( + instance, + "xrEnumerateRenderModelPathsFB", + (PFN_xrVoidFunction*)(&xrEnumerateRenderModelPathsFB_))); + oxr(xrGetInstanceProcAddr( + instance, + "xrGetRenderModelPropertiesFB", + (PFN_xrVoidFunction*)(&xrGetRenderModelPropertiesFB_))); + oxr(xrGetInstanceProcAddr( + instance, "xrLoadRenderModelFB", (PFN_xrVoidFunction*)(&xrLoadRenderModelFB_))); + } + + ~XrRenderModelHelper() override { + xrEnumerateRenderModelPathsFB_ = nullptr; + xrGetRenderModelPropertiesFB_ = nullptr; + xrLoadRenderModelFB_ = nullptr; + } + + /// XrHelper Interface + virtual bool SessionInit(XrSession session) override { + isIntialized_ = false; + session_ = session; + return true; + } + virtual bool SessionEnd() override { + session_ = XR_NULL_HANDLE; + return true; + } + virtual bool Update(XrSpace currentSpace, XrTime predictedDisplayTime) override { + OneTimeInitialize(); + return true; + } + + static std::vector RequiredExtensionNames() { + return {XR_FB_RENDER_MODEL_EXTENSION_NAME}; + } + + public: + /// Own interface + const std::vector& GetPaths() const { + return paths_; + } + const std::vector& GetProperties() const { + return properties_; + } + + std::vector LoadRenderModel(const std::string &strToCheck) { + std::vector buffer; + XrInstance instance = GetInstance(); + + for (const auto& p : paths_) { + char buf[256]; + uint32_t bufCount = 0; + // OpenXR two call pattern. First call gets buffer size, second call gets the buffer + // data + oxr(xrPathToString(instance, p.path, bufCount, &bufCount, nullptr)); + oxr(xrPathToString(instance, p.path, bufCount, &bufCount, &buf[0])); + std::string pathString = buf; + if (pathString.rfind(strToCheck, 0) == 0) { + XrRenderModelPropertiesFB prop{XR_TYPE_RENDER_MODEL_PROPERTIES_FB}; + XrResult result = xrGetRenderModelPropertiesFB_(session_, p.path, &prop); + if (result == XR_SUCCESS) { + if (prop.modelKey != XR_NULL_RENDER_MODEL_KEY_FB) { + XrRenderModelLoadInfoFB loadInfo = {XR_TYPE_RENDER_MODEL_LOAD_INFO_FB}; + loadInfo.modelKey = prop.modelKey; + + XrRenderModelBufferFB rmb{XR_TYPE_RENDER_MODEL_BUFFER_FB}; + rmb.next = nullptr; + rmb.bufferCapacityInput = 0; + rmb.buffer = nullptr; + if (oxr(xrLoadRenderModelFB_(session_, &loadInfo, &rmb))) { + XRLOG( + "XrRenderModelHelper: loading modelKey %u size %u ", + prop.modelKey, + rmb.bufferCountOutput); + buffer.resize(rmb.bufferCountOutput); + rmb.buffer = (uint8_t*)buffer.data(); + rmb.bufferCapacityInput = rmb.bufferCountOutput; + if (!oxr(xrLoadRenderModelFB_(session_, &loadInfo, &rmb))) { + XRLOG( + "XrRenderModelHelper: FAILED to load modelKey %u on pass 2", + prop.modelKey); + buffer.resize(0); + } else { + XRLOG( + "XrRenderModelHelper: loaded modelKey %u buffer size is %u", + prop.modelKey, + buffer.size()); + return buffer; + } + } else { + XRLOG( + "XrRenderModelHelper: FAILED to load modelKey %u on pass 1", + prop.modelKey); + } + } + } else { + XRLOG( + "XrRenderModelHelper: FAILED to load prop for path '%s'", + pathString.c_str()); + } + } + } + + return buffer; + } + + private: + void OneTimeInitialize() { + if (isIntialized_) + return; + /// Enumerate available models + XrInstance instance = GetInstance(); + if (xrEnumerateRenderModelPathsFB_) { + /// query path count + uint32_t pathCount = 0; + oxr(xrEnumerateRenderModelPathsFB_(session_, pathCount, &pathCount, nullptr)); + if (pathCount > 0) { + XRLOG("XrRenderModelHelper: found %u models ", pathCount); + paths_.resize(pathCount, { XR_TYPE_RENDER_MODEL_PATH_INFO_FB }); + /// Fill in the path data + oxr(xrEnumerateRenderModelPathsFB_(session_, pathCount, &pathCount, &paths_[0])); + /// Print paths for debug purpose + for (const auto& p : paths_) { + char buf[256]; + uint32_t bufCount = 0; + oxr(xrPathToString(instance, p.path, bufCount, &bufCount, nullptr)); + oxr(xrPathToString(instance, p.path, bufCount, &bufCount, &buf[0])); + XRLOG("XrRenderModelHelper: path=%u `%s`", (uint32_t)p.path, &buf[0]); + } + /// Get properties + for (const auto& p : paths_) { + XrRenderModelPropertiesFB prop{XR_TYPE_RENDER_MODEL_PROPERTIES_FB}; + XrResult result = xrGetRenderModelPropertiesFB_(session_, p.path, &prop); + if (result == XR_SUCCESS) { + properties_.push_back(prop); + } + } + } + } + isIntialized_ = true; + } + + private: + /// Session cache + XrSession session_ = XR_NULL_HANDLE; + /// RenderModel - extension functions + PFN_xrEnumerateRenderModelPathsFB xrEnumerateRenderModelPathsFB_ = nullptr; + PFN_xrGetRenderModelPropertiesFB xrGetRenderModelPropertiesFB_ = nullptr; + PFN_xrLoadRenderModelFB xrLoadRenderModelFB_ = nullptr; + /// RenderModel - data buffers + std::vector paths_; + std::vector properties_; + /// Lifetime + bool isIntialized_ = false; +}; diff --git a/Samples/XrSamples/XrHandDataSource/assets/Skybox.gltf.ovrscene b/Samples/XrSamples/XrHandDataSource/assets/Skybox.gltf.ovrscene new file mode 100644 index 0000000..a098497 Binary files /dev/null and b/Samples/XrSamples/XrHandDataSource/assets/Skybox.gltf.ovrscene differ diff --git a/Samples/XrSamples/XrHandDataSource/assets/SmallRoom.gltf.ovrscene b/Samples/XrSamples/XrHandDataSource/assets/SmallRoom.gltf.ovrscene new file mode 100644 index 0000000..2183af3 Binary files /dev/null and b/Samples/XrSamples/XrHandDataSource/assets/SmallRoom.gltf.ovrscene differ diff --git a/Samples/XrSamples/XrHandDataSource/assets/assets.txt b/Samples/XrSamples/XrHandDataSource/assets/assets.txt new file mode 100644 index 0000000..2cc30f7 --- /dev/null +++ b/Samples/XrSamples/XrHandDataSource/assets/assets.txt @@ -0,0 +1 @@ +This file is a placeholder. diff --git a/Samples/XrSamples/XrHandDataSource/assets/panel.ktx b/Samples/XrSamples/XrHandDataSource/assets/panel.ktx new file mode 100644 index 0000000..deb13e8 Binary files /dev/null and b/Samples/XrSamples/XrHandDataSource/assets/panel.ktx differ diff --git a/Samples/XrSamples/XrHandDataSource/java/MainActivity.java b/Samples/XrSamples/XrHandDataSource/java/MainActivity.java new file mode 100644 index 0000000..d253cfd --- /dev/null +++ b/Samples/XrSamples/XrHandDataSource/java/MainActivity.java @@ -0,0 +1,28 @@ +// Copyright (c) Facebook Technologies, LLC and its affiliates. All Rights reserved. +package com.oculus.sdk.xrhanddatasource; + +/** + * When using NativeActivity, we currently need to handle loading of dependent shared libraries + * manually before a shared library that depends on them is loaded, since there is not currently a + * way to specify a shared library dependency for NativeActivity via the manifest meta-data. + * + *

The simplest method for doing so is to subclass NativeActivity with an empty activity that + * calls System.loadLibrary on the dependent libraries, which is unfortunate when the goal is to + * write a pure native C/C++ only Android activity. + * + *

A native-code only solution is to load the dependent libraries dynamically using dlopen(). + * However, there are a few considerations, see: + * https://groups.google.com/forum/#!msg/android-ndk/l2E2qh17Q6I/wj6s_6HSjaYJ + * + *

1. Only call dlopen() if you're sure it will succeed as the bionic dynamic linker will + * remember if dlopen failed and will not re-try a dlopen on the same lib a second time. + * + *

2. Must remember what libraries have already been loaded to avoid infinitely looping when + * libraries have circular dependencies. + */ +public class MainActivity extends android.app.NativeActivity { + static { + System.loadLibrary("openxr_loader"); + System.loadLibrary("xrhanddatasource"); + } +} diff --git a/Samples/XrSamples/XrHandDataSource/res/values/strings.xml b/Samples/XrSamples/XrHandDataSource/res/values/strings.xml new file mode 100644 index 0000000..f4029f3 --- /dev/null +++ b/Samples/XrSamples/XrHandDataSource/res/values/strings.xml @@ -0,0 +1,4 @@ + + + Xr Hand Data Source Sample + diff --git a/Samples/XrSamples/XrHandTrackingWideMotionMode/CMakeLists.txt b/Samples/XrSamples/XrHandTrackingWideMotionMode/CMakeLists.txt new file mode 100755 index 0000000..abce2af --- /dev/null +++ b/Samples/XrSamples/XrHandTrackingWideMotionMode/CMakeLists.txt @@ -0,0 +1,43 @@ +# Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved. + +project(xrhandtrackingwidemotionmode) + +if(NOT TARGET OpenXR::openxr_loader) + find_package(OpenXR REQUIRED) +endif() + +file(GLOB_RECURSE SRC_FILES + Src/*.c + Src/*.cpp +) + +if(ANDROID) + add_library(${PROJECT_NAME} MODULE ${SRC_FILES}) + target_include_directories(${PROJECT_NAME} PUBLIC ${ANDROID_NDK}/sources/android/native_app_glue) + target_link_libraries(${PROJECT_NAME} PRIVATE + android + EGL + GLESv3 + log + ktx + ) + set_target_properties(${PROJECT_NAME} PROPERTIES LINK_FLAGS "-u ANativeActivity_onCreate") +elseif(WIN32) + add_definitions(-D_USE_MATH_DEFINES) + add_executable(${PROJECT_NAME} ${SRC_FILES}) + add_custom_command(TARGET ${PROJECT_NAME} PRE_BUILD + COMMAND "${CMAKE_COMMAND}" -E copy_directory + "${CMAKE_CURRENT_LIST_DIR}/assets" + "$/assets" + VERBATIM) + + add_custom_command(TARGET ${PROJECT_NAME} PRE_BUILD + COMMAND "${CMAKE_COMMAND}" -E copy_directory + "${CMAKE_SOURCE_DIR}/SampleXrFramework/res/raw" + "$/font/res/raw" + VERBATIM) +endif() + +# Common across platforms +target_include_directories(${PROJECT_NAME} PRIVATE Src) +target_link_libraries(${PROJECT_NAME} PRIVATE samplexrframework) diff --git a/Samples/XrSamples/XrHandTrackingWideMotionMode/Projects/Android/AndroidManifest.xml b/Samples/XrSamples/XrHandTrackingWideMotionMode/Projects/Android/AndroidManifest.xml new file mode 100755 index 0000000..20cc853 --- /dev/null +++ b/Samples/XrSamples/XrHandTrackingWideMotionMode/Projects/Android/AndroidManifest.xml @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Samples/XrSamples/XrHandTrackingWideMotionMode/Projects/Android/build.bat b/Samples/XrSamples/XrHandTrackingWideMotionMode/Projects/Android/build.bat new file mode 100755 index 0000000..facf79f --- /dev/null +++ b/Samples/XrSamples/XrHandTrackingWideMotionMode/Projects/Android/build.bat @@ -0,0 +1,29 @@ +@rem Only edit the master copy of this file in SDK_ROOT/bin/scripts/build/perproject + +@setlocal enableextensions enabledelayedexpansion + +@if not exist "build.gradle" @echo Build script must be executed from project directory. & goto :Abort + +@set P=.. + +:TryAgain + +@rem @echo P = %P% + +@if exist "%P%\bin\scripts\build\build.py.bat" goto :Found + +@if exist "%P%\bin\scripts\build" @echo "Could not find build.py.bat" & goto :Abort + +@set P=%P%\.. + +@goto :TryAgain + +:Found + +@set P=%P%\bin\scripts\build +@call %P%\build.py.bat %1 %2 %3 %4 %5 +@goto :End + +:Abort + +:End diff --git a/Samples/XrSamples/XrHandTrackingWideMotionMode/Projects/Android/build.gradle b/Samples/XrSamples/XrHandTrackingWideMotionMode/Projects/Android/build.gradle new file mode 100755 index 0000000..52b7614 --- /dev/null +++ b/Samples/XrSamples/XrHandTrackingWideMotionMode/Projects/Android/build.gradle @@ -0,0 +1,78 @@ +// Top-level build file where you can add configuration options common to all sub-projects/modules. +buildscript { + repositories { + google() + mavenCentral() + } + dependencies { + classpath "com.android.tools.build:gradle:7.0.3" + } +} + +repositories { + google() + mavenCentral() +} + +apply plugin: 'com.android.application' + +android { + compileSdk 32 + + defaultConfig { + applicationId "com.oculus.sdk.xrhandtrackingwidemotionmode" + minSdk 26 + targetSdk 32 + versionCode 1 + versionName "1.0" + + // override app plugin abiFilters for 64-bit support + externalNativeBuild { + ndk { + abiFilters 'arm64-v8a' + } + ndkBuild { + abiFilters 'arm64-v8a' + } + cmake { + targets "xrhandtrackingwidemotionmode" + } + } + } + + sourceSets { + main { + manifest.srcFile 'AndroidManifest.xml' + java.srcDirs = ['../../java'] + assets.srcDirs = ['../../assets'] + res.srcDirs = ['../../res'] + } + } + + buildTypes { + debug { + debuggable true + } + + release { + debuggable false + } + } + + externalNativeBuild { + cmake { + path file('../../../../CMakeLists.txt') + } + } + + // Enable prefab support for the OpenXR AAR + buildFeatures { + prefab true + } +} + +dependencies { + // Package/application AndroidManifest.xml properties, plus headers and libraries + // exposed to CMake + implementation 'org.khronos.openxr:openxr_loader_for_android:1.1.36' +} diff --git a/Samples/XrSamples/XrHandTrackingWideMotionMode/Projects/Android/build.py b/Samples/XrSamples/XrHandTrackingWideMotionMode/Projects/Android/build.py new file mode 100755 index 0000000..d4b6e58 --- /dev/null +++ b/Samples/XrSamples/XrHandTrackingWideMotionMode/Projects/Android/build.py @@ -0,0 +1,35 @@ +#!/usr/bin/python +# (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +# This first bit of code is common bootstrapping code +# to determine the SDK root, and to set up the import +# path for additional python code. + +# begin bootstrap +import os +import sys + + +def init(): + root = os.path.realpath(os.path.dirname(os.path.realpath(__file__))) + os.chdir(root) # make sure we are always executing from the project directory + while os.path.isdir(os.path.join(root, "bin/scripts/build")) == False: + root = os.path.realpath(os.path.join(root, "..")) + if ( + len(root) <= 5 + ): # Should catch both Posix and Windows root directories (e.g. '/' and 'C:\') + print("Unable to find SDK root. Exiting.") + sys.exit(1) + root = os.path.abspath(root) + os.environ["OCULUS_SDK_PATH"] = root + sys.path.append(root + "/bin/scripts/build") + + +init() +import ovrbuild + +ovrbuild.init() +# end bootstrap + + +ovrbuild.build() diff --git a/Samples/XrSamples/XrHandTrackingWideMotionMode/Projects/Android/gradle.properties b/Samples/XrSamples/XrHandTrackingWideMotionMode/Projects/Android/gradle.properties new file mode 100644 index 0000000..3e927b1 --- /dev/null +++ b/Samples/XrSamples/XrHandTrackingWideMotionMode/Projects/Android/gradle.properties @@ -0,0 +1,21 @@ +# Project-wide Gradle settings. +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. More details, visit +# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects +# org.gradle.parallel=true +# AndroidX package structure to make it clearer which packages are bundled with the +# Android operating system, and which are packaged with your app's APK +# https://developer.android.com/topic/libraries/support-library/androidx-rn +android.useAndroidX=true +# Enables namespacing of each library's R class so that its R class includes only the +# resources declared in the library itself and none from the library's dependencies, +# thereby reducing the size of the R class for that library +android.nonTransitiveRClass=true \ No newline at end of file diff --git a/Samples/XrSamples/XrHandTrackingWideMotionMode/Projects/Android/gradle/wrapper/gradle-wrapper.jar b/Samples/XrSamples/XrHandTrackingWideMotionMode/Projects/Android/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..7454180 Binary files /dev/null and b/Samples/XrSamples/XrHandTrackingWideMotionMode/Projects/Android/gradle/wrapper/gradle-wrapper.jar differ diff --git a/Samples/XrSamples/XrHandTrackingWideMotionMode/Projects/Android/gradle/wrapper/gradle-wrapper.properties b/Samples/XrSamples/XrHandTrackingWideMotionMode/Projects/Android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..ffed3a2 --- /dev/null +++ b/Samples/XrSamples/XrHandTrackingWideMotionMode/Projects/Android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/Samples/XrSamples/XrHandTrackingWideMotionMode/Projects/Android/gradlew b/Samples/XrSamples/XrHandTrackingWideMotionMode/Projects/Android/gradlew new file mode 100755 index 0000000..a69d9cb --- /dev/null +++ b/Samples/XrSamples/XrHandTrackingWideMotionMode/Projects/Android/gradlew @@ -0,0 +1,240 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# 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 +# +# https://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. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit + +APP_NAME="Gradle" +APP_BASE_NAME=${0##*/} + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/Samples/XrSamples/XrHandTrackingWideMotionMode/Projects/Android/gradlew.bat b/Samples/XrSamples/XrHandTrackingWideMotionMode/Projects/Android/gradlew.bat new file mode 100755 index 0000000..f127cfd --- /dev/null +++ b/Samples/XrSamples/XrHandTrackingWideMotionMode/Projects/Android/gradlew.bat @@ -0,0 +1,91 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/Samples/XrSamples/XrHandTrackingWideMotionMode/Projects/Android/settings.gradle b/Samples/XrSamples/XrHandTrackingWideMotionMode/Projects/Android/settings.gradle new file mode 100755 index 0000000..4a56918 --- /dev/null +++ b/Samples/XrSamples/XrHandTrackingWideMotionMode/Projects/Android/settings.gradle @@ -0,0 +1 @@ +rootProject.name = "XrHandTrackingWideMotionMode" diff --git a/Samples/XrSamples/XrHandTrackingWideMotionMode/README.md b/Samples/XrSamples/XrHandTrackingWideMotionMode/README.md new file mode 100644 index 0000000..cb4fb80 --- /dev/null +++ b/Samples/XrSamples/XrHandTrackingWideMotionMode/README.md @@ -0,0 +1,9 @@ +# OpenXR Hands Tracking Wide Motion Mode Sample + +## Overview +The `XR_META_hand_tracking_wide_motion_model` augments the `XR_EXT_hand_tracking` extension. + +This extension allows the application to request that the hand tracking data provided via the runtime go beyond normal performance considerations in order to provide poses outside of the platform’s normal tracking area. This may require significantly more processing power then normally used by the runtime and may not result in pose changes if the system is not capable of leveraging additional resources. + +## The Sample +Wide Motion Mode is a feature for hand tracking that can approximately position hands based on what the cameras can see of your arms if the hands themselves are hidden. The user should be able to obscure their hands (e.g. under a desk) and the device will approximately position the virtual hands in the right place. Note: It won’t be able to detect gestures. diff --git a/Samples/XrSamples/XrHandTrackingWideMotionMode/Src/main.cpp b/Samples/XrSamples/XrHandTrackingWideMotionMode/Src/main.cpp new file mode 100755 index 0000000..032d7bf --- /dev/null +++ b/Samples/XrSamples/XrHandTrackingWideMotionMode/Src/main.cpp @@ -0,0 +1,358 @@ +/******************************************************************************* + +Filename : Main.cpp +Content : Simple test app to test openxr hand tracking wide motion mode api +Language : C++ +Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved. + +*******************************************************************************/ + +#include +#include +#include +#include +#include + +#include "XrApp.h" + +#include "Input/HandRenderer.h" +#include "Input/AxisRenderer.h" +#include "Input/TinyUI.h" +#include "Render/GeometryRenderer.h" +#include "Render/SimpleBeamRenderer.h" + +/// add logging +#define XRLOG ALOG + +#include "xr_hand_helper.h" + +class XrHandTrackingWideMotionModeApp : public OVRFW::XrApp { + static constexpr std::string_view kSampleExplanation = + "Wide Motion Mode: \n" + " \n" + "This sample allows you to compare between normal hands \n" + "and Wide Motion Mode hands. It renders two sets of \n" + "tracked hands: \n" + "* Red are the default hand tracking hands. \n" + "* Green are the wide motion mode hands. \n" + "Click the buttons to toggle rendering of the hand types. \n" + " \n" + "The easiest test case to see the difference is to put your hands \n" + "under a desk in front of you. \n" + " \n" + "Red will disappear, while green remains. \n"; + + public: + XrHandTrackingWideMotionModeApp() : OVRFW::XrApp() { + BackgroundColor = OVR::Vector4f(0.60f, 0.95f, 0.4f, 1.0f); + } + + // Returns a list of OpenXr extensions needed for this app + virtual std::vector GetExtensions() override { + std::vector extensions = XrApp::GetExtensions(); + /// add hand extensions + for (const auto& handExtension : XrHandHelper::RequiredExtensionNames()) { + extensions.push_back(handExtension); + } + + /// add composition alpha blend + extensions.push_back(XR_FB_COMPOSITION_LAYER_ALPHA_BLEND_EXTENSION_NAME); + + /// log all extensions + ALOG("XrHandTrackingWideMotionModeApp requesting extensions:"); + for (const auto& e : extensions) { + ALOG(" --> %s", e); + } + + return extensions; + } + + // Must return true if the application initializes successfully. + virtual bool AppInit(const xrJava* context) override { + if (false == ui_.Init(context, GetFileSys())) { + ALOG("TinyUI::Init FAILED."); + return false; + } + + /// hand tracking + hand_Base_L_ = std::make_unique(GetInstance(), true, XrHandHelper::HandType::HANDTYPE_BASE); + OXR(hand_Base_L_->GetLastError()); + hand_Base_R_ = std::make_unique( + GetInstance(), false, XrHandHelper::HandType::HANDTYPE_BASE); + OXR(hand_Base_R_->GetLastError()); + + hand_WMM_L_ = std::make_unique( + GetInstance(), true, XrHandHelper::HandType::HANDTYPE_WMM); + OXR(hand_WMM_L_->GetLastError()); + hand_WMM_R_ = std::make_unique( + GetInstance(), false, XrHandHelper::HandType::HANDTYPE_WMM); + OXR(hand_WMM_R_->GetLastError()); + + /// Build UI + CreateSampleDescriptionPanel(); + + bigText_ = ui_.AddLabel("OpenXR Wide Motion Mode", {0.1f, -0.5f, -2.0f}, {1300.0f, 100.0f}); + bigText_->SetSurfaceColor(0 ,{0.0f, 0.0f, 1.0f, 1.0f}); + + renderLabelBaseHandsButton_ = ui_.AddLabel("X", {-0.51f, 0.25f, -2.01f}, {550.0f, 110.0f}); + renderLabelBaseHandsButton_->SetSurfaceColor(0, {1.0f, 0.0f, 0.0f, 1.0f}); + + renderBaseHandsButton_ = + ui_.AddButton("Stop Rendering Base Hands", {-0.5f, 0.25f, -2.0f}, {500.0f, 100.0f}, [=]() { + renderBaseHands_ = !renderBaseHands_; + if (renderBaseHands_) { + renderBaseHandsButton_->SetText("Stop Rendering Base Hands"); + renderLabelBaseHandsButton_->SetSurfaceColor(0, {1.0f, 0.0f, 0.0f, 1.0f}); + renderLabelBaseHandsButton_->SetText("X"); + } else { + renderBaseHandsButton_->SetText("Render Base Hands"); + renderLabelBaseHandsButton_->SetSurfaceColor(0, {0.75f, 0.0f, 0.0f, 0.25f}); + renderLabelBaseHandsButton_->SetText(""); + } + }); + + renderLabelWideMotionModeHandsButton_ = ui_.AddLabel("X", {-0.51f, 0.0, -2.01f}, {550.0f, 110.0f}); + renderLabelWideMotionModeHandsButton_->SetSurfaceColor(0, {0.0f, 1.0f, 0.0f, 1.0f}); + + renderWideMotionModeHandsButton_ = ui_.AddButton( + "Stop Rendering Wide Motion Mode Hands", {-0.5f, 0.0f, -2.0f}, {500.0f, 100.0f}, [=]() { + renderWideMotionModeHands_ = !renderWideMotionModeHands_; + if (renderWideMotionModeHands_) { + renderWideMotionModeHandsButton_->SetText("Stop Rendering Wide Motion Mode Hands"); + renderLabelWideMotionModeHandsButton_->SetSurfaceColor(0, {0.0f, 1.0f, 0.0f, 1.0f}); + renderLabelWideMotionModeHandsButton_->SetText("X"); + } else { + renderWideMotionModeHandsButton_->SetText("Render Wide Motion Mode Hands"); + renderLabelWideMotionModeHandsButton_->SetSurfaceColor(0, {0.0f, 0.75f, 0.0f, 0.25f}); + renderLabelWideMotionModeHandsButton_->SetText(""); + } + }); + + return true; + } + + void CreateSampleDescriptionPanel() { + // Panel to provide sample description to the user for context + auto descriptionLabel = ui_.AddLabel( + static_cast(kSampleExplanation), {1.15f, 0.25f, -1.8f}, {750.0f, 400.0f}); + + // Align and size the description text for readability + OVRFW::VRMenuFontParms fontParams{}; + fontParams.Scale = 0.5f; + fontParams.AlignHoriz = OVRFW::HORIZONTAL_LEFT; + descriptionLabel->SetFontParms(fontParams); + descriptionLabel->SetTextLocalPosition({-0.65f, 0, 0}); + + // Tilt the description billboard 45 degrees towards the user + descriptionLabel->SetLocalRotation( + OVR::Quat::FromRotationVector({0, OVR::DegreeToRad(-30.0f), 0})); + descriptionLabel->SetSurfaceColor(0 ,{0.0f, 0.0f, 1.0f, 1.0f}); + } + + virtual void AppShutdown(const xrJava* context) override { + hand_Base_L_ = nullptr; + hand_Base_R_ = nullptr; + hand_WMM_L_ = nullptr; + hand_WMM_R_ = nullptr; + + OVRFW::XrApp::AppShutdown(context); + ui_.Shutdown(); + } + + virtual bool SessionInit() override { + /// Use LocalSpace instead of Stage Space. + CurrentSpace = LocalSpace; + /// Disable scene navigation + GetScene().SetFootPos({0.0f, 0.0f, 0.0f}); + this->FreeMove = false; + beamRenderer_.Init(GetFileSys(), nullptr, OVR::Vector4f(1.0f), 1.0f); + + /// hands + hand_Base_L_->SessionInit(GetSession()); + hand_Base_R_->SessionInit(GetSession()); + hand_WMM_L_->SessionInit(GetSession()); + hand_WMM_R_->SessionInit(GetSession()); + + /// rendering; + handRenderer_Base_L_.Init(&hand_Base_L_->Mesh(), hand_Base_L_->IsLeft()); + handRenderer_Base_R_.Init(&hand_Base_R_->Mesh(), hand_Base_R_->IsLeft()); + handRenderer_WMM_L_.Init(&hand_WMM_L_->Mesh(), hand_WMM_L_->IsLeft()); + handRenderer_WMM_R_.Init(&hand_WMM_R_->Mesh(), hand_WMM_R_->IsLeft()); + axisRendererL_.Init(); + axisRendererR_.Init(); + + handRenderer_Base_L_.GlowColor = OVR::Vector3f(1.0f, 0.0f, 0.0f); + handRenderer_Base_L_.SpecularLightColor = OVR::Vector3f(1.0f, 0.15f, 0.15f) * 0.25f; + handRenderer_Base_L_.AmbientLightColor = OVR::Vector3f(1.0f, 0.15f, 0.15f) * 1.5f; + handRenderer_Base_R_.GlowColor = OVR::Vector3f(1.0f, 0.0f, 0.0f); + handRenderer_Base_R_.SpecularLightColor = OVR::Vector3f(1.0f, 0.15f, 0.15f) * 0.25f; + handRenderer_Base_R_.AmbientLightColor = OVR::Vector3f(1.0f, 0.15f, 0.15f) * 1.5f; + + handRenderer_WMM_L_.GlowColor = OVR::Vector3f(0.0f, 1.0f, 0.0f); + handRenderer_WMM_L_.SpecularLightColor = OVR::Vector3f(0.15f, 1.0f, 0.15f) * 0.25f; + handRenderer_WMM_L_.AmbientLightColor = OVR::Vector3f(0.15f, 1.0f, 0.15f) * 1.5f; + handRenderer_WMM_R_.GlowColor = OVR::Vector3f(0.0f, 1.0f, 0.0f); + handRenderer_WMM_R_.SpecularLightColor = OVR::Vector3f(0.15f, 1.0f, 0.15f) * 0.25f; + handRenderer_WMM_R_.AmbientLightColor = OVR::Vector3f(0.15f, 1.0f, 0.15f) * 1.5f; + + return true; + } + + virtual void SessionEnd() override { + /// hands + hand_Base_L_->SessionEnd(); + hand_Base_R_->SessionEnd(); + hand_WMM_L_->SessionEnd(); + hand_WMM_R_->SessionEnd(); + + beamRenderer_.Shutdown(); + handRenderer_Base_L_.Shutdown(); + handRenderer_Base_R_.Shutdown(); + handRenderer_WMM_L_.Shutdown(); + handRenderer_WMM_R_.Shutdown(); + axisRendererL_.Shutdown(); + axisRendererR_.Shutdown(); + } + + // Update state + virtual void Update(const OVRFW::ovrApplFrameIn& in) override { + XrSpace currentSpace = GetCurrentSpace(); + XrTime predictedDisplayTime = ToXrTime(in.PredictedDisplayTime); + + /// hands + hand_Base_L_->Update(currentSpace, predictedDisplayTime); + hand_Base_R_->Update(currentSpace, predictedDisplayTime); + hand_WMM_L_->Update(currentSpace, predictedDisplayTime); + hand_WMM_R_->Update(currentSpace, predictedDisplayTime); + + if (hand_Base_L_->AreLocationsActive()) { + handRenderer_Base_L_.Update(hand_Base_L_->Joints(), hand_Base_L_->RenderScale()); + } + + if (hand_Base_R_->AreLocationsActive()) { + handRenderer_Base_R_.Update(hand_Base_R_->Joints(), hand_Base_R_->RenderScale()); + } + + if (hand_WMM_L_->AreLocationsActive()) { + handRenderer_WMM_L_.Update(hand_WMM_L_->Joints(), hand_WMM_L_->RenderScale()); + + std::vector handJoints; + for (int i = 0; i < XR_HAND_JOINT_COUNT_EXT; ++i) { + if ((hand_WMM_L_->Joints()[i].locationFlags & + XR_SPACE_LOCATION_POSITION_TRACKED_BIT) == 0) { + const auto p = FromXrPosef(hand_WMM_L_->Joints()[i].pose); + handJoints.push_back(p); + } + } + axisRendererL_.Update(handJoints); + } + + if (hand_WMM_R_->AreLocationsActive()) { + handRenderer_WMM_R_.Update(hand_WMM_R_->Joints(), hand_WMM_R_->RenderScale()); + + std::vector handJoints; + for (int i = 0; i < XR_HAND_JOINT_COUNT_EXT; ++i) { + if ((hand_WMM_R_->Joints()[i].locationFlags & + XR_SPACE_LOCATION_POSITION_TRACKED_BIT) == 0) { + const auto p = FromXrPosef(hand_WMM_R_->Joints()[i].pose); + handJoints.push_back(p); + } + } + axisRendererR_.Update(handJoints); + } + + /// UI + ui_.HitTestDevices().clear(); + if (hand_Base_R_->AreLocationsActive()) { + ui_.AddHitTestRay(FromXrPosef(hand_Base_R_->AimPose()), hand_Base_R_->IndexPinching()); + } else if (in.RightRemoteTracked) { + const bool didPinch = in.RightRemoteIndexTrigger > 0.25f; + ui_.AddHitTestRay(in.RightRemotePointPose, didPinch); + } + + if (hand_Base_L_->AreLocationsActive()) { + ui_.AddHitTestRay(FromXrPosef(hand_Base_L_->AimPose()), hand_Base_L_->IndexPinching()); + } else if (in.LeftRemoteTracked) { + const bool didPinch = in.LeftRemoteIndexTrigger > 0.25f; + ui_.AddHitTestRay(in.LeftRemotePointPose, didPinch); + } + + ui_.Update(in); + beamRenderer_.Update(in, ui_.HitTestDevices()); + } + + // Render eye buffers while running + virtual void Render(const OVRFW::ovrApplFrameIn& in, OVRFW::ovrRendererOutput& out) override { + /// Render UI + ui_.Render(in, out); + + + /// Render beams + beamRenderer_.Render(in, out); + + + if (renderBaseHands_) { + if (hand_Base_L_->AreLocationsActive() && hand_Base_L_->IsPositionValid()) { + /// Render solid Hands + handRenderer_Base_L_.Solidity = 1.0f; + handRenderer_Base_L_.Render(out.Surfaces); + } + + if (hand_Base_R_->AreLocationsActive() && hand_Base_R_->IsPositionValid()) { + /// Render solid Hands + handRenderer_Base_R_.Solidity = 1.0f; + handRenderer_Base_R_.Render(out.Surfaces); + } + } + + if (renderWideMotionModeHands_) { + if (hand_WMM_L_->AreLocationsActive() && hand_WMM_L_->IsPositionValid()) { + /// Render solid Hands + handRenderer_WMM_L_.Solidity = 1.0f; + handRenderer_WMM_L_.Render(out.Surfaces); + axisRendererL_.Render(OVR::Matrix4f(), in, out); + } + + if (hand_WMM_R_->AreLocationsActive() && hand_WMM_R_->IsPositionValid()) { + /// Render solid Hands + handRenderer_WMM_R_.Solidity = 1.0f; + handRenderer_WMM_R_.Render(out.Surfaces); + axisRendererR_.Render(OVR::Matrix4f(), in, out); + } + } + } + + public: + private: + + OVRFW::TinyUI ui_; + OVRFW::SimpleBeamRenderer beamRenderer_; + std::vector beams_; + + /// hands - xr interface + std::unique_ptr hand_Base_L_; + std::unique_ptr hand_Base_R_; + std::unique_ptr hand_WMM_L_; + std::unique_ptr hand_WMM_R_; + /// hands - rendering + OVRFW::HandRenderer handRenderer_Base_L_; + OVRFW::HandRenderer handRenderer_Base_R_; + OVRFW::HandRenderer handRenderer_WMM_L_; + OVRFW::HandRenderer handRenderer_WMM_R_; + OVRFW::ovrAxisRenderer axisRendererL_; + OVRFW::ovrAxisRenderer axisRendererR_; + + // gui and state + bool renderBaseHands_ = true; + bool renderWideMotionModeHands_ = true; + + OVRFW::VRMenuObject* renderBaseHandsButton_ = nullptr; + OVRFW::VRMenuObject* renderWideMotionModeHandsButton_ = nullptr; + OVRFW::VRMenuObject* renderLabelBaseHandsButton_ = nullptr; + OVRFW::VRMenuObject* renderLabelWideMotionModeHandsButton_ = nullptr; + + // info text; + OVRFW::VRMenuObject* bigText_ = nullptr; +}; + +ENTRY_POINT(XrHandTrackingWideMotionModeApp) diff --git a/Samples/XrSamples/XrHandTrackingWideMotionMode/Src/xr_hand_helper.h b/Samples/XrSamples/XrHandTrackingWideMotionMode/Src/xr_hand_helper.h new file mode 100755 index 0000000..51f6382 --- /dev/null +++ b/Samples/XrSamples/XrHandTrackingWideMotionMode/Src/xr_hand_helper.h @@ -0,0 +1,220 @@ +/************************************************************************************************ +Filename : xr_hand_helper.h +Content : Helper Inteface for openxr hand extensions +Created : April 2021 +Authors : Federico Schliemann +Language : C++ +Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved. +************************************************************************************************/ +#pragma once + +#include +#include "xr_helper.h" + +#include + +class XrHandHelper : public XrHelper { + public: + enum HandType { HANDTYPE_BASE, HANDTYPE_WMM }; + + XrHandHelper(XrInstance instance, bool isLeft, HandType handType) + : XrHelper(instance), isLeft_(isLeft), handType_(handType) { + /// Hook up extensions for hand tracking + oxr(xrGetInstanceProcAddr( + instance, "xrCreateHandTrackerEXT", (PFN_xrVoidFunction*)(&xrCreateHandTrackerEXT_))); + oxr(xrGetInstanceProcAddr( + instance, "xrDestroyHandTrackerEXT", (PFN_xrVoidFunction*)(&xrDestroyHandTrackerEXT_))); + oxr(xrGetInstanceProcAddr( + instance, "xrLocateHandJointsEXT", (PFN_xrVoidFunction*)(&xrLocateHandJointsEXT_))); + /// Hook up extensions for hand rendering + oxr(xrGetInstanceProcAddr( + instance, "xrGetHandMeshFB", (PFN_xrVoidFunction*)(&xrGetHandMeshFB_))); + } + + ~XrHandHelper() override { + xrCreateHandTrackerEXT_ = nullptr; + xrDestroyHandTrackerEXT_ = nullptr; + xrLocateHandJointsEXT_ = nullptr; + xrGetHandMeshFB_ = nullptr; + } + + /// XrHelper Interface + virtual bool SessionInit(XrSession session) override { + if (xrCreateHandTrackerEXT_) { + XrHandTrackerCreateInfoEXT createInfo{XR_TYPE_HAND_TRACKER_CREATE_INFO_EXT}; + createInfo.handJointSet = XR_HAND_JOINT_SET_DEFAULT_EXT; + createInfo.hand = isLeft_ ? XR_HAND_LEFT_EXT : XR_HAND_RIGHT_EXT; + + XrHandTrackingWideMotionModeInfoMETA wideMotionModeInfo{XR_TYPE_HAND_TRACKING_WIDE_MOTION_MODE_INFO_META}; + + if (handType_ == HANDTYPE_WMM) { + wideMotionModeInfo.requestedWideMotionMode = XR_HAND_TRACKING_WIDE_MOTION_MODE_HIGH_FIDELITY_BODY_TRACKING_META; + createInfo.next = &wideMotionModeInfo; + } + + if (!oxr(xrCreateHandTrackerEXT_(session, &createInfo, &handTracker_))) { + return false; + } + + if (xrGetHandMeshFB_) { + mesh_.type = XR_TYPE_HAND_TRACKING_MESH_FB; + mesh_.next = nullptr; + mesh_.jointCapacityInput = 0; + mesh_.jointCountOutput = 0; + mesh_.vertexCapacityInput = 0; + mesh_.vertexCountOutput = 0; + mesh_.indexCapacityInput = 0; + mesh_.indexCountOutput = 0; + if (!oxr(xrGetHandMeshFB_(handTracker_, &mesh_))) { + return false; + } + /// update sizes + mesh_.jointCapacityInput = mesh_.jointCountOutput; + mesh_.vertexCapacityInput = mesh_.vertexCountOutput; + mesh_.indexCapacityInput = mesh_.indexCountOutput; + vertexPositions_.resize(mesh_.vertexCapacityInput); + vertexNormals_.resize(mesh_.vertexCapacityInput); + vertexUVs_.resize(mesh_.vertexCapacityInput); + vertexBlendIndices_.resize(mesh_.vertexCapacityInput); + vertexBlendWeights_.resize(mesh_.vertexCapacityInput); + indices_.resize(mesh_.indexCapacityInput); + + /// skeleton + mesh_.jointBindPoses = jointBindPoses_; + mesh_.jointParents = jointParents_; + mesh_.jointRadii = jointRadii_; + /// mesh + mesh_.vertexPositions = vertexPositions_.data(); + mesh_.vertexNormals = vertexNormals_.data(); + mesh_.vertexUVs = vertexUVs_.data(); + mesh_.vertexBlendIndices = vertexBlendIndices_.data(); + mesh_.vertexBlendWeights = vertexBlendWeights_.data(); + mesh_.indices = indices_.data(); + /// get mesh + if (!oxr(xrGetHandMeshFB_(handTracker_, &mesh_))) { + return false; + } + } + return true; + } + return false; + } + + virtual bool SessionEnd() override { + if (xrDestroyHandTrackerEXT_) { + return oxr(xrDestroyHandTrackerEXT_(handTracker_)); + } + return false; + } + + virtual bool Update(XrSpace currentSpace, XrTime predictedDisplayTime) override { + if (xrLocateHandJointsEXT_) { + /// aim + aimState_.next = nullptr; + /// scale + scale_.next = &aimState_; + scale_.sensorOutput = 1.0f; + scale_.currentOutput = 1.0f; + scale_.overrideHandScale = XR_TRUE; + scale_.overrideValueInput = 1.00f; + /// locations + locations_.next = &scale_; + locations_.jointCount = XR_HAND_JOINT_COUNT_EXT; + locations_.jointLocations = jointLocations_; + XrHandJointsLocateInfoEXT locateInfo{XR_TYPE_HAND_JOINTS_LOCATE_INFO_EXT}; + locateInfo.baseSpace = currentSpace; + locateInfo.time = predictedDisplayTime; + + if (oxr(xrLocateHandJointsEXT_(handTracker_, &locateInfo, &locations_))) { + return true; + } else { + return false; + } + } + return false; + } + + static std::vector RequiredExtensionNames() { + return { + XR_EXT_HAND_TRACKING_EXTENSION_NAME, + XR_FB_HAND_TRACKING_MESH_EXTENSION_NAME, + XR_FB_HAND_TRACKING_AIM_EXTENSION_NAME, + XR_META_HAND_TRACKING_WIDE_MOTION_MODE_EXTENSION_NAME + }; + } + + public: + /// Own interface + bool IsLeft() const { + return isLeft_; + } + const XrHandTrackingMeshFB& Mesh() const { + return mesh_; + } + const XrHandTrackingScaleFB& Scale() const { + return scale_; + } + const XrPosef* JointBindPoses() const { + return jointBindPoses_; + } + const XrHandJointEXT* JointParents() const { + return jointParents_; + } + const XrHandJointLocationEXT* Joints() const { + return jointLocations_; + } + float RenderScale() const { + return scale_.sensorOutput; + } + bool IsTracking() const { + return (handTracker_ != XR_NULL_HANDLE); + } + bool AreLocationsActive() const { + return IsTracking() && (locations_.isActive); + } + bool IsPositionValid() const { + return jointLocations_[XR_HAND_JOINT_PALM_EXT].locationFlags & + XR_SPACE_LOCATION_POSITION_VALID_BIT; + } + const XrPosef& AimPose() const { + return aimState_.aimPose; + } + bool IndexPinching() const { + return (aimState_.status & XR_HAND_TRACKING_AIM_INDEX_PINCHING_BIT_FB) != 0; + } + const XrHandTrackingAimStateFB& AimState() const { + return aimState_; + } + + private: + bool isLeft_; + HandType handType_; + /// Hands - extension functions + PFN_xrCreateHandTrackerEXT xrCreateHandTrackerEXT_ = nullptr; + PFN_xrDestroyHandTrackerEXT xrDestroyHandTrackerEXT_ = nullptr; + PFN_xrLocateHandJointsEXT xrLocateHandJointsEXT_ = nullptr; + /// Hands - FB mesh rendering extensions + PFN_xrGetHandMeshFB xrGetHandMeshFB_ = nullptr; + /// Hands - tracker handles + XrHandTrackerEXT handTracker_ = XR_NULL_HANDLE; + /// Hands - data buffers + XrHandJointLocationEXT jointLocations_[XR_HAND_JOINT_COUNT_EXT]; + XrPosef jointBindPoses_[XR_HAND_JOINT_COUNT_EXT]; + XrHandJointEXT jointParents_[XR_HAND_JOINT_COUNT_EXT]; + float jointRadii_[XR_HAND_JOINT_COUNT_EXT]; + /// mesh storage + XrHandTrackingMeshFB mesh_{XR_TYPE_HAND_TRACKING_MESH_FB}; + std::vector vertexPositions_; + std::vector vertexNormals_; + std::vector vertexUVs_; + std::vector vertexBlendIndices_; + std::vector vertexBlendWeights_; + std::vector indices_; + /// extension nodes + /// scale + XrHandTrackingScaleFB scale_{XR_TYPE_HAND_TRACKING_SCALE_FB}; + /// aim + XrHandTrackingAimStateFB aimState_{XR_TYPE_HAND_TRACKING_AIM_STATE_FB}; + /// location + XrHandJointLocationsEXT locations_{XR_TYPE_HAND_JOINT_LOCATIONS_EXT}; +}; diff --git a/Samples/XrSamples/XrHandTrackingWideMotionMode/Src/xr_helper.h b/Samples/XrSamples/XrHandTrackingWideMotionMode/Src/xr_helper.h new file mode 100755 index 0000000..58556ac --- /dev/null +++ b/Samples/XrSamples/XrHandTrackingWideMotionMode/Src/xr_helper.h @@ -0,0 +1,67 @@ +/************************************************************************************************ +Filename : xr_helper.h +Content : Base interface to wrap openxr extension functionality +Created : April 2021 +Authors : Federico Schliemann +Language : C++ +Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved. +************************************************************************************************/ +#pragma once + +#if defined(ANDROID) +#define XR_USE_GRAPHICS_API_OPENGL_ES 1 +#define XR_USE_PLATFORM_ANDROID 1 +#else +#include "unknwn.h" +#define XR_USE_GRAPHICS_API_OPENGL 1 +#define XR_USE_PLATFORM_WIN32 1 +#endif + +#include +#include + +#include +#include +#include +#include +#include +#include + +#if !defined(XRLOG) +#define XRLOG(...) +#endif + +class XrHelper { + public: + XrHelper(XrInstance instance) : instance_(instance), lastError_(XR_SUCCESS) {} + virtual ~XrHelper() = default; + virtual bool SessionInit(XrSession session) = 0; + virtual bool SessionEnd() = 0; + virtual bool Update(XrSpace currentSpace, XrTime predictedDisplayTime) = 0; + XrResult GetLastError() const { + return lastError_; + } + + protected: + bool _oxr(XrResult xr, const char* func) { + if (XR_FAILED(xr)) { + char errorBuffer[XR_MAX_RESULT_STRING_SIZE]; + xrResultToString(instance_, xr, errorBuffer); + XRLOG("XR FAIL: `%s` -> `%s` 0x%08X", func, errorBuffer, (uint64_t)xr); + lastError_ = xr; + return false; + } + return true; + } + XrInstance GetInstance() const { + return instance_; + } + + private: + XrInstance instance_; + XrResult lastError_; +}; + +#if !defined(oxr) +#define oxr(func) _oxr(func, #func) +#endif diff --git a/Samples/XrSamples/XrHandTrackingWideMotionMode/assets/assets.txt b/Samples/XrSamples/XrHandTrackingWideMotionMode/assets/assets.txt new file mode 100644 index 0000000..2cc30f7 --- /dev/null +++ b/Samples/XrSamples/XrHandTrackingWideMotionMode/assets/assets.txt @@ -0,0 +1 @@ +This file is a placeholder. diff --git a/Samples/XrSamples/XrHandTrackingWideMotionMode/assets/panel.ktx b/Samples/XrSamples/XrHandTrackingWideMotionMode/assets/panel.ktx new file mode 100644 index 0000000..deb13e8 Binary files /dev/null and b/Samples/XrSamples/XrHandTrackingWideMotionMode/assets/panel.ktx differ diff --git a/Samples/XrSamples/XrHandTrackingWideMotionMode/java/MainActivity.java b/Samples/XrSamples/XrHandTrackingWideMotionMode/java/MainActivity.java new file mode 100644 index 0000000..e607e38 --- /dev/null +++ b/Samples/XrSamples/XrHandTrackingWideMotionMode/java/MainActivity.java @@ -0,0 +1,28 @@ +// Copyright (c) Facebook Technologies, LLC and its affiliates. All Rights reserved. +package com.oculus.sdk.xrhandtrackingwidemotionmode; + +/** + * When using NativeActivity, we currently need to handle loading of dependent shared libraries + * manually before a shared library that depends on them is loaded, since there is not currently a + * way to specify a shared library dependency for NativeActivity via the manifest meta-data. + * + *

The simplest method for doing so is to subclass NativeActivity with an empty activity that + * calls System.loadLibrary on the dependent libraries, which is unfortunate when the goal is to + * write a pure native C/C++ only Android activity. + * + *

A native-code only solution is to load the dependent libraries dynamically using dlopen(). + * However, there are a few considerations, see: + * https://groups.google.com/forum/#!msg/android-ndk/l2E2qh17Q6I/wj6s_6HSjaYJ + * + *

1. Only call dlopen() if you're sure it will succeed as the bionic dynamic linker will + * remember if dlopen failed and will not re-try a dlopen on the same lib a second time. + * + *

2. Must remember what libraries have already been loaded to avoid infinitely looping when + * libraries have circular dependencies. + */ +public class MainActivity extends android.app.NativeActivity { + static { + System.loadLibrary("openxr_loader"); + System.loadLibrary("xrhandtrackingwidemotionmode"); + } +} diff --git a/Samples/XrSamples/XrHandTrackingWideMotionMode/res/values/strings.xml b/Samples/XrSamples/XrHandTrackingWideMotionMode/res/values/strings.xml new file mode 100644 index 0000000..2ee06ce --- /dev/null +++ b/Samples/XrSamples/XrHandTrackingWideMotionMode/res/values/strings.xml @@ -0,0 +1,4 @@ + + + Xr Hand Tracking Wide Motion Mode API Sample + diff --git a/Samples/XrSamples/XrHandsAndControllers/CMakeLists.txt b/Samples/XrSamples/XrHandsAndControllers/CMakeLists.txt new file mode 100755 index 0000000..076445f --- /dev/null +++ b/Samples/XrSamples/XrHandsAndControllers/CMakeLists.txt @@ -0,0 +1,43 @@ +# Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved. + +project(xrsamples_xrhands_and_controllers) + +if(NOT TARGET OpenXR::openxr_loader) + find_package(OpenXR REQUIRED) +endif() + +file(GLOB_RECURSE SRC_FILES + Src/*.c + Src/*.cpp +) + +if(ANDROID) + add_library(${PROJECT_NAME} MODULE ${SRC_FILES}) + target_include_directories(${PROJECT_NAME} PUBLIC ${ANDROID_NDK}/sources/android/native_app_glue) + target_link_libraries(${PROJECT_NAME} PRIVATE + android + EGL + GLESv3 + log + ktx + ) + set_target_properties(${PROJECT_NAME} PROPERTIES LINK_FLAGS "-u ANativeActivity_onCreate") +elseif(WIN32) + add_definitions(-D_USE_MATH_DEFINES) + add_executable(${PROJECT_NAME} ${SRC_FILES}) + add_custom_command(TARGET ${PROJECT_NAME} PRE_BUILD + COMMAND "${CMAKE_COMMAND}" -E copy_directory + "${CMAKE_CURRENT_LIST_DIR}/assets" + "$/assets" + VERBATIM) + + add_custom_command(TARGET ${PROJECT_NAME} PRE_BUILD + COMMAND "${CMAKE_COMMAND}" -E copy_directory + "${CMAKE_SOURCE_DIR}/SampleXrFramework/res/raw" + "$/font/res/raw" + VERBATIM) +endif() + +# Common across platforms +target_include_directories(${PROJECT_NAME} PRIVATE Src) +target_link_libraries(${PROJECT_NAME} PRIVATE samplexrframework) diff --git a/Samples/XrSamples/XrHandsAndControllers/Projects/Android/AndroidManifest.xml b/Samples/XrSamples/XrHandsAndControllers/Projects/Android/AndroidManifest.xml new file mode 100755 index 0000000..fe09d57 --- /dev/null +++ b/Samples/XrSamples/XrHandsAndControllers/Projects/Android/AndroidManifest.xml @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Samples/XrSamples/XrHandsAndControllers/Projects/Android/build.bat b/Samples/XrSamples/XrHandsAndControllers/Projects/Android/build.bat new file mode 100755 index 0000000..facf79f --- /dev/null +++ b/Samples/XrSamples/XrHandsAndControllers/Projects/Android/build.bat @@ -0,0 +1,29 @@ +@rem Only edit the master copy of this file in SDK_ROOT/bin/scripts/build/perproject + +@setlocal enableextensions enabledelayedexpansion + +@if not exist "build.gradle" @echo Build script must be executed from project directory. & goto :Abort + +@set P=.. + +:TryAgain + +@rem @echo P = %P% + +@if exist "%P%\bin\scripts\build\build.py.bat" goto :Found + +@if exist "%P%\bin\scripts\build" @echo "Could not find build.py.bat" & goto :Abort + +@set P=%P%\.. + +@goto :TryAgain + +:Found + +@set P=%P%\bin\scripts\build +@call %P%\build.py.bat %1 %2 %3 %4 %5 +@goto :End + +:Abort + +:End diff --git a/Samples/XrSamples/XrHandsAndControllers/Projects/Android/build.gradle b/Samples/XrSamples/XrHandsAndControllers/Projects/Android/build.gradle new file mode 100755 index 0000000..61057e3 --- /dev/null +++ b/Samples/XrSamples/XrHandsAndControllers/Projects/Android/build.gradle @@ -0,0 +1,78 @@ +// Top-level build file where you can add configuration options common to all sub-projects/modules. +buildscript { + repositories { + google() + mavenCentral() + } + dependencies { + classpath "com.android.tools.build:gradle:7.0.3" + } +} + +repositories { + google() + mavenCentral() +} + +apply plugin: 'com.android.application' + +android { + compileSdk 32 + + defaultConfig { + applicationId "com.oculus.xrsamples.xrhands_and_controllers" + minSdk 26 + targetSdk 32 + versionCode 1 + versionName "1.0" + + // override app plugin abiFilters for 64-bit support + externalNativeBuild { + ndk { + abiFilters 'arm64-v8a' + } + ndkBuild { + abiFilters 'arm64-v8a' + } + cmake { + targets "xrsamples_xrhands_and_controllers" + } + } + } + + sourceSets { + main { + manifest.srcFile 'AndroidManifest.xml' + java.srcDirs = ['../../java'] + assets.srcDirs = ['../../assets'] + res.srcDirs = ['../../res'] + } + } + + buildTypes { + debug { + debuggable true + } + + release { + debuggable false + } + } + + externalNativeBuild { + cmake { + path file('../../../../CMakeLists.txt') + } + } + + // Enable prefab support for the OpenXR AAR + buildFeatures { + prefab true + } +} + +dependencies { + // Package/application AndroidManifest.xml properties, plus headers and libraries + // exposed to CMake + implementation 'org.khronos.openxr:openxr_loader_for_android:1.1.36' +} diff --git a/Samples/XrSamples/XrHandsAndControllers/Projects/Android/build.py b/Samples/XrSamples/XrHandsAndControllers/Projects/Android/build.py new file mode 100755 index 0000000..d4b6e58 --- /dev/null +++ b/Samples/XrSamples/XrHandsAndControllers/Projects/Android/build.py @@ -0,0 +1,35 @@ +#!/usr/bin/python +# (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +# This first bit of code is common bootstrapping code +# to determine the SDK root, and to set up the import +# path for additional python code. + +# begin bootstrap +import os +import sys + + +def init(): + root = os.path.realpath(os.path.dirname(os.path.realpath(__file__))) + os.chdir(root) # make sure we are always executing from the project directory + while os.path.isdir(os.path.join(root, "bin/scripts/build")) == False: + root = os.path.realpath(os.path.join(root, "..")) + if ( + len(root) <= 5 + ): # Should catch both Posix and Windows root directories (e.g. '/' and 'C:\') + print("Unable to find SDK root. Exiting.") + sys.exit(1) + root = os.path.abspath(root) + os.environ["OCULUS_SDK_PATH"] = root + sys.path.append(root + "/bin/scripts/build") + + +init() +import ovrbuild + +ovrbuild.init() +# end bootstrap + + +ovrbuild.build() diff --git a/Samples/XrSamples/XrHandsAndControllers/Projects/Android/gradle.properties b/Samples/XrSamples/XrHandsAndControllers/Projects/Android/gradle.properties new file mode 100644 index 0000000..3e927b1 --- /dev/null +++ b/Samples/XrSamples/XrHandsAndControllers/Projects/Android/gradle.properties @@ -0,0 +1,21 @@ +# Project-wide Gradle settings. +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. More details, visit +# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects +# org.gradle.parallel=true +# AndroidX package structure to make it clearer which packages are bundled with the +# Android operating system, and which are packaged with your app's APK +# https://developer.android.com/topic/libraries/support-library/androidx-rn +android.useAndroidX=true +# Enables namespacing of each library's R class so that its R class includes only the +# resources declared in the library itself and none from the library's dependencies, +# thereby reducing the size of the R class for that library +android.nonTransitiveRClass=true \ No newline at end of file diff --git a/Samples/XrSamples/XrHandsAndControllers/Projects/Android/gradle/wrapper/gradle-wrapper.jar b/Samples/XrSamples/XrHandsAndControllers/Projects/Android/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..7454180 Binary files /dev/null and b/Samples/XrSamples/XrHandsAndControllers/Projects/Android/gradle/wrapper/gradle-wrapper.jar differ diff --git a/Samples/XrSamples/XrHandsAndControllers/Projects/Android/gradle/wrapper/gradle-wrapper.properties b/Samples/XrSamples/XrHandsAndControllers/Projects/Android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..ffed3a2 --- /dev/null +++ b/Samples/XrSamples/XrHandsAndControllers/Projects/Android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/Samples/XrSamples/XrHandsAndControllers/Projects/Android/gradlew b/Samples/XrSamples/XrHandsAndControllers/Projects/Android/gradlew new file mode 100755 index 0000000..a69d9cb --- /dev/null +++ b/Samples/XrSamples/XrHandsAndControllers/Projects/Android/gradlew @@ -0,0 +1,240 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# 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 +# +# https://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. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit + +APP_NAME="Gradle" +APP_BASE_NAME=${0##*/} + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/Samples/XrSamples/XrHandsAndControllers/Projects/Android/gradlew.bat b/Samples/XrSamples/XrHandsAndControllers/Projects/Android/gradlew.bat new file mode 100755 index 0000000..f127cfd --- /dev/null +++ b/Samples/XrSamples/XrHandsAndControllers/Projects/Android/gradlew.bat @@ -0,0 +1,91 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/Samples/XrSamples/XrHandsAndControllers/Projects/Android/settings.gradle b/Samples/XrSamples/XrHandsAndControllers/Projects/Android/settings.gradle new file mode 100755 index 0000000..3a416ad --- /dev/null +++ b/Samples/XrSamples/XrHandsAndControllers/Projects/Android/settings.gradle @@ -0,0 +1 @@ +rootProject.name = "XrHandsAndControllers" diff --git a/Samples/XrSamples/XrHandsAndControllers/README.md b/Samples/XrSamples/XrHandsAndControllers/README.md new file mode 100644 index 0000000..b4f7eb7 --- /dev/null +++ b/Samples/XrSamples/XrHandsAndControllers/README.md @@ -0,0 +1,43 @@ +# OpenXR Simultaneous Hands and Controllers Sample + +## Overview +Meta Quest devices have the ability to track both hands and controllers simultaneously (commonly referred to as multimodal hands and controllers input). However, this may consume additional power and system resources. The extensions `XR_META_detached_controllers` and `XR_META_simultaneous_hands_controllers_management` work together in this sample to allow hands and controllers to be tracked simultaneously. + +The `XR_META_simultaneous_hands_and_controllers` extension enables concurrent hand and controller tracking at the same time. It exposes two APIs that allow app developers to enable and disable this functionality at will. Enabling the feature will give developers a new input model to build experiences with; hands and controller data will now flow concurrently into the appropriate top-level paths depending on whether controllers are held or not. Disabling the feature returns to the traditional world of hands-only and controllers-only experiences, and can also save on compute. + +The `XR_META_detached_controllers` extension augments the `XR_META_simultaneous_hands_and_controllers` extension; enabling the extension opens up two new top level paths that are populated with controller data (either left or right, as appropriate) when controllers are unheld. The app developer can choose to ignore this feature or render/use the unheld controller data to build further app experiences. + +Both are demonstrated in this sample. + +## Supported Devices +* Mobile + * Quest Pro + Quest Touch Pro Controller + * Quest 2 + Quest Touch Controller + * Quest 2 + Quest Touch Pro Controller + * Quest 3 + Quest Touch Plus Controller + * Quest 3 + Quest Touch Pro Controller +* PC + * Quest Pro + Quest Touch Pro Controller + * Quest 2 + Quest Touch Controller + * Quest 2 + Quest Touch Pro Controller + * Quest 3 + Quest Touch Plus Controller + * Quest 3 + Quest Touch Pro Controller + +## The Sample +There are six buttons in the sample: +* A "Click me!" button exists to demonstrate that the UI can be interacted with via either hand or controller when holding one controller and using an empty tracked hand together. Try only holding one controller and clicking/pinching the button from each hand to demonstrate this. +* One further button allows the user to enable and disable the concurrent tracking of hands and controllers. This simply calls the associated APIs which is observable in the sample's code. Clicking this allows the developer to explore the input behavior when the feature is enabled and disabled in real time. +* The next four buttons demonstrate sending haptic feedback to controllers that are either held or not held. This demonstrates the APIs used to take advantage of these two features in combination. +![screen_shot](images/screen_shot.png) + +#### main.cpp +Main logic of the sample. It handles responding to button presses, setting up the scene, and demonstrates the real-time population of interaction profiles and action paths to help developers understand the behavior in real time. + +#### SkyboxRenderer.h, SkyboxRenderer.cpp +Helper class to handle the rendering of the skybox. + +#### EnvironmentRenderer.h, EnvironmentRenderer.cpp +Helper class to handle the rendering of the environment. + +#### ActionSetDisplayPanel.h, ActionSetDisplayPanel.cpp +Helper class to handle the logic of displaying action set population UI for easy real-time understanding. diff --git a/Samples/XrSamples/XrHandsAndControllers/Src/ActionSetDisplayPanel.cpp b/Samples/XrSamples/XrHandsAndControllers/Src/ActionSetDisplayPanel.cpp new file mode 100755 index 0000000..452d9e3 --- /dev/null +++ b/Samples/XrSamples/XrHandsAndControllers/Src/ActionSetDisplayPanel.cpp @@ -0,0 +1,226 @@ +#include "ActionSetDisplayPanel.h" + +#include +#include +#include "Render/BitmapFont.h" + +ActionSetDisplayPanel::ActionSetDisplayPanel( + std::string title, + XrSession session, + XrInstance instance, + OVRFW::TinyUI* ui, + OVR::Vector3f topLeftLocation) + : Session{session}, Instance{instance}, ui_{ui}, topLeftLocation_{topLeftLocation} { + auto label = ui_->AddLabel( + title, GetNextLabelLocation() + OVR::Vector3f{0, kHeaderHeight_, 0.00}, {widthPx_, 45.0f}); + labels_.push_back(label); +} + +void ActionSetDisplayPanel::AddBoolAction(XrAction action, const char* actionName) { + auto labelPair = CreateActionLabel(actionName); + auto headerLabel = labelPair.first; + auto actionStateLabel = labelPair.second; + boolActions_.push_back({action, actionStateLabel}); + labels_.push_back(headerLabel); +} + +void ActionSetDisplayPanel::AddFloatAction(XrAction action, const char* actionName) { + auto labelPair = CreateActionLabel(actionName); + auto headerLabel = labelPair.first; + auto actionStateLabel = labelPair.second; + floatActions_.push_back({action, actionStateLabel}); + labels_.push_back(headerLabel); +} + +void ActionSetDisplayPanel::AddVec2Action(XrAction action, const char* actionName) { + auto labelPair = CreateActionLabel(actionName); + auto headerLabel = labelPair.first; + auto actionStateLabel = labelPair.second; + vec2Actions_.push_back({action, actionStateLabel}); + labels_.push_back(headerLabel); +} + +void ActionSetDisplayPanel::AddPoseAction(XrAction action, const char* actionName) { + auto labelPair = CreateActionLabel(actionName); + auto headerLabel = labelPair.first; + auto actionStateLabel = labelPair.second; + poseActions_.push_back({action, actionStateLabel}); + labels_.push_back(headerLabel); +} + +std::pair ActionSetDisplayPanel::CreateActionLabel(const char* actionName) { + auto label = ui_->AddLabel(actionName, GetNextLabelLocation(), {widthPx_, 45.0f}); + auto stateLabel = ui_->AddLabel("state", GetNextStateLabelLocation(), {widthPx_, 250.0f}); + + OVRFW::VRMenuFontParms fontParams{}; + fontParams.Scale = 0.5f; + fontParams.AlignHoriz = OVRFW::HORIZONTAL_LEFT; + label->SetFontParms(fontParams); + label->SetTextLocalPosition({-0.45f * width_, 0, 0}); + stateLabel->SetFontParms(fontParams); + stateLabel->SetTextLocalPosition({-0.47f * width_, -0.02f * height_, 0}); + + label->SetColor({0.2, 0.2, 0.2, 1.0}); + elements_++; + return std::make_pair(label, stateLabel); +} + +OVR::Vector3f ActionSetDisplayPanel::GetNextLabelLocation() { + return topLeftLocation_ + + OVR::Vector3f{width_ * 0.5f, -elements_ * kElementGap_ - kHeaderHeight_, 0.01}; +} + +OVR::Vector3f ActionSetDisplayPanel::GetNextStateLabelLocation() { + return GetNextLabelLocation() + OVR::Vector3f{0.0, -kElementGap_ * 0.5f, 0.0}; +} + +void ActionSetDisplayPanel::Update() { + for (auto& pair : boolActions_) { + XrAction action = pair.first; + VRMenuObject* label = pair.second; + std::string bindingText = ListBoundSources(action); + + XrActionStateGetInfo getInfo{XR_TYPE_ACTION_STATE_GET_INFO}; + getInfo.action = action; + getInfo.subactionPath = XR_NULL_PATH; + XrActionStateBoolean state{XR_TYPE_ACTION_STATE_BOOLEAN}; + OXR(xrGetActionStateBoolean(Session, &getInfo, &state)); + + label->SetText( + "currentState: %s | changedSinceLastSync: %s\n" + "isActive: %s | lastChangeTime: %ldms\n" + + bindingText, + state.currentState ? "True " : "False", + state.changedSinceLastSync ? "True " : "False", + state.isActive ? "True " : "False", + state.lastChangeTime / (1000 * 1000)); // convert from ns to ms + label->SetSurfaceColor( + 0, state.isActive ? OVR::Vector4f(0., 0.1, 0., 1.) : OVR::Vector4f(0.05, 0.05, 0.05, 1.)); + label->SetSelected(state.currentState); + } + + for (auto& pair : floatActions_) { + XrAction action = pair.first; + VRMenuObject* label = pair.second; + std::string bindingText = ListBoundSources(action); + + XrActionStateGetInfo getInfo{XR_TYPE_ACTION_STATE_GET_INFO}; + getInfo.action = action; + getInfo.subactionPath = XR_NULL_PATH; + XrActionStateFloat state{XR_TYPE_ACTION_STATE_FLOAT}; + OXR(xrGetActionStateFloat(Session, &getInfo, &state)); + + label->SetText( + "currentState: %0.3f | changedSinceLastSync: %s\n" + "isActive: %s | lastChangeTime: %ldms\n" + + bindingText, + state.currentState, + state.changedSinceLastSync ? "True " : "False", + state.isActive ? "True " : "False", + state.lastChangeTime / (1000 * 1000)); // convert from ns to ms + label->SetSurfaceColor( + 0, state.isActive ? OVR::Vector4f(0., 0.1, 0., 1.) : OVR::Vector4f(0.05, 0.05, 0.05, 1.)); + } + + for (auto& pair : vec2Actions_) { + XrAction action = pair.first; + VRMenuObject* label = pair.second; + std::string bindingText = ListBoundSources(action); + + XrActionStateGetInfo getInfo{XR_TYPE_ACTION_STATE_GET_INFO}; + getInfo.action = action; + getInfo.subactionPath = XR_NULL_PATH; + XrActionStateVector2f state{XR_TYPE_ACTION_STATE_VECTOR2F}; + OXR(xrGetActionStateVector2f(Session, &getInfo, &state)); + + label->SetText( + "currentState: (%0.2f, %0.2f) | changedSinceLastSync: %s\n" + "isActive: %s | lastChangeTime: %ldms\n" + + bindingText, + state.currentState.x, + state.currentState.y, + state.changedSinceLastSync ? "True " : "False", + state.isActive ? "True " : "False", + state.lastChangeTime / (1000 * 1000)); // convert from ns to ms + label->SetSurfaceColor( + 0, state.isActive ? OVR::Vector4f(0., 0.1, 0., 1.) : OVR::Vector4f(0.05, 0.05, 0.05, 1.)); + } + + for (auto& pair : poseActions_) { + XrAction action = pair.first; + VRMenuObject* label = pair.second; + std::string bindingText = ListBoundSources(action); + + XrActionStateGetInfo getInfo{XR_TYPE_ACTION_STATE_GET_INFO}; + getInfo.action = action; + getInfo.subactionPath = XR_NULL_PATH; + XrActionStatePose state{XR_TYPE_ACTION_STATE_POSE}; + OXR(xrGetActionStatePose(Session, &getInfo, &state)); + + label->SetText("isActive: %s\n" + bindingText, state.isActive ? "True " : "False"); + label->SetSurfaceColor( + 0, state.isActive ? OVR::Vector4f(0., 0.1, 0., 1.) : OVR::Vector4f(0.05, 0.05, 0.05, 1.)); + } +} + +void ActionSetDisplayPanel::UpdateAllLabelRotation(OVR::Quatf const& rot) { + for (auto& label : labels_) { + label->SetLocalRotation(rot); + + } + for (auto& pair : boolActions_) { + VRMenuObject* label = pair.second; + label->SetLocalRotation(rot); + } + + for (auto& pair : floatActions_) { + VRMenuObject* label = pair.second; + label->SetLocalRotation(rot); + } + + for (auto& pair : vec2Actions_) { + VRMenuObject* label = pair.second; + label->SetLocalRotation(rot); + } + + for (auto& pair : poseActions_) { + VRMenuObject* label = pair.second; + label->SetLocalRotation(rot); + } +} + +std::string ActionSetDisplayPanel::ListBoundSources(XrAction action) { + XrBoundSourcesForActionEnumerateInfo enumerateInfo{ + XR_TYPE_BOUND_SOURCES_FOR_ACTION_ENUMERATE_INFO}; + enumerateInfo.action = action; + uint32_t sourcesCount = 0; + + std::stringstream bindingText; + + OXR(xrEnumerateBoundSourcesForAction(Session, &enumerateInfo, 0, &sourcesCount, nullptr)); + std::vector boundSources(sourcesCount); + OXR(xrEnumerateBoundSourcesForAction( + Session, &enumerateInfo, boundSources.size(), &sourcesCount, boundSources.data())); + + for (XrPath sourcePath : boundSources) { + uint32_t pathLength; + // OXR(xrPathToString(Instance, sourcePath, 0, &pathLength, nullptr)); + std::vector pathString(XR_MAX_PATH_LENGTH); + OXR(xrPathToString( + Instance, sourcePath, pathString.size(), &pathLength, pathString.data())); + + XrInputSourceLocalizedNameGetInfo sni{XR_TYPE_INPUT_SOURCE_LOCALIZED_NAME_GET_INFO}; + sni.sourcePath = sourcePath; + sni.whichComponents = XR_INPUT_SOURCE_LOCALIZED_NAME_USER_PATH_BIT | + XR_INPUT_SOURCE_LOCALIZED_NAME_COMPONENT_BIT | + XR_INPUT_SOURCE_LOCALIZED_NAME_INTERACTION_PROFILE_BIT; + uint32_t sourceNameLength; + OXR(xrGetInputSourceLocalizedName(Session, &sni, 0, &sourceNameLength, nullptr)); + std::vector sourceName(sourceNameLength); + OXR(xrGetInputSourceLocalizedName( + Session, &sni, sourceName.size(), &sourceNameLength, sourceName.data())); + + bindingText << "\nBinding: " << pathString.data() << "\n(" << sourceName.data() << ")\n"; + } + return std::string(bindingText.str()); +} diff --git a/Samples/XrSamples/XrHandsAndControllers/Src/ActionSetDisplayPanel.h b/Samples/XrSamples/XrHandsAndControllers/Src/ActionSetDisplayPanel.h new file mode 100755 index 0000000..885ca8f --- /dev/null +++ b/Samples/XrSamples/XrHandsAndControllers/Src/ActionSetDisplayPanel.h @@ -0,0 +1,49 @@ +#include + +#include "XrApp.h" +#include "Input/TinyUI.h" +#include "GUI/VRMenuObject.h" + +using OVRFW::VRMenuObject; +class ActionSetDisplayPanel { + public: + ActionSetDisplayPanel( + std::string title, + XrSession session, + XrInstance instance, + OVRFW::TinyUI* ui, + OVR::Vector3f topLeftLocation); + void AddBoolAction(XrAction action, const char* actionName); + void AddFloatAction(XrAction action, const char* actionName); + void AddVec2Action(XrAction action, const char* actionName); + void AddPoseAction(XrAction action, const char* actionName); + + void Update(); + void UpdateAllLabelRotation(OVR::Quatf const& rot); + + private: + std::pair CreateActionLabel(const char* actionName); + std::string ListBoundSources(XrAction action); + OVR::Vector3f GetNextLabelLocation(); + OVR::Vector3f GetNextStateLabelLocation(); + + // OVRFW::VRMenuObject* backgroundPane_{}; + std::vector> boolActions_{}; + std::vector> floatActions_{}; + std::vector> vec2Actions_{}; + std::vector> poseActions_{}; + std::vector labels_{}; + XrSession Session; + XrInstance Instance; + OVRFW::TinyUI* ui_; + + OVR::Vector3f topLeftLocation_; + int elements_{0}; + static constexpr float kHeaderHeight_{0.15}; + static constexpr float kElementGap_{0.65}; + + static constexpr float widthPx_{600}; + static constexpr float heightPx_{500}; + static constexpr float width_{widthPx_ * VRMenuObject::DEFAULT_TEXEL_SCALE}; + static constexpr float height_{heightPx_ * VRMenuObject::DEFAULT_TEXEL_SCALE}; +}; diff --git a/Samples/XrSamples/XrHandsAndControllers/Src/EnvironmentRenderer.cpp b/Samples/XrSamples/XrHandsAndControllers/Src/EnvironmentRenderer.cpp new file mode 100755 index 0000000..df9530a --- /dev/null +++ b/Samples/XrSamples/XrHandsAndControllers/Src/EnvironmentRenderer.cpp @@ -0,0 +1,223 @@ +/************************************************************************************************ +Filename : EnvironmentRenderer.cpp +Content : A variant of ModelRenderer suited for rendering gltf scenes with vertex color based fog +Created : July 2023 +Authors : Alexander Borsboom +Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved. +************************************************************************************************/ +#include "EnvironmentRenderer.h" +#include "Model/ModelFile.h" +#include "Model/ModelFileLoading.h" +#include "XrApp.h" +#include + +using OVR::Matrix4f; +using OVR::Posef; +using OVR::Quatf; +using OVR::Vector3f; +using OVR::Vector4f; + +namespace OVRFW { +namespace EnvironmentShaders { + +/// clang-format off +static const char* VertexShaderSrc = R"glsl( +attribute highp vec4 Position; +attribute highp vec3 Normal; +attribute highp vec2 TexCoord; +attribute lowp vec4 VertexColor; + +varying lowp vec3 oEye; +varying lowp vec3 oNormal; +varying lowp vec2 oTexCoord; +varying lowp vec4 oVertexColor; + +vec3 multiply( mat4 m, vec3 v ) +{ + return vec3( + m[0].x * v.x + m[1].x * v.y + m[2].x * v.z, + m[0].y * v.x + m[1].y * v.y + m[2].y * v.z, + m[0].z * v.x + m[1].z * v.y + m[2].z * v.z ); +} + +vec3 transposeMultiply( mat4 m, vec3 v ) +{ + return vec3( + m[0].x * v.x + m[0].y * v.y + m[0].z * v.z, + m[1].x * v.x + m[1].y * v.y + m[1].z * v.z, + m[2].x * v.x + m[2].y * v.y + m[2].z * v.z ); +} + +void main() +{ + gl_Position = TransformVertex( Position ); + vec3 eye = transposeMultiply( sm.ViewMatrix[VIEW_ID], -vec3( sm.ViewMatrix[VIEW_ID][3] ) ); + oEye = eye - vec3( ModelMatrix * Position ); + vec3 iNormal = Normal * 100.0f; + oNormal = multiply( ModelMatrix, iNormal ); + oTexCoord = TexCoord; + oVertexColor = VertexColor; +} +)glsl"; + +/// This shader uses vertex color.r for a fog, fading to a fog color as vertex color decreases to 0. +/// This gives behaviour consistent with our unity samples. +static const char* FragmentShaderSrc = R"glsl( +precision lowp float; + +uniform sampler2D Texture0; +uniform sampler2D Texture1; +uniform lowp vec3 SpecularLightDirection; +uniform lowp vec3 SpecularLightColor; +uniform lowp vec3 AmbientLightColor; +uniform lowp float FogStrength; +uniform lowp vec3 FogColor; + +varying lowp vec3 oEye; +varying lowp vec3 oNormal; +varying lowp vec2 oTexCoord; +varying lowp vec4 oVertexColor; + +lowp vec3 multiply( lowp mat3 m, lowp vec3 v ) +{ + return vec3( + m[0].x * v.x + m[1].x * v.y + m[2].x * v.z, + m[0].y * v.x + m[1].y * v.y + m[2].y * v.z, + m[0].z * v.x + m[1].z * v.y + m[2].z * v.z ); +} + +void main() +{ + lowp vec3 eyeDir = normalize( oEye.xyz ); + lowp vec3 Normal = normalize( oNormal ); + + lowp vec3 reflectionDir = dot( eyeDir, Normal ) * 2.0 * Normal - eyeDir; + lowp vec4 diffuse = texture2D( Texture0, oTexCoord ); + lowp vec4 detail = texture2D( Texture1, oTexCoord * 20.0 ); + lowp vec4 res = 0.5 * (diffuse + detail); + lowp vec3 ambientValue = res.xyz * AmbientLightColor; + + lowp float nDotL = max( dot( Normal , SpecularLightDirection ), 0.0 ); + lowp vec3 diffuseValue = res.xyz * SpecularLightColor * nDotL; + + lowp float specularPower = 1.0f - res.a; + specularPower = specularPower * specularPower; + + lowp vec3 H = normalize( SpecularLightDirection + eyeDir ); + lowp float nDotH = max( dot( Normal, H ), 0.0 ); + lowp float specularIntensity = pow( nDotH, 64.0f * ( specularPower ) ) * specularPower; + lowp vec3 specularValue = specularIntensity * SpecularLightColor; + + lowp vec3 controllerColor = diffuseValue + ambientValue + specularValue; + + lowp float fog = FogStrength * (1.0 - oVertexColor.r); + controllerColor = fog * FogColor + (1.0 - fog) * controllerColor; + + gl_FragColor.w = 1.0; + gl_FragColor.xyz = controllerColor; +} +)glsl"; + +/// clang-format on + +} // namespace EnvironmentRenderer + +bool EnvironmentRenderer::Init(std::string modelPath, OVRFW::ovrFileSys* fileSys) { + /// Shader + ovrProgramParm UniformParms[] = { + {"Texture0", ovrProgramParmType::TEXTURE_SAMPLED}, + {"Texture1", ovrProgramParmType::TEXTURE_SAMPLED}, // An optional detail texture. + {"SpecularLightDirection", ovrProgramParmType::FLOAT_VECTOR3}, + {"SpecularLightColor", ovrProgramParmType::FLOAT_VECTOR3}, + {"AmbientLightColor", ovrProgramParmType::FLOAT_VECTOR3}, + {"FogStrength", ovrProgramParmType::FLOAT}, + {"FogColor", ovrProgramParmType::FLOAT_VECTOR3}, + }; + ProgRenderModel = GlProgram::Build( + "", + EnvironmentShaders::VertexShaderSrc, + "", + EnvironmentShaders::FragmentShaderSrc, + UniformParms, + sizeof(UniformParms) / sizeof(ovrProgramParm)); + + MaterialParms materials = {}; + ModelGlPrograms programs = {}; + programs.ProgSingleTexture = &ProgRenderModel; + programs.ProgBaseColorPBR = &ProgRenderModel; + programs.ProgSkinnedBaseColorPBR = &ProgRenderModel; + programs.ProgLightMapped = &ProgRenderModel; + programs.ProgBaseColorEmissivePBR = &ProgRenderModel; + programs.ProgSkinnedBaseColorEmissivePBR = &ProgRenderModel; + programs.ProgSimplePBR = &ProgRenderModel; + programs.ProgSkinnedSimplePBR = &ProgRenderModel; + + if( fileSys ) { + OVRFW::ovrFileSys& fs = *fileSys; + RenderModel = LoadModelFile(fs, modelPath.c_str(), programs, materials); + } else { + ALOGE("Couldn't load model, we didn't get a valid filesystem"); + return false; + } + + if (RenderModel == nullptr || static_cast(RenderModel->Models.size()) < 1) { + ALOGE("Couldn't load modelrenderer model!"); + return false; + } + + FogStrengths = new OVR::Size[RenderModel->Models.size()]; + int modelIndex = 0; + for (auto& model : RenderModel->Models) { + auto& gc = model.surfaces[0].surfaceDef.graphicsCommand; + gc.UniformData[0].Data = &gc.Textures[0]; + gc.UniformData[1].Data = &gc.Textures[1]; + gc.UniformData[2].Data = &SpecularLightDirection; + gc.UniformData[3].Data = &SpecularLightColor; + gc.UniformData[4].Data = &AmbientLightColor; + FogStrengths[modelIndex] = OVR::Size(gc.GpuState.blendEnable == ovrGpuState::BLEND_ENABLE ? 1.0f : 0.0f); + gc.UniformData[5].Data = &FogStrengths[modelIndex]; + gc.UniformData[6].Data = &FogColor; + gc.GpuState.depthEnable = gc.GpuState.depthMaskEnable = true; + modelIndex++; + } + + /// Set defaults + SpecularLightDirection = Vector3f(1.0f, 1.0f, 0.0f); + SpecularLightColor = Vector3f(1.0f, 0.95f, 0.8f) * 0.75f; + AmbientLightColor = Vector3f(1.0f, 1.0f, 1.0f) * 0.15f; + FogColor = Vector3f(0.3372549f, 0.345098f, 0.3686275f); + + /// all good + Initialized = true; + return true; +} + +void EnvironmentRenderer::Shutdown() { + OVRFW::GlProgram::Free(ProgRenderModel); + if (RenderModel != nullptr) { + delete RenderModel; + RenderModel = nullptr; + } + if(FogStrengths != nullptr) { + delete FogStrengths; + FogStrengths = nullptr; + } +} + +void EnvironmentRenderer::Render(std::vector& surfaceList) { + /// toggle alpha override + if (RenderModel != nullptr) { + for( int i=0; i < static_cast(RenderModel->Models.size()); i++ ) { + auto& model = RenderModel->Models[i]; + auto& node = RenderModel->Nodes[i+1]; + ovrDrawSurface controllerSurface; + for( int j=0; j < static_cast(model.surfaces.size()); j++ ) { + controllerSurface.surface = &(model.surfaces[j].surfaceDef); + controllerSurface.modelMatrix = node.GetGlobalTransform(); + surfaceList.push_back(controllerSurface); + } + } + } +} + +} // namespace OVRFW diff --git a/Samples/XrSamples/XrHandsAndControllers/Src/EnvironmentRenderer.h b/Samples/XrSamples/XrHandsAndControllers/Src/EnvironmentRenderer.h new file mode 100755 index 0000000..0bbcfbb --- /dev/null +++ b/Samples/XrSamples/XrHandsAndControllers/Src/EnvironmentRenderer.h @@ -0,0 +1,63 @@ +/************************************************************************************************ +Filename : EnvironmentRenderer.h +Content : A variant of ModelRenderer suited for rendering gltf scenes with vertex color based fog +Created : July 2023 +Authors : Alexander Borsboom +Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved. +************************************************************************************************/ +#pragma once + +#include +#include +#include + +/// Sample Framework +#include "Misc/Log.h" +#include "Model/SceneView.h" +#include "Render/GlProgram.h" +#include "Render/SurfaceRender.h" +#include "OVR_FileSys.h" +#include "OVR_Math.h" + +#if defined(ANDROID) +#define XR_USE_GRAPHICS_API_OPENGL_ES 1 +#define XR_USE_PLATFORM_ANDROID 1 +#else +#include "unknwn.h" +#define XR_USE_GRAPHICS_API_OPENGL 1 +#define XR_USE_PLATFORM_WIN32 1 +#endif + +#include +#include +#include + +namespace OVRFW { + +class EnvironmentRenderer { + public: + EnvironmentRenderer() = default; + ~EnvironmentRenderer() = default; + + bool Init(std::vector& modelBuffer); + bool Init(std::string modelPath, OVRFW::ovrFileSys* fileSys); + void Shutdown(); + void Render(std::vector& surfaceList); + bool IsInitialized() const { return Initialized;} + + public: + OVR::Vector3f SpecularLightDirection; + OVR::Vector3f SpecularLightColor; + OVR::Vector3f AmbientLightColor; + OVR::Vector3f FogColor; + + private: + bool Initialized = false; + GlProgram ProgRenderModel; + ModelFile* RenderModel = nullptr; + GlTexture RenderModelTextureSolid; + OVR::Matrix4f Transform; + OVR::Size* FogStrengths; +}; + +} // namespace OVRFW diff --git a/Samples/XrSamples/XrHandsAndControllers/Src/SkyboxRenderer.cpp b/Samples/XrSamples/XrHandsAndControllers/Src/SkyboxRenderer.cpp new file mode 100755 index 0000000..5bb2348 --- /dev/null +++ b/Samples/XrSamples/XrHandsAndControllers/Src/SkyboxRenderer.cpp @@ -0,0 +1,176 @@ +/************************************************************************************************ +Filename : SkyboxRenderer.cpp +Content : A renderer suited for gradient skyboxes. +Created : July 2023 +Authors : Alex Borsboom +Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved. +************************************************************************************************/ +#include "SkyboxRenderer.h" +#include "Model/ModelFile.h" +#include "Model/ModelFileLoading.h" +#include "XrApp.h" +#include + +using OVR::Matrix4f; +using OVR::Posef; +using OVR::Quatf; +using OVR::Vector3f; +using OVR::Vector4f; + +namespace OVRFW { +namespace SkyboxShaders { + +/// clang-format off +static const char* VertexShaderSrc = R"glsl( +attribute highp vec4 Position; +attribute highp vec3 Normal; +attribute highp vec2 TexCoord; + +varying lowp vec3 oEye; +varying lowp vec3 oNormal; +varying lowp vec2 oTexCoord; + +vec3 multiply( mat4 m, vec3 v ) +{ + return vec3( + m[0].x * v.x + m[1].x * v.y + m[2].x * v.z, + m[0].y * v.x + m[1].y * v.y + m[2].y * v.z, + m[0].z * v.x + m[1].z * v.y + m[2].z * v.z ); +} + +vec3 transposeMultiply( mat4 m, vec3 v ) +{ + return vec3( + m[0].x * v.x + m[0].y * v.y + m[0].z * v.z, + m[1].x * v.x + m[1].y * v.y + m[1].z * v.z, + m[2].x * v.x + m[2].y * v.y + m[2].z * v.z ); +} + +void main() +{ + gl_Position = TransformVertex( Position ); + oTexCoord = TexCoord; +} +)glsl"; + +static const char* FragmentShaderSrc = R"glsl( +precision lowp float; + +uniform lowp vec3 TopColor; +uniform lowp vec3 MiddleColor; +uniform lowp vec3 BottomColor; + +varying lowp vec2 oTexCoord; + +lowp vec3 multiply( lowp mat3 m, lowp vec3 v ) +{ + return vec3( + m[0].x * v.x + m[1].x * v.y + m[2].x * v.z, + m[0].y * v.x + m[1].y * v.y + m[2].y * v.z, + m[0].z * v.x + m[1].z * v.y + m[2].z * v.z ); +} + +lowp float saturate(lowp float v) { + return clamp(v, 0.0f, 1.0f); +} + +void main() +{ + lowp float val = oTexCoord.y; + lowp float topVal = saturate(-3.0 + ( 4.0 * val )); + lowp float middleVal = saturate( 1.0 - 4.0 * abs(0.75 - val )); + lowp float bottomVal = saturate( 4.0 * ( 0.75 - val)); + + lowp vec3 finalColor = BottomColor.rgb * bottomVal + MiddleColor.rgb * middleVal + TopColor.rgb * topVal; + + gl_FragColor.w = 1.0f; + gl_FragColor.xyz = finalColor; +} +)glsl"; + +/// clang-format on + +} // namespace SkyboxShaders + +bool SkyboxRenderer::Init(std::string modelPath, OVRFW::ovrFileSys* fileSys) { + /// Shader + ovrProgramParm UniformParms[] = { + {"TopColor", ovrProgramParmType::FLOAT_VECTOR3}, + {"MiddleColor", ovrProgramParmType::FLOAT_VECTOR3}, + {"BottomColor", ovrProgramParmType::FLOAT_VECTOR3}, + }; + ProgRenderModel = GlProgram::Build( + "", + SkyboxShaders::VertexShaderSrc, + "", + SkyboxShaders::FragmentShaderSrc, + UniformParms, + sizeof(UniformParms) / sizeof(ovrProgramParm)); + + MaterialParms materials = {}; + ModelGlPrograms programs = {}; + programs.ProgSingleTexture = &ProgRenderModel; + programs.ProgBaseColorPBR = &ProgRenderModel; + programs.ProgSkinnedBaseColorPBR = &ProgRenderModel; + programs.ProgLightMapped = &ProgRenderModel; + programs.ProgBaseColorEmissivePBR = &ProgRenderModel; + programs.ProgSkinnedBaseColorEmissivePBR = &ProgRenderModel; + programs.ProgSimplePBR = &ProgRenderModel; + programs.ProgSkinnedSimplePBR = &ProgRenderModel; + + if( fileSys ) { + OVRFW::ovrFileSys& fs = *fileSys; + RenderModel = LoadModelFile(fs, modelPath.c_str(), programs, materials); + } else { + ALOGE("Couldn't load model, we didn't get a valid filesystem"); + return false; + } + + if (RenderModel == nullptr || static_cast(RenderModel->Models.size()) < 1) { + ALOGE("Couldn't load modelrenderer model!"); + return false; + } + + TopColor = OVR::Vector3f(0.937f, 0.9236477f, 0.883591f); + MiddleColor = OVR::Vector3f(0.6705883f, 0.6909091f, 0.7450981f); + BottomColor = OVR::Vector3f(0.3372549f, 0.345098f, 0.3686275f); + + for (auto& model : RenderModel->Models) { + auto& gc = model.surfaces[0].surfaceDef.graphicsCommand; + gc.UniformData[0].Data = &TopColor; + gc.UniformData[1].Data = &MiddleColor; + gc.UniformData[2].Data = &BottomColor; + gc.GpuState.depthMaskEnable = false; + gc.GpuState.depthEnable = false; + gc.GpuState.blendEnable = ovrGpuState::BLEND_DISABLE; + } + + /// all good + Initialized = true; + return true; +} + +void SkyboxRenderer::Shutdown() { + OVRFW::GlProgram::Free(ProgRenderModel); + if (RenderModel != nullptr) { + delete RenderModel; + RenderModel = nullptr; + } +} + +void SkyboxRenderer::Render(std::vector& surfaceList) { + if (RenderModel != nullptr) { + for( int i=0; i < static_cast(RenderModel->Models.size()); i++ ) { + auto& model = RenderModel->Models[i]; + auto& node = RenderModel->Nodes[i]; + ovrDrawSurface controllerSurface; + for( int j=0; j < static_cast(model.surfaces.size()); j++ ) { + controllerSurface.surface = &(model.surfaces[j].surfaceDef); + controllerSurface.modelMatrix = node.GetGlobalTransform(); + surfaceList.push_back(controllerSurface); + } + } + } +} + +} // namespace OVRFW diff --git a/Samples/XrSamples/XrHandsAndControllers/Src/SkyboxRenderer.h b/Samples/XrSamples/XrHandsAndControllers/Src/SkyboxRenderer.h new file mode 100755 index 0000000..5f99ef6 --- /dev/null +++ b/Samples/XrSamples/XrHandsAndControllers/Src/SkyboxRenderer.h @@ -0,0 +1,61 @@ +/************************************************************************************************ +Filename : SkyboxRenderer.h +Content : A renderer which displays gradient skyboxes. +Created : July 2023 +Authors : Alexander Borsboom +Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved. +************************************************************************************************/ +#pragma once + +#include +#include +#include + +/// Sample Framework +#include "Misc/Log.h" +#include "Model/SceneView.h" +#include "Render/GlProgram.h" +#include "Render/SurfaceRender.h" +#include "OVR_FileSys.h" +#include "OVR_Math.h" + +#if defined(ANDROID) +#define XR_USE_GRAPHICS_API_OPENGL_ES 1 +#define XR_USE_PLATFORM_ANDROID 1 +#else +#include "unknwn.h" +#define XR_USE_GRAPHICS_API_OPENGL 1 +#define XR_USE_PLATFORM_WIN32 1 +#endif + +#include +#include +#include + +namespace OVRFW { + +class SkyboxRenderer { + public: + SkyboxRenderer() = default; + ~SkyboxRenderer() = default; + + bool Init(std::string modelPath, OVRFW::ovrFileSys* fileSys); + void Shutdown(); + void Render(std::vector& surfaceList); + bool IsInitialized() const { return Initialized;} + + public: + OVR::Vector3f TopColor; + OVR::Vector3f MiddleColor; + OVR::Vector3f BottomColor; + OVR::Vector3f Direction; + + private: + bool Initialized = false; + GlProgram ProgRenderModel; + ModelFile* RenderModel = nullptr; + GlTexture RenderModelTextureSolid; + OVR::Matrix4f Transform; +}; + +} // namespace OVRFW diff --git a/Samples/XrSamples/XrHandsAndControllers/Src/main.cpp b/Samples/XrSamples/XrHandsAndControllers/Src/main.cpp new file mode 100755 index 0000000..f61df85 --- /dev/null +++ b/Samples/XrSamples/XrHandsAndControllers/Src/main.cpp @@ -0,0 +1,1595 @@ +/******************************************************************************* + +Filename : Main.cpp +Content : OpenXR sample showing use of the hands and controllers API +Created : +Authors : Matthew Langille +Language : C++ +Copyright : Copyright (c) Meta Platforms, Inc. and affiliates. + +*******************************************************************************/ + +#include +#include +#include + +#include + +#include "GUI/VRMenuObject.h" +#include "XrApp.h" +#include "ActionSetDisplayPanel.h" +#include "SkyboxRenderer.h" +#include "EnvironmentRenderer.h" + +#include "OVR_Math.h" +#include "Input/AxisRenderer.h" +#include "Input/ControllerRenderer.h" +#include "Input/HandRenderer.h" +#include "Input/TinyUI.h" +#include "Render/SimpleBeamRenderer.h" +#include "meta_openxr_preview/openxr_oculus_helpers.h" +#include "meta_openxr_preview/meta_detached_controllers.h" +#include "meta_openxr_preview/meta_simultaneous_hands_and_controllers.h" + +// All physical units in OpenXR are in meters, but sometimes it's more useful +// to think in cm, so this user defined literal converts from centimeters to meters +constexpr float operator"" _cm(long double centimeters) { + return static_cast(centimeters * 0.01); +} +constexpr float operator"" _cm(unsigned long long centimeters) { + return static_cast(centimeters * 0.01); +} + +// For expressiveness; use _m rather than f literals when we mean meters +constexpr float operator"" _m(long double meters) { + return static_cast(meters); +} +constexpr float operator"" _m(unsigned long long meters) { + return static_cast(meters); +} + +static int TimesClicked; + +class XrHandsAndControllersSampleApp : public OVRFW::XrApp { + public: + static constexpr std::string_view sampleExplanation = + "Sample demonstrating hands and controllers in action at\n" + "the same time, with detached controllers tracked when not\n" + "in hand"; + + XrHandsAndControllersSampleApp() : OVRFW::XrApp() { + BackgroundColor = OVR::Vector4f(0.45f, 0.35f, 0.2f, 1.0f); + TimesClicked = 0; + + // Disable framework input management, letting this sample explicitly + // call xrSyncActions() every frame; which includes control over which + // ActionSet to set as active each frame + SkipInputHandling = true; + } + + virtual std::vector GetExtensions() override { + std::vector extensions = XrApp::GetExtensions(); + isDetachedControllersExtensionAvailable_ = + isExtensionAvailable(XR_META_DETACHED_CONTROLLERS_EXTENSION_NAME); + isExtHandInteractionExtensionAvailable_ = + isExtensionAvailable(XR_EXT_HAND_INTERACTION_EXTENSION_NAME); + isSimultaneousHandsControllersExtensionAvailable_ = isExtensionAvailable( + XR_META_SIMULTANEOUS_HANDS_AND_CONTROLLERS_EXTENSION_NAME); + + extensions.push_back(XR_EXT_HAND_TRACKING_EXTENSION_NAME); + extensions.push_back(XR_FB_HAND_TRACKING_MESH_EXTENSION_NAME); + extensions.push_back(XR_FB_HAND_TRACKING_AIM_EXTENSION_NAME); + if (isExtHandInteractionExtensionAvailable_) { + extensions.push_back(XR_EXT_HAND_INTERACTION_EXTENSION_NAME); + } + extensions.push_back(XR_FB_TOUCH_CONTROLLER_PRO_EXTENSION_NAME); + extensions.push_back(XR_META_TOUCH_CONTROLLER_PLUS_EXTENSION_NAME); + if (isDetachedControllersExtensionAvailable_) { + extensions.push_back(XR_META_DETACHED_CONTROLLERS_EXTENSION_NAME); + } + + if (isSimultaneousHandsControllersExtensionAvailable_) { + extensions.push_back(XR_META_SIMULTANEOUS_HANDS_AND_CONTROLLERS_EXTENSION_NAME); + } + return extensions; + } + + std::unordered_map> GetSuggestedBindings( + XrInstance instance) override { + OXR(xrStringToPath(Instance, "/user/hand/left", &leftHandPath_)); + OXR(xrStringToPath(Instance, "/user/hand/right", &rightHandPath_)); + OXR(xrStringToPath( + Instance, "/user/detached_controller_meta/left", &leftDetachedControllerPath_)); + OXR(xrStringToPath( + Instance, "/user/detached_controller_meta/right", &rightDetachedControllerPath_)); + + // Get the default bindings suggested by XrApp framework + auto suggestedBindings = XrApp::GetSuggestedBindings(instance); + actionSetMenu_ = CreateActionSet(0, "menu_action_set", "UI Action Set"); + actionSetWorld_ = CreateActionSet(0, "world_action_set", "World Action Set"); + actionSetGestures_ = CreateActionSet(0, "gesture_action_set", "Gesture Action Set"); + actionSetExtHandInteractionValues_ = CreateActionSet(0, "ext_hand_interaction_values_action_set", "Ext Hand Interaction Values Action Set"); + actionSetExtHandInteractionReady_ = CreateActionSet(0, "ext_hand_interaction_ready_action_set", "Ext Hand Interaction Ready_Ext Action Set"); + actionSelect_ = CreateAction( + actionSetMenu_, + XR_ACTION_TYPE_BOOLEAN_INPUT, + "select", + "Select/Click UI Element" // Displayed to users, should be translated to the user's + // local language + ); + + actionGrab_ = CreateAction( + actionSetWorld_, XR_ACTION_TYPE_BOOLEAN_INPUT, "action_grab", "Simple Grab"); + actionTrigger_ = CreateAction( + actionSetGestures_, XR_ACTION_TYPE_BOOLEAN_INPUT, "action_trigger", "Simple Trigger"); + actionThumbstick_ = CreateAction( + actionSetGestures_, + XR_ACTION_TYPE_VECTOR2F_INPUT, + "action_thumbstick", + "Simple Thumbstick"); + actionThumbstickX_ = CreateAction( + actionSetGestures_, + XR_ACTION_TYPE_FLOAT_INPUT, + "action_thumbstick_x", + "Simple Thumbstick X"); + actionThumbstickY_ = CreateAction( + actionSetGestures_, + XR_ACTION_TYPE_FLOAT_INPUT, + "action_thumbstick_y", + "Simple Thumbstick Y"); + + // Actions for testing hands interaction + actionHandSelect_ = CreateAction( + actionSetMenu_, + XR_ACTION_TYPE_FLOAT_INPUT, + "hand_select", + "Select For Test" // Displayed to users, should be translated to the user's + // local language + ); + + actionHandSqueeze_ = CreateAction( + actionSetMenu_, + XR_ACTION_TYPE_BOOLEAN_INPUT, + "squeeze", + "Squeeze For Test" // Displayed to users, should be translated to the user's + // local language + ); + + // Actions for validating Ext Hand Interaction Values + actionExtHandInteractionLeftPinchValue_ = CreateAction( + actionSetExtHandInteractionValues_, + XR_ACTION_TYPE_FLOAT_INPUT, + "action_ext_hand_interaction_left_pinch_value", + "Left Hand Pinch Value"); + actionExtHandInteractionRightPinchValue_ = CreateAction( + actionSetExtHandInteractionValues_, + XR_ACTION_TYPE_FLOAT_INPUT, + "action_ext_hand_interaction_right_pinch_value", + "Right Hand Pinch Value"); + actionExtHandInteractionLeftGraspValue_ = CreateAction( + actionSetExtHandInteractionValues_, + XR_ACTION_TYPE_FLOAT_INPUT, + "action_ext_hand_interaction_left_grasp_value", + "Left Hand Grasp Value"); + actionExtHandInteractionRightGraspValue_ = CreateAction( + actionSetExtHandInteractionValues_, + XR_ACTION_TYPE_FLOAT_INPUT, + "action_ext_hand_interaction_right_grasp_value", + "Right Hand Grasp Value"); + actionExtHandInteractionLeftAimActivateValue_ = CreateAction( + actionSetExtHandInteractionValues_, + XR_ACTION_TYPE_FLOAT_INPUT, + "action_ext_hand_interaction_left_aim_activate_value", + "Left Hand Aim Activate Value"); + actionExtHandInteractionRightAimActivateValue_ = CreateAction( + actionSetExtHandInteractionValues_, + XR_ACTION_TYPE_FLOAT_INPUT, + "action_ext_hand_interaction_right_aim_activate_value", + "Right Hand Aim Activate Value"); + + actionExtHandInteractionLeftPinchReady_ = CreateAction( + actionSetExtHandInteractionReady_, + XR_ACTION_TYPE_BOOLEAN_INPUT, + "action_ext_hand_interaction_left_pinch_ready", + "Left Hand Pinch Ready"); + actionExtHandInteractionRightPinchReady_ = CreateAction( + actionSetExtHandInteractionReady_, + XR_ACTION_TYPE_BOOLEAN_INPUT, + "action_ext_hand_interaction_right_pinch_ready", + "Right Hand Pinch Ready"); + actionExtHandInteractionLeftGraspReady_ = CreateAction( + actionSetExtHandInteractionReady_, + XR_ACTION_TYPE_BOOLEAN_INPUT, + "action_ext_hand_interaction_left_grasp_ready", + "Left Hand Grasp Ready"); + actionExtHandInteractionRightGraspReady_ = CreateAction( + actionSetExtHandInteractionReady_, + XR_ACTION_TYPE_BOOLEAN_INPUT, + "action_ext_hand_interaction_right_grasp_ready", + "Right Hand Grasp Ready"); + actionExtHandInteractionLeftAimActivateReady_ = CreateAction( + actionSetExtHandInteractionReady_, + XR_ACTION_TYPE_BOOLEAN_INPUT, + "action_ext_hand_interaction_left_aim_activate_ready", + "Left Hand Aim Activate Ready"); + actionExtHandInteractionRightAimActivateReady_ = CreateAction( + actionSetExtHandInteractionReady_, + XR_ACTION_TYPE_BOOLEAN_INPUT, + "action_ext_hand_interaction_right_aim_activate_ready", + "Right Hand Aim Activate Ready"); + + // In order to be able to distinguish between left and right hand versions + // of these actions, we need to pass in subactionPaths + XrPath handsTopLevelPaths[]{leftHandPath_, rightHandPath_}; + actionControllerAimPose_ = CreateAction( + actionSetWorld_, + XR_ACTION_TYPE_POSE_INPUT, + "controller_aim_pose", + "Controller Aim Pose", + 2, + handsTopLevelPaths); + + actionControllerGripPose_ = CreateAction( + actionSetWorld_, + XR_ACTION_TYPE_POSE_INPUT, + "controller_grip_pose", + "Controller Grip Pose", + 2, + handsTopLevelPaths); + + actionHandAimPose_ = CreateAction( + actionSetWorld_, + XR_ACTION_TYPE_POSE_INPUT, + "hand_aim_pose", + "Hand Aim Pose", + 2, + handsTopLevelPaths); + + actionHandGripPose_ = CreateAction( + actionSetWorld_, + XR_ACTION_TYPE_POSE_INPUT, + "hand_grip_pose", + "Hand Grip Pose", + 2, + handsTopLevelPaths); + + actionHandPinchPose_ = CreateAction( + actionSetWorld_, + XR_ACTION_TYPE_POSE_INPUT, + "hand_pinch_pose", + "Hand Pinch Pose", + 2, + handsTopLevelPaths); + + actionHandPokePose_ = CreateAction( + actionSetWorld_, + XR_ACTION_TYPE_POSE_INPUT, + "hand_poke_pose", + "Hand Poke Pose", + 2, + handsTopLevelPaths); + + XrPath detachedControllerPaths[]{leftDetachedControllerPath_, rightDetachedControllerPath_}; + actionDetachedControllerAimPose_ = CreateAction( + actionSetWorld_, + XR_ACTION_TYPE_POSE_INPUT, + "controller_detached_aim_pose", + "Controller Detached Aim Pose", + 2, + detachedControllerPaths); + + actionDetachedControllerGripPose_ = CreateAction( + actionSetWorld_, + XR_ACTION_TYPE_POSE_INPUT, + "controller_detached_grip_pose", + "Controller Detached Grip Pose", + 2, + detachedControllerPaths); + + /// haptics + attachedHapticAction_ = CreateAction( + actionSetWorld_, + XR_ACTION_TYPE_VIBRATION_OUTPUT, + "attached_haptic", + nullptr, + 2, + handsTopLevelPaths); + detachedHapticAction_ = CreateAction( + actionSetWorld_, + XR_ACTION_TYPE_VIBRATION_OUTPUT, + "detached_haptic", + nullptr, + 2, + detachedControllerPaths); + + /////////////////////////////////////////////////////////// + /// touch_controller suggested bindings + /////////////////////////////////////////////////////////// + + OXR(xrStringToPath( + instance, + "/interaction_profiles/oculus/touch_controller", + &touchInteractionProfile_)); + + // Binding the same action to both hands is not a problem, + // since you can use subActionPath later to distinguish them + suggestedBindings[touchInteractionProfile_].emplace_back( + ActionSuggestedBinding(actionSelect_, "/user/hand/left/input/trigger/value")); + suggestedBindings[touchInteractionProfile_].emplace_back( + ActionSuggestedBinding(actionSelect_, "/user/hand/right/input/trigger/value")); + suggestedBindings[touchInteractionProfile_].emplace_back( + ActionSuggestedBinding(actionThumbstick_, "/user/hand/left/input/thumbstick")); + suggestedBindings[touchInteractionProfile_].emplace_back( + ActionSuggestedBinding(actionThumbstickX_, "/user/hand/right/input/thumbstick/x")); + suggestedBindings[touchInteractionProfile_].emplace_back( + ActionSuggestedBinding(actionThumbstickY_, "/user/hand/right/input/thumbstick/y")); + suggestedBindings[touchInteractionProfile_].emplace_back( + ActionSuggestedBinding(actionGrab_, "/user/hand/left/input/squeeze/value")); + suggestedBindings[touchInteractionProfile_].emplace_back( + ActionSuggestedBinding(actionGrab_, "/user/hand/right/input/squeeze/value")); + suggestedBindings[touchInteractionProfile_].emplace_back( + ActionSuggestedBinding(actionTrigger_, "/user/hand/left/input/trigger/value")); + suggestedBindings[touchInteractionProfile_].emplace_back( + ActionSuggestedBinding(actionTrigger_, "/user/hand/right/input/trigger/value")); + suggestedBindings[touchInteractionProfile_].emplace_back( + ActionSuggestedBinding(actionControllerAimPose_, "/user/hand/left/input/aim/pose")); + suggestedBindings[touchInteractionProfile_].emplace_back( + ActionSuggestedBinding(actionControllerAimPose_, "/user/hand/right/input/aim/pose")); + suggestedBindings[touchInteractionProfile_].emplace_back( + ActionSuggestedBinding(actionControllerGripPose_, "/user/hand/left/input/grip/pose")); + suggestedBindings[touchInteractionProfile_].emplace_back( + ActionSuggestedBinding(actionControllerGripPose_, "/user/hand/right/input/grip/pose")); + + if (isDetachedControllersExtensionAvailable_) { + // Detached controllers aim and grip poses + suggestedBindings[touchProInteractionProfile_].emplace_back(ActionSuggestedBinding( + actionDetachedControllerAimPose_, + "/user/detached_controller_meta/left/input/aim/pose")); + suggestedBindings[touchProInteractionProfile_].emplace_back(ActionSuggestedBinding( + actionDetachedControllerAimPose_, + "/user/detached_controller_meta/right/input/aim/pose")); + suggestedBindings[touchProInteractionProfile_].emplace_back(ActionSuggestedBinding( + actionDetachedControllerGripPose_, + "/user/detached_controller_meta/right/input/grip/pose")); + suggestedBindings[touchProInteractionProfile_].emplace_back(ActionSuggestedBinding( + actionDetachedControllerGripPose_, + "/user/detached_controller_meta/left/input/grip/pose")); + } + + /////////////////////////////////////////////////////////// + /// touch_controller_pro suggested bindings + /////////////////////////////////////////////////////////// + OXR(xrStringToPath( + instance, + "/interaction_profiles/facebook/touch_controller_pro", + &touchProInteractionProfile_)); + + // Binding the same action to both hands is not a problem, + // since you can use subActionPath later to distinguish them + suggestedBindings[touchProInteractionProfile_].emplace_back( + ActionSuggestedBinding(actionSelect_, "/user/hand/left/input/trigger/value")); + suggestedBindings[touchProInteractionProfile_].emplace_back( + ActionSuggestedBinding(actionSelect_, "/user/hand/right/input/trigger/value")); + suggestedBindings[touchProInteractionProfile_].emplace_back( + ActionSuggestedBinding(actionThumbstick_, "/user/hand/left/input/thumbstick")); + suggestedBindings[touchProInteractionProfile_].emplace_back( + ActionSuggestedBinding(actionThumbstickX_, "/user/hand/right/input/thumbstick/x")); + suggestedBindings[touchProInteractionProfile_].emplace_back( + ActionSuggestedBinding(actionThumbstickY_, "/user/hand/right/input/thumbstick/y")); + suggestedBindings[touchProInteractionProfile_].emplace_back( + ActionSuggestedBinding(actionGrab_, "/user/hand/left/input/squeeze/value")); + suggestedBindings[touchProInteractionProfile_].emplace_back( + ActionSuggestedBinding(actionGrab_, "/user/hand/right/input/squeeze/value")); + suggestedBindings[touchProInteractionProfile_].emplace_back( + ActionSuggestedBinding(actionTrigger_, "/user/hand/left/input/trigger/value")); + suggestedBindings[touchProInteractionProfile_].emplace_back( + ActionSuggestedBinding(actionTrigger_, "/user/hand/right/input/trigger/value")); + suggestedBindings[touchProInteractionProfile_].emplace_back( + ActionSuggestedBinding(actionControllerAimPose_, "/user/hand/left/input/aim/pose")); + suggestedBindings[touchProInteractionProfile_].emplace_back( + ActionSuggestedBinding(actionControllerAimPose_, "/user/hand/right/input/aim/pose")); + suggestedBindings[touchProInteractionProfile_].emplace_back( + ActionSuggestedBinding(actionControllerGripPose_, "/user/hand/left/input/grip/pose")); + suggestedBindings[touchProInteractionProfile_].emplace_back( + ActionSuggestedBinding(actionControllerGripPose_, "/user/hand/right/input/grip/pose")); + suggestedBindings[touchProInteractionProfile_].emplace_back( + ActionSuggestedBinding(attachedHapticAction_, "/user/hand/left/output/haptic")); + suggestedBindings[touchProInteractionProfile_].emplace_back( + ActionSuggestedBinding(attachedHapticAction_, "/user/hand/right/output/haptic")); + + if (isDetachedControllersExtensionAvailable_) { + // Detached controllers aim and grip poses + suggestedBindings[touchProInteractionProfile_].emplace_back(ActionSuggestedBinding( + actionDetachedControllerAimPose_, + "/user/detached_controller_meta/left/input/aim/pose")); + suggestedBindings[touchProInteractionProfile_].emplace_back(ActionSuggestedBinding( + actionDetachedControllerAimPose_, + "/user/detached_controller_meta/right/input/aim/pose")); + suggestedBindings[touchProInteractionProfile_].emplace_back(ActionSuggestedBinding( + actionDetachedControllerGripPose_, + "/user/detached_controller_meta/right/input/grip/pose")); + suggestedBindings[touchProInteractionProfile_].emplace_back(ActionSuggestedBinding( + actionDetachedControllerGripPose_, + "/user/detached_controller_meta/left/input/grip/pose")); + // Detached controller haptic + suggestedBindings[touchProInteractionProfile_].emplace_back(ActionSuggestedBinding( + detachedHapticAction_, "/user/detached_controller_meta/left/output/haptic")); + suggestedBindings[touchProInteractionProfile_].emplace_back(ActionSuggestedBinding( + detachedHapticAction_, "/user/detached_controller_meta/right/output/haptic")); + } + + /////////////////////////////////////////////////////////// + /// touch_controller_plus suggested bindings + /////////////////////////////////////////////////////////// + OXR(xrStringToPath( + instance, + "/interaction_profiles/meta/touch_controller_plus", + &touchPlusInteractionProfile_)); + + // Binding the same action to both hands is not a problem, + // since you can use subActionPath later to distinguish them + suggestedBindings[touchPlusInteractionProfile_].emplace_back( + ActionSuggestedBinding(actionSelect_, "/user/hand/left/input/trigger/value")); + suggestedBindings[touchPlusInteractionProfile_].emplace_back( + ActionSuggestedBinding(actionSelect_, "/user/hand/right/input/trigger/value")); + suggestedBindings[touchPlusInteractionProfile_].emplace_back( + ActionSuggestedBinding(actionThumbstick_, "/user/hand/left/input/thumbstick")); + suggestedBindings[touchPlusInteractionProfile_].emplace_back( + ActionSuggestedBinding(actionThumbstickX_, "/user/hand/right/input/thumbstick/x")); + suggestedBindings[touchPlusInteractionProfile_].emplace_back( + ActionSuggestedBinding(actionThumbstickY_, "/user/hand/right/input/thumbstick/y")); + suggestedBindings[touchPlusInteractionProfile_].emplace_back( + ActionSuggestedBinding(actionGrab_, "/user/hand/left/input/squeeze/value")); + suggestedBindings[touchPlusInteractionProfile_].emplace_back( + ActionSuggestedBinding(actionGrab_, "/user/hand/right/input/squeeze/value")); + suggestedBindings[touchPlusInteractionProfile_].emplace_back( + ActionSuggestedBinding(actionTrigger_, "/user/hand/left/input/trigger/value")); + suggestedBindings[touchPlusInteractionProfile_].emplace_back( + ActionSuggestedBinding(actionTrigger_, "/user/hand/right/input/trigger/value")); + suggestedBindings[touchPlusInteractionProfile_].emplace_back( + ActionSuggestedBinding(actionControllerAimPose_, "/user/hand/left/input/aim/pose")); + suggestedBindings[touchPlusInteractionProfile_].emplace_back( + ActionSuggestedBinding(actionControllerAimPose_, "/user/hand/right/input/aim/pose")); + suggestedBindings[touchPlusInteractionProfile_].emplace_back( + ActionSuggestedBinding(actionControllerGripPose_, "/user/hand/left/input/grip/pose")); + suggestedBindings[touchPlusInteractionProfile_].emplace_back( + ActionSuggestedBinding(actionControllerGripPose_, "/user/hand/right/input/grip/pose")); + suggestedBindings[touchPlusInteractionProfile_].emplace_back( + ActionSuggestedBinding(attachedHapticAction_, "/user/hand/left/output/haptic")); + suggestedBindings[touchPlusInteractionProfile_].emplace_back( + ActionSuggestedBinding(attachedHapticAction_, "/user/hand/right/output/haptic")); + + if (isDetachedControllersExtensionAvailable_) { + // Detached controllers aim and grip poses + suggestedBindings[touchPlusInteractionProfile_].emplace_back(ActionSuggestedBinding( + actionDetachedControllerAimPose_, + "/user/detached_controller_meta/left/input/aim/pose")); + suggestedBindings[touchPlusInteractionProfile_].emplace_back(ActionSuggestedBinding( + actionDetachedControllerAimPose_, + "/user/detached_controller_meta/right/input/aim/pose")); + suggestedBindings[touchPlusInteractionProfile_].emplace_back(ActionSuggestedBinding( + actionDetachedControllerGripPose_, + "/user/detached_controller_meta/right/input/grip/pose")); + suggestedBindings[touchPlusInteractionProfile_].emplace_back(ActionSuggestedBinding( + actionDetachedControllerGripPose_, + "/user/detached_controller_meta/left/input/grip/pose")); + + // Detached controller haptic + suggestedBindings[touchPlusInteractionProfile_].emplace_back(ActionSuggestedBinding( + detachedHapticAction_, "/user/detached_controller_meta/left/output/haptic")); + suggestedBindings[touchPlusInteractionProfile_].emplace_back(ActionSuggestedBinding( + detachedHapticAction_, "/user/detached_controller_meta/right/output/haptic")); + } + + /////////////////////////////////////////////////////////// + /// EXT_hand_interaction suggested bindings + /////////////////////////////////////////////////////////// + if (isExtHandInteractionExtensionAvailable_) { + OXR(xrStringToPath( + instance, + "/interaction_profiles/ext/hand_interaction_ext", + &extHandInteractionProfile_)); + + // Start with basic interactions + suggestedBindings[extHandInteractionProfile_].emplace_back( + ActionSuggestedBinding(actionHandSelect_, "/user/hand/left/input/pinch_ext/value")); + suggestedBindings[extHandInteractionProfile_].emplace_back( + ActionSuggestedBinding(actionHandSelect_, "/user/hand/right/input/pinch_ext/value")); + suggestedBindings[extHandInteractionProfile_].emplace_back( + ActionSuggestedBinding(actionHandSqueeze_, "/user/hand/left/input/aim_activate_ext/value")); + suggestedBindings[extHandInteractionProfile_].emplace_back( + ActionSuggestedBinding(actionHandSqueeze_, "/user/hand/right/input/aim_activate_ext/value")); + + // Further bind the raw values, to make it easier to display/debug + suggestedBindings[extHandInteractionProfile_].emplace_back( + ActionSuggestedBinding(actionExtHandInteractionLeftPinchValue_, "/user/hand/left/input/pinch_ext/value")); + suggestedBindings[extHandInteractionProfile_].emplace_back( + ActionSuggestedBinding(actionExtHandInteractionRightPinchValue_, "/user/hand/right/input/pinch_ext/value")); + suggestedBindings[extHandInteractionProfile_].emplace_back( + ActionSuggestedBinding(actionExtHandInteractionLeftAimActivateValue_, "/user/hand/left/input/aim_activate_ext/value")); + suggestedBindings[extHandInteractionProfile_].emplace_back( + ActionSuggestedBinding(actionExtHandInteractionRightAimActivateValue_, "/user/hand/right/input/aim_activate_ext/value")); + suggestedBindings[extHandInteractionProfile_].emplace_back( + ActionSuggestedBinding(actionExtHandInteractionLeftGraspValue_, "/user/hand/left/input/grasp_ext/value")); + suggestedBindings[extHandInteractionProfile_].emplace_back( + ActionSuggestedBinding(actionExtHandInteractionRightGraspValue_, "/user/hand/right/input/grasp_ext/value")); + + suggestedBindings[extHandInteractionProfile_].emplace_back( + ActionSuggestedBinding(actionExtHandInteractionLeftPinchReady_, "/user/hand/left/input/pinch_ext/ready_ext")); + suggestedBindings[extHandInteractionProfile_].emplace_back( + ActionSuggestedBinding(actionExtHandInteractionRightPinchReady_, "/user/hand/right/input/pinch_ext/ready_ext")); + suggestedBindings[extHandInteractionProfile_].emplace_back( + ActionSuggestedBinding(actionExtHandInteractionLeftAimActivateReady_, "/user/hand/left/input/aim_activate_ext/ready_ext")); + suggestedBindings[extHandInteractionProfile_].emplace_back( + ActionSuggestedBinding(actionExtHandInteractionRightAimActivateReady_, "/user/hand/right/input/aim_activate_ext/ready_ext")); + suggestedBindings[extHandInteractionProfile_].emplace_back( + ActionSuggestedBinding(actionExtHandInteractionLeftGraspReady_, "/user/hand/left/input/grasp_ext/ready_ext")); + suggestedBindings[extHandInteractionProfile_].emplace_back( + ActionSuggestedBinding(actionExtHandInteractionRightGraspReady_, "/user/hand/right/input/grasp_ext/ready_ext")); + + // Hands aim, grip, pinch, & poke poses + // T160738252: Pinch & poke funnel the data through but don't do anything right now + suggestedBindings[extHandInteractionProfile_].emplace_back( + ActionSuggestedBinding(actionHandAimPose_, "/user/hand/left/input/aim/pose")); + suggestedBindings[extHandInteractionProfile_].emplace_back( + ActionSuggestedBinding(actionHandAimPose_, "/user/hand/right/input/aim/pose")); + suggestedBindings[extHandInteractionProfile_].emplace_back( + ActionSuggestedBinding(actionHandGripPose_, "/user/hand/left/input/grip/pose")); + suggestedBindings[extHandInteractionProfile_].emplace_back( + ActionSuggestedBinding(actionHandGripPose_, "/user/hand/right/input/grip/pose")); + suggestedBindings[extHandInteractionProfile_].emplace_back( + ActionSuggestedBinding(actionHandPinchPose_, "/user/hand/left/input/pinch_ext/pose")); + suggestedBindings[extHandInteractionProfile_].emplace_back( + ActionSuggestedBinding(actionHandPinchPose_, "/user/hand/right/input/pinch_ext/pose")); + suggestedBindings[extHandInteractionProfile_].emplace_back( + ActionSuggestedBinding(actionHandPokePose_, "/user/hand/left/input/poke_ext/pose")); + suggestedBindings[extHandInteractionProfile_].emplace_back( + ActionSuggestedBinding(actionHandPokePose_, "/user/hand/right/input/poke_ext/pose")); + } + + return suggestedBindings; + } + + virtual bool AppInit(const xrJava* context) override { + int fontVertexBufferSize = 32 * 1024; // Custom large text buffer size for all the text + bool updateColors = true; // Update UI colors on interaction + if (false == ui_.Init(context, GetFileSys(), updateColors, fontVertexBufferSize)) { + ALOG("TinyUI::Init FAILED."); + return false; + } + + auto fileSys = std::unique_ptr(OVRFW::ovrFileSys::Create(*context)); + + if( fileSys ) { + std::string environmentPath = "apk:///assets/SmallRoom.gltf.ovrscene"; + environmentRenderer_.Init(environmentPath, fileSys.get()); + std::string skyboxPath = "apk:///assets/Skybox.gltf.ovrscene"; + skyboxRenderer_.Init(skyboxPath, fileSys.get()); + } + + // Inspect hand tracking system properties + XrSystemHandTrackingPropertiesEXT handTrackingSystemProperties{ + XR_TYPE_SYSTEM_HAND_TRACKING_PROPERTIES_EXT}; + XrSystemProperties systemProperties{ + XR_TYPE_SYSTEM_PROPERTIES, &handTrackingSystemProperties}; + XrSystemSimultaneousHandsAndControllersPropertiesMETA simultaneousHandsControllersSystemProperties{ + XR_TYPE_SYSTEM_SIMULTANEOUS_HANDS_AND_CONTROLLERS_PROPERTIES_META}; + reinterpret_cast(&handTrackingSystemProperties)->next = + reinterpret_cast(&simultaneousHandsControllersSystemProperties); + OXR(xrGetSystemProperties(GetInstance(), GetSystemId(), &systemProperties)); + if (!handTrackingSystemProperties.supportsHandTracking || + !simultaneousHandsControllersSystemProperties.supportsSimultaneousHandsAndControllers) { + ALOG("System does not support simultaneous hands and controllers. Exiting."); + return false; + } + + /// Hook up extensions for hand tracking, as well as simultaneous hands and controllers. + OXR(xrGetInstanceProcAddr( + GetInstance(), + "xrCreateHandTrackerEXT", + (PFN_xrVoidFunction*)(&xrCreateHandTrackerEXT_))); + assert(xrCreateHandTrackerEXT_); + OXR(xrGetInstanceProcAddr( + GetInstance(), + "xrDestroyHandTrackerEXT", + (PFN_xrVoidFunction*)(&xrDestroyHandTrackerEXT_))); + assert(xrDestroyHandTrackerEXT_); + OXR(xrGetInstanceProcAddr( + GetInstance(), + "xrLocateHandJointsEXT", + (PFN_xrVoidFunction*)(&xrLocateHandJointsEXT_))); + assert(xrLocateHandJointsEXT_); + OXR(xrGetInstanceProcAddr( + GetInstance(), "xrGetHandMeshFB", (PFN_xrVoidFunction*)(&xrGetHandMeshFB_))); + assert(xrGetHandMeshFB_); + OXR(xrGetInstanceProcAddr( + GetInstance(), + "xrResumeSimultaneousHandsAndControllersTrackingMETA", + (PFN_xrVoidFunction*)(&xrResumeSimultaneousHandsAndControllersTrackingMETA_))); + assert(xrResumeSimultaneousHandsAndControllersTrackingMETA_); + OXR(xrGetInstanceProcAddr( + GetInstance(), + "xrPauseSimultaneousHandsAndControllersTrackingMETA", + (PFN_xrVoidFunction*)(&xrPauseSimultaneousHandsAndControllersTrackingMETA_))); + assert(xrPauseSimultaneousHandsAndControllersTrackingMETA_); + + return true; + } + + virtual bool SessionInit() override { + + /// Use LocalSpace instead of Stage Space. + CurrentSpace = LocalSpace; + + /// Disable scene navigation + GetScene().SetFootPos({0.0_m, 0.0_m, 0.0_m}); + this->FreeMove = false; + + XrActionSpaceCreateInfo actionSpaceCreateInfo{XR_TYPE_ACTION_SPACE_CREATE_INFO}; + actionSpaceCreateInfo.action = actionControllerAimPose_; + actionSpaceCreateInfo.poseInActionSpace = {{0, 0, 0, 1}, {0_m, 0_m, 0_m}}; + + actionSpaceCreateInfo.subactionPath = leftHandPath_; + OXR(xrCreateActionSpace(GetSession(), &actionSpaceCreateInfo, &spaceControllerAimLeft_)); + actionSpaceCreateInfo.subactionPath = rightHandPath_; + OXR(xrCreateActionSpace(GetSession(), &actionSpaceCreateInfo, &spaceControllerAimRight_)); + + actionSpaceCreateInfo.action = actionControllerGripPose_; + actionSpaceCreateInfo.subactionPath = leftHandPath_; + OXR(xrCreateActionSpace(GetSession(), &actionSpaceCreateInfo, &spaceControllerGripLeft_)); + actionSpaceCreateInfo.subactionPath = rightHandPath_; + OXR(xrCreateActionSpace(GetSession(), &actionSpaceCreateInfo, &spaceControllerGripRight_)); + + // Hands + actionSpaceCreateInfo.action = actionHandAimPose_; + actionSpaceCreateInfo.subactionPath = leftHandPath_; + OXR(xrCreateActionSpace(GetSession(), &actionSpaceCreateInfo, &spaceHandAimLeft_)); + actionSpaceCreateInfo.subactionPath = rightHandPath_; + OXR(xrCreateActionSpace(GetSession(), &actionSpaceCreateInfo, &spaceHandAimRight_)); + + actionSpaceCreateInfo.action = actionHandGripPose_; + actionSpaceCreateInfo.subactionPath = leftHandPath_; + OXR(xrCreateActionSpace(GetSession(), &actionSpaceCreateInfo, &spaceHandGripLeft_)); + actionSpaceCreateInfo.subactionPath = rightHandPath_; + OXR(xrCreateActionSpace(GetSession(), &actionSpaceCreateInfo, &spaceHandGripRight_)); + + actionSpaceCreateInfo.action = actionHandPinchPose_; + actionSpaceCreateInfo.subactionPath = leftHandPath_; + OXR(xrCreateActionSpace(GetSession(), &actionSpaceCreateInfo, &spaceHandPinchLeft_)); + actionSpaceCreateInfo.subactionPath = rightHandPath_; + OXR(xrCreateActionSpace(GetSession(), &actionSpaceCreateInfo, &spaceHandPinchRight_)); + + actionSpaceCreateInfo.action = actionHandPokePose_; + actionSpaceCreateInfo.subactionPath = leftHandPath_; + OXR(xrCreateActionSpace(GetSession(), &actionSpaceCreateInfo, &spaceHandPokeLeft_)); + actionSpaceCreateInfo.subactionPath = rightHandPath_; + OXR(xrCreateActionSpace(GetSession(), &actionSpaceCreateInfo, &spaceHandPokeRight_)); + + actionSpaceCreateInfo.action = actionDetachedControllerAimPose_; + actionSpaceCreateInfo.poseInActionSpace = {{0, 0, 0, 1}, {0_m, 0_m, 0_m}}; + actionSpaceCreateInfo.subactionPath = leftDetachedControllerPath_; + OXR(xrCreateActionSpace( + GetSession(), &actionSpaceCreateInfo, &spaceDetachedControllerAimLeft_)); + actionSpaceCreateInfo.subactionPath = rightDetachedControllerPath_; + OXR(xrCreateActionSpace( + GetSession(), &actionSpaceCreateInfo, &spaceDetachedControllerAimRight_)); + actionSpaceCreateInfo.action = actionDetachedControllerGripPose_; + actionSpaceCreateInfo.subactionPath = leftDetachedControllerPath_; + OXR(xrCreateActionSpace( + GetSession(), &actionSpaceCreateInfo, &spaceDetachedControllerGripLeft_)); + actionSpaceCreateInfo.subactionPath = rightDetachedControllerPath_; + OXR(xrCreateActionSpace( + GetSession(), &actionSpaceCreateInfo, &spaceDetachedControllerGripRight_)); + + { + // Attach ActionSets to session + // This is required before any call to xrSyncActions for these action sets + std::vector actionSets{ + {actionSetWorld_, actionSetMenu_, actionSetGestures_, actionSetExtHandInteractionValues_, actionSetExtHandInteractionReady_}}; + XrSessionActionSetsAttachInfo attachInfo{XR_TYPE_SESSION_ACTION_SETS_ATTACH_INFO}; + attachInfo.countActionSets = actionSets.size(); + attachInfo.actionSets = actionSets.data(); + OXR(xrAttachSessionActionSets(Session, &attachInfo)); + // After this point all actions and bindings are final for the session + // (calls to xrSuggestInteractionProfileBindings and xrAttachSessionActionSets fail) + } + + ////////////////////////////////////////////////// + /// Create reference spaces + ////////////////////////////////////////////////// + XrReferenceSpaceCreateInfo referenceSpaceCreateInfo{XR_TYPE_REFERENCE_SPACE_CREATE_INFO}; + referenceSpaceCreateInfo.poseInReferenceSpace = {{0, 0, 0, 1}, {0_m, 0_m, 0_m}}; + referenceSpaceCreateInfo.referenceSpaceType = XR_REFERENCE_SPACE_TYPE_LOCAL; + OXR(xrCreateReferenceSpace(GetSession(), &referenceSpaceCreateInfo, &spaceLocal_)); + + { + // Setup all the UI panels to display the state of each action + actionSetPanels_.insert( + {actionSetMenu_, + ActionSetDisplayPanel( + "Menu Action Set", Session, Instance, &ui_, {-2.0_m, 2.5_m, -2.5_m})}); + actionSetPanels_.at(actionSetMenu_).AddBoolAction(actionSelect_, "Select"); + + actionSetPanels_.insert( + {actionSetWorld_, + ActionSetDisplayPanel( + "World Action Set", Session, Instance, &ui_, {-0.5_m, 2.5_m, -2.5_m})}); + actionSetPanels_.at(actionSetWorld_).AddBoolAction(actionGrab_, "Controller Grab"); + actionSetPanels_.at(actionSetWorld_) + .AddPoseAction(actionControllerGripPose_, "Controller Grip Pose"); + actionSetPanels_.at(actionSetWorld_) + .AddPoseAction(actionControllerAimPose_, "Controller Aim Pose"); + + if (isDetachedControllersExtensionAvailable_) { + actionSetPanels_.at(actionSetWorld_) + .AddPoseAction(actionDetachedControllerGripPose_, "Detached Grip Pose"); + actionSetPanels_.at(actionSetWorld_) + .AddPoseAction(actionDetachedControllerAimPose_, "Detached Aim Pose"); + } + + + actionSetPanels_.insert( + {actionSetGestures_, + ActionSetDisplayPanel( + "Gesture Action Set", Session, Instance, &ui_, {1.0_m, 2.5_m, -2.5_m})}); + actionSetPanels_.at(actionSetGestures_).AddBoolAction(actionTrigger_, "Trigger"); + actionSetPanels_.at(actionSetGestures_).AddVec2Action(actionThumbstick_, "Thumbstick"); + actionSetPanels_.at(actionSetGestures_).AddFloatAction(actionThumbstickX_, "Scale"); + actionSetPanels_.at(actionSetGestures_).AddFloatAction(actionThumbstickY_, "Translate"); + + if (isExtHandInteractionExtensionAvailable_) { + handActionSetPanels_.insert( + {actionSetExtHandInteractionValues_, + ActionSetDisplayPanel( + "Ext Hand Interaction Values Action Set", Session, Instance, &ui_, {2.5_m, 3.0_m, -1.5_m})}); + handActionSetPanels_.at(actionSetExtHandInteractionValues_).AddFloatAction(actionExtHandInteractionLeftPinchValue_, "Left Pinch Value"); + handActionSetPanels_.at(actionSetExtHandInteractionValues_).AddFloatAction(actionExtHandInteractionLeftAimActivateValue_, "Left Aim Activate Value"); + handActionSetPanels_.at(actionSetExtHandInteractionValues_).AddFloatAction(actionExtHandInteractionLeftGraspValue_, "Left Grasp Value"); + + handActionSetPanels_.at(actionSetExtHandInteractionValues_).AddFloatAction(actionExtHandInteractionRightPinchValue_, "Right Pinch Value"); + handActionSetPanels_.at(actionSetExtHandInteractionValues_).AddFloatAction(actionExtHandInteractionRightAimActivateValue_, "Right Aim Activate Value"); + handActionSetPanels_.at(actionSetExtHandInteractionValues_).AddFloatAction(actionExtHandInteractionRightGraspValue_, "Right Grasp Value"); + + handActionSetPanels_.insert( + {actionSetExtHandInteractionReady_, + ActionSetDisplayPanel( + "Ext Hand Interaction Ready_Ext Action Set", Session, Instance, &ui_, {3.5_m, 3.0_m, -0.5_m})}); + handActionSetPanels_.at(actionSetExtHandInteractionReady_).AddBoolAction(actionExtHandInteractionLeftPinchReady_, "Left Pinch Ready"); + handActionSetPanels_.at(actionSetExtHandInteractionReady_).AddBoolAction(actionExtHandInteractionLeftAimActivateReady_, "Left Aim Activate Ready"); + handActionSetPanels_.at(actionSetExtHandInteractionReady_).AddBoolAction(actionExtHandInteractionLeftGraspReady_, "Left Grasp Ready"); + + handActionSetPanels_.at(actionSetExtHandInteractionReady_).AddBoolAction(actionExtHandInteractionRightPinchReady_, "Right Pinch Ready"); + handActionSetPanels_.at(actionSetExtHandInteractionReady_).AddBoolAction(actionExtHandInteractionRightAimActivateReady_, "Right Aim Activate Ready"); + handActionSetPanels_.at(actionSetExtHandInteractionReady_).AddBoolAction(actionExtHandInteractionRightGraspReady_, "Right Grasp Ready"); + + handActionSetPanels_.insert( + {actionSetWorld_, + ActionSetDisplayPanel( + "Ext Hand Interaction Poses", Session, Instance, &ui_, {4.5_m, 2.45_m, 0.8_m})}); + handActionSetPanels_.at(actionSetWorld_).AddPoseAction(actionHandAimPose_, "Hand Aim Pose"); + handActionSetPanels_.at(actionSetWorld_).AddPoseAction(actionHandGripPose_, "Hand Grip Pose"); + handActionSetPanels_.at(actionSetWorld_).AddPoseAction(actionHandPinchPose_, "Hand Pinch Pose"); + handActionSetPanels_.at(actionSetWorld_).AddPoseAction(actionHandPokePose_, "Hand Poke Pose"); + + // Update the panel orientation to make the panels all visible + for (auto& panelPair : handActionSetPanels_) { + panelPair.second.UpdateAllLabelRotation( + OVR::Quat::FromRotationVector({0.0f, OVR::DegreeToRad(300.0f), 0.0f})); + } + } + } + + interactionProfileTextTitle_ = ui_.AddLabel( + "Interaction Profiles for Top Level Paths", {-2.5f, 2.45f, -1.5f}, {900.0f, 45.0f}); + interactionProfileText_ = ui_.AddLabel("Label", {-2.5f, 2.2f, -1.5f}, {900.0f, 160.0f}); + + // Align text for interaction profile view + OVRFW::VRMenuFontParms fontParams{}; + fontParams.Scale = 0.5f; + fontParams.AlignHoriz = OVRFW::HORIZONTAL_LEFT; + fontParams.AlignVert = OVRFW::VERTICAL_CENTER; + interactionProfileText_->SetFontParms(fontParams); + interactionProfileText_->SetTextLocalPosition({-0.82_m, 0, 0}); + + auto buttonLabel = ui_.AddLabel("Clicked 0 times", {-2.5f, 1.9f, -1.5f}, {700.0f, 45.0f}); + auto button = + ui_.AddButton("Click me!", {-2.5f, 1.7f, -1.5f}, {700.0f, 90.0f}, [buttonLabel]() { + TimesClicked++; + buttonLabel->SetText("Clicked %d times.", TimesClicked); + }); + + auto mmButtonLabel = + ui_.AddLabel("Simultaneous Hands and Controllers Enabled", {-2.5f, 1.5f, -1.5f}, {700.0f, 45.0f}); + auto mmButton = ui_.AddButton( + "Click to enable/disable simultaneous hands and controllers", + {-2.5f, 1.3f, -1.5f}, + {700.0f, 90.0f}, + [mmButtonLabel, self = this]() { + static bool isSimultaneousHandsControllersEnabled = true; + + isSimultaneousHandsControllersEnabled = !isSimultaneousHandsControllersEnabled; + self->setSimultaneousHandsControllersMode(isSimultaneousHandsControllersEnabled); + mmButtonLabel->SetText("Simultaneous Hands and Controllers %s", isSimultaneousHandsControllersEnabled ? "Enabled" : "Disabled"); + }); + + // Try out haptics + auto leftAttachedHapticButton = ui_.AddButton("Left Hand Haptic", {-2.5f, 1.1f, -1.5f}, {350.0f, 90.0f}, [=]() { + VibrateController(attachedHapticAction_, leftHandPath_, 1.0f, 157.0f, 1.0f); + }); + auto leftDetachedHapticButton = ui_.AddButton("Left Detached Haptic", {-2.5f, 0.9f, -1.5f}, {350.0f, 90.0f}, [=]() { + VibrateController(detachedHapticAction_, leftDetachedControllerPath_, 1.0f, 157.0f, 1.0f); + }); + auto rightAttachedHapticButton = ui_.AddButton("Right Hand Haptic", {-2.5f, 0.7f, -1.5f}, {350.0f, 90.0f}, [=]() { + VibrateController(attachedHapticAction_, rightHandPath_, 1.0f, 157.0f, 1.0f); + }); + auto rightDetachedHapticButton = ui_.AddButton("Right Detached Haptic", {-2.5f, 0.5f, -1.5f}, {350.0f, 90.0f}, [=]() { + VibrateController(detachedHapticAction_, rightDetachedControllerPath_, 1.0f, 157.0f, 1.0f); + }); + + // Tilt the interaction UI towards user + interactionProfileTextTitle_->SetLocalRotation( + OVR::Quat::FromRotationVector({0, OVR::DegreeToRad(60.0f), 0})); + interactionProfileText_->SetLocalRotation( + OVR::Quat::FromRotationVector({0, OVR::DegreeToRad(60.0f), 0})); + buttonLabel->SetLocalRotation( + OVR::Quat::FromRotationVector({0, OVR::DegreeToRad(60.0f), 0})); + button->SetLocalRotation( + OVR::Quat::FromRotationVector({0, OVR::DegreeToRad(60.0f), 0})); + mmButtonLabel->SetLocalRotation( + OVR::Quat::FromRotationVector({0, OVR::DegreeToRad(60.0f), 0})); + mmButton->SetLocalRotation( + OVR::Quat::FromRotationVector({0, OVR::DegreeToRad(60.0f), 0})); + leftAttachedHapticButton->SetLocalRotation( + OVR::Quat::FromRotationVector({0, OVR::DegreeToRad(60.0f), 0})); + leftDetachedHapticButton->SetLocalRotation( + OVR::Quat::FromRotationVector({0, OVR::DegreeToRad(60.0f), 0})); + rightAttachedHapticButton->SetLocalRotation( + OVR::Quat::FromRotationVector({0, OVR::DegreeToRad(60.0f), 0})); + rightDetachedHapticButton->SetLocalRotation( + OVR::Quat::FromRotationVector({0, OVR::DegreeToRad(60.0f), 0})); + + CreateSampleDescriptionPanel(); + + // Init objects that need OpenXR Session + if (false == controllerRendererL_.Init(true)) { + ALOG("SessionInit::Init L controller renderer FAILED."); + return false; + } + if (false == controllerRendererR_.Init(false)) { + ALOG("SessionInit::Init R controller renderer FAILED."); + return false; + } + + cursorBeamRenderer_.Init(GetFileSys(), nullptr, OVR::Vector4f(1.0f), 1.0f); + + // Initialize axis rendering + axisRendererL_.Init(); + axisRendererR_.Init(); + + /// Hand rendering + if (xrCreateHandTrackerEXT_) { + SetupHandTrackers(); + + // Enable simultaneous hands and controllers mode + XrSimultaneousHandsAndControllersTrackingResumeInfoMETA resumeInfo = { + XR_TYPE_SIMULTANEOUS_HANDS_AND_CONTROLLERS_TRACKING_RESUME_INFO_META}; + OXR(xrResumeSimultaneousHandsAndControllersTrackingMETA_(GetSession(), &resumeInfo)); + } + + return true; + } + + virtual void Update(const OVRFW::ovrApplFrameIn& in) override { + { + // xrSyncAction + std::vector activeActionSets = {{actionSetWorld_}, {actionSetMenu_}, + {actionSetGestures_}, {actionSetExtHandInteractionValues_}, + {actionSetExtHandInteractionReady_}}; + + XrActionsSyncInfo syncInfo = {XR_TYPE_ACTIONS_SYNC_INFO}; + syncInfo.countActiveActionSets = activeActionSets.size(); + syncInfo.activeActionSets = activeActionSets.data(); + OXR(xrSyncActions(Session, &syncInfo)); + } + + // The hit test devices are rays used for hit detection in the UI. + // Clear the rays from last frame + ui_.HitTestDevices().clear(); + + if (xrLocateHandJointsEXT_) { + UpdateHandsInformation(in); + } + + { + // + // Update current interaction profile display + // + auto getStringIpRepresentation = [&](const XrPath& xrPath, std::string& stringOut) { + XrInteractionProfileState ipState{XR_TYPE_INTERACTION_PROFILE_STATE}; + OXR(xrGetCurrentInteractionProfile(GetSession(), xrPath, &ipState)); + if (ipState.interactionProfile != XR_NULL_PATH) { + char buf[XR_MAX_PATH_LENGTH]; + uint32_t outLength = 0; + OXR(xrPathToString( + GetInstance(), + ipState.interactionProfile, + XR_MAX_PATH_LENGTH, + &outLength, + buf)); + stringOut = std::string(buf); + } + }; + + // This is for display purposes + std::string leftInteractionProfileString = "XR_NULL_PATH"; + std::string rightInteractionProfileString = "XR_NULL_PATH"; + std::string leftDetachedInteractionProfileString = "XR_NULL_PATH"; + std::string rightDetachedInteractionProfileString = "XR_NULL_PATH"; + getStringIpRepresentation(leftHandPath_, leftInteractionProfileString); + getStringIpRepresentation(rightHandPath_, rightInteractionProfileString); + getStringIpRepresentation( + leftDetachedControllerPath_, leftDetachedInteractionProfileString); + getStringIpRepresentation( + rightDetachedControllerPath_, rightDetachedInteractionProfileString); + + interactionProfileText_->SetText( + "/user/hand/left: %s\n" + "/user/hand/right: %s\n" + "/user/detached_controller_meta/left: %s\n" + "/user/detached_controller_meta/right: %s\n", + leftInteractionProfileString.c_str(), + rightInteractionProfileString.c_str(), + leftDetachedInteractionProfileString.c_str(), + rightDetachedInteractionProfileString.c_str()); + } + + { + // + // Locate controller grip and aim poses + // + + // DisplayTime is the time returned by the latest xrWaitFrame() call. + // It's the time when the current frame is expected to be shown to the user. + // xrLocateSpace returns a prediction of where these spaces spaces will be at that + // future time. + XrTime time = ToXrTime(in.PredictedDisplayTime); + OXR(xrLocateSpace(spaceControllerGripLeft_, spaceLocal_, time, &gripLeftLocation_)); + OXR(xrLocateSpace(spaceControllerGripRight_, spaceLocal_, time, &gripRightLocation_)); + OXR(xrLocateSpace(spaceControllerAimLeft_, spaceLocal_, time, &aimLeftLocation_)); + OXR(xrLocateSpace(spaceControllerAimRight_, spaceLocal_, time, &aimRightLocation_)); + + // Hands + OXR(xrLocateSpace(spaceHandGripLeft_, spaceLocal_, time, &handGripLeftLocation_)); + OXR(xrLocateSpace(spaceHandGripRight_, spaceLocal_, time, &handGripRightLocation_)); + OXR(xrLocateSpace(spaceHandAimLeft_, spaceLocal_, time, &handAimLeftLocation_)); + OXR(xrLocateSpace(spaceHandAimRight_, spaceLocal_, time, &handAimRightLocation_)); + OXR(xrLocateSpace(spaceHandPinchLeft_, spaceLocal_, time, &handPinchLeftLocation_)); + OXR(xrLocateSpace(spaceHandPinchRight_, spaceLocal_, time, &handPinchRightLocation_)); + OXR(xrLocateSpace(spaceHandPokeLeft_, spaceLocal_, time, &handPokeLeftLocation_)); + OXR(xrLocateSpace(spaceHandPokeRight_, spaceLocal_, time, &handPokeRightLocation_)); + + // Detached controllers + OXR(xrLocateSpace( + spaceDetachedControllerGripLeft_, + spaceLocal_, + time, + &gripDetachedLeftLocation_)); + OXR(xrLocateSpace( + spaceDetachedControllerGripRight_, + spaceLocal_, + time, + &gripDetachedRightLocation_)); + OXR(xrLocateSpace( + spaceDetachedControllerAimLeft_, spaceLocal_, time, &aimDetachedLeftLocation_)); + OXR(xrLocateSpace( + spaceDetachedControllerAimRight_, + spaceLocal_, + time, + &aimDetachedRightLocation_)); + } + + // Check validity of grip location before updating controllers with new location + // All apps rendering controllers should do this, otherwise you draw floating controllers + // in cases where tracking is lost or where there's a system menu on top taking input focus + if ((gripLeftLocation_.locationFlags & XR_SPACE_LOCATION_POSITION_VALID_BIT) != 0) { + controllerRendererL_.Update(FromXrPosef(gripLeftLocation_.pose)); + + bool click2 = GetActionStateBoolean(actionSelect_, leftHandPath_).currentState; + // Add new UI hit detection ray based on the aim pose (not grip!) + ui_.AddHitTestRay(FromXrPosef(aimLeftLocation_.pose), click2); + } + if ((gripRightLocation_.locationFlags & XR_SPACE_LOCATION_POSITION_VALID_BIT) != 0) { + controllerRendererR_.Update(FromXrPosef(gripRightLocation_.pose)); + + bool click = GetActionStateBoolean(actionSelect_, rightHandPath_).currentState; + ui_.AddHitTestRay(FromXrPosef(aimRightLocation_.pose), click); + } + + // Check validity of detached grip locations + if ((gripDetachedLeftLocation_.locationFlags & XR_SPACE_LOCATION_POSITION_VALID_BIT) != + 0) { + controllerRendererL_.Update(FromXrPosef(gripDetachedLeftLocation_.pose)); + } + if ((gripDetachedRightLocation_.locationFlags & XR_SPACE_LOCATION_POSITION_VALID_BIT) != + 0) { + controllerRendererR_.Update(FromXrPosef(gripDetachedRightLocation_.pose)); + } + + for (auto& panelPair : actionSetPanels_) { + panelPair.second.Update(); + } + + + if (isExtHandInteractionExtensionAvailable_) { + for (auto& panelPair : handActionSetPanels_) { + panelPair.second.Update(); + } + } + + cursorBeamRenderer_.Update(in, ui_.HitTestDevices()); + ui_.Update(in); + } + + virtual void Render(const OVRFW::ovrApplFrameIn& in, OVRFW::ovrRendererOutput& out) override { + skyboxRenderer_.Render(out.Surfaces); + environmentRenderer_.Render(out.Surfaces); + + ui_.Render(in, out); + if (isDetachedControllersExtensionAvailable_) { + // At DetachedControllerExtension enabled case, render controller when hand is not + // available or grip detached location is valid. + if (((gripLeftLocation_.locationFlags & XR_SPACE_LOCATION_POSITION_VALID_BIT) != 0 && + handTrackedL_ == false) || + ((gripDetachedLeftLocation_.locationFlags & XR_SPACE_LOCATION_POSITION_VALID_BIT) != + 0)) { + controllerRendererL_.Render(out.Surfaces); + } + if (((gripRightLocation_.locationFlags & XR_SPACE_LOCATION_POSITION_VALID_BIT) != 0 && + handTrackedR_ == false) || + ((gripDetachedRightLocation_.locationFlags & + XR_SPACE_LOCATION_POSITION_VALID_BIT) != 0)) { + controllerRendererR_.Render(out.Surfaces); + } + } else { + // At DetachedControllerExtension not enabled case, render controller whenever grip data + // is valid. + if ((gripLeftLocation_.locationFlags & XR_SPACE_LOCATION_POSITION_VALID_BIT) != 0) { + controllerRendererL_.Render(out.Surfaces); + } + if ((gripRightLocation_.locationFlags & XR_SPACE_LOCATION_POSITION_VALID_BIT) != 0) { + controllerRendererR_.Render(out.Surfaces); + } + } + + if (handTrackedL_ && handInFrameL_) { + // Render left axis + axisRendererL_.Render(OVR::Matrix4f(), in, out); + // Render actual hand + handRendererL_.Render(out.Surfaces); + } + + if (handTrackedR_ && handInFrameR_) { + // Render right axis + axisRendererR_.Render(OVR::Matrix4f(), in, out); + // Render actual hand + handRendererR_.Render(out.Surfaces); + } + + /// Render beams last, since they render with transparency (alpha blending) + cursorBeamRenderer_.Render(in, out); + } + + virtual void SessionEnd() override { + environmentRenderer_.Shutdown(); + skyboxRenderer_.Shutdown(); + controllerRendererL_.Shutdown(); + controllerRendererR_.Shutdown(); + cursorBeamRenderer_.Shutdown(); + + /// Hand Trackers & Renderers + OXR(xrDestroyHandTrackerEXT_(handTrackerL_)); + OXR(xrDestroyHandTrackerEXT_(handTrackerR_)); + axisRendererL_.Shutdown(); + axisRendererR_.Shutdown(); + handRendererL_.Shutdown(); + handRendererR_.Shutdown(); + + /// Switch out of simultaneous hands and controllers mode. + /// Not strictly necessary; terminating the app will + /// accomplish this as well + setSimultaneousHandsControllersMode(false); + } + + virtual void AppShutdown(const xrJava* context) override { + /// Unhook extensions for hand tracking + xrCreateHandTrackerEXT_ = nullptr; + xrDestroyHandTrackerEXT_ = nullptr; + xrLocateHandJointsEXT_ = nullptr; + xrGetHandMeshFB_ = nullptr; + xrResumeSimultaneousHandsAndControllersTrackingMETA_ = nullptr; + xrPauseSimultaneousHandsAndControllersTrackingMETA_ = nullptr; + + OVRFW::XrApp::AppShutdown(context); + ui_.Shutdown(); + } + + virtual void HandleXrEvents() override { + XrEventDataBuffer eventDataBuffer = {}; + XrInteractionProfileState ipState{XR_TYPE_INTERACTION_PROFILE_STATE}; + + // Poll for events + for (;;) { + XrEventDataBaseHeader* baseEventHeader = (XrEventDataBaseHeader*)(&eventDataBuffer); + baseEventHeader->type = XR_TYPE_EVENT_DATA_BUFFER; + baseEventHeader->next = NULL; + XrResult r; + OXR(r = xrPollEvent(Instance, &eventDataBuffer)); + if (r != XR_SUCCESS) { + break; + } + + switch (baseEventHeader->type) { + case XR_TYPE_EVENT_DATA_EVENTS_LOST: + ALOGV("xrPollEvent: received XR_TYPE_EVENT_DATA_EVENTS_LOST event"); + break; + case XR_TYPE_EVENT_DATA_INSTANCE_LOSS_PENDING: + ALOGV("xrPollEvent: received XR_TYPE_EVENT_DATA_INSTANCE_LOSS_PENDING event"); + break; + case XR_TYPE_EVENT_DATA_INTERACTION_PROFILE_CHANGED: + OXR(xrGetCurrentInteractionProfile(GetSession(), leftHandPath_, &ipState)); + handTrackedL_ = (ipState.interactionProfile == extHandInteractionProfile_); + OXR(xrGetCurrentInteractionProfile(GetSession(), rightHandPath_, &ipState)); + handTrackedR_ = (ipState.interactionProfile == extHandInteractionProfile_); + break; + case XR_TYPE_EVENT_DATA_PERF_SETTINGS_EXT: { + const XrEventDataPerfSettingsEXT* perf_settings_event = + (XrEventDataPerfSettingsEXT*)(baseEventHeader); + ALOGV( + "xrPollEvent: received XR_TYPE_EVENT_DATA_PERF_SETTINGS_EXT event: type %d subdomain %d : level %d -> level %d", + perf_settings_event->type, + perf_settings_event->subDomain, + perf_settings_event->fromLevel, + perf_settings_event->toLevel); + } break; + case XR_TYPE_EVENT_DATA_REFERENCE_SPACE_CHANGE_PENDING: + ALOGV( + "xrPollEvent: received XR_TYPE_EVENT_DATA_REFERENCE_SPACE_CHANGE_PENDING event"); + break; + case XR_TYPE_EVENT_DATA_SESSION_STATE_CHANGED: { + const XrEventDataSessionStateChanged* session_state_changed_event = + (XrEventDataSessionStateChanged*)(baseEventHeader); + ALOGV( + "xrPollEvent: received XR_TYPE_EVENT_DATA_SESSION_STATE_CHANGED: %d for session %p at time %f", + session_state_changed_event->state, + (void*)session_state_changed_event->session, + FromXrTime(session_state_changed_event->time)); + + switch (session_state_changed_event->state) { + case XR_SESSION_STATE_FOCUSED: + Focused = true; + break; + case XR_SESSION_STATE_VISIBLE: + Focused = false; + break; + case XR_SESSION_STATE_READY: + HandleSessionStateChanges(session_state_changed_event->state); + break; + case XR_SESSION_STATE_STOPPING: + HandleSessionStateChanges(session_state_changed_event->state); + break; + case XR_SESSION_STATE_EXITING: + ShouldExit = true; + break; + default: + break; + } + } break; + default: + ALOGV("xrPollEvent: Unknown event"); + break; + } + } + } + + void SetupHandTrackers() { + XrHandTrackerCreateInfoEXT createInfo{XR_TYPE_HAND_TRACKER_CREATE_INFO_EXT}; + createInfo.handJointSet = XR_HAND_JOINT_SET_DEFAULT_EXT; + createInfo.hand = XR_HAND_LEFT_EXT; + OXR(xrCreateHandTrackerEXT_(GetSession(), &createInfo, &handTrackerL_)); + createInfo.hand = XR_HAND_RIGHT_EXT; + OXR(xrCreateHandTrackerEXT_(GetSession(), &createInfo, &handTrackerR_)); + + ALOG("xrCreateHandTrackerEXT handTrackerL_=%llx", (long long)handTrackerL_); + ALOG("xrCreateHandTrackerEXT handTrackerR_=%llx", (long long)handTrackerR_); + + /// Setup skinning meshes for both hands + if (xrGetHandMeshFB_) { + for (int handIndex = 0; handIndex < 2; ++handIndex) { + /// Alias everything for initialization + const bool isLeft = (handIndex == 0); + auto& handTracker = isLeft ? handTrackerL_ : handTrackerR_; + auto& handRenderer = isLeft ? handRendererL_ : handRendererR_; + auto* jointLocations = isLeft ? jointLocationsL_ : jointLocationsR_; + + /// two-call pattern for mesh data + /// call 1 - figure out sizes + + /// mesh + XrHandTrackingMeshFB mesh{XR_TYPE_HAND_TRACKING_MESH_FB}; + mesh.next = nullptr; + /// mesh - skeleton + mesh.jointCapacityInput = 0; + mesh.jointCountOutput = 0; + mesh.jointBindPoses = nullptr; + mesh.jointRadii = nullptr; + mesh.jointParents = nullptr; + /// mesh - vertex + mesh.vertexCapacityInput = 0; + mesh.vertexCountOutput = 0; + mesh.vertexPositions = nullptr; + mesh.vertexNormals = nullptr; + mesh.vertexUVs = nullptr; + mesh.vertexBlendIndices = nullptr; + mesh.vertexBlendWeights = nullptr; + /// mesh - index + mesh.indexCapacityInput = 0; + mesh.indexCountOutput = 0; + mesh.indices = nullptr; + /// get mesh sizes + OXR(xrGetHandMeshFB_(handTracker, &mesh)); + + /// mesh storage - update sizes + mesh.jointCapacityInput = mesh.jointCountOutput; + mesh.vertexCapacityInput = mesh.vertexCountOutput; + mesh.indexCapacityInput = mesh.indexCountOutput; + /// skeleton + std::vector jointBindLocations; + std::vector parentData; + std::vector jointRadii; + jointBindLocations.resize(mesh.jointCountOutput); + parentData.resize(mesh.jointCountOutput); + jointRadii.resize(mesh.jointCountOutput); + mesh.jointBindPoses = jointBindLocations.data(); + mesh.jointParents = parentData.data(); + mesh.jointRadii = jointRadii.data(); + /// vertex + std::vector vertexPositions; + std::vector vertexNormals; + std::vector vertexUVs; + std::vector vertexBlendIndices; + std::vector vertexBlendWeights; + vertexPositions.resize(mesh.vertexCountOutput); + vertexNormals.resize(mesh.vertexCountOutput); + vertexUVs.resize(mesh.vertexCountOutput); + vertexBlendIndices.resize(mesh.vertexCountOutput); + vertexBlendWeights.resize(mesh.vertexCountOutput); + mesh.vertexPositions = vertexPositions.data(); + mesh.vertexNormals = vertexNormals.data(); + mesh.vertexUVs = vertexUVs.data(); + mesh.vertexBlendIndices = vertexBlendIndices.data(); + mesh.vertexBlendWeights = vertexBlendWeights.data(); + /// index + std::vector indices; + indices.resize(mesh.indexCountOutput); + mesh.indices = indices.data(); + + /// Get mesh data + OXR(xrGetHandMeshFB_(handTracker, &mesh)); + + /// Init renderer + handRenderer.Init(&mesh, true); + + /// Print hierarchy + { + for (int i = 0; i < XR_HAND_JOINT_COUNT_EXT; ++i) { + const OVR::Posef pose = FromXrPosef(jointLocations[i].pose); + ALOG( + " { {%.6f, %.6f, %.6f}, {%.6f, %.6f, %.6f, %.6f} } // joint = %d, parent = %d", + pose.Translation.x, + pose.Translation.y, + pose.Translation.z, + pose.Rotation.x, + pose.Rotation.y, + pose.Rotation.z, + pose.Rotation.w, + i, + (int)parentData[i]); + } + } + } + } + } + + void UpdateHandsInformation(const OVRFW::ovrApplFrameIn& in) { + // Perform the same operations for both hands + for (int handIndex = 0; handIndex < 2; ++handIndex) { + + const bool isLeft = (handIndex == 0); + auto& handPath = isLeft ? leftHandPath_ : rightHandPath_; + auto& handInFrame = isLeft ? handInFrameL_ : handInFrameR_; + auto& handTracked = isLeft ? handTrackedL_ : handTrackedR_; + auto& lastFrameClicked = isLeft ? lastFrameClickedL_ : lastFrameClickedR_; + auto* handJointLocations = isLeft ? jointLocationsL_ : jointLocationsR_; + auto& handRenderer = isLeft ? handRendererL_ : handRendererR_; + auto& handTracker = isLeft ? handTrackerL_ : handTrackerR_; + auto& axisRenderer = isLeft ? axisRendererL_ : axisRendererR_; + + XrHandTrackingScaleFB scale{XR_TYPE_HAND_TRACKING_SCALE_FB}; + scale.next = nullptr; + scale.sensorOutput = 1.0f; + scale.currentOutput = 1.0f; + scale.overrideValueInput = 1.00f; + scale.overrideHandScale = XR_FALSE; + XrHandTrackingAimStateFB aimState{XR_TYPE_HAND_TRACKING_AIM_STATE_FB}; + aimState.next = &scale; + XrHandJointLocationsEXT locations{XR_TYPE_HAND_JOINT_LOCATIONS_EXT}; + locations.next = &aimState; + locations.jointCount = XR_HAND_JOINT_COUNT_EXT; + locations.jointLocations = handJointLocations; + + XrHandJointsLocateInfoEXT locateInfo{XR_TYPE_HAND_JOINTS_LOCATE_INFO_EXT}; + locateInfo.baseSpace = GetLocalSpace(); + locateInfo.time = ToXrTime(in.PredictedDisplayTime); + OXR(xrLocateHandJointsEXT_(handTracker, &locateInfo, &locations)); + + std::vector handJoints; + handInFrame = false; + + XrInteractionProfileState ipState{XR_TYPE_INTERACTION_PROFILE_STATE}; + + OXR(xrGetCurrentInteractionProfile(GetSession(), handPath, &ipState)); + if (locations.isActive && + (ipState.interactionProfile == 0 || + ipState.interactionProfile == extHandInteractionProfile_)) { + handTracked = true; + + for (int i = 0; i < (int)locations.jointCount; ++i) { + if ((handJointLocations[i].locationFlags & XR_SPACE_LOCATION_POSITION_VALID_BIT) && + (handJointLocations[i].locationFlags & + XR_SPACE_LOCATION_ORIENTATION_VALID_BIT)) { + handInFrame = true; + const auto p = FromXrPosef(handJointLocations[i].pose); + handJoints.push_back(p); + } + } + + // Do not render the hand or axis unless at least one joint is in the actual frame + if (handInFrame) { + handRenderer.Update(&handJointLocations[0]); + axisRenderer.Update(handJoints); + } + + const bool didPinch = + (aimState.status & XR_HAND_TRACKING_AIM_INDEX_PINCHING_BIT_FB) != 0; + ui_.AddHitTestRay(FromXrPosef(aimState.aimPose), didPinch && !lastFrameClicked); + lastFrameClicked = didPinch; + } + } + } + + void CreateSampleDescriptionPanel() { + // Panel to provide sample description to the user for context + auto descriptionLabel = ui_.AddLabel( + static_cast(sampleExplanation), {-2.5_m, 0.3_m, -1.5_m}, {950.0f, 80.0f}); + // Align and size the description text for readability + OVRFW::VRMenuFontParms fontParams{}; + fontParams.Scale = 0.5f; + fontParams.AlignHoriz = OVRFW::HORIZONTAL_LEFT; + descriptionLabel->SetFontParms(fontParams); + descriptionLabel->SetTextLocalPosition({-0.65_m, 0, 0}); + + // Tilt the description billboard 60 degrees towards the user + descriptionLabel->SetLocalRotation( + OVR::Quat::FromRotationVector({0, OVR::DegreeToRad(60.0f), 0})); + } + + bool isExtensionAvailable(std::string desiredExtension) { + // Enumerate the extensions supported by the runtime + uint32_t extensionCount; + OXR(xrEnumerateInstanceExtensionProperties(nullptr, 0, &extensionCount, nullptr)); + std::vector extensions( + extensionCount, {XR_TYPE_EXTENSION_PROPERTIES}); + OXR(xrEnumerateInstanceExtensionProperties( + nullptr, extensionCount, &extensionCount, extensions.data())); + + // Check if the desired extension is supported + for (const auto& extension : extensions) { + if (desiredExtension == extension.extensionName) { + return true; + } + } + return false; + } + + void setSimultaneousHandsControllersMode(bool enabled) { + // Enable/disable simultaneous hands and controllers mode + + if (enabled) { + XrSimultaneousHandsAndControllersTrackingResumeInfoMETA resumeInfo = { + XR_TYPE_SIMULTANEOUS_HANDS_AND_CONTROLLERS_TRACKING_RESUME_INFO_META}; + + OXR(xrResumeSimultaneousHandsAndControllersTrackingMETA_(GetSession(), &resumeInfo)); + } else { + XrSimultaneousHandsAndControllersTrackingPauseInfoMETA pauseInfo = { + XR_TYPE_SIMULTANEOUS_HANDS_AND_CONTROLLERS_TRACKING_PAUSE_INFO_META}; + + OXR(xrPauseSimultaneousHandsAndControllersTrackingMETA_(GetSession(), &pauseInfo)); + } + } + + void VibrateController( + const XrAction& action, + const XrPath& subactionPath, + float duration, + float frequency, + float amplitude) { + // fire haptics using output action + XrHapticVibration v{XR_TYPE_HAPTIC_VIBRATION, nullptr}; + v.amplitude = amplitude; + v.duration = ToXrTime(duration); + v.frequency = frequency; + XrHapticActionInfo hai = {XR_TYPE_HAPTIC_ACTION_INFO, nullptr}; + hai.action = action; + hai.subactionPath = subactionPath; + OXR(xrApplyHapticFeedback(Session, &hai, (const XrHapticBaseHeader*)&v)); + } + + private: + // Extension status + bool isDetachedControllersExtensionAvailable_ = false; + bool isExtHandInteractionExtensionAvailable_ = false; + bool isSimultaneousHandsControllersExtensionAvailable_ = false; + + // Control of the simultaneous hands and controllers mode + XrResult (XRAPI_PTR *xrResumeSimultaneousHandsAndControllersTrackingMETA_)(XrSession session, + const XrSimultaneousHandsAndControllersTrackingResumeInfoMETA* createInfo) = nullptr; + XrResult (XRAPI_PTR *xrPauseSimultaneousHandsAndControllersTrackingMETA_)(XrSession session, + const XrSimultaneousHandsAndControllersTrackingPauseInfoMETA* createInfo) = nullptr; + + // Controllers + OVRFW::ControllerRenderer controllerRendererL_; + OVRFW::ControllerRenderer controllerRendererR_; + OVRFW::EnvironmentRenderer environmentRenderer_; + OVRFW::SkyboxRenderer skyboxRenderer_; + OVRFW::SimpleBeamRenderer cursorBeamRenderer_; + OVRFW::TinyUI ui_; + + // Hands + bool lastFrameClickedL_ = false; + bool lastFrameClickedR_ = false; + + /// Hands - extension functions + PFN_xrCreateHandTrackerEXT xrCreateHandTrackerEXT_ = nullptr; + PFN_xrDestroyHandTrackerEXT xrDestroyHandTrackerEXT_ = nullptr; + PFN_xrLocateHandJointsEXT xrLocateHandJointsEXT_ = nullptr; + + /// Hands - FB mesh rendering extensions + PFN_xrGetHandMeshFB xrGetHandMeshFB_ = nullptr; + + /// Hands - tracker handles + XrHandTrackerEXT handTrackerL_ = XR_NULL_HANDLE; + XrHandTrackerEXT handTrackerR_ = XR_NULL_HANDLE; + + XrHandJointLocationEXT jointLocationsL_[XR_HAND_JOINT_COUNT_EXT]; + XrHandJointLocationEXT jointLocationsR_[XR_HAND_JOINT_COUNT_EXT]; + + OVRFW::HandRenderer handRendererL_; + OVRFW::HandRenderer handRendererR_; + OVRFW::ovrAxisRenderer axisRendererL_; + OVRFW::ovrAxisRenderer axisRendererR_; + bool handTrackedL_ = false; + bool handTrackedR_ = false; + bool handInFrameL_ = false; + bool handInFrameR_ = false; + OVR::Vector4f jointColor_{0.4, 0.5, 0.2, 0.5}; + + XrActionSet actionSetMenu_{XR_NULL_HANDLE}; + XrActionSet actionSetWorld_{XR_NULL_HANDLE}; + XrActionSet actionSetGestures_{XR_NULL_HANDLE}; + XrActionSet actionSetExtHandInteractionValues_{XR_NULL_HANDLE}; + XrActionSet actionSetExtHandInteractionReady_{XR_NULL_HANDLE}; + + XrAction actionSelect_{XR_NULL_HANDLE}; + XrAction actionGrab_{XR_NULL_HANDLE}; + XrAction actionControllerGripPose_{XR_NULL_HANDLE}; + XrAction actionControllerAimPose_{XR_NULL_HANDLE}; + + XrAction actionHandSelect_{XR_NULL_HANDLE}; + XrAction actionHandSqueeze_{XR_NULL_HANDLE}; + XrAction actionHandGripPose_{XR_NULL_HANDLE}; + XrAction actionHandAimPose_{XR_NULL_HANDLE}; + XrAction actionHandPinchPose_{XR_NULL_HANDLE}; + XrAction actionHandPokePose_{XR_NULL_HANDLE}; + + XrAction actionDetachedControllerGripPose_{XR_NULL_HANDLE}; + XrAction actionDetachedControllerAimPose_{XR_NULL_HANDLE}; + + XrAction actionTrigger_{XR_NULL_HANDLE}; + XrAction actionThumbstick_{XR_NULL_HANDLE}; + XrAction actionThumbstickX_{XR_NULL_HANDLE}; + XrAction actionThumbstickY_{XR_NULL_HANDLE}; + + // Ext hand interaction value validation + XrAction actionExtHandInteractionLeftPinchValue_{XR_NULL_HANDLE}; + XrAction actionExtHandInteractionRightPinchValue_{XR_NULL_HANDLE}; + XrAction actionExtHandInteractionLeftPinchReady_{XR_NULL_HANDLE}; + XrAction actionExtHandInteractionRightPinchReady_{XR_NULL_HANDLE}; + XrAction actionExtHandInteractionLeftAimActivateValue_{XR_NULL_HANDLE}; + XrAction actionExtHandInteractionRightAimActivateValue_{XR_NULL_HANDLE}; + XrAction actionExtHandInteractionLeftAimActivateReady_{XR_NULL_HANDLE}; + XrAction actionExtHandInteractionRightAimActivateReady_{XR_NULL_HANDLE}; + XrAction actionExtHandInteractionLeftGraspValue_{XR_NULL_HANDLE}; + XrAction actionExtHandInteractionRightGraspValue_{XR_NULL_HANDLE}; + XrAction actionExtHandInteractionLeftGraspReady_{XR_NULL_HANDLE}; + XrAction actionExtHandInteractionRightGraspReady_{XR_NULL_HANDLE}; + + // Haptic actions + XrAction attachedHapticAction_{XR_NULL_HANDLE}; + XrAction detachedHapticAction_{XR_NULL_HANDLE}; + + OVRFW::VRMenuObject* interactionProfileTextTitle_{nullptr}; + OVRFW::VRMenuObject* interactionProfileText_{nullptr}; + + // Reference spaces + XrSpace spaceLocal_{XR_NULL_HANDLE}; + + // Space for controller poses + XrSpace spaceControllerAimLeft_{XR_NULL_HANDLE}; + XrSpace spaceControllerAimRight_{XR_NULL_HANDLE}; + XrSpace spaceDetachedControllerAimLeft_{XR_NULL_HANDLE}; + XrSpace spaceDetachedControllerAimRight_{XR_NULL_HANDLE}; + XrSpace spaceControllerGripLeft_{XR_NULL_HANDLE}; + XrSpace spaceControllerGripRight_{XR_NULL_HANDLE}; + + // Space for hand poses + XrSpace spaceHandAimLeft_{XR_NULL_HANDLE}; + XrSpace spaceHandAimRight_{XR_NULL_HANDLE}; + XrSpace spaceHandGripLeft_{XR_NULL_HANDLE}; + XrSpace spaceHandGripRight_{XR_NULL_HANDLE}; + XrSpace spaceHandPinchLeft_{XR_NULL_HANDLE}; + XrSpace spaceHandPinchRight_{XR_NULL_HANDLE}; + XrSpace spaceHandPokeLeft_{XR_NULL_HANDLE}; + XrSpace spaceHandPokeRight_{XR_NULL_HANDLE}; + + XrSpace spaceDetachedControllerGripLeft_{XR_NULL_HANDLE}; + XrSpace spaceDetachedControllerGripRight_{XR_NULL_HANDLE}; + + // Updated every frame + XrSpaceLocation aimLeftLocation_{XR_TYPE_SPACE_LOCATION}; + XrSpaceLocation aimRightLocation_{XR_TYPE_SPACE_LOCATION}; + XrSpaceLocation gripRightLocation_{XR_TYPE_SPACE_LOCATION}; + XrSpaceLocation gripLeftLocation_{XR_TYPE_SPACE_LOCATION}; + + // Hands + XrSpaceLocation handAimLeftLocation_{XR_TYPE_SPACE_LOCATION}; + XrSpaceLocation handAimRightLocation_{XR_TYPE_SPACE_LOCATION}; + XrSpaceLocation handGripRightLocation_{XR_TYPE_SPACE_LOCATION}; + XrSpaceLocation handGripLeftLocation_{XR_TYPE_SPACE_LOCATION}; + XrSpaceLocation handPinchLeftLocation_{XR_TYPE_SPACE_LOCATION}; + XrSpaceLocation handPinchRightLocation_{XR_TYPE_SPACE_LOCATION}; + XrSpaceLocation handPokeRightLocation_{XR_TYPE_SPACE_LOCATION}; + XrSpaceLocation handPokeLeftLocation_{XR_TYPE_SPACE_LOCATION}; + + // Detached controllers + XrSpaceLocation aimDetachedLeftLocation_{XR_TYPE_SPACE_LOCATION}; + XrSpaceLocation aimDetachedRightLocation_{XR_TYPE_SPACE_LOCATION}; + XrSpaceLocation gripDetachedLeftLocation_{XR_TYPE_SPACE_LOCATION}; + XrSpaceLocation gripDetachedRightLocation_{XR_TYPE_SPACE_LOCATION}; + + // XrPaths for convenience + XrPath leftHandPath_{XR_NULL_PATH}; + XrPath rightHandPath_{XR_NULL_PATH}; + XrPath leftDetachedControllerPath_{XR_NULL_PATH}; + XrPath rightDetachedControllerPath_{XR_NULL_PATH}; + + // Interaction profiles + XrPath extHandInteractionProfile_ = XR_NULL_PATH; + XrPath touchInteractionProfile_ = XR_NULL_PATH; + XrPath touchProInteractionProfile_ = XR_NULL_PATH; + XrPath touchPlusInteractionProfile_ = XR_NULL_PATH; + + std::unordered_map actionSetPanels_{}; + std::unordered_map handActionSetPanels_{}; +}; + +ENTRY_POINT(XrHandsAndControllersSampleApp) diff --git a/Samples/XrSamples/XrHandsAndControllers/assets/Skybox.gltf.ovrscene b/Samples/XrSamples/XrHandsAndControllers/assets/Skybox.gltf.ovrscene new file mode 100644 index 0000000..a098497 Binary files /dev/null and b/Samples/XrSamples/XrHandsAndControllers/assets/Skybox.gltf.ovrscene differ diff --git a/Samples/XrSamples/XrHandsAndControllers/assets/SmallRoom.gltf.ovrscene b/Samples/XrSamples/XrHandsAndControllers/assets/SmallRoom.gltf.ovrscene new file mode 100644 index 0000000..2183af3 Binary files /dev/null and b/Samples/XrSamples/XrHandsAndControllers/assets/SmallRoom.gltf.ovrscene differ diff --git a/Samples/XrSamples/XrHandsAndControllers/assets/assets.txt b/Samples/XrSamples/XrHandsAndControllers/assets/assets.txt new file mode 100644 index 0000000..2cc30f7 --- /dev/null +++ b/Samples/XrSamples/XrHandsAndControllers/assets/assets.txt @@ -0,0 +1 @@ +This file is a placeholder. diff --git a/Samples/XrSamples/XrHandsAndControllers/assets/panel.ktx b/Samples/XrSamples/XrHandsAndControllers/assets/panel.ktx new file mode 100644 index 0000000..deb13e8 Binary files /dev/null and b/Samples/XrSamples/XrHandsAndControllers/assets/panel.ktx differ diff --git a/Samples/XrSamples/XrHandsAndControllers/java/MainActivity.java b/Samples/XrSamples/XrHandsAndControllers/java/MainActivity.java new file mode 100644 index 0000000..19ca477 --- /dev/null +++ b/Samples/XrSamples/XrHandsAndControllers/java/MainActivity.java @@ -0,0 +1,28 @@ +// Copyright (c) Facebook Technologies, LLC and its affiliates. All Rights reserved. +package com.oculus.xrsamples.xrhands_and_controllers; + +/** + * When using NativeActivity, we currently need to handle loading of dependent shared libraries + * manually before a shared library that depends on them is loaded, since there is not currently a + * way to specify a shared library dependency for NativeActivity via the manifest meta-data. + * + *

The simplest method for doing so is to subclass NativeActivity with an empty activity that + * calls System.loadLibrary on the dependent libraries, which is unfortunate when the goal is to + * write a pure native C/C++ only Android activity. + * + *

A native-code only solution is to load the dependent libraries dynamically using dlopen(). + * However, there are a few considerations, see: + * https://groups.google.com/forum/#!msg/android-ndk/l2E2qh17Q6I/wj6s_6HSjaYJ + * + *

1. Only call dlopen() if you're sure it will succeed as the bionic dynamic linker will + * remember if dlopen failed and will not re-try a dlopen on the same lib a second time. + * + *

2. Must remember what libraries have already been loaded to avoid infinitely looping when + * libraries have circular dependencies. + */ +public class MainActivity extends android.app.NativeActivity { + static { + System.loadLibrary("openxr_loader"); + System.loadLibrary("xrsamples_xrhands_and_controllers"); + } +} diff --git a/Samples/XrSamples/XrHandsAndControllers/res/values/strings.xml b/Samples/XrSamples/XrHandsAndControllers/res/values/strings.xml new file mode 100644 index 0000000..ce5aa04 --- /dev/null +++ b/Samples/XrSamples/XrHandsAndControllers/res/values/strings.xml @@ -0,0 +1,4 @@ + + + OpenXR Input Hands and Controllers Sample + diff --git a/Samples/XrSamples/XrHandsFB/CMakeLists.txt b/Samples/XrSamples/XrHandsFB/CMakeLists.txt new file mode 100755 index 0000000..b1f059c --- /dev/null +++ b/Samples/XrSamples/XrHandsFB/CMakeLists.txt @@ -0,0 +1,43 @@ +# Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved. + +project(xrhandsfb) + +if(NOT TARGET OpenXR::openxr_loader) + find_package(OpenXR REQUIRED) +endif() + +file(GLOB_RECURSE SRC_FILES + Src/*.c + Src/*.cpp +) + +if(ANDROID) + add_library(${PROJECT_NAME} MODULE ${SRC_FILES}) + target_include_directories(${PROJECT_NAME} PUBLIC ${ANDROID_NDK}/sources/android/native_app_glue) + target_link_libraries(${PROJECT_NAME} PRIVATE + android + EGL + GLESv3 + log + ktx + ) + set_target_properties(${PROJECT_NAME} PROPERTIES LINK_FLAGS "-u ANativeActivity_onCreate") +elseif(WIN32) + add_definitions(-D_USE_MATH_DEFINES) + add_executable(${PROJECT_NAME} ${SRC_FILES}) + add_custom_command(TARGET ${PROJECT_NAME} PRE_BUILD + COMMAND "${CMAKE_COMMAND}" -E copy_directory + "${CMAKE_CURRENT_LIST_DIR}/assets" + "$/assets" + VERBATIM) + + add_custom_command(TARGET ${PROJECT_NAME} PRE_BUILD + COMMAND "${CMAKE_COMMAND}" -E copy_directory + "${CMAKE_SOURCE_DIR}/SampleXrFramework/res/raw" + "$/font/res/raw" + VERBATIM) +endif() + +# Common across platforms +target_include_directories(${PROJECT_NAME} PRIVATE Src) +target_link_libraries(${PROJECT_NAME} PRIVATE samplexrframework) diff --git a/Samples/XrSamples/XrHandsFB/Projects/Android/AndroidManifest.xml b/Samples/XrSamples/XrHandsFB/Projects/Android/AndroidManifest.xml new file mode 100755 index 0000000..a1769b8 --- /dev/null +++ b/Samples/XrSamples/XrHandsFB/Projects/Android/AndroidManifest.xml @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Samples/XrSamples/XrHandsFB/Projects/Android/build.bat b/Samples/XrSamples/XrHandsFB/Projects/Android/build.bat new file mode 100755 index 0000000..facf79f --- /dev/null +++ b/Samples/XrSamples/XrHandsFB/Projects/Android/build.bat @@ -0,0 +1,29 @@ +@rem Only edit the master copy of this file in SDK_ROOT/bin/scripts/build/perproject + +@setlocal enableextensions enabledelayedexpansion + +@if not exist "build.gradle" @echo Build script must be executed from project directory. & goto :Abort + +@set P=.. + +:TryAgain + +@rem @echo P = %P% + +@if exist "%P%\bin\scripts\build\build.py.bat" goto :Found + +@if exist "%P%\bin\scripts\build" @echo "Could not find build.py.bat" & goto :Abort + +@set P=%P%\.. + +@goto :TryAgain + +:Found + +@set P=%P%\bin\scripts\build +@call %P%\build.py.bat %1 %2 %3 %4 %5 +@goto :End + +:Abort + +:End diff --git a/Samples/XrSamples/XrHandsFB/Projects/Android/build.gradle b/Samples/XrSamples/XrHandsFB/Projects/Android/build.gradle new file mode 100755 index 0000000..eacca8f --- /dev/null +++ b/Samples/XrSamples/XrHandsFB/Projects/Android/build.gradle @@ -0,0 +1,78 @@ +// Top-level build file where you can add configuration options common to all sub-projects/modules. +buildscript { + repositories { + google() + mavenCentral() + } + dependencies { + classpath "com.android.tools.build:gradle:7.0.3" + } +} + +repositories { + google() + mavenCentral() +} + +apply plugin: 'com.android.application' + +android { + compileSdk 32 + + defaultConfig { + applicationId "com.oculus.sdk.xrhandsfb" + minSdk 26 + targetSdk 32 + versionCode 1 + versionName "1.0" + + // override app plugin abiFilters for 64-bit support + externalNativeBuild { + ndk { + abiFilters 'arm64-v8a' + } + ndkBuild { + abiFilters 'arm64-v8a' + } + cmake { + targets "xrhandsfb" + } + } + } + + sourceSets { + main { + manifest.srcFile 'AndroidManifest.xml' + java.srcDirs = ['../../java'] + assets.srcDirs = ['../../assets'] + res.srcDirs = ['../../res'] + } + } + + buildTypes { + debug { + debuggable true + } + + release { + debuggable false + } + } + + externalNativeBuild { + cmake { + path file('../../../../CMakeLists.txt') + } + } + + // Enable prefab support for the OpenXR AAR + buildFeatures { + prefab true + } +} + +dependencies { + // Package/application AndroidManifest.xml properties, plus headers and libraries + // exposed to CMake + implementation 'org.khronos.openxr:openxr_loader_for_android:1.1.36' +} diff --git a/Samples/XrSamples/XrHandsFB/Projects/Android/build.py b/Samples/XrSamples/XrHandsFB/Projects/Android/build.py new file mode 100755 index 0000000..d4b6e58 --- /dev/null +++ b/Samples/XrSamples/XrHandsFB/Projects/Android/build.py @@ -0,0 +1,35 @@ +#!/usr/bin/python +# (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +# This first bit of code is common bootstrapping code +# to determine the SDK root, and to set up the import +# path for additional python code. + +# begin bootstrap +import os +import sys + + +def init(): + root = os.path.realpath(os.path.dirname(os.path.realpath(__file__))) + os.chdir(root) # make sure we are always executing from the project directory + while os.path.isdir(os.path.join(root, "bin/scripts/build")) == False: + root = os.path.realpath(os.path.join(root, "..")) + if ( + len(root) <= 5 + ): # Should catch both Posix and Windows root directories (e.g. '/' and 'C:\') + print("Unable to find SDK root. Exiting.") + sys.exit(1) + root = os.path.abspath(root) + os.environ["OCULUS_SDK_PATH"] = root + sys.path.append(root + "/bin/scripts/build") + + +init() +import ovrbuild + +ovrbuild.init() +# end bootstrap + + +ovrbuild.build() diff --git a/Samples/XrSamples/XrHandsFB/Projects/Android/gradle.properties b/Samples/XrSamples/XrHandsFB/Projects/Android/gradle.properties new file mode 100644 index 0000000..3e927b1 --- /dev/null +++ b/Samples/XrSamples/XrHandsFB/Projects/Android/gradle.properties @@ -0,0 +1,21 @@ +# Project-wide Gradle settings. +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. More details, visit +# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects +# org.gradle.parallel=true +# AndroidX package structure to make it clearer which packages are bundled with the +# Android operating system, and which are packaged with your app's APK +# https://developer.android.com/topic/libraries/support-library/androidx-rn +android.useAndroidX=true +# Enables namespacing of each library's R class so that its R class includes only the +# resources declared in the library itself and none from the library's dependencies, +# thereby reducing the size of the R class for that library +android.nonTransitiveRClass=true \ No newline at end of file diff --git a/Samples/XrSamples/XrHandsFB/Projects/Android/gradle/wrapper/gradle-wrapper.jar b/Samples/XrSamples/XrHandsFB/Projects/Android/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..7454180 Binary files /dev/null and b/Samples/XrSamples/XrHandsFB/Projects/Android/gradle/wrapper/gradle-wrapper.jar differ diff --git a/Samples/XrSamples/XrHandsFB/Projects/Android/gradle/wrapper/gradle-wrapper.properties b/Samples/XrSamples/XrHandsFB/Projects/Android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..ffed3a2 --- /dev/null +++ b/Samples/XrSamples/XrHandsFB/Projects/Android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/Samples/XrSamples/XrHandsFB/Projects/Android/gradlew b/Samples/XrSamples/XrHandsFB/Projects/Android/gradlew new file mode 100755 index 0000000..a69d9cb --- /dev/null +++ b/Samples/XrSamples/XrHandsFB/Projects/Android/gradlew @@ -0,0 +1,240 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# 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 +# +# https://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. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit + +APP_NAME="Gradle" +APP_BASE_NAME=${0##*/} + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/Samples/XrSamples/XrHandsFB/Projects/Android/gradlew.bat b/Samples/XrSamples/XrHandsFB/Projects/Android/gradlew.bat new file mode 100755 index 0000000..f127cfd --- /dev/null +++ b/Samples/XrSamples/XrHandsFB/Projects/Android/gradlew.bat @@ -0,0 +1,91 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/Samples/XrSamples/XrHandsFB/Projects/Android/settings.gradle b/Samples/XrSamples/XrHandsFB/Projects/Android/settings.gradle new file mode 100755 index 0000000..e355534 --- /dev/null +++ b/Samples/XrSamples/XrHandsFB/Projects/Android/settings.gradle @@ -0,0 +1 @@ +rootProject.name = "XrHandsFB" diff --git a/Samples/XrSamples/XrHandsFB/README.md b/Samples/XrSamples/XrHandsFB/README.md new file mode 100644 index 0000000..20107cb --- /dev/null +++ b/Samples/XrSamples/XrHandsFB/README.md @@ -0,0 +1,13 @@ +# OpenXR Hands Tracking Sample + +## Overview +The extensions `XR_EXT_hand_tracking`, `XR_FB_hand_tracking_mesh`, `XR_FB_hand_tracking_capsules`, and `XR_FB_hand_tracking_aim` work together to allow applications to interact with the hand tracking functionality of the device. + +* `XR_EXT_hand_tracking` provides a list of hand joint poses. +* `XR_FB_hand_tracking_mesh` allows an application to get a skinned hand mesh and a bind pose skeleton that can be used to render a hand object driven by the joints from the XR_EXT_hand_tracking extension. +* `XR_FB_hand_tracking_capsules` allows an application to get a list of capsules that represent the volume of the hand when using the XR_EXT_hand_tracking extension. +* `XR_FB_hand_tracking_aim` allows an application to get a set of basic gesture states for the hand when using the XR_EXT_hand_tracking extension. + +## The Sample +In this sample, the user is able to toggle between mesh, capsules, and joints on the hand model by selecting a button using a gesture with hand tracking. +![screen_shot](images/screen_shot.png) diff --git a/Samples/XrSamples/XrHandsFB/Src/main.cpp b/Samples/XrSamples/XrHandsFB/Src/main.cpp new file mode 100755 index 0000000..457d7c1 --- /dev/null +++ b/Samples/XrSamples/XrHandsFB/Src/main.cpp @@ -0,0 +1,677 @@ +/******************************************************************************* + +Filename : Main.cpp +Content : Simple test app to test openxr hands +Created : Sept 2020 +Authors : Federico Schliemann +Language : C++ +Copyright: Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved. + +*******************************************************************************/ + +#include +#include + +#include "XrApp.h" + +#include "Input/SkeletonRenderer.h" +#include "Input/ControllerRenderer.h" +#include "Input/TinyUI.h" +#include "Input/AxisRenderer.h" +#include "Input/HandRenderer.h" +#include "Render/SimpleBeamRenderer.h" +#include "Render/GeometryRenderer.h" + +#define FORCE_ONLY_SIMPLE_CONTROLLER_PROFILE + +class XrHandsApp : public OVRFW::XrApp { + private: + static constexpr std::string_view kSampleExplanation = + "OpenXR Hand Tracking: \n" + " \n" + "Press the buttons under the Left and Right hand labels to: \n" + "Mesh: toggle rendering the hand mesh data. \n" + "Joints: toggle rendering the hand joints as spheres. \n" + "Capsules: toggle rendering the hand capsules as rounded cylinders. \n"; + + public: + XrHandsApp() : OVRFW::XrApp() { + BackgroundColor = OVR::Vector4f(0.60f, 0.95f, 0.4f, 1.0f); + } + + // Returns a list of OpenXr extensions needed for this app + virtual std::vector GetExtensions() override { + std::vector extensions = XrApp::GetExtensions(); + extensions.push_back(XR_EXT_HAND_TRACKING_EXTENSION_NAME); + extensions.push_back(XR_FB_HAND_TRACKING_MESH_EXTENSION_NAME); + extensions.push_back(XR_FB_HAND_TRACKING_AIM_EXTENSION_NAME); + extensions.push_back(XR_FB_HAND_TRACKING_CAPSULES_EXTENSION_NAME); + return extensions; + } + +#ifdef FORCE_ONLY_SIMPLE_CONTROLLER_PROFILE + // Returns a map from interaction profile paths to vectors of suggested bindings. + // xrSuggestInteractionProfileBindings() is called once for each interaction profile path in the + // returned map. + // Apps are encouraged to suggest bindings for every device/interaction profile they support. + // Override this for custom action bindings, or modify the default bindings. + std::unordered_map> GetSuggestedBindings( + XrInstance instance) override { + // Get base suggested bindings + std::unordered_map> allSuggestedBindings = + XrApp::GetSuggestedBindings(instance); + + std::unordered_map> + onlySimpleSuggestedBindings{}; + + XrPath simpleInteractionProfile = XR_NULL_PATH; + OXR(xrStringToPath( + instance, "/interaction_profiles/khr/simple_controller", &simpleInteractionProfile)); + + // Only copy over suggested bindings for the simple interaction profile + onlySimpleSuggestedBindings[simpleInteractionProfile] = + allSuggestedBindings[simpleInteractionProfile]; + + return onlySimpleSuggestedBindings; + } +#endif + + // Must return true if the application initializes successfully. + virtual bool AppInit(const xrJava* context) override { + if (false == ui_.Init(context, GetFileSys())) { + ALOG("TinyUI::Init FAILED."); + return false; + } + /// Build UI + CreateSampleDescriptionPanel(); + + ui_.AddLabel( + "OpenXR Hands + FB extensions Sample", {0.1f, 1.25f, -2.0f}, {1300.0f, 100.0f})->SetSurfaceColor(0 ,{0.0f, 0.0f, 1.0f, 1.0f}); + ui_.AddLabel("Left Hand", {-1.0f, 2.5f, -2.0f}, {200.0f, 100.0f})->SetSurfaceColor(0 ,{0.0f, 0.0f, 1.0f, 1.0f}); + + renderMeshLButton_ = ui_.AddButton("Mesh: On", {-1.0f, 2.25f, -2.0f}, {200.0f, 100.0f}, [=]() { + renderMeshL_ = !renderMeshL_; + if (renderMeshL_) { + renderMeshLButton_->SetText("Mesh: On"); + } else { + renderMeshLButton_->SetText("Mesh: Off"); + } + }); + renderJointsLButton_ = ui_.AddButton("Joints: Off", {-1.0f, 2.0f, -2.0f}, {200.0f, 100.0f}, [=]() { + renderJointsL_ = !renderJointsL_; + if (renderJointsL_) { + renderJointsLButton_->SetText("Joints: On"); + } else { + renderJointsLButton_->SetText("Joints: Off"); + } + }); + renderCapsulesLButton_ = ui_.AddButton("Capsules: Off", {-1.0f, 1.75f, -2.0f}, {200.0f, 100.0f}, [=]() { + renderCapsulesL_ = !renderCapsulesL_; + if (renderCapsulesL_) { + renderCapsulesLButton_->SetText("Capsules: On"); + } else { + renderCapsulesLButton_->SetText("Capsules: Off"); + } + }); + + ui_.AddLabel("Right Hand", {0.0f, 2.5f, -2.0f}, {200.0f, 100.0f})->SetSurfaceColor(0 ,{0.0f, 0.0f, 1.0f, 1.0f}); + + renderMeshRButton_ = ui_.AddButton("Mesh: On", {0.0f, 2.25f, -2.0f}, {200.0f, 100.0f}, [=]() { + renderMeshR_ = !renderMeshR_; + if (renderMeshR_) { + renderMeshRButton_->SetText("Mesh: On"); + } else { + renderMeshRButton_->SetText("Mesh: Off"); + } + }); + renderJointsRButton_ = ui_.AddButton("Joints: Off", {0.0f, 2.0f, -2.0f}, {200.0f, 100.0f}, [=]() { + renderJointsR_ = !renderJointsR_; + if (renderJointsR_) { + renderJointsRButton_->SetText("Joints: On"); + } else { + renderJointsRButton_->SetText("Joints: Off"); + } + }); + renderCapsulesRButton_ = ui_.AddButton("Capsules: Off", {0.0f, 1.75f, -2.0f}, {200.0f, 100.0f}, [=]() { + renderCapsulesR_ = !renderCapsulesR_; + if (renderCapsulesR_) { + renderCapsulesRButton_->SetText("Capsules: On"); + } else { + renderCapsulesRButton_->SetText("Capsules: Off"); + } + }); + + // Inspect hand tracking system properties + XrSystemHandTrackingPropertiesEXT handTrackingSystemProperties{ + XR_TYPE_SYSTEM_HAND_TRACKING_PROPERTIES_EXT}; + XrSystemProperties systemProperties{ + XR_TYPE_SYSTEM_PROPERTIES, &handTrackingSystemProperties}; + OXR(xrGetSystemProperties(GetInstance(), GetSystemId(), &systemProperties)); + if (!handTrackingSystemProperties.supportsHandTracking) { + // The system does not support hand tracking + ALOG("xrGetSystemProperties XR_TYPE_SYSTEM_HAND_TRACKING_PROPERTIES_EXT FAILED."); + return false; + } else { + ALOG( + "xrGetSystemProperties XR_TYPE_SYSTEM_HAND_TRACKING_PROPERTIES_EXT OK - initiallizing hand tracking..."); + } + + /// Hook up extensions for hand tracking + OXR(xrGetInstanceProcAddr( + GetInstance(), + "xrCreateHandTrackerEXT", + (PFN_xrVoidFunction*)(&xrCreateHandTrackerEXT_))); + OXR(xrGetInstanceProcAddr( + GetInstance(), + "xrDestroyHandTrackerEXT", + (PFN_xrVoidFunction*)(&xrDestroyHandTrackerEXT_))); + OXR(xrGetInstanceProcAddr( + GetInstance(), + "xrLocateHandJointsEXT", + (PFN_xrVoidFunction*)(&xrLocateHandJointsEXT_))); + + /// Hook up extensions for hand rendering + OXR(xrGetInstanceProcAddr( + GetInstance(), "xrGetHandMeshFB", (PFN_xrVoidFunction*)(&xrGetHandMeshFB_))); + + return true; + } + + void CreateSampleDescriptionPanel() { + // Panel to provide sample description to the user for context + auto descriptionLabel = ui_.AddLabel( + static_cast(kSampleExplanation), {1.55f, 2.355f, -1.8f}, {750.0f, 400.0f}); + + // Align and size the description text for readability + OVRFW::VRMenuFontParms fontParams{}; + fontParams.Scale = 0.5f; + fontParams.AlignHoriz = OVRFW::HORIZONTAL_LEFT; + descriptionLabel->SetFontParms(fontParams); + descriptionLabel->SetTextLocalPosition({-0.65f, 0, 0}); + + // Tilt the description billboard 45 degrees towards the user + descriptionLabel->SetLocalRotation( + OVR::Quat::FromRotationVector({0, OVR::DegreeToRad(-30.0f), 0})); + descriptionLabel->SetSurfaceColor(0 ,{0.0f, 0.0f, 1.0f, 1.0f}); + } + + virtual void AppShutdown(const xrJava* context) override { + /// unhook extensions for hand tracking + xrCreateHandTrackerEXT_ = nullptr; + xrDestroyHandTrackerEXT_ = nullptr; + xrLocateHandJointsEXT_ = nullptr; + xrGetHandMeshFB_ = nullptr; + + OVRFW::XrApp::AppShutdown(context); + ui_.Shutdown(); + } + + virtual bool SessionInit() override { + /// Disable scene navitgation + GetScene().SetFootPos({0.0f, 0.0f, 0.0f}); + this->FreeMove = false; + /// Init session bound objects + if (false == controllerRenderL_.Init(true)) { + ALOG("AppInit::Init L controller renderer FAILED."); + return false; + } + if (false == controllerRenderR_.Init(false)) { + ALOG("AppInit::Init R controller renderer FAILED."); + return false; + } + beamRenderer_.Init(GetFileSys(), nullptr, OVR::Vector4f(1.0f), 1.0f); + + /// Hand rendering + axisRendererL_.Init(); + axisRendererR_.Init(); + + /// Hand Trackers + if (xrCreateHandTrackerEXT_) { + XrHandTrackerCreateInfoEXT createInfo{XR_TYPE_HAND_TRACKER_CREATE_INFO_EXT}; + createInfo.handJointSet = XR_HAND_JOINT_SET_DEFAULT_EXT; + createInfo.hand = XR_HAND_LEFT_EXT; + OXR(xrCreateHandTrackerEXT_(GetSession(), &createInfo, &handTrackerL_)); + createInfo.hand = XR_HAND_RIGHT_EXT; + OXR(xrCreateHandTrackerEXT_(GetSession(), &createInfo, &handTrackerR_)); + + ALOG("xrCreateHandTrackerEXT handTrackerL_=%llx", (long long)handTrackerL_); + ALOG("xrCreateHandTrackerEXT handTrackerR_=%llx", (long long)handTrackerR_); + + /// Setup skinning meshes for both hands + if (xrGetHandMeshFB_) { + for (int handIndex = 0; handIndex < 2; ++handIndex) { + /// Alias everything for initialization + const bool isLeft = (handIndex == 0); + auto& handTracker = isLeft ? handTrackerL_ : handTrackerR_; + auto& handRenderer = isLeft ? handRendererL_ : handRendererR_; + auto& handJointRenderers = isLeft ? handJointRenderersL_ : handJointRenderersR_; + auto* jointLocations = isLeft ? jointLocationsL_ : jointLocationsR_; + auto& handCapsuleRenderers = + isLeft ? handCapsuleRenderersL_ : handCapsuleRenderersR_; + + /// two-call pattern for mesh data + /// call 1 - figure out sizes + + /// mesh + XrHandTrackingMeshFB mesh{XR_TYPE_HAND_TRACKING_MESH_FB}; + mesh.next = nullptr; + /// mesh - skeleton + mesh.jointCapacityInput = 0; + mesh.jointCountOutput = 0; + mesh.jointBindPoses = nullptr; + mesh.jointRadii = nullptr; + mesh.jointParents = nullptr; + /// mesh - vertex + mesh.vertexCapacityInput = 0; + mesh.vertexCountOutput = 0; + mesh.vertexPositions = nullptr; + mesh.vertexNormals = nullptr; + mesh.vertexUVs = nullptr; + mesh.vertexBlendIndices = nullptr; + mesh.vertexBlendWeights = nullptr; + /// mesh - index + mesh.indexCapacityInput = 0; + mesh.indexCountOutput = 0; + mesh.indices = nullptr; + /// get mesh sizes + OXR(xrGetHandMeshFB_(handTracker, &mesh)); + + /// mesh storage - update sizes + mesh.jointCapacityInput = mesh.jointCountOutput; + mesh.vertexCapacityInput = mesh.vertexCountOutput; + mesh.indexCapacityInput = mesh.indexCountOutput; + /// skeleton + std::vector jointBindLocations; + std::vector parentData; + std::vector jointRadii; + jointBindLocations.resize(mesh.jointCountOutput); + parentData.resize(mesh.jointCountOutput); + jointRadii.resize(mesh.jointCountOutput); + mesh.jointBindPoses = jointBindLocations.data(); + mesh.jointParents = parentData.data(); + mesh.jointRadii = jointRadii.data(); + /// vertex + std::vector vertexPositions; + std::vector vertexNormals; + std::vector vertexUVs; + std::vector vertexBlendIndices; + std::vector vertexBlendWeights; + vertexPositions.resize(mesh.vertexCountOutput); + vertexNormals.resize(mesh.vertexCountOutput); + vertexUVs.resize(mesh.vertexCountOutput); + vertexBlendIndices.resize(mesh.vertexCountOutput); + vertexBlendWeights.resize(mesh.vertexCountOutput); + mesh.vertexPositions = vertexPositions.data(); + mesh.vertexNormals = vertexNormals.data(); + mesh.vertexUVs = vertexUVs.data(); + mesh.vertexBlendIndices = vertexBlendIndices.data(); + mesh.vertexBlendWeights = vertexBlendWeights.data(); + /// index + std::vector indices; + indices.resize(mesh.indexCountOutput); + mesh.indices = indices.data(); + + /// call 2 - fill in the data + /// chain capsules + XrHandTrackingCapsulesStateFB capsuleState{ + XR_TYPE_HAND_TRACKING_CAPSULES_STATE_FB}; + capsuleState.next = nullptr; + mesh.next = &capsuleState; + + /// get mesh data + OXR(xrGetHandMeshFB_(handTracker, &mesh)); + /// init renderer + handRenderer.Init(&mesh, true); + /// Render jointRadius for all left hand joints + { + handJointRenderers.resize(XR_HAND_JOINT_COUNT_EXT); + for (int i = 0; i < XR_HAND_JOINT_COUNT_EXT; ++i) { + const OVR::Posef pose = FromXrPosef(jointLocations[i].pose); + OVRFW::GeometryRenderer& gr = handJointRenderers[i]; + gr.Init(OVRFW::BuildTesselatedCapsuleDescriptor( + mesh.jointRadii[i], 0.0f, 7, 7)); + gr.SetPose(pose); + gr.DiffuseColor = jointColor_; + } + } + /// One time init for capsules + { + handCapsuleRenderers.resize(XR_FB_HAND_TRACKING_CAPSULE_COUNT); + for (int i = 0; i < XR_FB_HAND_TRACKING_CAPSULE_COUNT; ++i) { + const OVR::Vector3f p0 = + FromXrVector3f(capsuleState.capsules[i].points[0]); + const OVR::Vector3f p1 = + FromXrVector3f(capsuleState.capsules[i].points[1]); + const OVR::Vector3f d = (p1 - p0); + const float h = d.Length(); + const float r = capsuleState.capsules[i].radius; + const OVR::Quatf look = OVR::Quatf::LookRotation(d, {0, 1, 0}); + OVRFW::GeometryRenderer& gr = handCapsuleRenderers[i]; + gr.Init(OVRFW::BuildTesselatedCapsuleDescriptor(r, h, 7, 7)); + gr.SetPose(OVR::Posef(look, p0)); + gr.DiffuseColor = capsuleColor_; + } + } + /// Print hierarchy + { + for (int i = 0; i < XR_HAND_JOINT_COUNT_EXT; ++i) { + const OVR::Posef pose = FromXrPosef(jointLocations[i].pose); + ALOG( + " { {%.6f, %.6f, %.6f}, {%.6f, %.6f, %.6f, %.6f} } // joint = %d, parent = %d", + pose.Translation.x, + pose.Translation.y, + pose.Translation.z, + pose.Rotation.x, + pose.Rotation.y, + pose.Rotation.z, + pose.Rotation.w, + i, + (int)parentData[i]); + } + } + } + } + } + + return true; + } + + virtual void SessionEnd() override { + /// Hand Tracker + if (xrDestroyHandTrackerEXT_) { + OXR(xrDestroyHandTrackerEXT_(handTrackerL_)); + OXR(xrDestroyHandTrackerEXT_(handTrackerR_)); + } + controllerRenderL_.Shutdown(); + controllerRenderR_.Shutdown(); + beamRenderer_.Shutdown(); + axisRendererL_.Shutdown(); + axisRendererR_.Shutdown(); + handRendererL_.Shutdown(); + handRendererR_.Shutdown(); + } + + // Update state + virtual void Update(const OVRFW::ovrApplFrameIn& in) override { + ui_.HitTestDevices().clear(); + + if ((in.AllButtons & OVRFW::ovrApplFrameIn::kButtonY) != 0) { + ALOG("Y button is pressed!"); + } + if ((in.AllButtons & OVRFW::ovrApplFrameIn::kButtonMenu) != 0) { + ALOG("Menu button is pressed!"); + } + if ((in.AllButtons & OVRFW::ovrApplFrameIn::kButtonA) != 0) { + ALOG("A button is pressed!"); + } + if ((in.AllButtons & OVRFW::ovrApplFrameIn::kButtonB) != 0) { + ALOG("B button is pressed!"); + } + if ((in.AllButtons & OVRFW::ovrApplFrameIn::kButtonX) != 0) { + ALOG("X button is pressed!"); + } + + /// Hands + if (xrLocateHandJointsEXT_) { + /// L + XrHandTrackingScaleFB scaleL{XR_TYPE_HAND_TRACKING_SCALE_FB}; + scaleL.next = nullptr; + scaleL.sensorOutput = 1.0f; + scaleL.currentOutput = 1.0f; + scaleL.overrideValueInput = 1.00f; + scaleL.overrideHandScale = XR_FALSE; // XR_TRUE; + XrHandTrackingCapsulesStateFB capsuleStateL{XR_TYPE_HAND_TRACKING_CAPSULES_STATE_FB}; + capsuleStateL.next = &scaleL; + XrHandTrackingAimStateFB aimStateL{XR_TYPE_HAND_TRACKING_AIM_STATE_FB}; + aimStateL.next = &capsuleStateL; + XrHandJointVelocitiesEXT velocitiesL{XR_TYPE_HAND_JOINT_VELOCITIES_EXT}; + velocitiesL.next = &aimStateL; + velocitiesL.jointCount = XR_HAND_JOINT_COUNT_EXT; + velocitiesL.jointVelocities = jointVelocitiesL_; + XrHandJointLocationsEXT locationsL{XR_TYPE_HAND_JOINT_LOCATIONS_EXT}; + locationsL.next = &velocitiesL; + locationsL.jointCount = XR_HAND_JOINT_COUNT_EXT; + locationsL.jointLocations = jointLocationsL_; + /// R + XrHandTrackingScaleFB scaleR{XR_TYPE_HAND_TRACKING_SCALE_FB}; + scaleR.next = nullptr; + scaleR.sensorOutput = 1.0f; + scaleR.currentOutput = 1.0f; + scaleR.overrideValueInput = 1.00f; + scaleR.overrideHandScale = XR_FALSE; // XR_TRUE; + XrHandTrackingCapsulesStateFB capsuleStateR{XR_TYPE_HAND_TRACKING_CAPSULES_STATE_FB}; + capsuleStateR.next = &scaleR; + XrHandTrackingAimStateFB aimStateR{XR_TYPE_HAND_TRACKING_AIM_STATE_FB}; + aimStateR.next = &capsuleStateR; + XrHandJointVelocitiesEXT velocitiesR{XR_TYPE_HAND_JOINT_VELOCITIES_EXT}; + velocitiesR.next = &aimStateR; + velocitiesR.jointCount = XR_HAND_JOINT_COUNT_EXT; + velocitiesR.jointVelocities = jointVelocitiesR_; + XrHandJointLocationsEXT locationsR{XR_TYPE_HAND_JOINT_LOCATIONS_EXT}; + locationsR.next = &velocitiesR; + locationsR.jointCount = XR_HAND_JOINT_COUNT_EXT; + locationsR.jointLocations = jointLocationsR_; + + XrHandJointsLocateInfoEXT locateInfoL{XR_TYPE_HAND_JOINTS_LOCATE_INFO_EXT}; + locateInfoL.baseSpace = GetStageSpace(); + locateInfoL.time = ToXrTime(in.PredictedDisplayTime); + OXR(xrLocateHandJointsEXT_(handTrackerL_, &locateInfoL, &locationsL)); + + XrHandJointsLocateInfoEXT locateInfoR{XR_TYPE_HAND_JOINTS_LOCATE_INFO_EXT}; + locateInfoR.baseSpace = GetStageSpace(); + locateInfoR.time = ToXrTime(in.PredictedDisplayTime); + OXR(xrLocateHandJointsEXT_(handTrackerR_, &locateInfoR, &locationsR)); + + std::vector handJointsL; + std::vector handJointsR; + + // Determine which joints are actually tracked + // XrSpaceLocationFlags isTracked = XR_SPACE_LOCATION_ORIENTATION_TRACKED_BIT + // | XR_SPACE_LOCATION_POSITION_TRACKED_BIT; + + // Tracked joints and computed joints can all be valid + XrSpaceLocationFlags isValid = + XR_SPACE_LOCATION_ORIENTATION_VALID_BIT | XR_SPACE_LOCATION_POSITION_VALID_BIT; + + handTrackedL_ = false; + handTrackedR_ = false; + + if (locationsL.isActive) { + for (int i = 0; i < XR_FB_HAND_TRACKING_CAPSULE_COUNT; ++i) { + const OVR::Vector3f p0 = FromXrVector3f(capsuleStateL.capsules[i].points[0]); + const OVR::Vector3f p1 = FromXrVector3f(capsuleStateL.capsules[i].points[1]); + const OVR::Vector3f d = (p1 - p0); + const OVR::Quatf look = OVR::Quatf::LookRotation(d, {0, 1, 0}); + /// apply inverse scale here + const float h = d.Length() / scaleL.currentOutput; + const OVR::Vector3f start = p0 + look.Rotate(OVR::Vector3f(0, 0, -h / 2)); + OVRFW::GeometryRenderer& gr = handCapsuleRenderersL_[i]; + gr.SetScale(OVR::Vector3f(scaleL.currentOutput)); + gr.SetPose(OVR::Posef(look, start)); + gr.Update(); + } + for (int i = 0; i < XR_HAND_JOINT_COUNT_EXT; ++i) { + if ((jointLocationsL_[i].locationFlags & isValid) != 0) { + const auto p = FromXrPosef(jointLocationsL_[i].pose); + handJointsL.push_back(p); + handTrackedL_ = true; + OVRFW::GeometryRenderer& gr = handJointRenderersL_[i]; + gr.SetScale(OVR::Vector3f(scaleL.currentOutput)); + gr.SetPose(p); + gr.Update(); + } + } + handRendererL_.Update(&jointLocationsL_[0]); + const bool didPinch = + (aimStateL.status & XR_HAND_TRACKING_AIM_INDEX_PINCHING_BIT_FB) != 0; + ui_.AddHitTestRay(FromXrPosef(aimStateL.aimPose), didPinch && !lastFrameClickedL_); + lastFrameClickedL_ = didPinch; + } + if (locationsR.isActive) { + for (int i = 0; i < XR_FB_HAND_TRACKING_CAPSULE_COUNT; ++i) { + const OVR::Vector3f p0 = FromXrVector3f(capsuleStateR.capsules[i].points[0]); + const OVR::Vector3f p1 = FromXrVector3f(capsuleStateR.capsules[i].points[1]); + const OVR::Vector3f d = (p1 - p0); + const OVR::Quatf look = OVR::Quatf::LookRotation(d, {0, 1, 0}); + /// apply inverse scale here + const float h = d.Length() / scaleR.currentOutput; + const OVR::Vector3f start = p0 + look.Rotate(OVR::Vector3f(0, 0, -h / 2)); + OVRFW::GeometryRenderer& gr = handCapsuleRenderersR_[i]; + gr.SetScale(OVR::Vector3f(scaleR.currentOutput)); + gr.SetPose(OVR::Posef(look, start)); + gr.Update(); + } + for (int i = 0; i < XR_HAND_JOINT_COUNT_EXT; ++i) { + if ((jointLocationsR_[i].locationFlags & isValid) != 0) { + const auto p = FromXrPosef(jointLocationsR_[i].pose); + handJointsR.push_back(p); + handTrackedR_ = true; + OVRFW::GeometryRenderer& gr = handJointRenderersR_[i]; + gr.SetScale(OVR::Vector3f(scaleR.currentOutput)); + gr.SetPose(p); + gr.Update(); + } + } + handRendererR_.Update(&jointLocationsR_[0]); + const bool didPinch = + (aimStateR.status & XR_HAND_TRACKING_AIM_INDEX_PINCHING_BIT_FB) != 0; + + ui_.AddHitTestRay(FromXrPosef(aimStateR.aimPose), didPinch && !lastFrameClickedR_); + lastFrameClickedR_ = didPinch; + } + axisRendererL_.Update(handJointsL); + axisRendererR_.Update(handJointsR); + } + + if (in.LeftRemoteTracked && !handTrackedL_) { + controllerRenderL_.Update(in.LeftRemotePose); + const bool didPinch = in.LeftRemoteIndexTrigger > 0.5f; + ui_.AddHitTestRay(in.LeftRemotePointPose, didPinch); + } + if (in.RightRemoteTracked && !handTrackedR_) { + controllerRenderR_.Update(in.RightRemotePose); + const bool didPinch = in.RightRemoteIndexTrigger > 0.5f; + ui_.AddHitTestRay(in.RightRemotePointPose, didPinch); + } + + ui_.Update(in); + beamRenderer_.Update(in, ui_.HitTestDevices()); + } + + // Render eye buffers while running + virtual void Render(const OVRFW::ovrApplFrameIn& in, OVRFW::ovrRendererOutput& out) override { + /// Render UI + ui_.Render(in, out); + + /// Render controllers when hands are not tracked + if (in.LeftRemoteTracked && !handTrackedL_) { + controllerRenderL_.Render(out.Surfaces); + } + if (in.RightRemoteTracked && !handTrackedR_) { + controllerRenderR_.Render(out.Surfaces); + } + + /// Render hand axes + if (handTrackedL_) { + axisRendererL_.Render(OVR::Matrix4f(), in, out); + + if (renderJointsL_) { + for (int i = 0; i < XR_HAND_JOINT_COUNT_EXT; ++i) { + OVRFW::GeometryRenderer& gr = handJointRenderersL_[i]; + gr.Render(out.Surfaces); + } + } + + if (renderCapsulesL_) { + for (int i = 0; i < XR_FB_HAND_TRACKING_CAPSULE_COUNT; ++i) { + OVRFW::GeometryRenderer& gr = handCapsuleRenderersL_[i]; + gr.Render(out.Surfaces); + } + } + + if (renderMeshL_) { + handRendererL_.Render(out.Surfaces); + } + } + if (handTrackedR_) { + axisRendererR_.Render(OVR::Matrix4f(), in, out); + + if (renderJointsR_) { + for (int i = 0; i < XR_HAND_JOINT_COUNT_EXT; ++i) { + OVRFW::GeometryRenderer& gr = handJointRenderersR_[i]; + gr.Render(out.Surfaces); + } + } + + if (renderCapsulesR_) { + for (int i = 0; i < XR_FB_HAND_TRACKING_CAPSULE_COUNT; ++i) { + OVRFW::GeometryRenderer& gr = handCapsuleRenderersR_[i]; + gr.Render(out.Surfaces); + } + } + + if (renderMeshR_) { + handRendererR_.Render(out.Surfaces); + } + } + + /// Render beams + beamRenderer_.Render(in, out); + } + + public: + /// Hands - extension functions + PFN_xrCreateHandTrackerEXT xrCreateHandTrackerEXT_ = nullptr; + PFN_xrDestroyHandTrackerEXT xrDestroyHandTrackerEXT_ = nullptr; + PFN_xrLocateHandJointsEXT xrLocateHandJointsEXT_ = nullptr; + /// Hands - FB mesh rendering extensions + PFN_xrGetHandMeshFB xrGetHandMeshFB_ = nullptr; + /// Hands - tracker handles + XrHandTrackerEXT handTrackerL_ = XR_NULL_HANDLE; + XrHandTrackerEXT handTrackerR_ = XR_NULL_HANDLE; + /// Hands - data buffers + XrHandJointLocationEXT jointLocationsL_[XR_HAND_JOINT_COUNT_EXT]; + XrHandJointLocationEXT jointLocationsR_[XR_HAND_JOINT_COUNT_EXT]; + XrHandJointVelocityEXT jointVelocitiesL_[XR_HAND_JOINT_COUNT_EXT]; + XrHandJointVelocityEXT jointVelocitiesR_[XR_HAND_JOINT_COUNT_EXT]; + + private: + OVRFW::ControllerRenderer controllerRenderL_; + OVRFW::ControllerRenderer controllerRenderR_; + OVRFW::HandRenderer handRendererL_; + OVRFW::HandRenderer handRendererR_; + OVRFW::TinyUI ui_; + OVRFW::SimpleBeamRenderer beamRenderer_; + std::vector beams_; + OVRFW::ovrAxisRenderer axisRendererL_; + OVRFW::ovrAxisRenderer axisRendererR_; + bool handTrackedL_ = false; + bool handTrackedR_ = false; + + bool lastFrameClickedL_ = false; + bool lastFrameClickedR_ = false; + + OVR::Vector4f jointColor_{0.4, 0.5, 0.2, 0.5}; + OVR::Vector4f capsuleColor_{0.4, 0.2, 0.2, 0.5}; + + bool renderMeshL_ = true; + bool renderMeshR_ = true; + bool renderJointsL_ = false; + bool renderJointsR_ = false; + bool renderCapsulesL_ = false; + bool renderCapsulesR_ = false; + + OVRFW::VRMenuObject* renderMeshLButton_ = nullptr; + OVRFW::VRMenuObject* renderMeshRButton_ = nullptr; + OVRFW::VRMenuObject* renderJointsLButton_ = nullptr; + OVRFW::VRMenuObject* renderJointsRButton_ = nullptr; + OVRFW::VRMenuObject* renderCapsulesLButton_ = nullptr; + OVRFW::VRMenuObject* renderCapsulesRButton_ = nullptr; + + std::vector handJointRenderersL_; + std::vector handJointRenderersR_; + std::vector handCapsuleRenderersL_; + std::vector handCapsuleRenderersR_; +}; + +ENTRY_POINT(XrHandsApp) diff --git a/Samples/XrSamples/XrHandsFB/assets/assets.txt b/Samples/XrSamples/XrHandsFB/assets/assets.txt new file mode 100644 index 0000000..2cc30f7 --- /dev/null +++ b/Samples/XrSamples/XrHandsFB/assets/assets.txt @@ -0,0 +1 @@ +This file is a placeholder. diff --git a/Samples/XrSamples/XrHandsFB/assets/panel.ktx b/Samples/XrSamples/XrHandsFB/assets/panel.ktx new file mode 100644 index 0000000..deb13e8 Binary files /dev/null and b/Samples/XrSamples/XrHandsFB/assets/panel.ktx differ diff --git a/Samples/XrSamples/XrHandsFB/java/MainActivity.java b/Samples/XrSamples/XrHandsFB/java/MainActivity.java new file mode 100644 index 0000000..3afba0c --- /dev/null +++ b/Samples/XrSamples/XrHandsFB/java/MainActivity.java @@ -0,0 +1,28 @@ +// Copyright (c) Facebook Technologies, LLC and its affiliates. All Rights reserved. +package com.oculus.sdk.xrhandsfb; + +/** + * When using NativeActivity, we currently need to handle loading of dependent shared libraries + * manually before a shared library that depends on them is loaded, since there is not currently a + * way to specify a shared library dependency for NativeActivity via the manifest meta-data. + * + *

The simplest method for doing so is to subclass NativeActivity with an empty activity that + * calls System.loadLibrary on the dependent libraries, which is unfortunate when the goal is to + * write a pure native C/C++ only Android activity. + * + *

A native-code only solution is to load the dependent libraries dynamically using dlopen(). + * However, there are a few considerations, see: + * https://groups.google.com/forum/#!msg/android-ndk/l2E2qh17Q6I/wj6s_6HSjaYJ + * + *

1. Only call dlopen() if you're sure it will succeed as the bionic dynamic linker will + * remember if dlopen failed and will not re-try a dlopen on the same lib a second time. + * + *

2. Must remember what libraries have already been loaded to avoid infinitely looping when + * libraries have circular dependencies. + */ +public class MainActivity extends android.app.NativeActivity { + static { + System.loadLibrary("openxr_loader"); + System.loadLibrary("xrhandsfb"); + } +} diff --git a/Samples/XrSamples/XrHandsFB/res/values/strings.xml b/Samples/XrSamples/XrHandsFB/res/values/strings.xml new file mode 100644 index 0000000..db904ba --- /dev/null +++ b/Samples/XrSamples/XrHandsFB/res/values/strings.xml @@ -0,0 +1,4 @@ + + + XrHandsFB Sample + diff --git a/Samples/XrSamples/XrInput/CMakeLists.txt b/Samples/XrSamples/XrInput/CMakeLists.txt new file mode 100755 index 0000000..87032fd --- /dev/null +++ b/Samples/XrSamples/XrInput/CMakeLists.txt @@ -0,0 +1,43 @@ +# Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved. + +project(xrsamples_xrinput) + +if(NOT TARGET OpenXR::openxr_loader) + find_package(OpenXR REQUIRED) +endif() + +file(GLOB_RECURSE SRC_FILES + Src/*.c + Src/*.cpp +) + +if(ANDROID) + add_library(${PROJECT_NAME} MODULE ${SRC_FILES}) + target_include_directories(${PROJECT_NAME} PUBLIC ${ANDROID_NDK}/sources/android/native_app_glue) + target_link_libraries(${PROJECT_NAME} PRIVATE + android + EGL + GLESv3 + log + ktx + ) + set_target_properties(${PROJECT_NAME} PROPERTIES LINK_FLAGS "-u ANativeActivity_onCreate") +elseif(WIN32) + add_definitions(-D_USE_MATH_DEFINES) + add_executable(${PROJECT_NAME} ${SRC_FILES}) + add_custom_command(TARGET ${PROJECT_NAME} PRE_BUILD + COMMAND "${CMAKE_COMMAND}" -E copy_directory + "${CMAKE_CURRENT_LIST_DIR}/assets" + "$/assets" + VERBATIM) + + add_custom_command(TARGET ${PROJECT_NAME} PRE_BUILD + COMMAND "${CMAKE_COMMAND}" -E copy_directory + "${CMAKE_SOURCE_DIR}/SampleXrFramework/res/raw" + "$/font/res/raw" + VERBATIM) +endif() + +# Common across platforms +target_include_directories(${PROJECT_NAME} PRIVATE Src) +target_link_libraries(${PROJECT_NAME} PRIVATE samplexrframework) diff --git a/Samples/XrSamples/XrInput/Projects/Android/AndroidManifest.xml b/Samples/XrSamples/XrInput/Projects/Android/AndroidManifest.xml new file mode 100755 index 0000000..0394842 --- /dev/null +++ b/Samples/XrSamples/XrInput/Projects/Android/AndroidManifest.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Samples/XrSamples/XrInput/Projects/Android/build.bat b/Samples/XrSamples/XrInput/Projects/Android/build.bat new file mode 100755 index 0000000..facf79f --- /dev/null +++ b/Samples/XrSamples/XrInput/Projects/Android/build.bat @@ -0,0 +1,29 @@ +@rem Only edit the master copy of this file in SDK_ROOT/bin/scripts/build/perproject + +@setlocal enableextensions enabledelayedexpansion + +@if not exist "build.gradle" @echo Build script must be executed from project directory. & goto :Abort + +@set P=.. + +:TryAgain + +@rem @echo P = %P% + +@if exist "%P%\bin\scripts\build\build.py.bat" goto :Found + +@if exist "%P%\bin\scripts\build" @echo "Could not find build.py.bat" & goto :Abort + +@set P=%P%\.. + +@goto :TryAgain + +:Found + +@set P=%P%\bin\scripts\build +@call %P%\build.py.bat %1 %2 %3 %4 %5 +@goto :End + +:Abort + +:End diff --git a/Samples/XrSamples/XrInput/Projects/Android/build.gradle b/Samples/XrSamples/XrInput/Projects/Android/build.gradle new file mode 100755 index 0000000..0ceae8b --- /dev/null +++ b/Samples/XrSamples/XrInput/Projects/Android/build.gradle @@ -0,0 +1,78 @@ +// Top-level build file where you can add configuration options common to all sub-projects/modules. +buildscript { + repositories { + google() + mavenCentral() + } + dependencies { + classpath "com.android.tools.build:gradle:7.0.3" + } +} + +repositories { + google() + mavenCentral() +} + +apply plugin: 'com.android.application' + +android { + compileSdk 32 + + defaultConfig { + applicationId "com.oculus.xrsamples.xrinput" + minSdk 26 + targetSdk 32 + versionCode 1 + versionName "1.0" + + // override app plugin abiFilters for 64-bit support + externalNativeBuild { + ndk { + abiFilters 'arm64-v8a' + } + ndkBuild { + abiFilters 'arm64-v8a' + } + cmake { + targets "xrsamples_xrinput" + } + } + } + + sourceSets { + main { + manifest.srcFile 'AndroidManifest.xml' + java.srcDirs = ['../../java'] + assets.srcDirs = ['../../assets'] + res.srcDirs = ['../../res'] + } + } + + buildTypes { + debug { + debuggable true + } + + release { + debuggable false + } + } + + externalNativeBuild { + cmake { + path file('../../../../CMakeLists.txt') + } + } + + // Enable prefab support for the OpenXR AAR + buildFeatures { + prefab true + } +} + +dependencies { + // Package/application AndroidManifest.xml properties, plus headers and libraries + // exposed to CMake + implementation 'org.khronos.openxr:openxr_loader_for_android:1.1.36' +} diff --git a/Samples/XrSamples/XrInput/Projects/Android/build.py b/Samples/XrSamples/XrInput/Projects/Android/build.py new file mode 100755 index 0000000..d4b6e58 --- /dev/null +++ b/Samples/XrSamples/XrInput/Projects/Android/build.py @@ -0,0 +1,35 @@ +#!/usr/bin/python +# (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +# This first bit of code is common bootstrapping code +# to determine the SDK root, and to set up the import +# path for additional python code. + +# begin bootstrap +import os +import sys + + +def init(): + root = os.path.realpath(os.path.dirname(os.path.realpath(__file__))) + os.chdir(root) # make sure we are always executing from the project directory + while os.path.isdir(os.path.join(root, "bin/scripts/build")) == False: + root = os.path.realpath(os.path.join(root, "..")) + if ( + len(root) <= 5 + ): # Should catch both Posix and Windows root directories (e.g. '/' and 'C:\') + print("Unable to find SDK root. Exiting.") + sys.exit(1) + root = os.path.abspath(root) + os.environ["OCULUS_SDK_PATH"] = root + sys.path.append(root + "/bin/scripts/build") + + +init() +import ovrbuild + +ovrbuild.init() +# end bootstrap + + +ovrbuild.build() diff --git a/Samples/XrSamples/XrInput/Projects/Android/gradle.properties b/Samples/XrSamples/XrInput/Projects/Android/gradle.properties new file mode 100644 index 0000000..3e927b1 --- /dev/null +++ b/Samples/XrSamples/XrInput/Projects/Android/gradle.properties @@ -0,0 +1,21 @@ +# Project-wide Gradle settings. +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. More details, visit +# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects +# org.gradle.parallel=true +# AndroidX package structure to make it clearer which packages are bundled with the +# Android operating system, and which are packaged with your app's APK +# https://developer.android.com/topic/libraries/support-library/androidx-rn +android.useAndroidX=true +# Enables namespacing of each library's R class so that its R class includes only the +# resources declared in the library itself and none from the library's dependencies, +# thereby reducing the size of the R class for that library +android.nonTransitiveRClass=true \ No newline at end of file diff --git a/Samples/XrSamples/XrInput/Projects/Android/gradle/wrapper/gradle-wrapper.jar b/Samples/XrSamples/XrInput/Projects/Android/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..7454180 Binary files /dev/null and b/Samples/XrSamples/XrInput/Projects/Android/gradle/wrapper/gradle-wrapper.jar differ diff --git a/Samples/XrSamples/XrInput/Projects/Android/gradle/wrapper/gradle-wrapper.properties b/Samples/XrSamples/XrInput/Projects/Android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..ffed3a2 --- /dev/null +++ b/Samples/XrSamples/XrInput/Projects/Android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/Samples/XrSamples/XrInput/Projects/Android/gradlew b/Samples/XrSamples/XrInput/Projects/Android/gradlew new file mode 100755 index 0000000..a69d9cb --- /dev/null +++ b/Samples/XrSamples/XrInput/Projects/Android/gradlew @@ -0,0 +1,240 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# 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 +# +# https://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. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit + +APP_NAME="Gradle" +APP_BASE_NAME=${0##*/} + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/Samples/XrSamples/XrInput/Projects/Android/gradlew.bat b/Samples/XrSamples/XrInput/Projects/Android/gradlew.bat new file mode 100755 index 0000000..f127cfd --- /dev/null +++ b/Samples/XrSamples/XrInput/Projects/Android/gradlew.bat @@ -0,0 +1,91 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/Samples/XrSamples/XrInput/Projects/Android/settings.gradle b/Samples/XrSamples/XrInput/Projects/Android/settings.gradle new file mode 100755 index 0000000..f6d69ce --- /dev/null +++ b/Samples/XrSamples/XrInput/Projects/Android/settings.gradle @@ -0,0 +1 @@ +rootProject.name = "XrInput" diff --git a/Samples/XrSamples/XrInput/README.md b/Samples/XrSamples/XrInput/README.md new file mode 100644 index 0000000..320a85b --- /dev/null +++ b/Samples/XrSamples/XrInput/README.md @@ -0,0 +1,6 @@ +# OpenXR Action System Sample + +## Overview +The OpenXR Action System is an approach to handling input across a multitude of VR/AR devices. It allows developers to define "actions" within their applications and bind these actions to specific inputs on different devices. This device-agnostic system not only makes applications compatible with a wide range of devices without needing specific code for each one but also future-proofs applications against new devices. + +More details can be viewed at: https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#input diff --git a/Samples/XrSamples/XrInput/Src/ActionSetDisplayPanel.cpp b/Samples/XrSamples/XrInput/Src/ActionSetDisplayPanel.cpp new file mode 100755 index 0000000..6242479 --- /dev/null +++ b/Samples/XrSamples/XrInput/Src/ActionSetDisplayPanel.cpp @@ -0,0 +1,190 @@ +#include "ActionSetDisplayPanel.h" + +#include +#include +#include "Render/BitmapFont.h" + +ActionSetDisplayPanel::ActionSetDisplayPanel( + std::string title, + XrSession session, + XrInstance instance, + OVRFW::TinyUI* ui, + OVR::Vector3f topLeftLocation) + : Session{session}, Instance{instance}, ui_{ui}, topLeftLocation_{topLeftLocation} { + ui_->AddLabel( + title, GetNextLabelLocation() + OVR::Vector3f{0, kHeaderHeight_, 0.00}, {widthPx_, 45.0f}); +} + +void ActionSetDisplayPanel::AddBoolAction(XrAction action, const char* actionName) { + auto actionStateLabel = CreateActionLabel(actionName); + boolActions_.push_back({action, actionStateLabel}); +} + +void ActionSetDisplayPanel::AddFloatAction(XrAction action, const char* actionName) { + auto actionStateLabel = CreateActionLabel(actionName); + floatActions_.push_back({action, actionStateLabel}); +} + +void ActionSetDisplayPanel::AddVec2Action(XrAction action, const char* actionName) { + auto actionStateLabel = CreateActionLabel(actionName); + vec2Actions_.push_back({action, actionStateLabel}); +} + +void ActionSetDisplayPanel::AddPoseAction(XrAction action, const char* actionName) { + auto actionStateLabel = CreateActionLabel(actionName); + poseActions_.push_back({action, actionStateLabel}); +} + +VRMenuObject* ActionSetDisplayPanel::CreateActionLabel(const char* actionName) { + auto label = ui_->AddLabel(actionName, GetNextLabelLocation(), {widthPx_, 45.0f}); + auto stateLabel = ui_->AddLabel("state", GetNextStateLabelLocation(), {widthPx_, 250.0f}); + + OVRFW::VRMenuFontParms fontParams{}; + fontParams.Scale = 0.5f; + fontParams.AlignHoriz = OVRFW::HORIZONTAL_LEFT; + label->SetFontParms(fontParams); + label->SetTextLocalPosition({-0.45f * width_, 0, 0}); + stateLabel->SetFontParms(fontParams); + stateLabel->SetTextLocalPosition({-0.47f * width_, -0.02f * height_, 0}); + + label->SetColor({0.2, 0.2, 0.2, 1.0}); + elements_++; + return stateLabel; +} + +OVR::Vector3f ActionSetDisplayPanel::GetNextLabelLocation() { + return topLeftLocation_ + + OVR::Vector3f{width_ * 0.5f, -elements_ * kElementGap_ - kHeaderHeight_, 0.01}; +} + +OVR::Vector3f ActionSetDisplayPanel::GetNextStateLabelLocation() { + return GetNextLabelLocation() + OVR::Vector3f{0.0, -kElementGap_ * 0.5f, 0.0}; +} + +void ActionSetDisplayPanel::Update() { + for (auto& pair : boolActions_) { + XrAction action = pair.first; + VRMenuObject* label = pair.second; + std::string bindingText = ListBoundSources(action); + + XrActionStateGetInfo getInfo{XR_TYPE_ACTION_STATE_GET_INFO}; + getInfo.action = action; + getInfo.subactionPath = XR_NULL_PATH; + XrActionStateBoolean state{XR_TYPE_ACTION_STATE_BOOLEAN}; + OXR(xrGetActionStateBoolean(Session, &getInfo, &state)); + + label->SetText( + "currentState: %s | changedSinceLastSync: %s\n" + "isActive: %s | lastChangeTime: %ldms\n" + + bindingText, + state.currentState ? "True " : "False", + state.changedSinceLastSync ? "True " : "False", + state.isActive ? "True " : "False", + state.lastChangeTime / (1000 * 1000)); // convert from ns to ms + label->SetSurfaceColor( + 0, + state.isActive ? OVR::Vector4f(0., 0.1, 0., 1.) : OVR::Vector4f(0.05, 0.05, 0.05, 1.)); + label->SetSelected(state.currentState); + } + for (auto& pair : floatActions_) { + XrAction action = pair.first; + VRMenuObject* label = pair.second; + std::string bindingText = ListBoundSources(action); + + XrActionStateGetInfo getInfo{XR_TYPE_ACTION_STATE_GET_INFO}; + getInfo.action = action; + getInfo.subactionPath = XR_NULL_PATH; + XrActionStateFloat state{XR_TYPE_ACTION_STATE_FLOAT}; + OXR(xrGetActionStateFloat(Session, &getInfo, &state)); + + label->SetText( + "currentState: %0.3f | changedSinceLastSync: %s\n" + "isActive: %s | lastChangeTime: %ldms\n" + + bindingText, + state.currentState, + state.changedSinceLastSync ? "True " : "False", + state.isActive ? "True " : "False", + state.lastChangeTime / (1000 * 1000)); // convert from ns to ms + label->SetSurfaceColor( + 0, + state.isActive ? OVR::Vector4f(0., 0.1, 0., 1.) : OVR::Vector4f(0.05, 0.05, 0.05, 1.)); + } + + for (auto& pair : vec2Actions_) { + XrAction action = pair.first; + VRMenuObject* label = pair.second; + std::string bindingText = ListBoundSources(action); + + XrActionStateGetInfo getInfo{XR_TYPE_ACTION_STATE_GET_INFO}; + getInfo.action = action; + getInfo.subactionPath = XR_NULL_PATH; + XrActionStateVector2f state{XR_TYPE_ACTION_STATE_VECTOR2F}; + OXR(xrGetActionStateVector2f(Session, &getInfo, &state)); + + label->SetText( + "currentState: (%0.2f, %0.2f) | changedSinceLastSync: %s\n" + "isActive: %s | lastChangeTime: %ldms\n" + + bindingText, + state.currentState.x, + state.currentState.y, + state.changedSinceLastSync ? "True " : "False", + state.isActive ? "True " : "False", + state.lastChangeTime / (1000 * 1000)); // convert from ns to ms + label->SetSurfaceColor( + 0, + state.isActive ? OVR::Vector4f(0., 0.1, 0., 1.) : OVR::Vector4f(0.05, 0.05, 0.05, 1.)); + } + + for (auto& pair : poseActions_) { + XrAction action = pair.first; + VRMenuObject* label = pair.second; + std::string bindingText = ListBoundSources(action); + + XrActionStateGetInfo getInfo{XR_TYPE_ACTION_STATE_GET_INFO}; + getInfo.action = action; + getInfo.subactionPath = XR_NULL_PATH; + XrActionStatePose state{XR_TYPE_ACTION_STATE_POSE}; + OXR(xrGetActionStatePose(Session, &getInfo, &state)); + + label->SetText("isActive: %s\n" + bindingText, state.isActive ? "True " : "False"); + label->SetSurfaceColor( + 0, + state.isActive ? OVR::Vector4f(0., 0.1, 0., 1.) : OVR::Vector4f(0.05, 0.05, 0.05, 1.)); + } +} + +std::string ActionSetDisplayPanel::ListBoundSources(XrAction action) { + XrBoundSourcesForActionEnumerateInfo enumerateInfo{ + XR_TYPE_BOUND_SOURCES_FOR_ACTION_ENUMERATE_INFO}; + enumerateInfo.action = action; + uint32_t sourcesCount = 0; + + std::stringstream bindingText; + + OXR(xrEnumerateBoundSourcesForAction(Session, &enumerateInfo, 0, &sourcesCount, nullptr)); + std::vector boundSources(sourcesCount); + OXR(xrEnumerateBoundSourcesForAction( + Session, &enumerateInfo, boundSources.size(), &sourcesCount, boundSources.data())); + + for (XrPath sourcePath : boundSources) { + uint32_t pathLength; + // OXR(xrPathToString(Instance, sourcePath, 0, &pathLength, nullptr)); + std::vector pathString(XR_MAX_PATH_LENGTH); + OXR(xrPathToString( + Instance, sourcePath, pathString.size(), &pathLength, pathString.data())); + + XrInputSourceLocalizedNameGetInfo sni{XR_TYPE_INPUT_SOURCE_LOCALIZED_NAME_GET_INFO}; + sni.sourcePath = sourcePath; + sni.whichComponents = XR_INPUT_SOURCE_LOCALIZED_NAME_USER_PATH_BIT | + XR_INPUT_SOURCE_LOCALIZED_NAME_COMPONENT_BIT | + XR_INPUT_SOURCE_LOCALIZED_NAME_INTERACTION_PROFILE_BIT; + uint32_t sourceNameLength; + OXR(xrGetInputSourceLocalizedName(Session, &sni, 0, &sourceNameLength, nullptr)); + std::vector sourceName(sourceNameLength); + OXR(xrGetInputSourceLocalizedName( + Session, &sni, sourceName.size(), &sourceNameLength, sourceName.data())); + + bindingText << "\nBinding: " << pathString.data() << "\n(" << sourceName.data() << ")\n"; + } + return std::string(bindingText.str()); +} diff --git a/Samples/XrSamples/XrInput/Src/ActionSetDisplayPanel.h b/Samples/XrSamples/XrInput/Src/ActionSetDisplayPanel.h new file mode 100755 index 0000000..e8ffd93 --- /dev/null +++ b/Samples/XrSamples/XrInput/Src/ActionSetDisplayPanel.h @@ -0,0 +1,47 @@ +#include + +#include "XrApp.h" +#include "Input/TinyUI.h" +#include "GUI/VRMenuObject.h" + +using OVRFW::VRMenuObject; +class ActionSetDisplayPanel { + public: + ActionSetDisplayPanel( + std::string title, + XrSession session, + XrInstance instance, + OVRFW::TinyUI* ui, + OVR::Vector3f topLeftLocation); + void AddBoolAction(XrAction action, const char* actionName); + void AddFloatAction(XrAction action, const char* actionName); + void AddVec2Action(XrAction action, const char* actionName); + void AddPoseAction(XrAction action, const char* actionName); + + void Update(); + + private: + VRMenuObject* CreateActionLabel(const char* actionName); + std::string ListBoundSources(XrAction action); + OVR::Vector3f GetNextLabelLocation(); + OVR::Vector3f GetNextStateLabelLocation(); + + // OVRFW::VRMenuObject* backgroundPane_{}; + std::vector> boolActions_{}; + std::vector> floatActions_{}; + std::vector> vec2Actions_{}; + std::vector> poseActions_{}; + XrSession Session; + XrInstance Instance; + OVRFW::TinyUI* ui_; + + OVR::Vector3f topLeftLocation_; + int elements_{0}; + static constexpr float kHeaderHeight_{0.15}; + static constexpr float kElementGap_{0.65}; + + static constexpr float widthPx_{600}; + static constexpr float heightPx_{500}; + static constexpr float width_{widthPx_ * VRMenuObject::DEFAULT_TEXEL_SCALE}; + static constexpr float height_{heightPx_ * VRMenuObject::DEFAULT_TEXEL_SCALE}; +}; diff --git a/Samples/XrSamples/XrInput/Src/main.cpp b/Samples/XrSamples/XrInput/Src/main.cpp new file mode 100755 index 0000000..9d09838 --- /dev/null +++ b/Samples/XrSamples/XrInput/Src/main.cpp @@ -0,0 +1,1247 @@ +/******************************************************************************* + +Filename : Main.cpp +Content : OpenXR sample showing use of the input API +Created : +Authors : Andreas Selvik, Eryk Pecyna +Language : C++ +Copyright: Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved. + +*******************************************************************************/ + +#include +#include +#include + +#include + +#include "GUI/VRMenuObject.h" +#include "Render/BitmapFont.h" +#include "Render/GeometryBuilder.h" +#include "Render/GlGeometry.h" +#include "XrApp.h" +#include "ActionSetDisplayPanel.h" +#include "GUI/VRMenuObject.h" + +#include "OVR_Math.h" +#include "Input/ControllerRenderer.h" +#include "Input/HandRenderer.h" +#include "Input/TinyUI.h" +#include "Render/GlGeometry.h" +#include "Render/GeometryRenderer.h" +#include "Render/SimpleBeamRenderer.h" +#include "meta_openxr_preview/openxr_oculus_helpers.h" + +// All physical units in OpenXR are in meters, but sometimes it's more useful +// to think in cm, so this user defined literal converts from centimeters to meters +constexpr float operator"" _cm(long double centimeters) { + return static_cast(centimeters * 0.01); +} +constexpr float operator"" _cm(unsigned long long centimeters) { + return static_cast(centimeters * 0.01); +} + +// For expressiveness; use _m rather than f literals when we mean meters +constexpr float operator"" _m(long double meters) { + return static_cast(meters); +} +constexpr float operator"" _m(unsigned long long meters) { + return static_cast(meters); +} + +class XrInputSampleApp : public OVRFW::XrApp { + public: + static constexpr std::string_view kSampleIntroduction = + "This sample is an introduction to using the OpenXR action system to get input.\n" + "The OpenXR action system is designed to be adaptable to a wide variety of input\n" + "devices, including forward compatibility with future devices. This is achieved by\n" + "using the concept of \"actions\" to drive the application logic, separating out\n" + "the device specific \"bindings\" of controller input to actions.\n\n" + "Walking through the code will introduce you to how to use Actions, ActionSets, and\n" + "Spaces, as well as motivate their design. Playing around in here will allow you to see\n" + "how exactly actions react to change of active action sets, action set priorities, and\n" + "multiple bindings.\n\n" + "Try picking up the tool in front of you and create some art. How about a castle, or\n" + "a palm tree?\n" + "Notice how the tool action set is only active while you're holding the cube tool.\n"; + + static constexpr std::string_view kSampleInstructions = + "Pick up the cube tool to start modelling with cubes!\n" + "Hover your controller over the tool and press the grip button (touch controller)\n" + "to pick it up. Press it again to drop it. If using hand tracking,\n" + "pinch the tool to pick it up, and use the menu button to drop it\n" + "\n" + "Touch Controller (while tool is held): \n" + "Trigger (with tool hand): Place cube\n" + "Left Thumbstick: Rotate template cube \n" + "Right Thumbstick Up/Down: Offset template cube \n" + "Right Thumbsitck Left/Right: Change scale of template cube\n" + "A button: Change cube color \n" + "\n" + "Tracked hand controls (while tool is held): \n" + "Pinch (with tool hand): Place cube \n" + "Distance between hands (off hand pinched): Change scale of template cube \n"; + + XrInputSampleApp() : OVRFW::XrApp() { + BackgroundColor = OVR::Vector4f(0.55f, 0.35f, 0.1f, 1.0f); + + // Disable framework input management, letting this sample explicitly + // call xrSyncActions() every frame; which includes control over which + // ActionSet to set as active + SkipInputHandling = true; + } + + // Returns a list of OpenXR extensions requested for this app + // Note that the sample framework will filter out any extension + // that is not listed as supported. + virtual std::vector GetExtensions() override { + std::vector extensions = XrApp::GetExtensions(); + extensions.push_back(XR_EXT_HAND_TRACKING_EXTENSION_NAME); + extensions.push_back(XR_FB_TOUCH_CONTROLLER_PRO_EXTENSION_NAME); + return extensions; + } + + std::unordered_map> GetSuggestedBindings( + XrInstance instance) override { + OXR(xrStringToPath(Instance, "/user/hand/left", &leftHandPath_)); + OXR(xrStringToPath(Instance, "/user/hand/right", &rightHandPath_)); + + // Actions in OpenXR are attached to action sets, which can be thought of as a "context" + // for when those actions will be available. An application selects which + // actionsets to enable every frame. For instance, a game might have an actionset + // for its main world navigation, one for menu interaction, and another for when the player + // is seated in a helicopter. + + // The OpenXR input system is designed in a way which allows systems to provide + // highly flexible rebinding solutions, which requires information about the usage of the + // actions beyond a simple button-focused API. + + // This sample uses three action sets: + // - Menu: For actions used to select and press buttons on the UI panels + // - World: The base action set that's always active + // - Tool: For usage of the cube-spawning tool. This actionset is only active + // while the user is holding the tool. + // + // Note: Action sets have a numerical priority value which is used to resolve conflict + // on a per-binding action. In this sample the tool action set has a higher priority than + // the others, which disables the menu interactions while the tool is held. + // Try changing the priorities! And notice how it impacts the isActive value of actions + actionSetMenu_ = CreateActionSet(0, "menu_action_set", "UI Action Set"); + actionSetWorld_ = CreateActionSet(0, "world_action_set", "World Action Set"); + actionSetTool_ = CreateActionSet(9, "tool_action_set", "Tool Action Set"); + + // If we do not specify subActionPaths, we cannot use them to distinguish input from + // separate sources later. It is being used here to allow us to bind the same action + // to both hands while still being able to query the state of a specific hand. + XrPath bothHands[]{leftHandPath_, rightHandPath_}; + + actionSelect_ = CreateAction( + actionSetMenu_, + XR_ACTION_TYPE_BOOLEAN_INPUT, + "select", + "Select/Click UI Element", // Displayed to users, should be translated to the user's + // local language + 2, + bothHands); + + actionGrabRelease_ = CreateAction( + actionSetWorld_, + XR_ACTION_TYPE_BOOLEAN_INPUT, + "activate_tool", + "Activate Tool", + 2, + bothHands); + + actionToggleColor_ = CreateAction( + actionSetTool_, XR_ACTION_TYPE_BOOLEAN_INPUT, "toggle_color", "Change Box Color"); + + actionHandsDrop_ = CreateAction( + actionSetWorld_, XR_ACTION_TYPE_BOOLEAN_INPUT, "drop_tool", "Drop Tool", 2, bothHands); + + actionSpawnCube_ = CreateAction( + actionSetTool_, XR_ACTION_TYPE_BOOLEAN_INPUT, "spawn_cube", "Spawn Cube", 2, bothHands); + + actionRotateCube_ = CreateAction( + actionSetTool_, XR_ACTION_TYPE_VECTOR2F_INPUT, "rotate_cube", "Rotate Cube"); + + actionScaleCube_ = + CreateAction(actionSetTool_, XR_ACTION_TYPE_FLOAT_INPUT, "scale_cube", "Scale Cube"); + + actionTranslateCube_ = CreateAction( + actionSetTool_, XR_ACTION_TYPE_FLOAT_INPUT, "translate_cube", "Translate Cube"); + + // All controller interaction profiles in OpenXR defines two separate poses; aim and pose. + // These are used to get controller position and orientation, and it is important to + // understand the difference between the two: + // - Grip pose is defined to be centered inside the controller aligned with the + // center of the users palm. Anything that the user is holding, whether it's a + // controller representation or a tomato should use the grip pose. In this sample both + // the controller model and tool pose is driven by grip. + // - Aim pose is defined to be a system dependent way to get a good origin and direction + // of a ray used for pointing and selecting things. Note that this can vary depending + // on system conventions and controller geometry, but is the preferred way to draw UI + // selection rays, as is done in this sample + // + // For a more exact defintion of the grip and aim pose, see "Standard pose identifier" part + // of the OpenXR 1.0 specification. See also: XR_EXT_palm_pose for use cases where you need + // to know where the users palm surface is. + + actionMenuBeamPose_ = CreateAction( + actionSetWorld_, + XR_ACTION_TYPE_POSE_INPUT, + "menu_beam_pose", + "Menu Beam Pose", + 2, + bothHands); + + actionCubeAimPose_ = CreateAction( + actionSetTool_, + XR_ACTION_TYPE_POSE_INPUT, + "cube_aim_pose", + "Cube Aim Pose", + 2, + bothHands); + + actionControllerGripPose_ = CreateAction( + actionSetWorld_, XR_ACTION_TYPE_POSE_INPUT, "grip_pose", "Grip Pose", 2, bothHands); + + // A few things worth pointing out about these bindings: + // - Binding the same action to both hands is not a problem, + // since you can use subActionPath later to distinguish them + // + // - actionRotateCube_ gets bound to input/thumbstick rather than ../x and ../y + // to get the state as a 2D float vector. While actionScaleCube_ and actionTranslateCube_ + // gets bound to one specific axis /x and /y, respectively. + + // clang-format off + // == Bindings for /interaction_profiles/oculus/touch_controller == + std::vector> touchBindings{ + {actionSelect_, "/user/hand/left/input/trigger/value"}, + {actionSpawnCube_, "/user/hand/left/input/trigger/value"}, + {actionGrabRelease_, "/user/hand/left/input/squeeze/value"}, + {actionRotateCube_, "/user/hand/left/input/thumbstick"}, + {actionMenuBeamPose_, "/user/hand/left/input/aim/pose"}, + {actionCubeAimPose_, "/user/hand/left/input/aim/pose"}, + {actionControllerGripPose_, "/user/hand/left/input/grip/pose"}, + + {actionSelect_, "/user/hand/right/input/trigger/value"}, + {actionSpawnCube_, "/user/hand/right/input/trigger/value"}, + {actionGrabRelease_, "/user/hand/right/input/squeeze/value"}, + {actionScaleCube_, "/user/hand/right/input/thumbstick/x"}, + {actionTranslateCube_, "/user/hand/right/input/thumbstick/y"}, + {actionToggleColor_, "/user/hand/right/input/a/click"}, + {actionMenuBeamPose_, "/user/hand/right/input/aim/pose"}, + {actionCubeAimPose_, "/user/hand/right/input/aim/pose"}, + {actionControllerGripPose_, "/user/hand/right/input/grip/pose"}}; + + // If the touch controller pro bindings below are dropped, the touch + // controller will be automatically emulated. Try it for yourself! + // == Bindings for /interaction_profiles/oculus/touch_controller_pro + std::vector> touchProBindings{ + {actionSelect_, "/user/hand/left/input/trigger/value"}, + {actionSpawnCube_, "/user/hand/left/input/trigger/value"}, + {actionGrabRelease_, "/user/hand/left/input/squeeze/value"}, + {actionRotateCube_, "/user/hand/left/input/thumbstick"}, + {actionMenuBeamPose_, "/user/hand/left/input/aim/pose"}, + {actionCubeAimPose_, "/user/hand/left/input/aim/pose"}, + {actionControllerGripPose_, "/user/hand/left/input/grip/pose"}, + + {actionSelect_, "/user/hand/right/input/trigger/value"}, + {actionSpawnCube_, "/user/hand/right/input/trigger/value"}, + {actionGrabRelease_, "/user/hand/right/input/squeeze/value"}, + {actionScaleCube_, "/user/hand/right/input/thumbstick/x"}, + {actionTranslateCube_, "/user/hand/right/input/thumbstick/y"}, + {actionToggleColor_, "/user/hand/right/input/a/click"}, + {actionMenuBeamPose_, "/user/hand/right/input/aim/pose"}, + {actionCubeAimPose_, "/user/hand/right/input/aim/pose"}, + {actionControllerGripPose_, "/user/hand/right/input/grip/pose"}}; + + // == Bindings for /interaction_profiles/khr/simple_controller == + // + // While interaction profiles in general map to specific input hardware, + // khr/simple_controller is a special general purpose interaction profile + // that most input can bind to, including hand tracking. + // In this sample these bindings are used to drive hand tracking interactions, + // but if the touch controller can also use these bindings (try commenting out the + // touchBindings) + std::vector> simpleBindings{ + {actionSelect_, "/user/hand/left/input/select/click"}, + {actionGrabRelease_, "/user/hand/left/input/select/click"}, + {actionSpawnCube_, "/user/hand/left/input/select/click"}, + {actionHandsDrop_, "/user/hand/left/input/menu/click"}, + {actionControllerGripPose_, "/user/hand/left/input/grip/pose"}, + {actionMenuBeamPose_, "/user/hand/left/input/aim/pose"}, + {actionCubeAimPose_, "/user/hand/left/input/aim/pose"}, + + {actionSelect_, "/user/hand/right/input/select/click"}, + {actionGrabRelease_, "/user/hand/right/input/select/click"}, + {actionSpawnCube_, "/user/hand/right/input/select/click"}, + {actionHandsDrop_, "/user/hand/right/input/menu/click"}, + {actionControllerGripPose_, "/user/hand/right/input/grip/pose"}, + {actionMenuBeamPose_, "/user/hand/right/input/aim/pose"}, + {actionCubeAimPose_, "/user/hand/right/input/aim/pose"}}; + // clang-format on + + OXR(xrStringToPath( + instance, "/interaction_profiles/oculus/touch_controller", &touchInteractionProfile_)); + OXR(xrStringToPath( + instance, "/interaction_profiles/khr/simple_controller", &simpleInteractionProfile_)); + OXR(xrStringToPath( + instance, + "/interaction_profiles/facebook/touch_controller_pro", + &touchProInteractionProfile_)); + + // Get the default bindings suggested by XrApp framework + auto suggestedBindings = XrApp::GetSuggestedBindings(instance); + + // Append the binding information to the sample framework specific + // data structure + for (const auto& binding : touchBindings) { + suggestedBindings[touchInteractionProfile_].emplace_back( + ActionSuggestedBinding(binding.first, binding.second)); + } + for (const auto& binding : simpleBindings) { + suggestedBindings[simpleInteractionProfile_].emplace_back( + ActionSuggestedBinding(binding.first, binding.second)); + } + for (const auto& binding : touchProBindings) { + suggestedBindings[touchProInteractionProfile_].emplace_back( + ActionSuggestedBinding(binding.first, binding.second)); + } + + // The sample framework uses this data structure to call + // xrSuggestInteractionProfileBindings() for each of the + // interaction profiles provided. + // + // Be sure to pay attention to any error codes returned from + // xrSuggestInteractionProfileBindings(), as even a single + // typo in a path will fail the setup for a full interaction profile + return suggestedBindings; + } + + // OVRFW::XrApp::Init() calls, among other things; + // - xrInitializeLoaderKHR(...) + // - xrCreateInstance with the extensions from GetExtensions(...), + // - xrSuggestInteractionProfileBindings(...) to set up action bindings + // before calling the function below; AppInit(): + virtual bool AppInit(const xrJava* context) override { + /// TinyUI setup + int fontVertexBufferSize = 32 * 1024; // Custom large text buffer size for all the text + bool updateColors = true; // Update UI colors on interaction + if (false == ui_.Init(context, GetFileSys(), updateColors, fontVertexBufferSize)) { + ALOG("TinyUI::Init FAILED."); + return false; + } + + // Even if the runtime supports the hand tracking extension, + // the actual device might not support hand tracking. + // Inspect the system properties to find out. + XrSystemHandTrackingPropertiesEXT handTrackingSystemProperties{ + XR_TYPE_SYSTEM_HAND_TRACKING_PROPERTIES_EXT}; + XrSystemProperties systemProperties{ + XR_TYPE_SYSTEM_PROPERTIES, &handTrackingSystemProperties}; + OXR(xrGetSystemProperties(GetInstance(), GetSystemId(), &systemProperties)); + supportsHandTracking_ = handTrackingSystemProperties.supportsHandTracking; + + if (supportsHandTracking_) { + /// Hook up extensions for hand tracking + OXR(xrGetInstanceProcAddr( + GetInstance(), + "xrCreateHandTrackerEXT", + (PFN_xrVoidFunction*)(&xrCreateHandTrackerEXT_))); + assert(xrCreateHandTrackerEXT_); + + OXR(xrGetInstanceProcAddr( + GetInstance(), + "xrDestroyHandTrackerEXT", + (PFN_xrVoidFunction*)(&xrDestroyHandTrackerEXT_))); + assert(xrDestroyHandTrackerEXT_); + + OXR(xrGetInstanceProcAddr( + GetInstance(), + "xrLocateHandJointsEXT", + (PFN_xrVoidFunction*)(&xrLocateHandJointsEXT_))); + assert(xrLocateHandJointsEXT_); + } + + return true; + } + + // XrApp::InitSession() calls: + // - xrCreateSession(...) to create our Session + // - xrCreateReferenceSpace(...) for local and stage space + // - Create swapchain with xrCreateSwapchain(...) + // - xrAttachSessionActionSets(...) + // before calling SessionInit(): + virtual bool SessionInit() override { + // --- Creation of action spaces + // + // Pose actions are located by first creating an XrSpace, which can later be used + // in xrLocateSpace(). Note how subactionPath is used to create two XrSpaces from + // the same action that's bound to both hands. + + XrActionSpaceCreateInfo actionSpaceCreateInfo{XR_TYPE_ACTION_SPACE_CREATE_INFO}; + actionSpaceCreateInfo.action = actionMenuBeamPose_; + actionSpaceCreateInfo.poseInActionSpace = {{0, 0, 0, 1}, {0_m, 0_m, 0_m}}; // Identity Pose + actionSpaceCreateInfo.subactionPath = leftHandPath_; + OXR(xrCreateActionSpace(GetSession(), &actionSpaceCreateInfo, &spaceMenuBeamLeft_)); + actionSpaceCreateInfo.subactionPath = rightHandPath_; + OXR(xrCreateActionSpace(GetSession(), &actionSpaceCreateInfo, &spaceMenuBeamRight_)); + + actionSpaceCreateInfo.action = actionCubeAimPose_; + // Offset the space for creating cubes, a nudge away from the user + actionSpaceCreateInfo.poseInActionSpace = {{0, 0, 0, 1}, {0_cm, 0_cm, -5_cm}}; + actionSpaceCreateInfo.subactionPath = leftHandPath_; + OXR(xrCreateActionSpace(GetSession(), &actionSpaceCreateInfo, &spaceCubeAimLeft_)); + actionSpaceCreateInfo.subactionPath = rightHandPath_; + OXR(xrCreateActionSpace(GetSession(), &actionSpaceCreateInfo, &spaceCubeAimRight_)); + + actionSpaceCreateInfo.action = actionControllerGripPose_; + actionSpaceCreateInfo.poseInActionSpace = {{0, 0, 0, 1}, {0_m, 0_m, 0_m}}; // Identity Pose + actionSpaceCreateInfo.subactionPath = leftHandPath_; + OXR(xrCreateActionSpace(GetSession(), &actionSpaceCreateInfo, &spaceGripLeft_)); + actionSpaceCreateInfo.subactionPath = rightHandPath_; + OXR(xrCreateActionSpace(GetSession(), &actionSpaceCreateInfo, &spaceGripRight_)); + + // --- Creation of reference spaces + // + // OpenXR does not provide a concept of "World Space", since different devices + // provide different types of tracking, which can't garantuee a stable + // global world space. + // Instead, OpenXR defines a set of "well-known reference spaces" that can be used + // for spatial reasoning. The two most common ones are: + // - LOCAL: Guaranteed to be available. Origin is set at user eye-height, and + // can be recentered by the user at will. This changes both rotation (gravity locked) + // and moves the origin to the current user head location. + // - STAGE: This space is locked to the real world, with the origin at floor level. It is + // not affected by user recenter events. On Quest it is tied to the guardian + // definition. However, it is not guaranteed to exist on all OpenXR systems, as it + // requires 6DOF tracking + // + // This sample uses LOCAL for easy recentering, but feel free to try changing it! + // + // See the Spaces chapter in the OpenXR specification for more details + + XrReferenceSpaceCreateInfo referenceSpaceCreateInfo{XR_TYPE_REFERENCE_SPACE_CREATE_INFO}; + referenceSpaceCreateInfo.poseInReferenceSpace = {{0, 0, 0, 1}, {0_m, 0_m, 0_m}}; // Identity + referenceSpaceCreateInfo.referenceSpaceType = XR_REFERENCE_SPACE_TYPE_LOCAL; + OXR(xrCreateReferenceSpace(GetSession(), &referenceSpaceCreateInfo, &spaceLocal_)); + + referenceSpaceCreateInfo.referenceSpaceType = XR_REFERENCE_SPACE_TYPE_STAGE; + OXR(xrCreateReferenceSpace(GetSession(), &referenceSpaceCreateInfo, &spaceStage_)); + + // Try switching this to see the difference between local and stage + mainReferenceSpace_ = spaceLocal_; + + // Make sure the sample framework is set to the correct space as well + CurrentSpace = mainReferenceSpace_; + + // --- Attach ActionSets to session + // + // This is required before any call to xrSyncActions for these action sets + // and can only be done once. This mechanism ensures immutability of actions and actionsets + // used for a session, which allows runtimes to know the whole set of actions up-front for + // rebinding purposes. + std::vector actionSets{{actionSetWorld_, actionSetMenu_, actionSetTool_}}; + XrSessionActionSetsAttachInfo attachInfo{XR_TYPE_SESSION_ACTION_SETS_ATTACH_INFO}; + attachInfo.countActionSets = actionSets.size(); + attachInfo.actionSets = actionSets.data(); + OXR(xrAttachSessionActionSets(Session, &attachInfo)); + // After this point all actions and bindings are final for the session + // (calls to xrSuggestInteractionProfileBindings and xrAttachSessionActionSets fail) + + // --- Hand rendering setup + if (supportsHandTracking_) { + SetupHandTrackers(); + } + + // --- Create the model for the cube-spawning tool + OVRFW::GeometryBuilder toolGeometry; + + OVR::Vector4f toolColor = {0.1f, 0.1f, 0.1f, 1.0f}; + + std::vector toolElementTransforms{ + OVR::Matrix4f::Scaling(0.05f, 0.12f, 0.05f), + OVR::Matrix4f::RotationZ(OVR::DegreeToRad(-45.f)) * + OVR::Matrix4f::Translation(6_cm, -2_cm, 0_cm) * + OVR::Matrix4f::Scaling(0.075f, 0.05f, 0.05f), + OVR::Matrix4f::RotationZ(OVR::DegreeToRad(45.f)) * + OVR::Matrix4f::Translation(-6_cm, -2_cm, 0_cm) * + OVR::Matrix4f::Scaling(0.075f, 0.05f, 0.05f), + OVR::Matrix4f::Translation(-5.5_cm, -9_cm, 0_cm) * + OVR::Matrix4f::Scaling(0.025f, 0.05f, 0.025f), + OVR::Matrix4f::Translation(5.5_cm, -9_cm, 0_cm) * + OVR::Matrix4f::Scaling(0.025f, 0.05f, 0.025f), + OVR::Matrix4f::RotationX(OVR::DegreeToRad(45.f)) * + OVR::Matrix4f::Scaling(0.05f, 0.05f, 0.05f), + OVR::Matrix4f::RotationZ(OVR::DegreeToRad(45.f)) * + OVR::Matrix4f::Scaling(0.05f, 0.05f, 0.05f), + OVR::Matrix4f::Translation(0_cm, -3_cm, 0_cm) * + OVR::Matrix4f::RotationX(OVR::DegreeToRad(45.f)) * + OVR::Matrix4f::Scaling(0.05f, 0.05f, 0.05f), + OVR::Matrix4f::Translation(0_cm, 3_cm, 0_cm) * + OVR::Matrix4f::RotationX(OVR::DegreeToRad(45.f)) * + OVR::Matrix4f::Scaling(0.05f, 0.05f, 0.05f), + OVR::Matrix4f::Translation(0_cm, 3_cm, 0_cm) * + OVR::Matrix4f::RotationZ(OVR::DegreeToRad(45.f)) * + OVR::Matrix4f::Scaling(0.05f, 0.05f, 0.05f), + }; + + for (auto transform : toolElementTransforms) { + // Slight adjustment to make the tool point at the cube + transform = OVR::Matrix4f::Translation(0_cm, 0_cm, -3.5_cm) * + (OVR::Matrix4f::RotationX(OVR::DegreeToRad(30.f)) * transform); + toolGeometry.Add( + OVRFW::BuildUnitCubeDescriptor(), + OVRFW::GeometryBuilder::kInvalidIndex, + toolColor, + transform); + } + + toolRenderer_.Init(toolGeometry.ToGeometryDescriptor()); + toolRenderer_.SetPose(OVR::Posef(OVR::Quat::Identity(), {0_m, -0.3_m, -0.5_m})); + + // Display a translucent version of the cube before it is placed + OVRFW::GeometryBuilder templateCubeGeometry; + templateCubeGeometry.Add( + OVRFW::BuildUnitCubeDescriptor(), + OVRFW::GeometryBuilder::kInvalidIndex, + colorOptions_[cubeColorIndex_]); + templateCubeRenderer_.ChannelControl = OVR::Vector4f(1, 1, 1, 0.8); + templateCubeRenderer_.Init(templateCubeGeometry.ToGeometryDescriptor()); + templateCubeRenderer_.SetPose( + OVR::Posef(OVR::Quat::Identity(), {0_m, -0.3_m, -0.65_m})); + + // Scale to 5cm cube + templateCubeRenderer_.SetScale({5_cm, 5_cm, 5_cm}); + + // --- UI setup + CreateSampleDescriptionPanel(); + SetupActionUIPanels(); + SetupMenuPanels(); + + /// Disable scene navitgation + GetScene().SetFootPos({0.0_m, 0.0_m, 0.0_m}); + this->FreeMove = false; + + if (false == controllerRenderL_.Init(true)) { + ALOG("SessionInit::Init L controller renderer FAILED."); + return false; + } + + if (false == controllerRenderR_.Init(false)) { + ALOG("SessionInit::Init R controller renderer FAILED."); + return false; + } + + cursorBeamRenderer_.Init(GetFileSys(), nullptr, OVR::Vector4f(1.0f), 1.0f); + + return true; + } + + // The update function is called every frame before the Render() function. + // Some of the key OpenXR function called by the framework prior to calling this function: + // - xrPollEvent(...) + // - xrWaitFrame(...) + virtual void Update(const OVRFW::ovrApplFrameIn& in) override { + // + // --- xrSyncAction + // + + std::vector activeActionSets = {{actionSetWorld_}, {actionSetMenu_}}; + + // Only activate the tool action set while the tool is being held + // This is the mechanism that makes the trigger button only spawn cubes + // while the tool is held + if (toolHeldInLeft_ || toolHeldInRight_) { + activeActionSets.push_back({actionSetTool_}); + } + + // xrSyncAction updates the state of all the input at once + // and subsequent calls to xrGetActionState* just retrieves + // the state that was synced during this call. This is important + // to ensure that the state during a frame is consistent. For instance + // if you call xrGetActionStateBoolean(myAction) twice between + // calls to xrSyncActions, they are guaranteed to return the same + // data. + XrActionsSyncInfo syncInfo = {XR_TYPE_ACTIONS_SYNC_INFO}; + syncInfo.countActiveActionSets = activeActionSets.size(); + syncInfo.activeActionSets = activeActionSets.data(); + OXR(xrSyncActions(Session, &syncInfo)); + + // The hit test devices are rays used for hit detection in the UI. + // Clear the rays from last frame + ui_.HitTestDevices().clear(); + + if (supportsHandTracking_) { + UpdateHands(in.PredictedDisplayTime); + } + + // + // --- Locate controller grip and aim poses + // + + // DisplayTime is the time returned by the latest xrWaitFrame() call. + // It's the time when the current frame is expected to be shown to the user. + // xrLocateSpace returns a prediction of where these spaces spaces will be at that + // future time. + // IMPORTANT: Make sure the correct time is passed to xrLocateSpace, otherwise + // there will be additional latency + XrTime time = ToXrTime(in.PredictedDisplayTime); + OXR(xrLocateSpace(spaceGripRight_, mainReferenceSpace_, time, &locationGripRight_)); + OXR(xrLocateSpace(spaceGripLeft_, mainReferenceSpace_, time, &locationGripLeft_)); + OXR(xrLocateSpace(spaceMenuBeamLeft_, mainReferenceSpace_, time, &locationMenuBeamLeft_)); + OXR(xrLocateSpace(spaceMenuBeamRight_, mainReferenceSpace_, time, &locationMenuBeamRight_)); + OXR(xrLocateSpace(spaceCubeAimLeft_, mainReferenceSpace_, time, &locationCubeAimLeft_)); + OXR(xrLocateSpace(spaceCubeAimRight_, mainReferenceSpace_, time, &locationCubeAimRight_)); + + // It is also possible to use xrLocateSpace between action spaces + XrSpaceLocation locationGripRelative{XR_TYPE_SPACE_LOCATION}; + OXR(xrLocateSpace(spaceGripRight_, spaceGripLeft_, time, &locationGripRelative)); + distBetweenHands_ = FromXrPosef(locationGripRelative.pose).Translation.Length(); + + // Get current interaction profile to adapt behavior to simple controller + XrInteractionProfileState ipState{XR_TYPE_INTERACTION_PROFILE_STATE}; + OXR(xrGetCurrentInteractionProfile(GetSession(), leftHandPath_, &ipState)); + auto currentInteractionProfile = ipState.interactionProfile; + OXR(xrGetCurrentInteractionProfile(GetSession(), rightHandPath_, &ipState)); + currentInteractionProfile = (currentInteractionProfile == XR_NULL_PATH) + ? ipState.interactionProfile + : currentInteractionProfile; + + // + // --- Picking up and dropping the tool + // + auto grabState = GetActionStateBoolean(actionGrabRelease_); + auto dropState = GetActionStateBoolean(actionHandsDrop_); + + // We are allowed to specifically query the rightHand XrPath for this action only + // because it was listed under subActionPaths when actionGrabRelease_ was created. This + // lets us differentiate between the different possible inputs that could have caused this + // action. + auto grabbedRight = GetActionStateBoolean(actionGrabRelease_, rightHandPath_).currentState; + + // Since changedSinceLastSync is only true for a single frame after + // a boolean action has changed, it is a useful way to detect + // the "rising edge of the signal", that is the first frame after + // the state has changed. + + // Detect rising edge of grabState or DropState + if ((grabState.changedSinceLastSync && grabState.currentState) || + (dropState.changedSinceLastSync && dropState.currentState)) { + // If holding the tool, drop it if using the correct hand to drop + // Sepcifically only allow dropping by the hand holding the tool + if (toolHeldInLeft_ || toolHeldInRight_) { + // Special case for simple controller (limited inputs) + // to allow dropping it from the off hand. + // (On quest right hand menu action is used as a system gesture) + if (currentInteractionProfile == simpleInteractionProfile_) { + if (dropState.currentState) { + toolHeldInLeft_ = toolHeldInRight_ = false; + } + } else { + if (toolHeldInLeft_ && !grabbedRight) { + toolHeldInLeft_ = false; + } + if (toolHeldInRight_ && grabbedRight) { + toolHeldInRight_ = false; + } + } + } else { + // Tool not held so pick up if it is close to hand + OVR::Posef toolPos = toolRenderer_.GetPose(); + auto& grabberGripLocation = grabbedRight ? locationGripRight_ : locationGripLeft_; + if (toolPos.Translation.Distance( + FromXrPosef(grabberGripLocation.pose).Translation) < toolHitBox_) { + toolHeldInRight_ = grabbedRight; + toolHeldInLeft_ = !grabbedRight; + } + } + } + + // + // --- Update location of the tool when held + // + if (toolHeldInLeft_ || toolHeldInRight_) { + auto xrToolPose = toolHeldInRight_ ? locationGripRight_.pose : locationGripLeft_.pose; + xrToolPose.orientation = toolHeldInRight_ ? locationCubeAimRight_.pose.orientation + : locationCubeAimLeft_.pose.orientation; + auto toolPose = FromXrPosef(xrToolPose); + + // This is a 60 degree rotation around the X-axis of the aim pose to make the + // tool point towards the template cube + toolPose.Rotation *= OVR::Quatf({OVR::DegreeToRad(60.f), 0.f, 0.f}, 1.f); + toolRenderer_.SetPose(toolPose); + + auto templatePose = FromXrPosef( + toolHeldInRight_ ? locationCubeAimRight_.pose : locationCubeAimLeft_.pose); + + // The aim pose is defined with the Y axis pointing up, and -Z pointing away from + // the controller. + templatePose.Translation = templatePose.Transform({0_cm, 0_cm, -templateCubeOffset_}); + templatePose.Rotation *= templateCubeRotation_; + templateCubeRenderer_.SetPose(templatePose); + templateCubeRenderer_.SetScale(templateCubeScale_ * OVR::Vector3f{1.0f, 1.0f, 1.0f}); + } + // Call the update method on the renderer to update the model matrix + toolRenderer_.Update(); + + // + // --- Spawn cubes! + // + auto spawnLeftState = GetActionStateBoolean(actionSpawnCube_, leftHandPath_); + auto spawnRightState = GetActionStateBoolean(actionSpawnCube_, rightHandPath_); + + // Detect spawn action rising edge from the hand holding the tool + if ((toolHeldInLeft_ && spawnLeftState.changedSinceLastSync && + spawnLeftState.currentState) || + (toolHeldInRight_ && spawnRightState.changedSinceLastSync && + spawnRightState.currentState)) { + auto transform = OVR::Matrix4f(templateCubeRenderer_.GetPose()) * + OVR::Matrix4f::Scaling(templateCubeRenderer_.GetScale()); + + cubeGeometry_.Add( + OVRFW::BuildUnitCubeDescriptor(), + OVRFW::GeometryBuilder::kInvalidIndex, + colorOptions_[cubeColorIndex_], + transform); + + cubeRenderer_.Init(cubeGeometry_.ToGeometryDescriptor()); + } + // Update matrices! + cubeRenderer_.Update(); + + // + // --- Change cube color + // + auto toggleColorState = GetActionStateBoolean(actionToggleColor_); + if (toggleColorState.changedSinceLastSync && toggleColorState.currentState) { + cubeColorIndex_ = (cubeColorIndex_ + 1) % colorOptions_.size(); + + OVRFW::GeometryBuilder templateCubeGeometry; + auto templatePose = templateCubeRenderer_.GetPose(); + templateCubeGeometry.Add( + OVRFW::BuildUnitCubeDescriptor(), + OVRFW::GeometryBuilder::kInvalidIndex, + colorOptions_[cubeColorIndex_]); + templateCubeRenderer_.Init(templateCubeGeometry.ToGeometryDescriptor()); + templateCubeRenderer_.SetPose(templatePose); + templateCubeRenderer_.SetScale(templateCubeScale_ * OVR::Vector3f{1.0f, 1.0f, 1.0f}); + } + templateCubeRenderer_.Update(); + + // Using the current interaction profile to change behavior is a common technique. + // In this case we're introducing an alternate behavior for control schemes + // that lack a thumbstick (hand tracking!) and we instead use the + // distance between the hands to scale the cube. + // + // Note that xrGetCurrentInteractionProfile() is guaranteed to only return interaction + // profiles for which the app has suggested bindings for (or XR_NULL_PATH), so it can + // safely be used to change behavior. + // + // (Be aware that the actual controller being used might not correspond to the + // interaction profile, for compatibility reasons. For instance, a Quest Pro controller will + // "pretend" to be a Quest controller if the app only have bindings for Quest) + // + if (currentInteractionProfile == simpleInteractionProfile_) { + // Detect press or release of the offhand spawn action + if ((toolHeldInLeft_ && spawnRightState.changedSinceLastSync) || + (toolHeldInRight_ && spawnLeftState.changedSinceLastSync)) { + if ((toolHeldInLeft_ && spawnRightState.currentState) || + (toolHeldInRight_ && spawnLeftState.currentState)) { + currentlyScalingTemplate_ = true; + oldTemplateCubeScale_ = templateCubeScale_; + startingScalingDistance_ = distBetweenHands_; + } + if ((toolHeldInLeft_ && !spawnRightState.currentState) || + (toolHeldInRight_ && !spawnLeftState.currentState)) { + currentlyScalingTemplate_ = false; + } + } + if (!toolHeldInLeft_ && !toolHeldInRight_) { + currentlyScalingTemplate_ = false; + } + } + + // + // --- Rotate, scale and move the cube template + // + auto cubeRotateState = GetActionStateVector2(actionRotateCube_); + auto cubeTranslateState = GetActionStateFloat(actionTranslateCube_); + auto cubeScaleState = GetActionStateFloat(actionScaleCube_); + + float deltaCubeOffset = + cubeTranslateState.isActive ? cubeTranslateState.currentState : 0.0_m; + float deltaCubeScale = cubeScaleState.isActive ? cubeScaleState.currentState : 0.0f; + float manualCubeScale = 0.0f; + + if (currentlyScalingTemplate_) { + manualCubeScale = distBetweenHands_ - startingScalingDistance_; + templateCubeScale_ = oldTemplateCubeScale_ + manualCubeScale; + } + + // Only scale or translate cube at once, pick the action with largest magnitude + if (abs(deltaCubeOffset) > abs(deltaCubeScale)) { + templateCubeOffset_ += 1.5f * in.DeltaSeconds * deltaCubeOffset; + } else { + templateCubeScale_ += in.DeltaSeconds * deltaCubeScale; + } + + // Clamp cube offset and scale + if (templateCubeOffset_ > kMaxTemplateCubeOffset_) { + templateCubeOffset_ = kMaxTemplateCubeOffset_; + } + if (templateCubeOffset_ < kMinTemplateCubeOffset_) { + templateCubeOffset_ = kMinTemplateCubeOffset_; + } + if (templateCubeScale_ > kMaxTemplateCubeScale_) { + templateCubeScale_ = kMaxTemplateCubeScale_; + } + if (templateCubeScale_ < kMinTemplateCubeScale_) { + templateCubeScale_ = kMinTemplateCubeScale_; + } + + if (cubeRotateState.isActive) { + // Quaternion magic! (R.Conj() * new_rotation * R) gives us a small rotation + // relative to the current tool space. Multiplying it back into templateCubeRotation + // to accumulate the rotation. + templateCubeRotation_ *= + templateCubeRotation_.Conj() * + OVR::Quatf::FromRotationVector( + 2.5f * in.DeltaSeconds * // multiply by delta frametime for consistent + // rotation speed + OVR::Vector3f{ + -cubeRotateState.currentState.y, cubeRotateState.currentState.x, 0.0f}) * + templateCubeRotation_; + templateCubeRotation_.Normalize(); + } + + // Check validity of grip location before updating controllers with new location + // All apps rendering controllers should do this, otherwise you draw floating + // controllers in cases where tracking is lost or where there's a system menu on top + // taking input focus + if ((locationGripLeft_.locationFlags & XR_SPACE_LOCATION_POSITION_VALID_BIT) != 0 && + !toolHeldInLeft_) { + controllerRenderL_.Update(FromXrPosef(locationGripLeft_.pose)); + } + if ((locationGripRight_.locationFlags & XR_SPACE_LOCATION_POSITION_VALID_BIT) != 0 && + !toolHeldInRight_) { + controllerRenderR_.Update(FromXrPosef(locationGripRight_.pose)); + } + + // Note that these flags will be forced to 0 when the tool action set is active + // due to the collision with actionCubeAimPose_ and the higher priority of the + // tool action set. + bool menuBeamActiveLeft = ActionPoseIsActive(actionMenuBeamPose_, rightHandPath_); + if (menuBeamActiveLeft && + (locationMenuBeamLeft_.locationFlags & XR_SPACE_LOCATION_POSITION_VALID_BIT) && + (locationMenuBeamLeft_.locationFlags & XR_SPACE_LOCATION_ORIENTATION_VALID_BIT)) { + // Add new UI hit detection ray based on the aim pose (not grip!) + bool click = GetActionStateBoolean(actionSelect_, leftHandPath_).currentState; + ui_.AddHitTestRay(FromXrPosef(locationMenuBeamLeft_.pose), click); + } + + bool menuBeamActiveRight = ActionPoseIsActive(actionMenuBeamPose_, rightHandPath_); + if (menuBeamActiveRight && + (locationMenuBeamRight_.locationFlags & XR_SPACE_LOCATION_POSITION_VALID_BIT) && + (locationMenuBeamRight_.locationFlags & XR_SPACE_LOCATION_ORIENTATION_VALID_BIT)) { + bool click = GetActionStateBoolean(actionSelect_, rightHandPath_).currentState; + ui_.AddHitTestRay(FromXrPosef(locationMenuBeamRight_.pose), click); + } + + cursorBeamRenderer_.Update(in, ui_.HitTestDevices()); + + UpdateUI(in); + } + + // Utility function to split out the UI updates + void UpdateUI(const OVRFW::ovrApplFrameIn& in) { + // Update all the action panels + for (auto& panelPair : actionSetPanels_) { + panelPair.second.Update(); + } + + boxCountLabel_->SetText("%d boxes placed.", cubeGeometry_.Nodes().size()); + boxColorLabel_->SetText("Box Color: %s", colorNames_[cubeColorIndex_].c_str()); + + // + // Update current interaction profile display + // + std::string leftInteractionProfileString = "XR_NULL_PATH"; + XrInteractionProfileState ipState{XR_TYPE_INTERACTION_PROFILE_STATE}; + OXR(xrGetCurrentInteractionProfile(GetSession(), leftHandPath_, &ipState)); + if (ipState.interactionProfile != XR_NULL_PATH) { + char buf[XR_MAX_PATH_LENGTH]; + uint32_t outLength = 0; + OXR(xrPathToString( + GetInstance(), ipState.interactionProfile, XR_MAX_PATH_LENGTH, &outLength, buf)); + leftInteractionProfileString = std::string(buf); + } + + std::string rightInteractionProfileString = "XR_NULL_PATH"; + OXR(xrGetCurrentInteractionProfile(GetSession(), rightHandPath_, &ipState)); + if (ipState.interactionProfile != XR_NULL_PATH) { + char buf[XR_MAX_PATH_LENGTH]; + uint32_t outLength = 0; + OXR(xrPathToString( + GetInstance(), ipState.interactionProfile, XR_MAX_PATH_LENGTH, &outLength, buf)); + rightInteractionProfileString = std::string(buf); + } + + currentInteractionProfileText_->SetText( + "xrGetCurrentInteractionProfile(...):\n" + "/user/hand/left: %s\n" + "/user/hand/right: %s\n", + leftInteractionProfileString.c_str(), + rightInteractionProfileString.c_str()); + + ui_.Update(in); + } + + // Called by the XrApp framework after the Update function + virtual void Render(const OVRFW::ovrApplFrameIn& in, OVRFW::ovrRendererOutput& out) override { + XrInteractionProfileState ipState{XR_TYPE_INTERACTION_PROFILE_STATE}; + OXR(xrGetCurrentInteractionProfile(GetSession(), leftHandPath_, &ipState)); + auto currentInteractionProfile = ipState.interactionProfile; + OXR(xrGetCurrentInteractionProfile(GetSession(), rightHandPath_, &ipState)); + currentInteractionProfile = (currentInteractionProfile == XR_NULL_PATH) + ? ipState.interactionProfile + : currentInteractionProfile; + + ui_.Render(in, out); + toolRenderer_.Render(out.Surfaces); + cubeRenderer_.Render(out.Surfaces); + templateCubeRenderer_.Render(out.Surfaces); + + // Check validity of grip location before updating controllers with new location + // All apps rendering controllers should do this, otherwise you draw floating + // controllers in cases where tracking is lost or where there's a system menu on top + // taking input focus + if ((locationGripLeft_.locationFlags & XR_SPACE_LOCATION_POSITION_VALID_BIT) != 0) { + // Only render tool, not controller, while held + // And don't render the controller if the hand is tracked + if (!toolHeldInLeft_ && !handTrackedL_) { + controllerRenderL_.Render(out.Surfaces); + } + } + + if ((locationGripRight_.locationFlags & XR_SPACE_LOCATION_POSITION_VALID_BIT) != 0) { + if (!toolHeldInRight_ && !handTrackedR_) { + controllerRenderR_.Render(out.Surfaces); + } + } + + if (supportsHandTracking_) { + for (int i = 0; i < XR_HAND_JOINT_COUNT_EXT; ++i) { + if (handTrackedR_ && !toolHeldInRight_) { + handJointRenderersR_[i].Render(out.Surfaces); + } + if (handTrackedL_ && !toolHeldInLeft_) { + handJointRenderersL_[i].Render(out.Surfaces); + } + } + } + + /// Render beams last, since they render with transparency (alpha blending) + cursorBeamRenderer_.Render(in, out); + } + + virtual void SessionEnd() override { + controllerRenderL_.Shutdown(); + controllerRenderR_.Shutdown(); + cursorBeamRenderer_.Shutdown(); + toolRenderer_.Shutdown(); + cubeRenderer_.Shutdown(); + templateCubeRenderer_.Shutdown(); + + if (supportsHandTracking_) { + /// Hand Trackers + OXR(xrDestroyHandTrackerEXT_(handTrackerL_)); + OXR(xrDestroyHandTrackerEXT_(handTrackerR_)); + for (int i = 0; i < XR_HAND_JOINT_COUNT_EXT; ++i) { + handJointRenderersR_[i].Shutdown(); + handJointRenderersR_[i].Shutdown(); + } + } + } + + virtual void AppShutdown(const xrJava* context) override { + /// Unhook extensions for hand tracking + xrCreateHandTrackerEXT_ = nullptr; + xrDestroyHandTrackerEXT_ = nullptr; + xrLocateHandJointsEXT_ = nullptr; + + OVRFW::XrApp::AppShutdown(context); + ui_.Shutdown(); + } + + void SetupHandTrackers() { + XrHandTrackerCreateInfoEXT createInfo{XR_TYPE_HAND_TRACKER_CREATE_INFO_EXT}; + createInfo.handJointSet = XR_HAND_JOINT_SET_DEFAULT_EXT; + createInfo.hand = XR_HAND_LEFT_EXT; + OXR(xrCreateHandTrackerEXT_(GetSession(), &createInfo, &handTrackerL_)); + createInfo.hand = XR_HAND_RIGHT_EXT; + OXR(xrCreateHandTrackerEXT_(GetSession(), &createInfo, &handTrackerR_)); + + for (int handIndex = 0; handIndex < 2; ++handIndex) { + /// Alias everything for initialization + const bool isLeft = (handIndex == 0); + auto& handJointRenderers = isLeft ? handJointRenderersL_ : handJointRenderersR_; + handJointRenderers.resize(XR_HAND_JOINT_COUNT_EXT); + for (int i = 0; i < XR_HAND_JOINT_COUNT_EXT; ++i) { + OVRFW::GeometryRenderer& gr = handJointRenderers[i]; + gr.Init(OVRFW::BuildUnitCubeDescriptor()); + gr.SetScale({0.01f, 0.01f, 0.01f}); + gr.DiffuseColor = jointColor_; + } + } + } + + void UpdateHands(const double predictedDisplayTime) { + for (int handIndex = 0; handIndex < 2; ++handIndex) { + const bool isLeft = (handIndex == 0); + auto& handJointRenderers = isLeft ? handJointRenderersL_ : handJointRenderersR_; + auto& handTracker = isLeft ? handTrackerL_ : handTrackerR_; + auto& handTracked = isLeft ? handTrackedL_ : handTrackedR_; + auto* jointLocations = isLeft ? jointLocationsL_ : jointLocationsR_; + + XrHandJointLocationsEXT locations{XR_TYPE_HAND_JOINT_LOCATIONS_EXT}; + locations.jointCount = XR_HAND_JOINT_COUNT_EXT; + locations.jointLocations = jointLocations; + + XrHandJointsLocateInfoEXT locateInfo{XR_TYPE_HAND_JOINTS_LOCATE_INFO_EXT}; + locateInfo.baseSpace = mainReferenceSpace_; + locateInfo.time = ToXrTime(predictedDisplayTime); + + OXR(xrLocateHandJointsEXT_(handTracker, &locateInfo, &locations)); + + handTracked = false; + if (locations.isActive) { + handTracked = true; + for (uint32_t i = 0; i < locations.jointCount; ++i) { + OVRFW::GeometryRenderer& gr = handJointRenderers[i]; + if ((jointLocations[i].locationFlags & XR_SPACE_LOCATION_POSITION_VALID_BIT) && + (jointLocations[i].locationFlags & + XR_SPACE_LOCATION_ORIENTATION_VALID_BIT)) { + const auto p = FromXrPosef(jointLocations[i].pose); + gr.SetPose(p); + gr.Update(); + } + } + } + } + } + + void SetupActionUIPanels() { // Setup all the UI panels to display the state of each action + // Sets up the UI panels that displays the state of all the actions + // See ActionSetDisplayPanel.cpp for the implementation + + // Action sets + actionSetPanels_.insert( + {actionSetMenu_, + ActionSetDisplayPanel( + "Menu Action Set", Session, Instance, &ui_, {-2.0_m, 1.0_m, -2.5_m})}); + + actionSetPanels_.insert( + {actionSetWorld_, + ActionSetDisplayPanel( + "World Action Set", Session, Instance, &ui_, {-0.5_m, 1.0_m, -2.5_m})}); + + actionSetPanels_.insert( + {actionSetTool_, + ActionSetDisplayPanel( + "Tool Action Set", Session, Instance, &ui_, {1.0_m, 1.0_m, -2.5_m})}); + + // Menu actions + actionSetPanels_.at(actionSetMenu_).AddBoolAction(actionSelect_, "Select"); + actionSetPanels_.at(actionSetMenu_).AddPoseAction(actionMenuBeamPose_, "Menu Beam Pose"); + + // World actions + actionSetPanels_.at(actionSetWorld_).AddBoolAction(actionGrabRelease_, "Grab/Release"); + actionSetPanels_.at(actionSetWorld_).AddBoolAction(actionHandsDrop_, "Drop (hands)"); + actionSetPanels_.at(actionSetWorld_).AddPoseAction(actionControllerGripPose_, "Grip Pose"); + + // Tool actions + actionSetPanels_.at(actionSetTool_).AddPoseAction(actionCubeAimPose_, "Cube Aim Pose"); + actionSetPanels_.at(actionSetTool_).AddBoolAction(actionSpawnCube_, "Spawn"); + actionSetPanels_.at(actionSetTool_).AddBoolAction(actionToggleColor_, "Toggle Color"); + actionSetPanels_.at(actionSetTool_).AddVec2Action(actionRotateCube_, "Rotate"); + actionSetPanels_.at(actionSetTool_).AddFloatAction(actionScaleCube_, "Scale"); + actionSetPanels_.at(actionSetTool_).AddFloatAction(actionTranslateCube_, "Translate"); + } + + void SetupMenuPanels() { // Setup all the UI panels to display the state of each action + currentInteractionProfileText_ = ui_.AddLabel( + "xrGetCurrentInteractionProfile(...): \n/user/hand/left: N/A \n/user/hand/right: N/A", + {3.0_m, 0.1_m, -1.5_m}, + {850.0f, 120.0f}); + + boxCountLabel_ = ui_.AddLabel("0 cubes placed.", {3.0_m, -0.1_m, -1.5_m}, {450.0f, 45.0f}); + boxColorLabel_ = ui_.AddLabel("Box Color: Red", {3.0_m, -0.2_m, -1.5_m}, {450.0f, 45.0f}); + + auto button = ui_.AddButton( + "Clear placed cubes", {3.0_m, -0.315_m, -1.5_m}, {450.0f, 60.0f}, [this]() { + this->cubeGeometry_.clear_nodes(); + this->cubeRenderer_.Init(this->cubeGeometry_.ToGeometryDescriptor()); + this->boxCountLabel_->SetText("0 cubes placed."); + }); + + // Tilt the interaction UI towards user + currentInteractionProfileText_->SetLocalRotation( + OVR::Quat::FromRotationVector({0, OVR::DegreeToRad(-60.0f), 0})); + boxCountLabel_->SetLocalRotation( + OVR::Quat::FromRotationVector({0, OVR::DegreeToRad(-60.0f), 0})); + button->SetLocalRotation( + OVR::Quat::FromRotationVector({0, OVR::DegreeToRad(-60.0f), 0})); + boxColorLabel_->SetLocalRotation( + OVR::Quat::FromRotationVector({0, OVR::DegreeToRad(-60.0f), 0})); + } + + void CreateSampleDescriptionPanel() { + // Panel to provide sample description to the user for context + auto titleLabel = ui_.AddLabel("XrInput Sample", {-2.5_m, 0.7_m, -1.5_m}, {950.0f, 80.0f}); + auto descriptionLabel = ui_.AddLabel( + static_cast(kSampleIntroduction), + {-2.5_m, 0.15_m, -1.5_m}, + {950.0f, 430.0f}); + auto instructionsTitleLabel = + ui_.AddLabel("Instructions", {-2.5_m, -0.395_m, -1.5_m}, {950.0f, 80.0f}); + auto instructionsLabel = ui_.AddLabel( + static_cast(kSampleInstructions), + {-2.5_m, -0.93_m, -1.5_m}, + {950.0f, 420.0f}); + + // Align and size the description text for readability + OVRFW::VRMenuFontParms fontParams{}; + fontParams.Scale = 0.5f; + fontParams.AlignHoriz = OVRFW::HORIZONTAL_LEFT; + descriptionLabel->SetFontParms(fontParams); + descriptionLabel->SetTextLocalPosition({-0.88_m, -0.02_m, 0}); + instructionsLabel->SetFontParms(fontParams); + instructionsLabel->SetTextLocalPosition({-0.88_m, -0.03_m, 0}); + fontParams.Scale = 1.f; + fontParams.AlignHoriz = OVRFW::HORIZONTAL_CENTER; + titleLabel->SetFontParms(fontParams); + instructionsTitleLabel->SetFontParms(fontParams); + + // Tilt the description billboard 60 degrees towards the user + descriptionLabel->SetLocalRotation( + OVR::Quat::FromRotationVector({0, OVR::DegreeToRad(60.0f), 0})); + instructionsLabel->SetLocalRotation( + OVR::Quat::FromRotationVector({0, OVR::DegreeToRad(60.0f), 0})); + instructionsTitleLabel->SetLocalRotation( + OVR::Quat::FromRotationVector({0, OVR::DegreeToRad(60.0f), 0})); + titleLabel->SetLocalRotation( + OVR::Quat::FromRotationVector({0, OVR::DegreeToRad(60.0f), 0})); + } + + private: + OVRFW::TinyUI ui_; + + OVRFW::ControllerRenderer controllerRenderL_; + OVRFW::ControllerRenderer controllerRenderR_; + OVRFW::SimpleBeamRenderer cursorBeamRenderer_; + OVRFW::GeometryRenderer toolRenderer_; + + // Menu Objects + OVRFW::VRMenuObject* boxCountLabel_{nullptr}; + OVRFW::VRMenuObject* currentInteractionProfileText_{nullptr}; + OVRFW::VRMenuObject* boxColorLabel_{nullptr}; + const std::vector colorOptions_{ + {0.65f, 0.f, 0.f, 1.f}, + {0.f, 0.65f, 0.f, 1.f}, + {0.f, 0.f, 0.65f, 1.f}}; + const std::vector colorNames_{"Red", "Green", "Blue"}; + unsigned cubeColorIndex_ = 0; + + // Collection of all placed cubes + OVRFW::GeometryBuilder cubeGeometry_; + + // Renderer of all the placed cubes, gets reset from cubeGeometry for any new cube + OVRFW::GeometryRenderer cubeRenderer_; + + // Renderer of the single cube that shows where the user is about to place a cube + OVRFW::GeometryRenderer templateCubeRenderer_; + float templateCubeOffset_{20_cm}; // Default position 20cm out from the tool + float oldTemplateCubeScale_{0.05f}; + float startingScalingDistance_{0.0f}; + float distBetweenHands_{0.0_m}; + bool currentlyScalingTemplate_{false}; + + float templateCubeScale_{0.1f}; // Default size 10cm cube + OVR::Quatf templateCubeRotation_{}; + float toolHitBox_{17_cm}; // Circular hitbox + + static constexpr float kMinTemplateCubeOffset_{0_m}; + static constexpr float kMaxTemplateCubeOffset_{5_m}; // Maximum 5m reach + static constexpr float kMinTemplateCubeScale_{0.01f}; // 1 centimeter cube minimum + static constexpr float kMaxTemplateCubeScale_{1.f}; // 1 meter cube max + + XrActionSet actionSetMenu_{XR_NULL_HANDLE}; + XrActionSet actionSetWorld_{XR_NULL_HANDLE}; + XrActionSet actionSetTool_{XR_NULL_HANDLE}; + + XrAction actionSelect_{XR_NULL_HANDLE}; + XrAction actionMenuBeamPose_{XR_NULL_HANDLE}; + + XrAction actionToggleColor_{XR_NULL_HANDLE}; + XrAction actionGrabRelease_{XR_NULL_HANDLE}; + XrAction actionHandsDrop_{XR_NULL_HANDLE}; + XrAction actionControllerGripPose_{XR_NULL_HANDLE}; + XrAction actionSpawnCube_{XR_NULL_HANDLE}; + XrAction actionCubeAimPose_{XR_NULL_HANDLE}; + XrAction actionRotateCube_{XR_NULL_HANDLE}; + XrAction actionScaleCube_{XR_NULL_HANDLE}; + XrAction actionTranslateCube_{XR_NULL_HANDLE}; + + bool toolHeldInRight_{false}; + bool toolHeldInLeft_{false}; + + // Reference spaces + XrSpace spaceStage_{XR_NULL_HANDLE}; + XrSpace spaceLocal_{XR_NULL_HANDLE}; + XrSpace mainReferenceSpace_{XR_NULL_HANDLE}; + + // Action spaces + XrSpace spaceMenuBeamLeft_{XR_NULL_HANDLE}; + XrSpace spaceMenuBeamRight_{XR_NULL_HANDLE}; + XrSpace spaceCubeAimLeft_{XR_NULL_HANDLE}; + XrSpace spaceCubeAimRight_{XR_NULL_HANDLE}; + XrSpace spaceGripLeft_{XR_NULL_HANDLE}; + XrSpace spaceGripRight_{XR_NULL_HANDLE}; + + // Updated every frame + XrSpaceLocation locationMenuBeamLeft_{XR_TYPE_SPACE_LOCATION}; + XrSpaceLocation locationMenuBeamRight_{XR_TYPE_SPACE_LOCATION}; + XrSpaceLocation locationCubeAimLeft_{XR_TYPE_SPACE_LOCATION}; + XrSpaceLocation locationCubeAimRight_{XR_TYPE_SPACE_LOCATION}; + XrSpaceLocation locationGripRight_{XR_TYPE_SPACE_LOCATION}; + XrSpaceLocation locationGripLeft_{XR_TYPE_SPACE_LOCATION}; + + // XrPaths for convenience + XrPath leftHandPath_{XR_NULL_PATH}; + XrPath rightHandPath_{XR_NULL_PATH}; + XrPath simpleInteractionProfile_{XR_NULL_PATH}; + XrPath touchInteractionProfile_{XR_NULL_PATH}; + XrPath touchProInteractionProfile_{XR_NULL_PATH}; + + /// Hands + PFN_xrCreateHandTrackerEXT xrCreateHandTrackerEXT_ = nullptr; + PFN_xrDestroyHandTrackerEXT xrDestroyHandTrackerEXT_ = nullptr; + PFN_xrLocateHandJointsEXT xrLocateHandJointsEXT_ = nullptr; + XrHandTrackerEXT handTrackerL_ = XR_NULL_HANDLE; + XrHandTrackerEXT handTrackerR_ = XR_NULL_HANDLE; + bool supportsHandTracking_{false}; + + XrHandJointLocationEXT jointLocationsL_[XR_HAND_JOINT_COUNT_EXT]; + XrHandJointLocationEXT jointLocationsR_[XR_HAND_JOINT_COUNT_EXT]; + std::vector handJointRenderersL_; + std::vector handJointRenderersR_; + bool handTrackedL_ = false; + bool handTrackedR_ = false; + OVR::Vector4f jointColor_{0.196, 0.3725, 0.1412, 0.8}; + + std::unordered_map actionSetPanels_{}; +}; + +ENTRY_POINT(XrInputSampleApp) diff --git a/Samples/XrSamples/XrInput/assets/assets.txt b/Samples/XrSamples/XrInput/assets/assets.txt new file mode 100644 index 0000000..2cc30f7 --- /dev/null +++ b/Samples/XrSamples/XrInput/assets/assets.txt @@ -0,0 +1 @@ +This file is a placeholder. diff --git a/Samples/XrSamples/XrInput/assets/panel.ktx b/Samples/XrSamples/XrInput/assets/panel.ktx new file mode 100644 index 0000000..deb13e8 Binary files /dev/null and b/Samples/XrSamples/XrInput/assets/panel.ktx differ diff --git a/Samples/XrSamples/XrInput/java/MainActivity.java b/Samples/XrSamples/XrInput/java/MainActivity.java new file mode 100644 index 0000000..831e60f --- /dev/null +++ b/Samples/XrSamples/XrInput/java/MainActivity.java @@ -0,0 +1,28 @@ +// Copyright (c) Facebook Technologies, LLC and its affiliates. All Rights reserved. +package com.oculus.xrsamples.xrinput; + +/** + * When using NativeActivity, we currently need to handle loading of dependent shared libraries + * manually before a shared library that depends on them is loaded, since there is not currently a + * way to specify a shared library dependency for NativeActivity via the manifest meta-data. + * + *

The simplest method for doing so is to subclass NativeActivity with an empty activity that + * calls System.loadLibrary on the dependent libraries, which is unfortunate when the goal is to + * write a pure native C/C++ only Android activity. + * + *

A native-code only solution is to load the dependent libraries dynamically using dlopen(). + * However, there are a few considerations, see: + * https://groups.google.com/forum/#!msg/android-ndk/l2E2qh17Q6I/wj6s_6HSjaYJ + * + *

1. Only call dlopen() if you're sure it will succeed as the bionic dynamic linker will + * remember if dlopen failed and will not re-try a dlopen on the same lib a second time. + * + *

2. Must remember what libraries have already been loaded to avoid infinitely looping when + * libraries have circular dependencies. + */ +public class MainActivity extends android.app.NativeActivity { + static { + System.loadLibrary("openxr_loader"); + System.loadLibrary("xrsamples_xrinput"); + } +} diff --git a/Samples/XrSamples/XrInput/res/values/strings.xml b/Samples/XrSamples/XrInput/res/values/strings.xml new file mode 100644 index 0000000..f9424a9 --- /dev/null +++ b/Samples/XrSamples/XrInput/res/values/strings.xml @@ -0,0 +1,4 @@ + + + OpenXR Input Sample + diff --git a/Samples/XrSamples/XrKeyboard/CMakeLists.txt b/Samples/XrSamples/XrKeyboard/CMakeLists.txt new file mode 100755 index 0000000..34ad15b --- /dev/null +++ b/Samples/XrSamples/XrKeyboard/CMakeLists.txt @@ -0,0 +1,43 @@ +# Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved. + +project(xrkeyboard) + +if(NOT TARGET OpenXR::openxr_loader) + find_package(OpenXR REQUIRED) +endif() + +file(GLOB_RECURSE SRC_FILES + Src/*.c + Src/*.cpp +) + +if(ANDROID) + add_library(${PROJECT_NAME} MODULE ${SRC_FILES}) + target_include_directories(${PROJECT_NAME} PUBLIC ${ANDROID_NDK}/sources/android/native_app_glue) + target_link_libraries(${PROJECT_NAME} PRIVATE + android + EGL + GLESv3 + log + ktx + ) + set_target_properties(${PROJECT_NAME} PROPERTIES LINK_FLAGS "-u ANativeActivity_onCreate") +elseif(WIN32) + add_definitions(-D_USE_MATH_DEFINES) + add_executable(${PROJECT_NAME} ${SRC_FILES}) + add_custom_command(TARGET ${PROJECT_NAME} PRE_BUILD + COMMAND "${CMAKE_COMMAND}" -E copy_directory + "${CMAKE_CURRENT_LIST_DIR}/assets" + "$/assets" + VERBATIM) + + add_custom_command(TARGET ${PROJECT_NAME} PRE_BUILD + COMMAND "${CMAKE_COMMAND}" -E copy_directory + "${CMAKE_SOURCE_DIR}/SampleXrFramework/res/raw" + "$/font/res/raw" + VERBATIM) +endif() + +# Common across platforms +target_include_directories(${PROJECT_NAME} PRIVATE Src) +target_link_libraries(${PROJECT_NAME} PRIVATE samplexrframework) diff --git a/Samples/XrSamples/XrKeyboard/Projects/Android/AndroidManifest.xml b/Samples/XrSamples/XrKeyboard/Projects/Android/AndroidManifest.xml new file mode 100755 index 0000000..dd5bb11 --- /dev/null +++ b/Samples/XrSamples/XrKeyboard/Projects/Android/AndroidManifest.xml @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Samples/XrSamples/XrKeyboard/Projects/Android/build.bat b/Samples/XrSamples/XrKeyboard/Projects/Android/build.bat new file mode 100755 index 0000000..facf79f --- /dev/null +++ b/Samples/XrSamples/XrKeyboard/Projects/Android/build.bat @@ -0,0 +1,29 @@ +@rem Only edit the master copy of this file in SDK_ROOT/bin/scripts/build/perproject + +@setlocal enableextensions enabledelayedexpansion + +@if not exist "build.gradle" @echo Build script must be executed from project directory. & goto :Abort + +@set P=.. + +:TryAgain + +@rem @echo P = %P% + +@if exist "%P%\bin\scripts\build\build.py.bat" goto :Found + +@if exist "%P%\bin\scripts\build" @echo "Could not find build.py.bat" & goto :Abort + +@set P=%P%\.. + +@goto :TryAgain + +:Found + +@set P=%P%\bin\scripts\build +@call %P%\build.py.bat %1 %2 %3 %4 %5 +@goto :End + +:Abort + +:End diff --git a/Samples/XrSamples/XrKeyboard/Projects/Android/build.gradle b/Samples/XrSamples/XrKeyboard/Projects/Android/build.gradle new file mode 100755 index 0000000..ae8d2a1 --- /dev/null +++ b/Samples/XrSamples/XrKeyboard/Projects/Android/build.gradle @@ -0,0 +1,78 @@ +// Top-level build file where you can add configuration options common to all sub-projects/modules. +buildscript { + repositories { + google() + mavenCentral() + } + dependencies { + classpath "com.android.tools.build:gradle:7.0.3" + } +} + +repositories { + google() + mavenCentral() +} + +apply plugin: 'com.android.application' + +android { + compileSdk 32 + + defaultConfig { + applicationId "com.oculus.sdk.xrkeyboard" + minSdk 26 + targetSdk 32 + versionCode 1 + versionName "1.0" + + // override app plugin abiFilters for 64-bit support + externalNativeBuild { + ndk { + abiFilters 'arm64-v8a' + } + ndkBuild { + abiFilters 'arm64-v8a' + } + cmake { + targets "xrkeyboard" + } + } + } + + sourceSets { + main { + manifest.srcFile 'AndroidManifest.xml' + java.srcDirs = ['../../java'] + assets.srcDirs = ['../../assets'] + res.srcDirs = ['../../res'] + } + } + + buildTypes { + debug { + debuggable true + } + + release { + debuggable false + } + } + + externalNativeBuild { + cmake { + path file('../../../../CMakeLists.txt') + } + } + + // Enable prefab support for the OpenXR AAR + buildFeatures { + prefab true + } +} + +dependencies { + // Package/application AndroidManifest.xml properties, plus headers and libraries + // exposed to CMake + implementation 'org.khronos.openxr:openxr_loader_for_android:1.1.36' +} diff --git a/Samples/XrSamples/XrKeyboard/Projects/Android/build.py b/Samples/XrSamples/XrKeyboard/Projects/Android/build.py new file mode 100755 index 0000000..d4b6e58 --- /dev/null +++ b/Samples/XrSamples/XrKeyboard/Projects/Android/build.py @@ -0,0 +1,35 @@ +#!/usr/bin/python +# (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +# This first bit of code is common bootstrapping code +# to determine the SDK root, and to set up the import +# path for additional python code. + +# begin bootstrap +import os +import sys + + +def init(): + root = os.path.realpath(os.path.dirname(os.path.realpath(__file__))) + os.chdir(root) # make sure we are always executing from the project directory + while os.path.isdir(os.path.join(root, "bin/scripts/build")) == False: + root = os.path.realpath(os.path.join(root, "..")) + if ( + len(root) <= 5 + ): # Should catch both Posix and Windows root directories (e.g. '/' and 'C:\') + print("Unable to find SDK root. Exiting.") + sys.exit(1) + root = os.path.abspath(root) + os.environ["OCULUS_SDK_PATH"] = root + sys.path.append(root + "/bin/scripts/build") + + +init() +import ovrbuild + +ovrbuild.init() +# end bootstrap + + +ovrbuild.build() diff --git a/Samples/XrSamples/XrKeyboard/Projects/Android/gradle.properties b/Samples/XrSamples/XrKeyboard/Projects/Android/gradle.properties new file mode 100644 index 0000000..3e927b1 --- /dev/null +++ b/Samples/XrSamples/XrKeyboard/Projects/Android/gradle.properties @@ -0,0 +1,21 @@ +# Project-wide Gradle settings. +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. More details, visit +# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects +# org.gradle.parallel=true +# AndroidX package structure to make it clearer which packages are bundled with the +# Android operating system, and which are packaged with your app's APK +# https://developer.android.com/topic/libraries/support-library/androidx-rn +android.useAndroidX=true +# Enables namespacing of each library's R class so that its R class includes only the +# resources declared in the library itself and none from the library's dependencies, +# thereby reducing the size of the R class for that library +android.nonTransitiveRClass=true \ No newline at end of file diff --git a/Samples/XrSamples/XrKeyboard/Projects/Android/gradle/wrapper/gradle-wrapper.jar b/Samples/XrSamples/XrKeyboard/Projects/Android/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..7454180 Binary files /dev/null and b/Samples/XrSamples/XrKeyboard/Projects/Android/gradle/wrapper/gradle-wrapper.jar differ diff --git a/Samples/XrSamples/XrKeyboard/Projects/Android/gradle/wrapper/gradle-wrapper.properties b/Samples/XrSamples/XrKeyboard/Projects/Android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..ffed3a2 --- /dev/null +++ b/Samples/XrSamples/XrKeyboard/Projects/Android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/Samples/XrSamples/XrKeyboard/Projects/Android/gradlew b/Samples/XrSamples/XrKeyboard/Projects/Android/gradlew new file mode 100755 index 0000000..a69d9cb --- /dev/null +++ b/Samples/XrSamples/XrKeyboard/Projects/Android/gradlew @@ -0,0 +1,240 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# 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 +# +# https://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. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit + +APP_NAME="Gradle" +APP_BASE_NAME=${0##*/} + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/Samples/XrSamples/XrKeyboard/Projects/Android/gradlew.bat b/Samples/XrSamples/XrKeyboard/Projects/Android/gradlew.bat new file mode 100755 index 0000000..f127cfd --- /dev/null +++ b/Samples/XrSamples/XrKeyboard/Projects/Android/gradlew.bat @@ -0,0 +1,91 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/Samples/XrSamples/XrKeyboard/Projects/Android/settings.gradle b/Samples/XrSamples/XrKeyboard/Projects/Android/settings.gradle new file mode 100755 index 0000000..b2fceed --- /dev/null +++ b/Samples/XrSamples/XrKeyboard/Projects/Android/settings.gradle @@ -0,0 +1 @@ +rootProject.name = "XrKeyboard" diff --git a/Samples/XrSamples/XrKeyboard/README.md b/Samples/XrSamples/XrKeyboard/README.md new file mode 100644 index 0000000..54cb5f1 --- /dev/null +++ b/Samples/XrSamples/XrKeyboard/README.md @@ -0,0 +1,676 @@ +# Physical Keyboard Tracking Sample + +There are several components that you must set up to provide users with a rich tracked keyboard experience. These components are: + +* Tracked Keyboard +* Hands +* Passthrough + +The Tracked Keyboard is the keyboard itself in virtual space. This component looks for a keyboard in physical space and tries to match it with the user’s selected keyboard. If the component tracks a physical keyboard, it will render it in virtual space. + +The Hands component assists in tracking and displaying hand models in virtual space that correspond to the user’s actual hands. + +[Passthrough](https://developer.oculus.com/documentation/native/android/mobile-passthrough/) allows for the user’s real hands to be seen in virtual space using passthrough camera layers. When the hands are not near the keyboard, they are rendered as a VR model. When the hands are near the keyboard, rendering switches to using Passthrough mode, which displays the user’s actual hands to the user. + +For the complete API reference as part of the OpenXR spec, read [`XR_FB_keyboard_tracking`](https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#XR_FB_keyboard_tracking). + +## Prerequisites + +Before you begin working with the tracked keyboard, you need the following: + +* An Oculus Quest 2, Quest Pro, or Quest 3 headset with the latest Oculus software. +* One of the keyboards listed on the [Tracked Keyboards for Meta Quest 2](https://www.meta.com/help/quest/articles/headsets-and-accessories/meta-quest-accessories/tracked-keyboards-meta-quest/) support page. + +Make sure to use the latest version of the Meta Quest operating system. To verify this, do the following: + +1. In the headset, go to **Settings** > **System** > **Software Update**. +1. Check the version. +1. If the version is not 37 or higher, update the software to the latest available version. + +## Android Setup + +The `AndroidManifest.xml` file requires the following features and permissions to unlock the essential functionality for rendering physically tracked keyboards. + +``` + + + + + + + + +``` + +Your Android project files must include a `NativeActivity` that will load the OpenXR library manually like in the following example: + +``` +public class MainActivity extends android.app.NativeActivity { + static { + System.loadLibrary("openxr_loader"); + } +} +``` + +## Native Integration + +### Using XrApp + +We recommend your main application class to inherit from `XrApp`. This gives you a good starting point and access to many helpful methods and objects, one of which is the [`XrInstance`](https://www.khronos.org/registry/OpenXR/specs/1.0/html/xrspec.html#XrInstance) object. You can use it in many OpenXR API calls. + +``` +class XrExampleApp : public OVRFW::XrApp +``` + +By implementing this class you can retrieve the `XrInstance` through calling `GetInstance()` or the [`XrSession`](https://www.khronos.org/registry/OpenXR/specs/1.0/html/xrspec.html#XrSession) through calling `GetSession()`. + +``` +XrInstance* instance = GetInstance(); +XrSession* session = GetSession(); +``` + +For details, read [Creating Instances and Sessions](https://developer.oculus.com/documentation/native/android/mobile-openxr-instance-session/). + +### Extensions + +The following extensions are required for leveraging the physical keyboard tracking functionality: + +* Keyboard Tracking: + * `XR_FB_KEYBOARD_TRACKING_EXTENSION_NAME` +* Dynamic Render Model: + * `XR_FB_RENDER_MODEL_EXTENSION_NAME` +* Passthrough Hands: + * `XR_FB_PASSTHROUGH_KEYBOARD_HANDS_EXTENSION_NAME` + * `XR_FB_PASSTHROUGH_EXTENSION_NAME` + * `XR_FB_TRIANGLE_MESH_EXTENSION_NAME` +* Hands: + * `XR_EXT_HAND_TRACKING_EXTENSION_NAME` + * `XR_FB_HAND_TRACKING_MESH_EXTENSION_NAME` + * `XR_FB_HAND_TRACKING_AIM_EXTENSION_NAME` +* Compositor Layer blending: + * `XR_FB_COMPOSITION_LAYER_ALPHA_BLEND_EXTENSION_NAME` + +You can expose these extensions by overriding the `GetExtensions` method of `XrApp` and returning the result to ensure all essential extensions are returned when needed. + +``` +// Returns a list of OpenXr extensions needed for this app +virtual std::vector GetExtensions() override { + std::vector extensions = XrApp::GetExtensions(); + extensions.push_back(XR_FB_KEYBOARD_TRACKING_EXTENSION_NAME); + extensions.push_back(XR_FB_RENDER_MODEL_EXTENSION_NAME); + extensions.push_back(XR_FB_PASSTHROUGH_KEYBOARD_HANDS_EXTENSION_NAME); + extensions.push_back(XR_FB_PASSTHROUGH_EXTENSION_NAME); + extensions.push_back(XR_FB_TRIANGLE_MESH_EXTENSION_NAME); + extensions.push_back(XR_EXT_HAND_TRACKING_EXTENSION_NAME); + extensions.push_back(XR_FB_HAND_TRACKING_MESH_EXTENSION_NAME); + extensions.push_back(XR_FB_HAND_TRACKING_AIM_EXTENSION_ANEM); + extensions.push_back(XR_FB_COMPOSITION_LAYER_ALPHA_BLEND_EXTENSION_NAME); + extensions.push_back(kbdExtension); + return extensions; +} +``` + +### Initialization + +Call to OpenXR to initialize specific component extensions by using the `XrInstance`. You must call the following extensions to initialize keyboard tracking with OpenXR: + +``` +oxr(xrGetInstanceProcAddr( + instance, + "xrQuerySystemTrackedKeyboardFB", + (PFN_xrVoidFunction*)(&xrQuerySystemTrackedKeyboardFB_))); +oxr(xrGetInstanceProcAddr( + instance, + "xrCreateKeyboardSpaceFB", + (PFN_xrVoidFunction*)(&xrCreateKeyboardSpaceFB_))); +``` + +Similarly, to allow users to see their hands through passthrough when using a tracked keyboard, you must initialize passthrough by using [`xrGetInstanceProcAddr`](https://www.khronos.org/registry/OpenXR/specs/1.0/html/xrspec.html#xrGetInstanceProcAddr). For example: + +``` +/// passthrough +oxr(xrGetInstanceProcAddr( + instance, "xrCreatePassthroughFB", (PFN_xrVoidFunction*)(&xrCreatePassthroughFB_))); +oxr(xrGetInstanceProcAddr( + instance, "xrDestroyPassthroughFB", (PFN_xrVoidFunction*)(&xrDestroyPassthroughFB_))); +oxr(xrGetInstanceProcAddr( + instance, "xrPassthroughStartFB", (PFN_xrVoidFunction*)(&xrPassthroughStartFB_))); +oxr(xrGetInstanceProcAddr( + instance, "xrPassthroughPauseFB", (PFN_xrVoidFunction*)(&xrPassthroughPauseFB_))); +/// layer +oxr(xrGetInstanceProcAddr( + instance, + "xrCreatePassthroughLayerFB", + (PFN_xrVoidFunction*)(&xrCreatePassthroughLayerFB_))); +oxr(xrGetInstanceProcAddr( + instance, + "xrDestroyPassthroughLayerFB", + (PFN_xrVoidFunction*)(&xrDestroyPassthroughLayerFB_))); +oxr(xrGetInstanceProcAddr( + instance, + "xrPassthroughLayerPauseFB", + (PFN_xrVoidFunction*)(&xrPassthroughLayerPauseFB_))); +oxr(xrGetInstanceProcAddr( + instance, + "xrPassthroughLayerResumeFB", + (PFN_xrVoidFunction*)(&xrPassthroughLayerResumeFB_))); +/// style +oxr(xrGetInstanceProcAddr( + instance, + "xrPassthroughLayerSetStyleFB", + (PFN_xrVoidFunction*)(&xrPassthroughLayerSetStyleFB_))); +/// geometry +oxr(xrGetInstanceProcAddr( + instance, + "xrCreateGeometryInstanceFB", + (PFN_xrVoidFunction*)(&xrCreateGeometryInstanceFB_))); +oxr(xrGetInstanceProcAddr( + instance, + "xrDestroyGeometryInstanceFB", + (PFN_xrVoidFunction*)(&xrDestroyGeometryInstanceFB_))); +oxr(xrGetInstanceProcAddr( + instance, + "xrGeometryInstanceSetTransformFB", + (PFN_xrVoidFunction*)(&xrGeometryInstanceSetTransformFB_))); +/// Passthrough - keyboard hands function +oxr(xrGetInstanceProcAddr( + instance, "xrPassthroughLayerSetKeyboardHandsIntensityFB", (PFN_xrVoidFunction*)(&xrPassthroughLayerSetKeyboardHandsIntensityFB_))); +/// Passthrough - mesh extension functions +/// mesh +oxr(xrGetInstanceProcAddr( + instance, "xrCreateTriangleMeshFB", (PFN_xrVoidFunction*)(&xrCreateTriangleMeshFB_))); +oxr(xrGetInstanceProcAddr( + instance, "xrDestroyTriangleMeshFB", (PFN_xrVoidFunction*)(&xrDestroyTriangleMeshFB_))); +/// buffers +oxr(xrGetInstanceProcAddr( + instance, + "xrTriangleMeshGetVertexBufferFB", + (PFN_xrVoidFunction*)(&xrTriangleMeshGetVertexBufferFB_))); +oxr(xrGetInstanceProcAddr( + instance, + "xrTriangleMeshGetIndexBufferFB", + (PFN_xrVoidFunction*)(&xrTriangleMeshGetIndexBufferFB_))); +/// update +oxr(xrGetInstanceProcAddr( + instance, + "xrTriangleMeshBeginUpdateFB", + (PFN_xrVoidFunction*)(&xrTriangleMeshBeginUpdateFB_))); +oxr(xrGetInstanceProcAddr( + instance, + "xrTriangleMeshEndUpdateFB", + (PFN_xrVoidFunction*)(&xrTriangleMeshEndUpdateFB_))); +oxr(xrGetInstanceProcAddr( + instance, + "xrTriangleMeshBeginVertexBufferUpdateFB", + (PFN_xrVoidFunction*)(&xrTriangleMeshBeginVertexBufferUpdateFB_))); +oxr(xrGetInstanceProcAddr( + instance, + "xrTriangleMeshEndVertexBufferUpdateFB", + (PFN_xrVoidFunction*)(&xrTriangleMeshEndVertexBufferUpdateFB_))); +``` + +Similarly, for Hands, follow this way: + +``` +/// Hook up extensions for hand tracking +oxr(xrGetInstanceProcAddr( + instance, "xrCreateHandTrackerEXT", (PFN_xrVoidFunction*)(&xrCreateHandTrackerEXT_))); +oxr(xrGetInstanceProcAddr( + instance, "xrDestroyHandTrackerEXT", (PFN_xrVoidFunction*)(&xrDestroyHandTrackerEXT_))); +oxr(xrGetInstanceProcAddr( + instance, "xrLocateHandJointsEXT", (PFN_xrVoidFunction*)(&xrLocateHandJointsEXT_))); +/// Hook up extensions for hand rendering +oxr(xrGetInstanceProcAddr( + instance, "xrGetHandMeshFB", (PFN_xrVoidFunction*)(&xrGetHandMeshFB_))); +``` + +Finally, add the extensions for render model assistance: + +``` +/// Hook up extensions for device settings +oxr(xrGetInstanceProcAddr( + instance, + "xrEnumerateRenderModelPathsFB", + (PFN_xrVoidFunction*)(&xrEnumerateRenderModelPathsFB_))); +oxr(xrGetInstanceProcAddr( + instance, + "xrGetRenderModelPropertiesFB", + (PFN_xrVoidFunction*)(&xrGetRenderModelPropertiesFB_))); +oxr(xrGetInstanceProcAddr( + instance, "xrLoadRenderModelFB", (PFN_xrVoidFunction*)(&xrLoadRenderModelFB_))); +``` + +You can initialize and manage these extensions however you wish. We recommend you to break them apart into helper classes that can own their individual responsibilities, although this is not a requirement. + +### Query System for Keyboard Tracking Info + +To receive tracked keyboard information so that you update your keyboard model, you must query the system. This will inform you if a keyboard exists and, if so, whether it can be tracked. Later on, you will also be able to query for updated state values of the keyboard. + +``` +if (xrQuerySystemTrackedKeyboardFB_) { + // current query + { + XrKeyboardTrackingQueryFB queryInfo{XR_TYPE_KEYBOARD_TRACKING_QUERY_FB}; + queryInfo.flags = XR_KEYBOARD_TRACKING_QUERY_LOCAL_BIT_FB; + XrKeyboardTrackingDescriptionFB desc; + if (oxr(xrQuerySystemTrackedKeyboardFB_(session_, &queryInfo, &desc))) { + if ((desc.flags & XR_KEYBOARD_TRACKING_EXISTS_BIT_FB) != 0) { + // found keyboard + if (!systemKeyboardExists_ || + systemKeyboardDesc_.trackedKeyboardId != desc.trackedKeyboardId || + systemKeyboardDesc_.flags != desc.flags) { + ALOG( + "Found new system keyboard '%d' '%s'", + desc.trackedKeyboardId, + desc.name); + systemKeyboardExists_ = true; + systemKeyboardDesc_ = desc; + systemKeyboardConnected_ = + systemKeyboardDesc_.flags & XR_KEYBOARD_TRACKING_CONNECTED_BIT_FB; + if ((systemKeyboardDesc_.flags & XR_KEYBOARD_TRACKING_LOCAL_BIT_FB)) { + trackingSystemKeyboard_ = false; + if (trackSystemKeyboard_) { + if (systemKeyboardConnected_ || + !requireKeyboardConnectedToTrack_) { + if (StartTrackingSystemKeyboard()) { + trackingSystemKeyboard_ = true; + } + } + } + if (!trackingSystemKeyboard_) { + StopTracking(); + } + } else { + ALOG( + "Found new system keyboard '%d' '%s', but not tracking because it isn't local", + desc.trackedKeyboardId, + desc.name); + } + + systemKeyboardStateChanged_ = true; + } + } else { + // no keyboard + if (systemKeyboardExists_) { + systemKeyboardExists_ = false; + if (trackSystemKeyboard_) { + StopTracking(); + trackingSystemKeyboard_ = false; + } + systemKeyboardStateChanged_ = true; + } + } + } + } +} + +if (keyboardSpace_ != XR_NULL_HANDLE) { + location_.next = nullptr; + return oxr( + xrLocateSpace(keyboardSpace_, currentSpace, predictedDisplayTime, &location_)); +} +``` + +### Start and Stop Tracking + +The following code is an example of how you can tell the system to start and stop keyboard tracking. + +``` +bool StartTrackingSystemKeyboard() { + /// delete old ... + StopTracking(); + + if (xrCreateKeyboardSpaceFB_ && systemKeyboardExists_) { + XrKeyboardSpaceCreateInfoFB createInfo{XR_TYPE_KEYBOARD_SPACE_CREATE_INFO_FB}; + createInfo.trackedKeyboardId = systemKeyboardDesc_.trackedKeyboardId; + if (XR_SUCCEEDED( + oxr(xrCreateKeyboardSpaceFB_(session_, &createInfo, &keyboardSpace_)))) { + size_ = systemKeyboardDesc_.size; + return true; + } + } + + return false; +} + +bool StopTracking() { + bool result = false; + if (keyboardSpace_ != XR_NULL_HANDLE) { + result = oxr(xrDestroySpace(keyboardSpace_)); + if (result) { + keyboardSpace_ = XR_NULL_HANDLE; + } else { + ALOG("Failed to destroy keyboardSpace_ %p", keyboardSpace_); + } + } + return result; +} +``` + +### Loading Keyboard Render Model + +To load the keyboard render model, you must first enumerate all available render model paths. This involves making a few calls to retrieve the paths and their properties as shown in the example below. + +`XR_TYPE_RENDER_MODEL_PROPERTIES_FB` is a struct definition that will be populated with the method [`xrGetRenderModelPropertiesFB_`](https://www.khronos.org/registry/OpenXR/specs/1.0/html/xrspec.html#xrGetRenderModelPropertiesFB). + +``` +/// Enumerate available models +XrInstance instance = GetInstance(); +if (xrEnumerateRenderModelPathsFB_) { + /// Query path count + uint32_t pathCount = 0; + oxr(xrEnumerateRenderModelPathsFB_(session_, pathCount, &pathCount, nullptr)); + if (pathCount > 0) { + XRLOG("XrRenderModelHelper: found %u models ", pathCount); + paths_.resize(pathCount); + /// Fill in the path data + oxr(xrEnumerateRenderModelPathsFB_(session_, pathCount, &pathCount, &paths_[0])); + /// Get properties + for (const auto& p : paths_) { + XrRenderModelPropertiesFB prop{XR_TYPE_RENDER_MODEL_PROPERTIES_FB}; + XrResult result = xrGetRenderModelPropertiesFB_(session_, p.path, &prop); + if (result == XR_SUCCESS) { + properties_.push_back(prop); + } + } + } +} +``` + +Once the paths are discovered, you can query the keyboard render model(s). First, execute a two-call pattern to get the buffer for the render model(s). The first call retrieves the buffer size and the second call retrieves the buffer. + +Once the paths are retrieved, the `modelKey` can be passed into an [`XrRenderModelLoadInfoFB`](https://www.khronos.org/registry/OpenXR/specs/1.0/html/xrspec.html#XrRenderModelLoadInfoFB) object and used to load the models themselves by calling [`xrLoadRenderModelFB_`](https://www.khronos.org/registry/OpenXR/specs/1.0/html/xrspec.html#xrLoadRenderModelFB). Below is an example of this use. + +``` +std::vector buffer; +XrInstance instance = GetInstance(); +for (const auto& p : paths_) { + char buf[256]; + uint32_t bufCount = 0; + // OpenXR two call pattern: first call gets buffer size, second call gets the buffer + // data + oxr(xrPathToString(instance, p.path, bufCount, &bufCount, nullptr)); + oxr(xrPathToString(instance, p.path, bufCount, &bufCount, &buf[0])); + std::string pathString = buf; + if (pathString.rfind("/model_fb/keyboard", 0) == 0) { + XrRenderModelPropertiesFB prop{XR_TYPE_RENDER_MODEL_PROPERTIES_FB}; + XrResult result = xrGetRenderModelPropertiesFB_(session_, p.path, &prop); + if (result == XR_SUCCESS) { + if (prop.modelKey != XR_NULL_RENDER_MODEL_KEY_FB) { + XrRenderModelLoadInfoFB loadInfo = {XR_TYPE_RENDER_MODEL_LOAD_INFO_FB}; + loadInfo.modelKey = prop.modelKey; + + XrRenderModelBufferFB rmb{XR_TYPE_RENDER_MODEL_BUFFER_FB}; + rmb.next = nullptr; + rmb.bufferCapacityInput = 0; + rmb.buffer = nullptr; + if (oxr(xrLoadRenderModelFB_(session_, &loadInfo, &rmb))) { + XRLOG( + "Loading modelKey %u size %u ", + prop.modelKey, + rmb.bufferCountOutput); + buffer.resize(rmb.bufferCountOutput); + rmb.buffer = (uint8_t*)buffer.data(); + rmb.bufferCapacityInput = rmb.bufferCountOutput; + if (!oxr(xrLoadRenderModelFB_(session_, &loadInfo, &rmb))) { + XRLOG( + "FAILED to load modelKey %u on pass 2", + prop.modelKey); + buffer.resize(0); + } + } + } + } + } +} +``` + +If the above code runs successfully, you will have a data buffer containing the raw render model data for the user’s selected keyboard (if supported). This raw data will come from the model file and you must parse it before using it as seen in the following section. + +### Parsing and Rendering + +To render the keyboard models, you must first parse the data. Because all keyboard models will be in `*.glb` format, you can call the method to parse glb directly. + +``` +KeyboardModel = LoadModelFile_glB( + "keyboard", (const char*)buffer.data(), buffer.size(), programs, materials); +``` + +The `programs` and `materials` arguments in the previous method may vary depending on your use case. Here are some example values that you can use: + +``` +/// Shader +ovrProgramParm UniformParms[] = { + {"Texture0", ovrProgramParmType::TEXTURE_SAMPLED}, + {"SpecularLightDirection", ovrProgramParmType::FLOAT_VECTOR3}, + {"SpecularLightColor", ovrProgramParmType::FLOAT_VECTOR3}, + {"AmbientLightColor", ovrProgramParmType::FLOAT_VECTOR3}, + {"Opacity", ovrProgramParmType::FLOAT}, + {"AlphaBlend", ovrProgramParmType::FLOAT}, +}; +ProgKeyboard = GlProgram::Build( + "", + Keyboard::VertexShaderSrc, + "", + Keyboard::FragmentShaderSrc, + UniformParms, + sizeof(UniformParms) / sizeof(ovrProgramParm)); + +MaterialParms materials = {}; +ModelGlPrograms programs = {}; +programs.ProgSingleTexture = &ProgKeyboard; +programs.ProgBaseColorPBR = &ProgKeyboard; +programs.ProgSkinnedBaseColorPBR = &ProgKeyboard; +programs.ProgLightMapped = &ProgKeyboard; +programs.ProgBaseColorEmissivePBR = &ProgKeyboard; +programs.ProgSkinnedBaseColorEmissivePBR = &ProgKeyboard; +programs.ProgSimplePBR = &ProgKeyboard; +programs.ProgSkinnedSimplePBR = &ProgKeyboard; +``` + +Once the data is parsed, there may be more than one model found. Additional setup can configure all the returned models like in the following example : + +``` +for (auto& model : KeyboardModel->Models) { + auto& gc = model.surfaces[0].surfaceDef.graphicsCommand; + gc.UniformData[0].Data = &gc.Textures[0]; + gc.UniformData[1].Data = &SpecularLightDirection; + gc.UniformData[2].Data = &SpecularLightColor; + gc.UniformData[3].Data = &AmbientLightColor; + gc.UniformData[4].Data = &Opacity; + gc.UniformData[5].Data = &AlphaBlendFactor; + gc.GpuState.depthEnable = gc.GpuState.depthMaskEnable = true; + gc.GpuState.blendEnable = ovrGpuState::BLEND_ENABLE; + gc.GpuState.blendMode = GL_FUNC_ADD; + gc.GpuState.blendSrc = GL_ONE; + gc.GpuState.blendDst = GL_ONE_MINUS_SRC_ALPHA; +} + +/// Set defaults +SpecularLightDirection = Vector3f(1.0f, 1.0f, 0.0f); +SpecularLightColor = Vector3f(1.0f, 0.95f, 0.8f) * 0.75f; +AmbientLightColor = Vector3f(1.0f, 1.0f, 1.0f) * 0.15f; +``` + +Finally, you can render the model by using the following method: + +``` +virtual void Render(const OVRFW::ovrApplFrameIn& in, OVRFW::ovrRendererOutput& out) override { + if (ShowModel && KeyboardModel != nullptr) { + for (auto& model : KeyboardModel->Models) { + ovrDrawSurface controllerSurface; + controllerSurface.surface = &(model.surfaces[0].surfaceDef); + controllerSurface.modelMatrix = Transform; + out.Surfaces.push_back(controllerSurface); + } + } +} +``` + +### Updating + +In addition to rendering the keyboard, you must track updates to the keyboard’s state. This can be things such as validity or position in 3D space. Updating the keyboard will ensure you are displaying the correct state of the keyboard to the user in real time. + +The following example retrieves the space and time of the given update frame. Next, it checks if the latest space and location of the queried keyboard is valid. If so, it updates the keyboard pose to the values returned by the system for the given frame. + +``` +virtual void Update(const OVRFW::ovrApplFrameIn& in) override { + XrSpace currentSpace = GetCurrentSpace(); + XrTime predictedDisplayTime = ToXrTime(in.PredictedDisplayTime); + const XrSpaceLocationFlags isTracked = + XR_SPACE_LOCATION_ORIENTATION_TRACKED_BIT | XR_SPACE_LOCATION_POSITION_TRACKED_BIT; + const XrSpaceLocationFlags isValid = + XR_SPACE_LOCATION_ORIENTATION_VALID_BIT | XR_SPACE_LOCATION_POSITION_VALID_BIT; + XrSpaceLocationFlags flags = isTracked | isValid; + + if ((keyboardSpace_ != XR_NULL_HANDLE) && (location_.locationFlags & flags)) { + renderKeyboard_ = true; + std::vector keyboardPoses; + // Tracked joints and computed joints can all be valid + XrSpaceLocationFlags isValid = + XR_SPACE_LOCATION_ORIENTATION_VALID_BIT | XR_SPACE_LOCATION_POSITION_VALID_BIT; + if ((location_ +.locationFlags & isValid) != 0) { + /// render a box + pose_ = FromXrPosef(location_ +.pose); + dimensions_ = FromXrVector3f(keyboard_->Size()); + /// add center + keyboardPoses.push_back(pose_); + /// add corners + OVR::Posef point; + point = OVR::Posef::Identity(); + point.Translation.x += dimensions_.x / 2.0f; + point.Translation.z += dimensions_.z / 2.0f; + keyboardPoses.push_back(pose_ * point); + point = OVR::Posef::Identity(); + point.Translation.x += dimensions_.x / 2.0f; + point.Translation.z -= dimensions_.z / 2.0f; + keyboardPoses.push_back(pose_ * point); + point = OVR::Posef::Identity(); + point.Translation.x -= dimensions_.x / 2.0f; + point.Translation.z += dimensions_.z / 2.0f; + keyboardPoses.push_back(pose_ * point); + point = OVR::Posef::Identity(); + point.Translation.x -= dimensions_.x / 2.0f; + point.Translation.z -= dimensions_.z / 2.0f; + keyboardPoses.push_back(pose_ * point); + } + } + + /// Compute transform for the root + Transform = Matrix4f(pose_); +} +``` + +### Passthrough Keyboard Hands + +By using the [`XR_FB_PASSTHROUGH_KEYBOARD_HANDS`](https://www.khronos.org/registry/OpenXR/specs/1.0/html/xrspec.html#XR_FB_passthrough_keyboard_hands) extension, the Meta Quest system can render the passthrough view of the hands over the keyboard. You can accomplish that by creating a new passthrough layer of type `XR_PASSTHROUGH_LAYER_PURPOSE_TRACKED_KEYBOARDS_HANDS_FB`. You can set the intensity of each hand through the [`xrPassthroughLayerSetKeyboardHandsIntensityFB`](https://www.khronos.org/registry/OpenXR/specs/1.0/html/xrspec.html#xrPassthroughLayerSetKeyboardHandsIntensityFB) function. + +### Passthrough Window + +Alternatively, you can use a `GeometryRenderer` and create a passthrough window cutout. Then in the Update method, update the plane that these should be rendered on. + +**Initialization:** + +``` +/// setup geometry renderer for passthrough cutout +OVRFW::GeometryRenderer gr_; +gr_.Init(OVRFW::BuildTesselatedQuadDescriptor(4, 4, false)); // quad in XY plane, facing +Z +gr_.DiffuseColor = {1.0f, 1.0f, 1.0f, 1.0f}; +gr_.ChannelControl = {0, 1, 0, 1}; +gr_.AmbientLightColor = {1, 1, 1}; +gr_.BlendMode = GL_FUNC_REVERSE_SUBTRACT; +``` + +**Update:** + +``` +/// update cut out plane pose +OVR::Posef planePose = pose_; +planePose.Translation.y -= 0.02f; +planePose.Rotation *= OVR::Quatf({1.0f, 0.0f, 0.0f}, OVR::DegreeToRad(-90.0f)); +gr_.SetPose(planePose); +const float padding = 0.1f; // provide some padding +gr_.SetScale( + {(dimensions_.x * 0.5f) + padding, + (dimensions_.z * 0.5f) + padding, + 1.0f}); +gr_.Update(); +``` + +**Render:** + +``` +gr_.Render(out.Surfaces); +``` + +### KTX2 Support + +To support KTX2 textures, you must include the [Khronos KTX library](https://github.com/KhronosGroup/KTX-Software) in your project and then explicitly declare the use of KTX2 by defining the `SUPPORTS_KTX2` configuration parameter. This enables parsing and using KTX2 textures. Otherwise, they will be ignored. + + +### KTX2 Support + +To support KTX2 textures, you must include the [Khronos KTX library](https://github.com/KhronosGroup/KTX-Software) in your project and then explicitly declare the use of KTX2 by defining the `SUPPORTS_KTX2` configuration parameter. This enables parsing and using KTX2 textures. Otherwise, they will be ignored. + +# Sample Notes + +## Prerequisites + +Before you begin working with the tracked keyboard, you need the following: + +* An Oculus Quest 2, Quest Pro, or Quest 3 headset with the latest Oculus software. +* One of the keyboards listed on the [Tracked Keyboards for Meta Quest 2](https://www.meta.com/help/quest/articles/headsets-and-accessories/meta-quest-accessories/tracked-keyboards-meta-quest/) support page. + +Make sure to use the latest version of the Meta Quest operating system. To verify this, do the following: + +1. In the headset, go to **Settings** > **System** > **Software Update**. +1. Check the version. +1. If the version is not 37 or higher, update the software to the latest available version. + +## Pair Keyboard + +To pair your keyboard, follow these steps: + +1. Set the keyboard in pairing mode. +2. In the headset, go to **Settings** > **Devices** > **Bluetooth** and select **Pair**. +3. Select the tracked keyboard from the list of available devices. If the keyboard is not listed, ensure it is not already connected to any other device such as your computer. +4. Using the tracked keyboard, type the pairing code and then press Return/Enter. When the **See Your Keyboard in VR** dialog appears, the keyboard pairs with the headset. +5. Select **OK** and restart the headset. + +The keyboard should now appear in VR. If you place your hands on or close to the top of the keyboard, you will see your hands in passthrough. If you do not, ensure there is good lighting and the keyboard is set against a contrasting surface. + +## Navigating the Keyboard Tracking Sample App + +Once your keyboard is connected, launch the XrKeyboard sample app which is included in the [Oculus OpenXR Mobile SDK](https://developer.oculus.com/downloads/package/oculus-openxr-mobile-sdk/). + +If the keyboard is connected properly, the app will start searching for your keyboard immediately. As soon as the app tracks the keyboard, the 3D model renders in VR on top of where it would exist in the real world. + +You can find the connection and tracking status of your keyboard in the UI screen display. The name of the keyboard model that you have selected appears in **Settings**. + +The following UI options allow users to change the behavior of the tracked keyboard: + +**Toggle Local or Remote Keyboard** + +![Remote Keyboard](imagesnative_keyboard_tracking_UI_3.png) + +This option changes the expected tracked keyboard type between **Remote** and **Local (Bluetooth)** keyboard. + +**Use Model with Passthrough Hands** + +![Model with Passthrough Hands](images/native_keyboard_tracking_UI_2.png) + +In this mode, you see the full keyboard model rendered in VR space. Moving your hands over the keyboard enables a passthrough layer that allows you to see your own hands over the rendered model. + +**Use Passthrough Window** + +![Passthrough Window](images/native_keyboard_tracking_UI_4.png) + +This mode enables a passthrough window that is displayed around the tracked keyboard and gives a more natural feel. With this mode active, the VR model of the keyboard is no longer rendered. + +**Toggle Require Keyboard Connected** + +![Connection required](images/native_keyboard_tracking_UI_1.png) + +This option toggles between requiring the tracked keyboard to be connected through Bluetooth or not. If disabled, it allows the keyboard to still be tracked and rendered. However, there is no way for the device to capture input from keys pressed if the keyboard is not connected. diff --git a/Samples/XrSamples/XrKeyboard/Src/Input/KeyboardRenderer.cpp b/Samples/XrSamples/XrKeyboard/Src/Input/KeyboardRenderer.cpp new file mode 100755 index 0000000..482f82a --- /dev/null +++ b/Samples/XrSamples/XrKeyboard/Src/Input/KeyboardRenderer.cpp @@ -0,0 +1,201 @@ +/************************************************************************************************ +Filename : KeyboardRenderer.cpp +Content : A one stop for rendering keyboards +Created : April 2021 +Authors : Federico Schliemann +Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved. +************************************************************************************************/ +#include "KeyboardRenderer.h" +#include "Model/ModelFile.h" +#include "Model/ModelFileLoading.h" +#include "XrApp.h" + +using OVR::Matrix4f; +using OVR::Posef; +using OVR::Quatf; +using OVR::Vector3f; +using OVR::Vector4f; + +namespace OVRFW { +namespace Keyboard { + +/// clang-format off +static const char* VertexShaderSrc = R"glsl( +attribute highp vec4 Position; +attribute highp vec3 Normal; +attribute highp vec2 TexCoord; + +varying lowp vec3 oEye; +varying lowp vec3 oNormal; +varying lowp vec2 oTexCoord; + +vec3 multiply( mat4 m, vec3 v ) +{ + return vec3( + m[0].x * v.x + m[1].x * v.y + m[2].x * v.z, + m[0].y * v.x + m[1].y * v.y + m[2].y * v.z, + m[0].z * v.x + m[1].z * v.y + m[2].z * v.z ); +} + +vec3 transposeMultiply( mat4 m, vec3 v ) +{ + return vec3( + m[0].x * v.x + m[0].y * v.y + m[0].z * v.z, + m[1].x * v.x + m[1].y * v.y + m[1].z * v.z, + m[2].x * v.x + m[2].y * v.y + m[2].z * v.z ); +} + +void main() +{ + gl_Position = TransformVertex( Position ); + vec3 eye = transposeMultiply( sm.ViewMatrix[VIEW_ID], -vec3( sm.ViewMatrix[VIEW_ID][3] ) ); + oEye = eye - vec3( ModelMatrix * Position ); + vec3 iNormal = Normal * 100.0f; + oNormal = multiply( ModelMatrix, iNormal ); + oTexCoord = TexCoord; +} +)glsl"; + +static const char* FragmentShaderSrc = R"glsl( +precision lowp float; + +uniform sampler2D Texture0; +uniform lowp vec3 SpecularLightDirection; +uniform lowp vec3 SpecularLightColor; +uniform lowp vec3 AmbientLightColor; +uniform float Opacity; +uniform float AlphaBlend; + +varying lowp vec3 oEye; +varying lowp vec3 oNormal; +varying lowp vec2 oTexCoord; + +lowp vec3 multiply( lowp mat3 m, lowp vec3 v ) +{ + return vec3( + m[0].x * v.x + m[1].x * v.y + m[2].x * v.z, + m[0].y * v.x + m[1].y * v.y + m[2].y * v.z, + m[0].z * v.x + m[1].z * v.y + m[2].z * v.z ); +} + +void main() +{ + lowp vec3 eyeDir = normalize( oEye.xyz ); + lowp vec3 Normal = normalize( oNormal ); + + lowp vec3 reflectionDir = dot( eyeDir, Normal ) * 2.0 * Normal - eyeDir; + lowp vec4 diffuse = texture2D( Texture0, oTexCoord ); + lowp vec3 ambientValue = diffuse.xyz * AmbientLightColor; + + lowp float nDotL = max( dot( Normal , SpecularLightDirection ), 0.0 ); + lowp vec3 diffuseValue = diffuse.xyz * SpecularLightColor * nDotL; + + lowp float specularPower = 1.0f - diffuse.a; + specularPower = specularPower * specularPower; + + lowp vec3 H = normalize( SpecularLightDirection + eyeDir ); + lowp float nDotH = max( dot( Normal, H ), 0.0 ); + lowp float specularIntensity = pow( nDotH, 64.0f * ( specularPower ) ) * specularPower; + lowp vec3 specularValue = specularIntensity * SpecularLightColor; + + lowp vec3 controllerColor = diffuseValue + ambientValue + specularValue; + + float alphaBlendFactor = max(diffuse.w, AlphaBlend) * Opacity; + + // apply alpha + gl_FragColor.w = alphaBlendFactor; + // premult + gl_FragColor.xyz = controllerColor * gl_FragColor.w; +} +)glsl"; + +/// clang-format on + +} // namespace Keyboard + +bool KeyboardRenderer::Init(std::vector& keyboardBuffer) { + /// Shader + ovrProgramParm UniformParms[] = { + {"Texture0", ovrProgramParmType::TEXTURE_SAMPLED}, + {"SpecularLightDirection", ovrProgramParmType::FLOAT_VECTOR3}, + {"SpecularLightColor", ovrProgramParmType::FLOAT_VECTOR3}, + {"AmbientLightColor", ovrProgramParmType::FLOAT_VECTOR3}, + {"Opacity", ovrProgramParmType::FLOAT}, + {"AlphaBlend", ovrProgramParmType::FLOAT}, + }; + ProgKeyboard = GlProgram::Build( + "", + Keyboard::VertexShaderSrc, + "", + Keyboard::FragmentShaderSrc, + UniformParms, + sizeof(UniformParms) / sizeof(ovrProgramParm)); + + MaterialParms materials = {}; + ModelGlPrograms programs = {}; + programs.ProgSingleTexture = &ProgKeyboard; + programs.ProgBaseColorPBR = &ProgKeyboard; + programs.ProgSkinnedBaseColorPBR = &ProgKeyboard; + programs.ProgLightMapped = &ProgKeyboard; + programs.ProgBaseColorEmissivePBR = &ProgKeyboard; + programs.ProgSkinnedBaseColorEmissivePBR = &ProgKeyboard; + programs.ProgSimplePBR = &ProgKeyboard; + programs.ProgSkinnedSimplePBR = &ProgKeyboard; + + KeyboardModel = LoadModelFile_glB( + "keyboard", (const char*)keyboardBuffer.data(), keyboardBuffer.size(), programs, materials); + + if (KeyboardModel == nullptr || static_cast(KeyboardModel->Models.size()) < 1) { + ALOGE("Couldn't load keyboard model!"); + return false; + } + + for (auto& model : KeyboardModel->Models) { + auto& gc = model.surfaces[0].surfaceDef.graphicsCommand; + gc.UniformData[0].Data = &gc.Textures[0]; + gc.UniformData[1].Data = &SpecularLightDirection; + gc.UniformData[2].Data = &SpecularLightColor; + gc.UniformData[3].Data = &AmbientLightColor; + gc.UniformData[4].Data = &Opacity; + gc.UniformData[5].Data = &AlphaBlendFactor; + gc.GpuState.depthEnable = gc.GpuState.depthMaskEnable = true; + gc.GpuState.blendEnable = ovrGpuState::BLEND_ENABLE; + gc.GpuState.blendMode = GL_FUNC_ADD; + gc.GpuState.blendSrc = GL_ONE; + gc.GpuState.blendDst = GL_ONE_MINUS_SRC_ALPHA; + } + + /// Set defaults + SpecularLightDirection = Vector3f(1.0f, 1.0f, 0.0f); + SpecularLightColor = Vector3f(1.0f, 0.95f, 0.8f) * 0.75f; + AmbientLightColor = Vector3f(1.0f, 1.0f, 1.0f) * 0.15f; + + /// all good + return true; +} + +void KeyboardRenderer::Shutdown() { + OVRFW::GlProgram::Free(ProgKeyboard); + if (KeyboardModel != nullptr) { + delete KeyboardModel; + KeyboardModel = nullptr; + } +} + +void KeyboardRenderer::Update(const OVR::Posef& pose, const OVR::Vector3f& scale) { + /// Compute transform for the root + Transform = Matrix4f(pose); +} + +void KeyboardRenderer::Render(std::vector& surfaceList) { + if (ShowModel && KeyboardModel != nullptr) { + for (auto& model : KeyboardModel->Models) { + ovrDrawSurface controllerSurface; + controllerSurface.surface = &(model.surfaces[0].surfaceDef); + controllerSurface.modelMatrix = Transform; + surfaceList.push_back(controllerSurface); + } + } +} + +} // namespace OVRFW diff --git a/Samples/XrSamples/XrKeyboard/Src/Input/KeyboardRenderer.h b/Samples/XrSamples/XrKeyboard/Src/Input/KeyboardRenderer.h new file mode 100755 index 0000000..7f7caab --- /dev/null +++ b/Samples/XrSamples/XrKeyboard/Src/Input/KeyboardRenderer.h @@ -0,0 +1,62 @@ +/************************************************************************************************ +Filename : KeyboardRenderer.h +Content : A one stop for rendering keyboards +Created : April 2021 +Authors : Federico Schliemann +Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved. +************************************************************************************************/ +#pragma once + +#include +#include +#include + +/// Sample Framework +#include "Misc/Log.h" +#include "Model/SceneView.h" +#include "Render/GlProgram.h" +#include "Render/SurfaceRender.h" +#include "OVR_FileSys.h" +#include "OVR_Math.h" + +#if defined(ANDROID) +#define XR_USE_GRAPHICS_API_OPENGL_ES 1 +#define XR_USE_PLATFORM_ANDROID 1 +#else +#include "unknwn.h" +#define XR_USE_GRAPHICS_API_OPENGL 1 +#define XR_USE_PLATFORM_WIN32 1 +#endif + +#include +#include +#include + +namespace OVRFW { + +class KeyboardRenderer { + public: + KeyboardRenderer() = default; + ~KeyboardRenderer() = default; + + bool Init(std::vector& keyboardBuffer); + void Shutdown(); + void Update(const OVR::Posef& pose, const OVR::Vector3f& scale = OVR::Vector3f(1.0, 1.0, 1.0f)); + void Render(std::vector& surfaceList); + + public: + OVR::Vector3f SpecularLightDirection; + OVR::Vector3f SpecularLightColor; + OVR::Vector3f AmbientLightColor; + float Opacity = 1.0f; + bool ShowModel = true; + + private: + float AlphaBlendFactor = 1.0f; + GlProgram ProgKeyboard; + ModelFile* KeyboardModel = nullptr; + GlTexture KeyboardTextureSolid; + OVR::Matrix4f Transform; +}; + +} // namespace OVRFW diff --git a/Samples/XrSamples/XrKeyboard/Src/main.cpp b/Samples/XrSamples/XrKeyboard/Src/main.cpp new file mode 100755 index 0000000..59fa937 --- /dev/null +++ b/Samples/XrSamples/XrKeyboard/Src/main.cpp @@ -0,0 +1,749 @@ +/******************************************************************************* + +Filename : Main.cpp +Content : Simple test app to test openxr keyboard extension +Created : Dec 2020 +Authors : Federico Schliemann +Language : C++ +Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved. + +*******************************************************************************/ + +#include +#include +#include +#include +#include + +#include "XrApp.h" + +#include "Input/AxisRenderer.h" +#include "Input/ControllerRenderer.h" +#include "Input/HandMaskRenderer.h" +#include "Input/HandRenderer.h" +#include "Input/KeyboardRenderer.h" +#include "Input/TinyUI.h" +#include "Render/GeometryRenderer.h" +#include "Render/SimpleBeamRenderer.h" + +/// add logging +#define XRLOG ALOG + +#include "xr_hand_helper.h" +#include "xr_keyboard_helper.h" +#include "xr_passthrough_helper.h" +#include "xr_render_model_helper.h" + +class XrKeyboardApp : public OVRFW::XrApp { + private: + static constexpr std::string_view kSampleExplanation = + "The Tracked Keyboard is the physical keyboard in virtual space. \n" + " \n" + "When this component sees a keyboard device in the physical \n" + "world, it will try to match that physical keyboard to one of \n" + "the user-configured virtual keyboard models. If the match \n" + "succeeds, the keyboard is “tracked”, and a virtual keyboard \n" + "model will render in the position and orientation of the \n" + "physical keyboard. \n" + " \n" + "You will need to setup your device with a connected or remote \n" + "tracked keyboard. You can do this from Quest Settings. "; + public: + XrKeyboardApp() : OVRFW::XrApp() { + BackgroundColor = OVR::Vector4f(0.60f, 0.95f, 0.4f, 1.0f); + } + + // Returns a list of OpenXr extensions needed for this app + virtual std::vector GetExtensions() override { + std::vector extensions = XrApp::GetExtensions(); + /// add keyboard extensions + for (const auto& kbdExtension : XrKeyboardHelper::RequiredExtensionNames()) { + extensions.push_back(kbdExtension); + } + /// add hand extensions + for (const auto& handExtension : XrHandHelper::RequiredExtensionNames()) { + extensions.push_back(handExtension); + } + /// add passthrough extensions + for (const auto& passthroughExtension : XrPassthroughHelper::RequiredExtensionNames()) { + extensions.push_back(passthroughExtension); + } + /// add render model extensions + for (const auto& renderModelExtension : XrRenderModelHelper::RequiredExtensionNames()) { + extensions.push_back(renderModelExtension); + } + + /// add composition alpha blend + extensions.push_back(XR_FB_COMPOSITION_LAYER_ALPHA_BLEND_EXTENSION_NAME); + + /// log all extensions + ALOG("XrKeyboardApp requesting extensions:"); + for (const auto& e : extensions) { + ALOG(" --> %s", e); + } + + return extensions; + } + + // Must return true if the application initializes successfully. + virtual bool AppInit(const xrJava* context) override { + if (false == ui_.Init(context, GetFileSys())) { + ALOG("TinyUI::Init FAILED."); + return false; + } + + /// hand tracking + handL_ = std::make_unique(GetInstance(), true); + OXR(handL_->GetLastError()); + handR_ = std::make_unique(GetInstance(), false); + OXR(handR_->GetLastError()); + + /// keyboard tracking + keyboard_ = std::make_unique(GetInstance()); + OXR(keyboard_->GetLastError()); + + /// passthrough + passthrough_ = std::make_unique(GetInstance(), GetCurrentSpace()); + OXR(passthrough_->GetLastError()); + + /// render model + renderModel_ = std::make_unique(GetInstance()); + OXR(renderModel_->GetLastError()); + + /// Build UI + CreateSampleDescriptionPanel(); + bigText_ = ui_.AddLabel("OpenXR Keyboard", {0.1f, -0.25f, -2.0f}, {1300.0f, 100.0f}); + systemText_ = ui_.AddLabel("No System Keyboard", {0.1f, -0.5f, -2.0f}, {1300.0f, 100.0f}); + ui_.AddToggleButton( + "Use Passthrough Window", + "Use Model with Passthrough Hands", + &usePassthroughWindow_, + {-0.7f, 0.0f, -1.9f}, + {450.0f, 100.0f}); + connectionRequiredButton_ = ui_.AddButton( + "Toggle Require Keyboard Connected", {-0.7f, 0.25f, -1.9f}, {450.0f, 100.0f}, [=]() { + bool currentState = keyboard_->RequireKeyboardConnectedToTrack(); + bool newState = !currentState; + SetSwitchConnectionRequired(bigText_, connectionRequiredButton_, newState); + }); + + SetSwitchConnectionRequired(bigText_, connectionRequiredButton_, true); + + useRemoteKeyboardButton_ = ui_.AddButton( + "Toggle Local or Remote Keyboard", {-0.7f, 0.5f, -1.9f}, {450.0f, 100.0f}, [=]() { + bool currentState = keyboard_->UseRemoteKeyboard(); + bool newState = !currentState; + SetSwitchUseRemoteKeyboard( + bigText_, connectionRequiredButton_, useRemoteKeyboardButton_, newState); + }); + + SetSwitchUseRemoteKeyboard( + bigText_, connectionRequiredButton_, useRemoteKeyboardButton_, false); + + trackingRequiredButton_ = + ui_.AddButton("Show Untracked Keyboard", {-0.7f, 0.75f, -1.9f}, {450.0f, 100.0f}, [=]() { + bool currentState = keyboard_->TrackingRequired(); + bool newState = !currentState; + SetTrackingRequired( + bigText_, connectionRequiredButton_, trackingRequiredButton_, newState); + }); + + SetTrackingRequired(bigText_, connectionRequiredButton_, trackingRequiredButton_, true); + + return true; + } + + void CreateSampleDescriptionPanel() { + // Panel to provide sample description to the user for context + auto descriptionLabel = ui_.AddLabel( + static_cast(kSampleExplanation), {0.56f, 0.4f, -2.0f}, {760.0f, 360.0f}); + + // Align and size the description text for readability + OVRFW::VRMenuFontParms fontParams{}; + fontParams.Scale = 0.5f; + fontParams.AlignHoriz = OVRFW::HORIZONTAL_LEFT; + descriptionLabel->SetFontParms(fontParams); + descriptionLabel->SetTextLocalPosition({-0.7f, 0, 0}); + } + + virtual void AppShutdown(const xrJava* context) override { + handL_ = nullptr; + handR_ = nullptr; + keyboard_ = nullptr; + passthrough_ = nullptr; + renderModel_ = nullptr; + + OVRFW::XrApp::AppShutdown(context); + ui_.Shutdown(); + } + + virtual bool SessionInit() override { + /// Use LocalSpace instead of Stage Space. + CurrentSpace = LocalSpace; + /// Disable scene navigation + GetScene().SetFootPos({0.0f, 0.0f, 0.0f}); + this->FreeMove = false; + /// Init session bound objects + if (false == controllerRenderL_.Init(true)) { + ALOG("SessionInit::Init L controller renderer FAILED."); + return false; + } + if (false == controllerRenderR_.Init(false)) { + ALOG("SessionInit::Init R controller renderer FAILED."); + return false; + } + beamRenderer_.Init(GetFileSys(), nullptr, OVR::Vector4f(1.0f), 1.0f); + + /// hands + handL_->SessionInit(GetSession()); + handR_->SessionInit(GetSession()); + /// keyboard + keyboard_->SessionInit(GetSession()); + /// passthrough + passthrough_->SetDefaultSpace(GetCurrentSpace()); + passthrough_->SessionInit(GetSession()); + /// render model + renderModel_->SessionInit(GetSession()); + + /// rendering + axisRenderer_.Init(); + handRendererL_.Init(&handL_->Mesh(), handL_->IsLeft()); + handRendererR_.Init(&handR_->Mesh(), handR_->IsLeft()); + handMaskRendererL_.Init(handL_->IsLeft() /*leftHand*/); + handMaskRendererR_.Init(handR_->IsLeft() /*leftHand*/); + + gr_.Init(OVRFW::BuildTesselatedQuadDescriptor( + 4, 4, false /*twoSided*/)); // quad in XY plane, facing +Z + gr_.DiffuseColor = {1.0f, 1.0f, 1.0f, 1.0f}; + gr_.ChannelControl = {0, 1, 0, 1}; + gr_.AmbientLightColor = {1, 1, 1}; + gr_.BlendMode = GL_FUNC_REVERSE_SUBTRACT; + + /// configure hand mask rendering + handMaskRendererL_.RenderInverseSubtract = true; + handMaskRendererR_.RenderInverseSubtract = true; + handMaskRendererL_.UseBorderFade = false; + handMaskRendererR_.UseBorderFade = false; + + /// hand presence + ovrFramebuffer_Create(GetSession(), &presenceBuffer_, GL_SRGB8_ALPHA8, 256, 256, 1); + + return true; + } + + virtual void SessionEnd() override { + /// hands + handL_->SessionEnd(); + handR_->SessionEnd(); + /// keyboard + keyboard_->SessionEnd(); + /// passthrough + passthrough_->SessionEnd(); + /// render model + renderModel_->SessionEnd(); + + controllerRenderL_.Shutdown(); + controllerRenderR_.Shutdown(); + beamRenderer_.Shutdown(); + axisRenderer_.Shutdown(); + handRendererL_.Shutdown(); + handRendererR_.Shutdown(); + handMaskRendererL_.Shutdown(); + handMaskRendererR_.Shutdown(); + keyboardRenderer_.Shutdown(); + + ovrFramebuffer_Destroy(&presenceBuffer_); + } + + // Update state + virtual void Update(const OVRFW::ovrApplFrameIn& in) override { + XrSpace currentSpace = GetCurrentSpace(); + XrTime predictedDisplayTime = ToXrTime(in.PredictedDisplayTime); + + /// Toggle visualizations together + if (!usePassthroughWindow_) { + /// render hand presence using a hand mask layer + renderRealPassthroughLayerUnder_ = false; + renderRealPassthroughLayerOver_ = true; + } else { + /// render hand presence using a planar layer and locally rendered cutout + renderRealPassthroughLayerUnder_ = true; + renderRealPassthroughLayerOver_ = false; + } + + /// render model + renderModel_->Update(currentSpace, predictedDisplayTime); + /// hands + handL_->Update(currentSpace, predictedDisplayTime); + handR_->Update(currentSpace, predictedDisplayTime); + /// Keyboard + keyboard_->Update(currentSpace, predictedDisplayTime); + + if (keyboard_->GetAndClearSystemKeyboardStateChanged()) { + // update the render model + if (keyboard_->SystemKeyboardExists()) { + std::vector keyboardBuffer = + renderModel_->LoadRenderModel(keyboard_->UseRemoteKeyboard()); + if (keyboardBuffer.size() > 0) { + ALOG("### Keyboard Render Model Size: %u", (uint32_t)keyboardBuffer.size()); + keyboardRenderer_.Init(keyboardBuffer); + } else { + ALOG("### Failed to Load keyboard Render Model"); + } + } else { + ALOG("### no system keyboard, clearing model"); + } + + // rebuild the system keyboard string + std::stringstream ss; + ss << std::setprecision(4) << std::fixed; + + if (keyboard_->UseRemoteKeyboard()) { + ss << "(REMOTE) "; + } else { + ss << "(LOCAL) "; + } + + if (keyboard_->SystemKeyboardExists()) { + ss << "System Keyboard: " << keyboard_->SystemKeyboardDesc().trackedKeyboardId + << ": " << keyboard_->SystemKeyboardDesc().name << " " + << (keyboard_->SystemKeyboardConnected() ? "Connected" : "Not Connected"); + if (keyboard_->TrackingSystemKeyboard()) { + ss << " TRACKING"; + } else { + ss << " not TRACKING"; + } + } else { + ss << "No System Keyboard"; + } + if (systemText_ != nullptr) { + systemText_->SetText(ss.str().c_str()); + } + } + + if (keyboard_->IsLocationActive()) { + renderKeyboard_ = true; + auto location = keyboard_->Location(); + std::vector keyboardPoses; + // Tracked joints and computed joints can all be valid + XrSpaceLocationFlags isValid = + XR_SPACE_LOCATION_ORIENTATION_VALID_BIT | XR_SPACE_LOCATION_POSITION_VALID_BIT; + if ((location.locationFlags & isValid) != 0) { + /// render a box + pose_ = FromXrPosef(location.pose); + dimensions_ = FromXrVector3f(keyboard_->Size()); + /// add center + keyboardPoses.push_back(pose_); + /// add corners + OVR::Posef point; + point = OVR::Posef::Identity(); + point.Translation.x += dimensions_.x / 2.0f; + point.Translation.z += dimensions_.z / 2.0f; + keyboardPoses.push_back(pose_ * point); + point = OVR::Posef::Identity(); + point.Translation.x += dimensions_.x / 2.0f; + point.Translation.z -= dimensions_.z / 2.0f; + keyboardPoses.push_back(pose_ * point); + point = OVR::Posef::Identity(); + point.Translation.x -= dimensions_.x / 2.0f; + point.Translation.z += dimensions_.z / 2.0f; + keyboardPoses.push_back(pose_ * point); + point = OVR::Posef::Identity(); + point.Translation.x -= dimensions_.x / 2.0f; + point.Translation.z -= dimensions_.z / 2.0f; + keyboardPoses.push_back(pose_ * point); + } + axisRenderer_.Update(keyboardPoses); + + /// update cut out plane pose + OVR::Posef planePose = pose_; + planePose.Translation.y -= 0.02f; + planePose.Rotation *= OVR::Quatf({1.0f, 0.0f, 0.0f}, OVR::DegreeToRad(-90.0f)); + gr_.SetPose(planePose); + const float padding = 0.1f; // provide some padding + gr_.SetScale( + {(dimensions_.x * 0.5f) + padding, + (dimensions_.z * 0.5f) + padding, + 1.0f}); + gr_.Update(); + } else { + renderKeyboard_ = false; + } + + /// Compute fade heuristics + bool isKeyboardStale = ComputeKeyboardOpacity(in.PredictedDisplayTime); + if (!isKeyboardStale) { + /// force keyboard render at LKG, do opacity fade + renderKeyboard_ = true; + } + + if (renderKeyboard_) { + /// render keyboard model + keyboardRenderer_.Update(pose_); + + /// render passthrough + OVR::Vector3f scale{1, 1, 1}; + if (renderRealPassthroughLayerUnder_) { + /// TODO: size the passthrough layer based on coverage of hands + /// instead of hard coded 3x of keyboard size + /// Underlay layer needs some padding + scale.x *= 3.0f; + scale.z *= 3.0f; + } + + /// Send the transform to the planar layer, (hand layer ignores it) + OVR::Posef passthroughPose = pose_; + if (usePassthroughWindow_) { + passthroughPose.Translation.y += (dimensions_.y / 2.0f); + } + passthrough_->SetTransform( + ToXrPosef(passthroughPose), ToXrVector3f(dimensions_ * scale)); + } + + /// passthrough + if (!passthrough_->Update(currentSpace, predictedDisplayTime)) { + ALOG("passthrough_->Update() FAILED."); + OXR(passthrough_->GetLastError()); + } + + controllerRenderL_.Update(in.LeftRemotePose); + controllerRenderR_.Update(in.RightRemotePose); + + if (handL_->AreLocationsActive()) { + handRendererL_.Update(handL_->Joints(), handL_->RenderScale()); + handMaskRendererL_.Update( + in.HeadPose, + FromXrPosef(handL_->Joints()[XR_HAND_JOINT_WRIST_EXT].pose), + handRendererL_.Transforms(), + handL_->RenderScale()); + } + + if (handR_->AreLocationsActive()) { + handRendererR_.Update(handR_->Joints(), handR_->RenderScale()); + handMaskRendererR_.Update( + in.HeadPose, + FromXrPosef(handR_->Joints()[XR_HAND_JOINT_WRIST_EXT].pose), + handRendererR_.Transforms(), + handR_->RenderScale()); + } + + /// UI + ui_.HitTestDevices().clear(); + + if (handR_->AreLocationsActive()) { + ui_.AddHitTestRay(FromXrPosef(handR_->AimPose()), handR_->IndexPinching()); + } else if (in.RightRemoteTracked) { + const bool didPinch = in.RightRemoteIndexTrigger > 0.25f; + ui_.AddHitTestRay(in.RightRemotePointPose, didPinch); + } + + if (handL_->AreLocationsActive()) { + ui_.AddHitTestRay(FromXrPosef(handL_->AimPose()), handL_->IndexPinching()); + } else if (in.LeftRemoteTracked) { + const bool didPinch = in.LeftRemoteIndexTrigger > 0.25f; + ui_.AddHitTestRay(in.LeftRemotePointPose, didPinch); + } + + ui_.Update(in); + beamRenderer_.Update(in, ui_.HitTestDevices()); + } + + // Render eye buffers while running + virtual void Render(const OVRFW::ovrApplFrameIn& in, OVRFW::ovrRendererOutput& out) override { + /// Render plane + if (usePassthroughWindow_ && renderKeyboard_) { + gr_.Render(out.Surfaces); + } + + /// configure hand mask rendering + const float kMaskSize = usePassthroughWindow_ ? 0.0125f : 0.0175f; + handMaskRendererL_.AlphaMaskSize = kMaskSize; + handMaskRendererR_.AlphaMaskSize = kMaskSize; + + /// Render UI + ui_.Render(in, out); + + /// Render keyboard + if (renderKeyboard_) { + /// render axis to show tracking status + if (keyboard_->IsLocationActive()) { + axisRenderer_.Render(OVR::Matrix4f(), in, out); + } + + /// Always render the keyboard at the LKG location ... + keyboardRenderer_.ShowModel = !usePassthroughWindow_; + /// Opacity fade when losing tracking + keyboardRenderer_.Opacity = keyboardOpacity_; + keyboardRenderer_.Render(out.Surfaces); + } + + /// Render beams + beamRenderer_.Render(in, out); + + /// Rendering a blend of hand mask and solid hands + /// keep hands semi-transparent for regular mask, solid for passthrough window + const float HandMaskBlendFactor = usePassthroughWindow_ ? 1.0f : 0.4f; + const float HandFadeIntensity = usePassthroughWindow_ ? 1.0f : 0.75f; + const float HandBlendFactor = 1.0f; + + renderPassthroughHands_ = false; + if (handL_->AreLocationsActive() && handL_->IsPositionValid()) { + /// Determine hand / mask blending + /// based on proximity to keyboard heuristic + const float MaskBlend = ComputeHandBlendFactor( + FromXrPosef(handL_->Joints()[XR_HAND_JOINT_MIDDLE_TIP_EXT].pose).Translation, + pose_.Translation); + + /// only render hand mask when we are using a planar layer + if (renderKeyboard_ && usePassthroughWindow_) { + /// ... and render the cutout mask + handMaskRendererL_.LayerBlend = (MaskBlend)*HandMaskBlendFactor; + handMaskRendererL_.FadeIntensity = HandFadeIntensity; + handMaskRendererL_.Render(out.Surfaces); + } + + /// Render solid Hands + handRendererL_.Confidence = (1.0f - MaskBlend) * HandBlendFactor; + handRendererL_.Render(out.Surfaces); + + /// Render passthrough hands if either hand is within visible range + if (MaskBlend > 0.0f) { + renderPassthroughHands_ = true; + } + } else if (in.LeftRemoteTracked) { + // Only render controller when hands are not rendered + // Note: Hand tracking can drive controller positions as well. + controllerRenderL_.Render(out.Surfaces); + } + + if (handR_->AreLocationsActive() && handR_->IsPositionValid()) { + /// Determine hand / mask blending + /// based on proximity to keyboard heuristic + const float MaskBlend = ComputeHandBlendFactor( + FromXrPosef(handR_->Joints()[XR_HAND_JOINT_MIDDLE_TIP_EXT].pose).Translation, + pose_.Translation); + + /// only render hand mask when we are using a planar layer + if (renderKeyboard_ && usePassthroughWindow_) { + /// ... and render the cutout mask + handMaskRendererR_.LayerBlend = (MaskBlend)*HandMaskBlendFactor; + handMaskRendererR_.FadeIntensity = HandFadeIntensity; + handMaskRendererR_.Render(out.Surfaces); + } + + /// Render solid Hands + handRendererR_.Confidence = (1.0f - MaskBlend) * HandBlendFactor; + handRendererR_.Render(out.Surfaces); + + /// Render passthrough hands if either hand is within visible range + if (MaskBlend > 0.0f) { + renderPassthroughHands_ = true; + } + } else if (in.RightRemoteTracked) { + // Only render controller when hands are not rendered + // Note: Hand tracking can drive controller positions as well. + controllerRenderR_.Render(out.Surfaces); + } + } + + float ComputeHandBlendFactor(const OVR::Vector3f& hand, const OVR::Vector3f& keyboard) { + const OVR::Vector3f v = hand - keyboard; + const float l = v.Length(); + + /// simple blend in ranger + const float kHiThreshold = 0.20f; + const float kLoThreshold = 0.10f; + if (l > kHiThreshold) { + /// too far + return 0.0f; + } else if (l > kLoThreshold) { + /// blend + return (kHiThreshold - l) / (kHiThreshold - kLoThreshold); + } else { + /// near + return 1.0f; + } + } + + bool ComputeKeyboardOpacity(double predictedDisplayTimeInSeconds) { + /// How soon before the keyboard begins to fade due to not being tracked. + static const double kStaleDelaySeconds = 1.0; + static const double kStaleFadeDurationSeconds = 0.5; + + bool isTrackingFullyStale = false; + if (!keyboard_->IsLocationActive()) { + // The keyboard is not tracked this frame. + double elapsedTime = predictedDisplayTimeInSeconds - lastTrackedTimeSeconds_; + if (elapsedTime < kStaleDelaySeconds) { + keyboardOpacity_ = 1.0f; + } else { + const double elapsedPostDelayTime = elapsedTime - kStaleDelaySeconds; + isTrackingFullyStale = elapsedPostDelayTime > kStaleFadeDurationSeconds; + keyboardOpacity_ = 1.0f - + std::clamp(float(elapsedPostDelayTime / kStaleFadeDurationSeconds), 0.0f, 1.0f); + } + } else { + // keyboard is tracked + keyboardOpacity_ = 1.0f; + lastTrackedTimeSeconds_ = predictedDisplayTimeInSeconds; + } + return isTrackingFullyStale; + } + + void AddPassthroughLayer(xrCompositorLayerUnion* layers, int& layerCount) { + /// Add the blending layer as a chain to the passthrough one + /// NOTE: this needs to be static or otherwise stay around when this call executes + /// or the actual call to xrEndFrame will get garbage memory + static XrCompositionLayerAlphaBlendFB alphaBlend{XR_TYPE_COMPOSITION_LAYER_ALPHA_BLEND_FB}; + alphaBlend.next = nullptr; + alphaBlend.srcFactorColor = XR_BLEND_FACTOR_ONE_FB; + alphaBlend.dstFactorColor = XR_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA_FB; + alphaBlend.srcFactorAlpha = XR_BLEND_FACTOR_ONE_FB; + alphaBlend.dstFactorAlpha = XR_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA_FB; + + /// Add the passthrough layer + XrCompositionLayerPassthroughFB passthroughLayer{XR_TYPE_COMPOSITION_LAYER_PASSTHROUGH_FB}; + passthroughLayer.next = &alphaBlend; + passthroughLayer.layerHandle = GetLayer(); + if (passthroughLayer.layerHandle != XR_NULL_HANDLE) { + layers[layerCount++].Passthrough = passthroughLayer; + /// ALOG("adding XrCompositionLayerPassthroughFB SUCCESS."); + } else { + ALOG("adding XrCompositionLayerPassthroughFB FAILED."); + } + } + + virtual void PreProjectionAddLayer(xrCompositorLayerUnion* layers, int& layerCount) override { + if (!renderKeyboard_) { + return; + } + /// add passthrough layer first + if (renderRealPassthroughLayerUnder_) { + AddPassthroughLayer(layers, layerCount); + } + } + + virtual void PostProjectionAddLayer(xrCompositorLayerUnion* layers, int& layerCount) override { + if (!renderKeyboard_ || !renderPassthroughHands_) { + return; + } + /// add passthrough layer + if (renderRealPassthroughLayerOver_) { + AddPassthroughLayer(layers, layerCount); + } + } + + const XrPassthroughLayerFB& GetLayer() const { + return usePassthroughWindow_ ? passthrough_->GetProjectedLayer() + : passthrough_->GetHandsLayer(); + } + + void SetTrackingRequired( + OVRFW::VRMenuObject* bigText, + OVRFW::VRMenuObject* connectionRequiredButton, + OVRFW::VRMenuObject* toggleButton, + bool state) { + if (state) { + keyboard_->SetTrackingRequired(true); + toggleButton->SetSurfaceColor(0, OVR::Vector4f(0.25f, 0.25f, 1.0f, 1.0f)); + toggleButton->SetText("Show Untracked Keyboard"); + } else { + // set connection required to false since if we aren't tracking we are likely in debug + // mode and connection isn't necessary + SetSwitchConnectionRequired(bigText, connectionRequiredButton, false); + keyboard_->SetTrackingRequired(false); + toggleButton->SetSurfaceColor(0, OVR::Vector4f(1.0f, 0.25f, 0.25f, 1.0f)); + toggleButton->SetText("Do Not Show Untracked Keyboard"); + } + } + + void SetSwitchConnectionRequired( + OVRFW::VRMenuObject* bigText, + OVRFW::VRMenuObject* toggleButton, + bool state) { + if (state) { + bigText->SetText("Open XR Keyboard: Connection Required"); + keyboard_->SetRequireKeyboardConnectedToTrack(true); + toggleButton->SetSurfaceColor(0, OVR::Vector4f(0.25f, 0.25f, 1.0f, 1.0f)); + } else { + bigText->SetText("Open XR Keyboard: no Connection Required"); + keyboard_->SetRequireKeyboardConnectedToTrack(false); + toggleButton->SetSurfaceColor(0, OVR::Vector4f(1.0f, 0.25f, 0.25f, 1.0f)); + } + } + + void SetSwitchUseRemoteKeyboard( + OVRFW::VRMenuObject* bigText, + OVRFW::VRMenuObject* connectionRequiredButton, + OVRFW::VRMenuObject* toggleButton, + bool state) { + if (state) { + // Set the connction required to false because at the current time, remote will always + // be unconnected. + SetSwitchConnectionRequired(bigText, connectionRequiredButton, false); + keyboard_->SetUseRemoteKeyboard(true); + toggleButton->SetSurfaceColor(0, OVR::Vector4f(1.0f, 0.25f, 0.25f, 1.0f)); + } else { + keyboard_->SetUseRemoteKeyboard(false); + toggleButton->SetSurfaceColor(0, OVR::Vector4f(0.25f, 0.25f, 1.0f, 1.0f)); + } + } + + public: + private: + OVRFW::ControllerRenderer controllerRenderL_; + OVRFW::ControllerRenderer controllerRenderR_; + OVRFW::TinyUI ui_; + OVRFW::SimpleBeamRenderer beamRenderer_; + std::vector beams_; + + /// hands - xr interface + std::unique_ptr handL_; + std::unique_ptr handR_; + /// hands - rendering + OVRFW::HandRenderer handRendererL_; + OVRFW::HandRenderer handRendererR_; + OVRFW::HandMaskRenderer handMaskRendererL_; + OVRFW::HandMaskRenderer handMaskRendererR_; + + /// keyboard - xr interface + std::unique_ptr keyboard_; + /// keyboard - rendering + OVRFW::ovrAxisRenderer axisRenderer_; + OVRFW::KeyboardRenderer keyboardRenderer_; + /// keyboard - hand presence rendering + OVR::Posef pose_; + OVR::Vector3f dimensions_; + ovrFramebuffer presenceBuffer_; + + /// passthrough - xr interface + std::unique_ptr passthrough_; + + /// render model - xr interface + std::unique_ptr renderModel_; + + /// control + bool renderRealPassthroughLayerUnder_ = true; + bool renderRealPassthroughLayerOver_ = false; + + /// state + bool renderKeyboard_ = false; + bool renderPassthroughHands_ = false; + bool usePassthroughWindow_ = false; + + /// geometry renderer for passthrough cutout + OVRFW::GeometryRenderer gr_; + + // system keyboard info text; + OVRFW::VRMenuObject* bigText_ = nullptr; + OVRFW::VRMenuObject* systemText_ = nullptr; + OVRFW::VRMenuObject* connectionRequiredButton_ = nullptr; + OVRFW::VRMenuObject* useRemoteKeyboardButton_ = nullptr; + OVRFW::VRMenuObject* trackingRequiredButton_ = nullptr; + + /// fade heuristic to match VrShell + float keyboardOpacity_ = 0.0f; + double lastTrackedTimeSeconds_ = 0.0; +}; + +ENTRY_POINT(XrKeyboardApp) diff --git a/Samples/XrSamples/XrKeyboard/Src/xr_hand_helper.h b/Samples/XrSamples/XrKeyboard/Src/xr_hand_helper.h new file mode 100755 index 0000000..a948224 --- /dev/null +++ b/Samples/XrSamples/XrKeyboard/Src/xr_hand_helper.h @@ -0,0 +1,198 @@ +/************************************************************************************************ +Filename : xr_hand_helper.h +Content : Helper Inteface for openxr hand extensions +Created : April 2021 +Authors : Federico Schliemann +Language : C++ +Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved. +************************************************************************************************/ +#pragma once + +#include "xr_helper.h" + +class XrHandHelper : public XrHelper { + public: + XrHandHelper(XrInstance instance, bool isLeft) : XrHelper(instance), isLeft_(isLeft) { + /// Hook up extensions for hand tracking + oxr(xrGetInstanceProcAddr( + instance, "xrCreateHandTrackerEXT", (PFN_xrVoidFunction*)(&xrCreateHandTrackerEXT_))); + oxr(xrGetInstanceProcAddr( + instance, "xrDestroyHandTrackerEXT", (PFN_xrVoidFunction*)(&xrDestroyHandTrackerEXT_))); + oxr(xrGetInstanceProcAddr( + instance, "xrLocateHandJointsEXT", (PFN_xrVoidFunction*)(&xrLocateHandJointsEXT_))); + /// Hook up extensions for hand rendering + oxr(xrGetInstanceProcAddr( + instance, "xrGetHandMeshFB", (PFN_xrVoidFunction*)(&xrGetHandMeshFB_))); + } + + ~XrHandHelper() override { + xrCreateHandTrackerEXT_ = nullptr; + xrDestroyHandTrackerEXT_ = nullptr; + xrLocateHandJointsEXT_ = nullptr; + xrGetHandMeshFB_ = nullptr; + }; + + /// XrHelper Interface + virtual bool SessionInit(XrSession session) override { + if (xrCreateHandTrackerEXT_) { + XrHandTrackerCreateInfoEXT createInfo{XR_TYPE_HAND_TRACKER_CREATE_INFO_EXT}; + createInfo.handJointSet = XR_HAND_JOINT_SET_DEFAULT_EXT; + createInfo.hand = isLeft_ ? XR_HAND_LEFT_EXT : XR_HAND_RIGHT_EXT; + if (!oxr(xrCreateHandTrackerEXT_(session, &createInfo, &handTracker_))) { + return false; + } + + if (xrGetHandMeshFB_) { + mesh_.type = XR_TYPE_HAND_TRACKING_MESH_FB; + mesh_.next = nullptr; + mesh_.jointCapacityInput = 0; + mesh_.jointCountOutput = 0; + mesh_.vertexCapacityInput = 0; + mesh_.vertexCountOutput = 0; + mesh_.indexCapacityInput = 0; + mesh_.indexCountOutput = 0; + if (!oxr(xrGetHandMeshFB_(handTracker_, &mesh_))) { + return false; + } + /// update sizes + mesh_.jointCapacityInput = mesh_.jointCountOutput; + mesh_.vertexCapacityInput = mesh_.vertexCountOutput; + mesh_.indexCapacityInput = mesh_.indexCountOutput; + vertexPositions_.resize(mesh_.vertexCapacityInput); + vertexNormals_.resize(mesh_.vertexCapacityInput); + vertexUVs_.resize(mesh_.vertexCapacityInput); + vertexBlendIndices_.resize(mesh_.vertexCapacityInput); + vertexBlendWeights_.resize(mesh_.vertexCapacityInput); + indices_.resize(mesh_.indexCapacityInput); + + /// skeleton + mesh_.jointBindPoses = jointBindPoses_; + mesh_.jointParents = jointParents_; + mesh_.jointRadii = jointRadii_; + /// mesh + mesh_.vertexPositions = vertexPositions_.data(); + mesh_.vertexNormals = vertexNormals_.data(); + mesh_.vertexUVs = vertexUVs_.data(); + mesh_.vertexBlendIndices = vertexBlendIndices_.data(); + mesh_.vertexBlendWeights = vertexBlendWeights_.data(); + mesh_.indices = indices_.data(); + /// get mesh + if (!oxr(xrGetHandMeshFB_(handTracker_, &mesh_))) { + return false; + } + } + return true; + } + return false; + } + + virtual bool SessionEnd() override { + if (xrDestroyHandTrackerEXT_) { + return oxr(xrDestroyHandTrackerEXT_(handTracker_)); + } + return false; + } + + virtual bool Update(XrSpace currentSpace, XrTime predictedDisplayTime) override { + if (xrLocateHandJointsEXT_) { + /// aim + aimState_.next = nullptr; + /// scale + scale_.next = &aimState_; + scale_.sensorOutput = 1.0f; + scale_.currentOutput = 1.0f; + scale_.overrideHandScale = XR_TRUE; + scale_.overrideValueInput = 1.00f; + /// locations + locations_.next = &scale_; + locations_.jointCount = XR_HAND_JOINT_COUNT_EXT; + locations_.jointLocations = jointLocations_; + XrHandJointsLocateInfoEXT locateInfo{XR_TYPE_HAND_JOINTS_LOCATE_INFO_EXT}; + locateInfo.baseSpace = currentSpace; + locateInfo.time = predictedDisplayTime; + return oxr(xrLocateHandJointsEXT_(handTracker_, &locateInfo, &locations_)); + } + return false; + } + + static std::vector RequiredExtensionNames() { + return { + XR_EXT_HAND_TRACKING_EXTENSION_NAME, + XR_FB_HAND_TRACKING_MESH_EXTENSION_NAME, + XR_FB_HAND_TRACKING_AIM_EXTENSION_NAME}; + } + + public: + /// Own interface + bool IsLeft() const { + return isLeft_; + } + const XrHandTrackingMeshFB& Mesh() const { + return mesh_; + } + const XrHandTrackingScaleFB& Scale() const { + return scale_; + } + const XrPosef* JointBindPoses() const { + return jointBindPoses_; + } + const XrHandJointEXT* JointParents() const { + return jointParents_; + } + const XrHandJointLocationEXT* Joints() const { + return jointLocations_; + } + float RenderScale() const { + return scale_.sensorOutput; + } + bool IsTracking() const { + return (handTracker_ != XR_NULL_HANDLE); + } + bool AreLocationsActive() const { + return IsTracking() && (locations_.isActive); + } + bool IsPositionValid() const { + return jointLocations_[XR_HAND_JOINT_PALM_EXT].locationFlags & + XR_SPACE_LOCATION_POSITION_VALID_BIT; + } + const XrPosef& AimPose() const { + return aimState_.aimPose; + } + bool IndexPinching() const { + return (aimState_.status & XR_HAND_TRACKING_AIM_INDEX_PINCHING_BIT_FB) != 0; + } + const XrHandTrackingAimStateFB& AimState() const { + return aimState_; + } + + private: + bool isLeft_; + /// Hands - extension functions + PFN_xrCreateHandTrackerEXT xrCreateHandTrackerEXT_ = nullptr; + PFN_xrDestroyHandTrackerEXT xrDestroyHandTrackerEXT_ = nullptr; + PFN_xrLocateHandJointsEXT xrLocateHandJointsEXT_ = nullptr; + /// Hands - FB mesh rendering extensions + PFN_xrGetHandMeshFB xrGetHandMeshFB_ = nullptr; + /// Hands - tracker handles + XrHandTrackerEXT handTracker_ = XR_NULL_HANDLE; + /// Hands - data buffers + XrHandJointLocationEXT jointLocations_[XR_HAND_JOINT_COUNT_EXT]; + XrPosef jointBindPoses_[XR_HAND_JOINT_COUNT_EXT]; + XrHandJointEXT jointParents_[XR_HAND_JOINT_COUNT_EXT]; + float jointRadii_[XR_HAND_JOINT_COUNT_EXT]; + /// mesh storage + XrHandTrackingMeshFB mesh_{XR_TYPE_HAND_TRACKING_MESH_FB}; + std::vector vertexPositions_; + std::vector vertexNormals_; + std::vector vertexUVs_; + std::vector vertexBlendIndices_; + std::vector vertexBlendWeights_; + std::vector indices_; + /// extension nodes + /// scale + XrHandTrackingScaleFB scale_{XR_TYPE_HAND_TRACKING_SCALE_FB}; + /// aim + XrHandTrackingAimStateFB aimState_{XR_TYPE_HAND_TRACKING_AIM_STATE_FB}; + /// location + XrHandJointLocationsEXT locations_{XR_TYPE_HAND_JOINT_LOCATIONS_EXT}; +}; diff --git a/Samples/XrSamples/XrKeyboard/Src/xr_helper.h b/Samples/XrSamples/XrKeyboard/Src/xr_helper.h new file mode 100755 index 0000000..58556ac --- /dev/null +++ b/Samples/XrSamples/XrKeyboard/Src/xr_helper.h @@ -0,0 +1,67 @@ +/************************************************************************************************ +Filename : xr_helper.h +Content : Base interface to wrap openxr extension functionality +Created : April 2021 +Authors : Federico Schliemann +Language : C++ +Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved. +************************************************************************************************/ +#pragma once + +#if defined(ANDROID) +#define XR_USE_GRAPHICS_API_OPENGL_ES 1 +#define XR_USE_PLATFORM_ANDROID 1 +#else +#include "unknwn.h" +#define XR_USE_GRAPHICS_API_OPENGL 1 +#define XR_USE_PLATFORM_WIN32 1 +#endif + +#include +#include + +#include +#include +#include +#include +#include +#include + +#if !defined(XRLOG) +#define XRLOG(...) +#endif + +class XrHelper { + public: + XrHelper(XrInstance instance) : instance_(instance), lastError_(XR_SUCCESS) {} + virtual ~XrHelper() = default; + virtual bool SessionInit(XrSession session) = 0; + virtual bool SessionEnd() = 0; + virtual bool Update(XrSpace currentSpace, XrTime predictedDisplayTime) = 0; + XrResult GetLastError() const { + return lastError_; + } + + protected: + bool _oxr(XrResult xr, const char* func) { + if (XR_FAILED(xr)) { + char errorBuffer[XR_MAX_RESULT_STRING_SIZE]; + xrResultToString(instance_, xr, errorBuffer); + XRLOG("XR FAIL: `%s` -> `%s` 0x%08X", func, errorBuffer, (uint64_t)xr); + lastError_ = xr; + return false; + } + return true; + } + XrInstance GetInstance() const { + return instance_; + } + + private: + XrInstance instance_; + XrResult lastError_; +}; + +#if !defined(oxr) +#define oxr(func) _oxr(func, #func) +#endif diff --git a/Samples/XrSamples/XrKeyboard/Src/xr_keyboard_helper.h b/Samples/XrSamples/XrKeyboard/Src/xr_keyboard_helper.h new file mode 100755 index 0000000..2275e61 --- /dev/null +++ b/Samples/XrSamples/XrKeyboard/Src/xr_keyboard_helper.h @@ -0,0 +1,308 @@ +/************************************************************************************************ +Filename : xr_keyboard_helper.h +Content : Helper Inteface for openxr keyboard extensions +Created : April 2021 +Authors : Federico Schliemann +Language : C++ +Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved. +************************************************************************************************/ +#pragma once + +#include "xr_helper.h" + +class XrKeyboardHelper : public XrHelper { + public: + XrKeyboardHelper(XrInstance instance) : XrHelper(instance) { + instance_ = instance; + /// Hook up extensions for keyboard tracking + oxr(xrGetInstanceProcAddr( + instance, + "xrQuerySystemTrackedKeyboardFB", + (PFN_xrVoidFunction*)(&xrQuerySystemTrackedKeyboardFB_))); + oxr(xrGetInstanceProcAddr( + instance, "xrCreateKeyboardSpaceFB", (PFN_xrVoidFunction*)(&xrCreateKeyboardSpaceFB_))); + + fakeLocation_.locationFlags = + XR_SPACE_LOCATION_ORIENTATION_TRACKED_BIT | + XR_SPACE_LOCATION_POSITION_TRACKED_BIT | + XR_SPACE_LOCATION_ORIENTATION_VALID_BIT | + XR_SPACE_LOCATION_POSITION_VALID_BIT; + } + + ~XrKeyboardHelper() override { + xrQuerySystemTrackedKeyboardFB_ = nullptr; + xrCreateKeyboardSpaceFB_ = nullptr; + }; + + /// XrHelper Interface + virtual bool SessionInit(XrSession session) override { + session_ = session; + return true; + } + + virtual bool SessionEnd() override { + session_ = XR_NULL_HANDLE; + return StopTracking(); + } + + virtual bool Update(XrSpace currentSpace, XrTime predictedDisplayTime) override { + if (xrQuerySystemTrackedKeyboardFB_) { + // current query + { + XrKeyboardTrackingQueryFB queryInfo{XR_TYPE_KEYBOARD_TRACKING_QUERY_FB}; + if (useRemoteKeyboard_) { + queryInfo.flags = XR_KEYBOARD_TRACKING_QUERY_REMOTE_BIT_FB; + } else { + queryInfo.flags = XR_KEYBOARD_TRACKING_QUERY_LOCAL_BIT_FB; + } + + XrKeyboardTrackingDescriptionFB desc; + if (oxr(xrQuerySystemTrackedKeyboardFB_(session_, &queryInfo, &desc))) { + if ((desc.flags & XR_KEYBOARD_TRACKING_EXISTS_BIT_FB) != 0) { + // found keyboard + if (!systemKeyboardExists_ || + systemKeyboardDesc_.trackedKeyboardId != desc.trackedKeyboardId || + systemKeyboardDesc_.flags != desc.flags) { + ALOG( + "Found new system keyboard '%d' '%s'", + desc.trackedKeyboardId, + desc.name); + systemKeyboardExists_ = true; + systemKeyboardDesc_ = desc; + systemKeyboardConnected_ = + systemKeyboardDesc_.flags & XR_KEYBOARD_TRACKING_CONNECTED_BIT_FB; + + bool correctKeyboardType = false; + if (useRemoteKeyboard_) { + correctKeyboardType = + systemKeyboardDesc_.flags & XR_KEYBOARD_TRACKING_REMOTE_BIT_FB; + } else { + correctKeyboardType = + systemKeyboardDesc_.flags & XR_KEYBOARD_TRACKING_LOCAL_BIT_FB; + } + + if (correctKeyboardType) { + trackingSystemKeyboard_ = false; + if (trackSystemKeyboard_) { + if (systemKeyboardConnected_ || + !requireKeyboardConnectedToTrack_) { + if (StartTrackingSystemKeyboard()) { + trackingSystemKeyboard_ = true; + } + } + } + if (!trackingSystemKeyboard_) { + StopTracking(); + } + } else { + ALOG( + "Found new system keyboard '%d' '%s', but not tracking because it doesn't match remote vs local types", + desc.trackedKeyboardId, + desc.name); + } + + systemKeyboardStateChanged_ = true; + } + } else { + // no keyboard + if (systemKeyboardExists_) { + systemKeyboardExists_ = false; + if (trackSystemKeyboard_) { + StopTracking(); + trackingSystemKeyboard_ = false; + } + systemKeyboardStateChanged_ = true; + } + } + } + } + } + + if (keyboardSpace_ != XR_NULL_HANDLE) { + location_.next = nullptr; + return oxr( + xrLocateSpace(keyboardSpace_, currentSpace, predictedDisplayTime, &location_)); + } + return true; + } + + static std::vector RequiredExtensionNames() { + return {XR_FB_KEYBOARD_TRACKING_EXTENSION_NAME}; + } + + public: + /// Own interface + const XrSpaceLocation& Location() const { + if (IsLocationActive() || trackingRequired_) { + return location_; + } + return fakeLocation_; + } + bool IsTracking() const { + return (keyboardSpace_ != XR_NULL_HANDLE); + } + bool IsLocationActive() const { + const XrSpaceLocationFlags isTracked = + XR_SPACE_LOCATION_ORIENTATION_TRACKED_BIT | XR_SPACE_LOCATION_POSITION_TRACKED_BIT; + const XrSpaceLocationFlags isValid = + XR_SPACE_LOCATION_ORIENTATION_VALID_BIT | XR_SPACE_LOCATION_POSITION_VALID_BIT; + XrSpaceLocationFlags flags = isTracked | isValid; + return (IsTracking() && (location_.locationFlags & flags)) || !trackingRequired_; + } + + void ResetSystemKeyboardTracking() { + trackingSystemKeyboard_ = false; + StopTracking(); + if (trackSystemKeyboard_ && systemKeyboardExists_ && + (systemKeyboardConnected_ || !requireKeyboardConnectedToTrack_)) { + if (StartTrackingSystemKeyboard()) { + trackingSystemKeyboard_ = true; + } + } + systemKeyboardStateChanged_ = true; + } + + bool StartTrackingSystemKeyboard() { + StopTracking(); + + if (xrCreateKeyboardSpaceFB_ && systemKeyboardExists_) { + XrKeyboardSpaceCreateInfoFB createInfo{XR_TYPE_KEYBOARD_SPACE_CREATE_INFO_FB}; + createInfo.trackedKeyboardId = systemKeyboardDesc_.trackedKeyboardId; + if (XR_SUCCEEDED( + oxr(xrCreateKeyboardSpaceFB_(session_, &createInfo, &keyboardSpace_)))) { + size_ = systemKeyboardDesc_.size; + return true; + } + } + + return false; + } + + bool StopTracking() { + bool result = false; + if (keyboardSpace_ != XR_NULL_HANDLE) { + result = oxr(xrDestroySpace(keyboardSpace_)); + if (result) { + keyboardSpace_ = XR_NULL_HANDLE; + } else { + ALOG("Failed to destroy keyboardSpace_ %p", keyboardSpace_); + } + } + return result; + } + + XrVector3f Size() { + return size_; + } + + bool TracksSystemKeyboard() { + return trackSystemKeyboard_; + } + + bool SetTracksSystemKeyboard(bool track) { + if (track != trackSystemKeyboard_) { + trackSystemKeyboard_ = track; + ResetSystemKeyboardTracking(); + return true; + } + return false; + } + + bool RequireKeyboardConnectedToTrack() { + return requireKeyboardConnectedToTrack_; + } + + bool SetRequireKeyboardConnectedToTrack(bool require) { + if (require != requireKeyboardConnectedToTrack_) { + requireKeyboardConnectedToTrack_ = require; + ResetSystemKeyboardTracking(); + return true; + } + return false; + } + + bool UseRemoteKeyboard() { + return useRemoteKeyboard_; + } + + bool SetUseRemoteKeyboard(bool state) { + if (state != useRemoteKeyboard_) { + // no keyboard + if (systemKeyboardExists_) { + systemKeyboardExists_ = false; + if (trackSystemKeyboard_) { + StopTracking(); + trackingSystemKeyboard_ = false; + } + systemKeyboardStateChanged_ = true; + } + + useRemoteKeyboard_ = state; + return true; + } + return false; + } + + bool TrackingRequired() { + return trackingRequired_; + } + + bool SetTrackingRequired(bool state) { + if (state != trackingRequired_) { + trackingRequired_ = state; + ResetSystemKeyboardTracking(); + return true; + } + return false; + } + + bool TrackingSystemKeyboard() { + return trackingSystemKeyboard_; + } + + bool SystemKeyboardExists() { + return systemKeyboardExists_; + } + + const XrKeyboardTrackingDescriptionFB& SystemKeyboardDesc() { + return systemKeyboardDesc_; + } + + bool SystemKeyboardConnected() { + return systemKeyboardConnected_; + } + + bool GetAndClearSystemKeyboardStateChanged() { + if (systemKeyboardStateChanged_) { + systemKeyboardStateChanged_ = false; + return true; + } + return false; + } + + private: + /// Session cache + XrSession session_ = XR_NULL_HANDLE; + /// Instance cache + XrInstance instance_ = XR_NULL_HANDLE; + /// Keyboard - extension functions + PFN_xrQuerySystemTrackedKeyboardFB xrQuerySystemTrackedKeyboardFB_ = nullptr; + PFN_xrCreateKeyboardSpaceFB xrCreateKeyboardSpaceFB_ = nullptr; + /// Keyboard - space + XrSpace keyboardSpace_ = XR_NULL_HANDLE; + /// Keyboard - data + XrSpaceLocation location_{XR_TYPE_SPACE_LOCATION}; + XrSpaceLocation fakeLocation_{XR_TYPE_SPACE_LOCATION}; + + XrVector3f size_; + // mapping the system level active tracked keyboard + bool trackSystemKeyboard_ = true; + bool trackingSystemKeyboard_ = false; + bool systemKeyboardExists_ = false; + bool systemKeyboardConnected_ = false; + bool systemKeyboardStateChanged_ = false; + bool requireKeyboardConnectedToTrack_ = true; + bool useRemoteKeyboard_ = false; + bool trackingRequired_ = false; + XrKeyboardTrackingDescriptionFB systemKeyboardDesc_; +}; diff --git a/Samples/XrSamples/XrKeyboard/Src/xr_passthrough_helper.h b/Samples/XrSamples/XrKeyboard/Src/xr_passthrough_helper.h new file mode 100755 index 0000000..20fd0b4 --- /dev/null +++ b/Samples/XrSamples/XrKeyboard/Src/xr_passthrough_helper.h @@ -0,0 +1,373 @@ +/************************************************************************************************ +Filename : xr_passthrough_helper.h +Content : Helper Inteface for openxr passthrough extensions +Created : May 2021 +Authors : Federico Schliemann +Language : C++ +Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved. +************************************************************************************************/ +#pragma once + +#include "xr_helper.h" + +class XrPassthroughHelper : public XrHelper { + public: + XrPassthroughHelper(XrInstance instance, XrSpace defaultSpace) : XrHelper(instance) { + /// cache space + defaultSpace_ = defaultSpace; + + /// passthrough + oxr(xrGetInstanceProcAddr( + instance, "xrCreatePassthroughFB", (PFN_xrVoidFunction*)(&xrCreatePassthroughFB_))); + oxr(xrGetInstanceProcAddr( + instance, "xrDestroyPassthroughFB", (PFN_xrVoidFunction*)(&xrDestroyPassthroughFB_))); + oxr(xrGetInstanceProcAddr( + instance, "xrPassthroughStartFB", (PFN_xrVoidFunction*)(&xrPassthroughStartFB_))); + oxr(xrGetInstanceProcAddr( + instance, "xrPassthroughPauseFB", (PFN_xrVoidFunction*)(&xrPassthroughPauseFB_))); + /// layer + oxr(xrGetInstanceProcAddr( + instance, + "xrCreatePassthroughLayerFB", + (PFN_xrVoidFunction*)(&xrCreatePassthroughLayerFB_))); + oxr(xrGetInstanceProcAddr( + instance, + "xrDestroyPassthroughLayerFB", + (PFN_xrVoidFunction*)(&xrDestroyPassthroughLayerFB_))); + oxr(xrGetInstanceProcAddr( + instance, + "xrPassthroughLayerPauseFB", + (PFN_xrVoidFunction*)(&xrPassthroughLayerPauseFB_))); + oxr(xrGetInstanceProcAddr( + instance, + "xrPassthroughLayerResumeFB", + (PFN_xrVoidFunction*)(&xrPassthroughLayerResumeFB_))); + /// style + oxr(xrGetInstanceProcAddr( + instance, + "xrPassthroughLayerSetStyleFB", + (PFN_xrVoidFunction*)(&xrPassthroughLayerSetStyleFB_))); + /// geometry + oxr(xrGetInstanceProcAddr( + instance, + "xrCreateGeometryInstanceFB", + (PFN_xrVoidFunction*)(&xrCreateGeometryInstanceFB_))); + oxr(xrGetInstanceProcAddr( + instance, + "xrDestroyGeometryInstanceFB", + (PFN_xrVoidFunction*)(&xrDestroyGeometryInstanceFB_))); + oxr(xrGetInstanceProcAddr( + instance, + "xrGeometryInstanceSetTransformFB", + (PFN_xrVoidFunction*)(&xrGeometryInstanceSetTransformFB_))); + /// Passthrough - mesh extension functions + /// mesh + oxr(xrGetInstanceProcAddr( + instance, "xrCreateTriangleMeshFB", (PFN_xrVoidFunction*)(&xrCreateTriangleMeshFB_))); + oxr(xrGetInstanceProcAddr( + instance, "xrDestroyTriangleMeshFB", (PFN_xrVoidFunction*)(&xrDestroyTriangleMeshFB_))); + /// buffers + oxr(xrGetInstanceProcAddr( + instance, + "xrTriangleMeshGetVertexBufferFB", + (PFN_xrVoidFunction*)(&xrTriangleMeshGetVertexBufferFB_))); + oxr(xrGetInstanceProcAddr( + instance, + "xrTriangleMeshGetIndexBufferFB", + (PFN_xrVoidFunction*)(&xrTriangleMeshGetIndexBufferFB_))); + /// update + oxr(xrGetInstanceProcAddr( + instance, + "xrTriangleMeshBeginUpdateFB", + (PFN_xrVoidFunction*)(&xrTriangleMeshBeginUpdateFB_))); + oxr(xrGetInstanceProcAddr( + instance, + "xrTriangleMeshEndUpdateFB", + (PFN_xrVoidFunction*)(&xrTriangleMeshEndUpdateFB_))); + oxr(xrGetInstanceProcAddr( + instance, + "xrTriangleMeshBeginVertexBufferUpdateFB", + (PFN_xrVoidFunction*)(&xrTriangleMeshBeginVertexBufferUpdateFB_))); + oxr(xrGetInstanceProcAddr( + instance, + "xrTriangleMeshEndVertexBufferUpdateFB", + (PFN_xrVoidFunction*)(&xrTriangleMeshEndVertexBufferUpdateFB_))); + } + + ~XrPassthroughHelper() override { + /// Passthrough - extension functions + /// feature + xrCreatePassthroughFB_ = nullptr; + xrDestroyPassthroughFB_ = nullptr; + xrPassthroughStartFB_ = nullptr; + xrPassthroughPauseFB_ = nullptr; + /// layer + xrCreatePassthroughLayerFB_ = nullptr; + xrDestroyPassthroughLayerFB_ = nullptr; + xrPassthroughLayerPauseFB_ = nullptr; + xrPassthroughLayerResumeFB_ = nullptr; + /// style + xrPassthroughLayerSetStyleFB_ = nullptr; + /// geometry + xrCreateGeometryInstanceFB_ = nullptr; + xrDestroyGeometryInstanceFB_ = nullptr; + xrGeometryInstanceSetTransformFB_ = nullptr; + /// Passthrough - mesh extension functions + /// mesh + xrCreateTriangleMeshFB_ = nullptr; + xrDestroyTriangleMeshFB_ = nullptr; + xrTriangleMeshGetVertexBufferFB_ = nullptr; + xrTriangleMeshGetIndexBufferFB_ = nullptr; + xrTriangleMeshBeginUpdateFB_ = nullptr; + xrTriangleMeshEndUpdateFB_ = nullptr; + xrTriangleMeshBeginVertexBufferUpdateFB_ = nullptr; + xrTriangleMeshEndVertexBufferUpdateFB_ = nullptr; + }; + + /// XrHelper Interface + virtual bool SessionInit(XrSession session) override { + session_ = session; + if (!xrCreatePassthroughFB_) + return false; + if (!xrCreatePassthroughLayerFB_) + return false; + if (!xrCreateTriangleMeshFB_) + return false; + if (!xrCreateGeometryInstanceFB_) + return false; + if (!xrPassthroughLayerSetStyleFB_) + return false; + + /// feature + XrPassthroughCreateInfoFB passthroughInfo{XR_TYPE_PASSTHROUGH_CREATE_INFO_FB}; + passthroughInfo.next = nullptr; + passthroughInfo.flags = 0u; + if (!oxr(xrCreatePassthroughFB_(session_, &passthroughInfo, &passthrough_))) { + return false; + } + + /// layer + XrPassthroughLayerCreateInfoFB layerInfo{XR_TYPE_PASSTHROUGH_LAYER_CREATE_INFO_FB}; + layerInfo.next = nullptr; + layerInfo.passthrough = passthrough_; + layerInfo.purpose = XR_PASSTHROUGH_LAYER_PURPOSE_PROJECTED_FB; + layerInfo.flags = XR_PASSTHROUGH_IS_RUNNING_AT_CREATION_BIT_FB; + if (!oxr(xrCreatePassthroughLayerFB_(session_, &layerInfo, &layerProjected_))) { + return false; + } + /// also create a hand layer + layerInfo.purpose = XR_PASSTHROUGH_LAYER_PURPOSE_TRACKED_KEYBOARD_HANDS_FB; + if (!oxr(xrCreatePassthroughLayerFB_(session_, &layerInfo, &layerHands_))) { + // If the runtime does not support passthrough keyboard hands, that's ok + // we will keep going with the layer we have. + // return false; + } + + /// style + XrPassthroughStyleFB style{XR_TYPE_PASSTHROUGH_STYLE_FB}; + style.next = nullptr; + style.textureOpacityFactor = 1.0f; + style.edgeColor = {0.0f, 0.0f, 0.0f, 0.0f}; + if (!oxr(xrPassthroughLayerSetStyleFB_(layerProjected_, &style))) { + return false; + } + + const int colorMapSize = XR_PASSTHROUGH_COLOR_MAP_MONO_SIZE_FB; + const float step = 1.0f / (XR_PASSTHROUGH_COLOR_MAP_MONO_SIZE_FB - 1); + + /// default color map + colorMap_.next = nullptr; + for (int i = 0; i < colorMapSize; ++i) { + const float t = step * i; + XrColor4f c{t, 1.0f - t, 0.0f, 1.0f}; + GetColorMap()[i] = c; + } + + /// default mono map + monoMap_.next = nullptr; + for (int i = 0; i < colorMapSize; ++i) { + GetMonoMap()[i] = (uint8_t)i; + } + + /// mesh + // Surface-projected passthrough geometry (horizontal (xz) unit square) + /* clang-format off */ + static const XrVector3f squareVertices[4] = { + {-0.5, 0, -0.5}, + {-0.5, 0, 0.5}, + {0.5, 0, 0.5}, + {0.5, 0, -0.5}}; + uint32_t squareIndices[6] = {0, 1, 2, 2, 3, 0}; + /* clang-format on */ + + XrTriangleMeshCreateInfoFB meshInfo{XR_TYPE_TRIANGLE_MESH_CREATE_INFO_FB}; + + meshInfo.next = nullptr; + meshInfo.flags = 0u; + meshInfo.vertexCount = 4; + meshInfo.vertexBuffer = squareVertices; + meshInfo.triangleCount = 2; + meshInfo.indexBuffer = squareIndices; + meshInfo.windingOrder = XR_WINDING_ORDER_UNKNOWN_FB; + if (!oxr(xrCreateTriangleMeshFB_(session_, &meshInfo, &mesh_))) { + return false; + } + + /// transform - intialize defaults + transform_.type = XR_TYPE_GEOMETRY_INSTANCE_TRANSFORM_FB; + transform_.next = nullptr; + transform_.baseSpace = defaultSpace_; + transform_.pose.orientation = {0.0f, 0.0f, 0.0f, 1.0f}; + transform_.pose.position = {0.0f, 0.0f, 0.0f}; + transform_.scale = {1.0f, 1.0f, 1.0f}; + + /// geometry + XrGeometryInstanceCreateInfoFB geometryInfo{XR_TYPE_GEOMETRY_INSTANCE_CREATE_INFO_FB}; + geometryInfo.next = nullptr; + geometryInfo.layer = layerProjected_; + geometryInfo.mesh = mesh_; + geometryInfo.scale = transform_.scale; + geometryInfo.baseSpace = transform_.baseSpace; + geometryInfo.pose = transform_.pose; + if (!oxr(xrCreateGeometryInstanceFB_(session_, &geometryInfo, &geometryProjected_))) { + return false; + } + + Start(); + + return true; + } + + virtual bool SessionEnd() override { + session_ = XR_NULL_HANDLE; + bool success = true; + if (xrDestroyGeometryInstanceFB_) { + success = success && oxr(xrDestroyGeometryInstanceFB_(geometryProjected_)); + } + if (xrDestroyTriangleMeshFB_) { + success = success && oxr(xrDestroyTriangleMeshFB_(mesh_)); + } + if (xrDestroyPassthroughLayerFB_) { + success = success && oxr(xrDestroyPassthroughLayerFB_(layerProjected_)); + success = success && oxr(xrDestroyPassthroughLayerFB_(layerHands_)); + } + if (xrDestroyPassthroughFB_) { + success = success && oxr(xrDestroyPassthroughFB_(passthrough_)); + } + + return success; + } + + virtual bool Update(XrSpace currentSpace, XrTime predictedDisplayTime) override { + if (xrGeometryInstanceSetTransformFB_) { + transform_.baseSpace = currentSpace; + transform_.time = predictedDisplayTime; + return oxr(xrGeometryInstanceSetTransformFB_(geometryProjected_, &transform_)); + } + return false; + } + + static std::vector RequiredExtensionNames() { + return { + XR_FB_PASSTHROUGH_EXTENSION_NAME, + XR_FB_TRIANGLE_MESH_EXTENSION_NAME, + XR_FB_PASSTHROUGH_KEYBOARD_HANDS_EXTENSION_NAME}; + } + + public: + /// Own interface + void SetTransform(const XrPosef& pose, const XrVector3f& scale) { + transform_.pose = pose; + transform_.scale = scale; + } + const XrPassthroughLayerFB& GetProjectedLayer() const { + return layerProjected_; + } + const XrPassthroughLayerFB& GetHandsLayer() const { + return layerHands_; + } + void SetDefaultSpace(XrSpace defaultSpace) { + defaultSpace_ = defaultSpace; + } + bool Start() { + if (xrPassthroughStartFB_) { + return oxr(xrPassthroughStartFB_(passthrough_)); + } + return false; + } + bool Pause() { + if (xrPassthroughPauseFB_) { + return oxr(xrPassthroughPauseFB_(passthrough_)); + } + return false; + } + XrColor4f* GetColorMap() { + return &(colorMap_.textureColorMap[0]); + } + uint8_t* GetMonoMap() { + return &(monoMap_.textureColorMap[0]); + } + bool SetStyleColor(const XrPassthroughLayerFB& layer) { + colorMap_.next = nullptr; + XrPassthroughStyleFB style{XR_TYPE_PASSTHROUGH_STYLE_FB}; + style.next = &colorMap_; + style.textureOpacityFactor = 1.0f; + style.edgeColor = {0.0f, 0.0f, 0.0f, 0.0f}; + style.textureOpacityFactor = 1.0f; + style.edgeColor = {0.0f, 0.0f, 0.0f, 0.0f}; + return oxr(xrPassthroughLayerSetStyleFB_(layer, &style)); + } + bool SetStyleMono(const XrPassthroughLayerFB& layer) { + monoMap_.next = nullptr; + XrPassthroughStyleFB style{XR_TYPE_PASSTHROUGH_STYLE_FB}; + style.next = &monoMap_; + style.textureOpacityFactor = 1.0f; + style.edgeColor = {0.0f, 0.0f, 0.0f, 0.0f}; + style.textureOpacityFactor = 1.0f; + style.edgeColor = {0.0f, 0.0f, 0.0f, 0.0f}; + return oxr(xrPassthroughLayerSetStyleFB_(layer, &style)); + } + + private: + /// Session cache + XrSpace defaultSpace_ = XR_NULL_HANDLE; + XrSession session_ = XR_NULL_HANDLE; + + /// Passthrough - extension functions + /// feature + PFN_xrCreatePassthroughFB xrCreatePassthroughFB_ = nullptr; + PFN_xrDestroyPassthroughFB xrDestroyPassthroughFB_ = nullptr; + PFN_xrPassthroughStartFB xrPassthroughStartFB_ = nullptr; + PFN_xrPassthroughPauseFB xrPassthroughPauseFB_ = nullptr; + /// layer + PFN_xrCreatePassthroughLayerFB xrCreatePassthroughLayerFB_ = nullptr; + PFN_xrDestroyPassthroughLayerFB xrDestroyPassthroughLayerFB_ = nullptr; + PFN_xrPassthroughLayerPauseFB xrPassthroughLayerPauseFB_ = nullptr; + PFN_xrPassthroughLayerResumeFB xrPassthroughLayerResumeFB_ = nullptr; + /// style + PFN_xrPassthroughLayerSetStyleFB xrPassthroughLayerSetStyleFB_ = nullptr; + /// geometry + PFN_xrCreateGeometryInstanceFB xrCreateGeometryInstanceFB_ = nullptr; + PFN_xrDestroyGeometryInstanceFB xrDestroyGeometryInstanceFB_ = nullptr; + PFN_xrGeometryInstanceSetTransformFB xrGeometryInstanceSetTransformFB_ = nullptr; + /// Passthrough - mesh extension functions + /// mesh + PFN_xrCreateTriangleMeshFB xrCreateTriangleMeshFB_ = nullptr; + PFN_xrDestroyTriangleMeshFB xrDestroyTriangleMeshFB_ = nullptr; + PFN_xrTriangleMeshGetVertexBufferFB xrTriangleMeshGetVertexBufferFB_ = nullptr; + PFN_xrTriangleMeshGetIndexBufferFB xrTriangleMeshGetIndexBufferFB_ = nullptr; + PFN_xrTriangleMeshBeginUpdateFB xrTriangleMeshBeginUpdateFB_ = nullptr; + PFN_xrTriangleMeshEndUpdateFB xrTriangleMeshEndUpdateFB_ = nullptr; + PFN_xrTriangleMeshBeginVertexBufferUpdateFB xrTriangleMeshBeginVertexBufferUpdateFB_ = nullptr; + PFN_xrTriangleMeshEndVertexBufferUpdateFB xrTriangleMeshEndVertexBufferUpdateFB_ = nullptr; + + /// data + XrPassthroughFB passthrough_ = XR_NULL_HANDLE; + XrPassthroughLayerFB layerProjected_ = XR_NULL_HANDLE; + XrPassthroughLayerFB layerHands_ = XR_NULL_HANDLE; + XrTriangleMeshFB mesh_ = XR_NULL_HANDLE; + XrGeometryInstanceFB geometryProjected_ = XR_NULL_HANDLE; + XrGeometryInstanceTransformFB transform_{XR_TYPE_GEOMETRY_INSTANCE_TRANSFORM_FB}; + XrPassthroughColorMapMonoToRgbaFB colorMap_{XR_TYPE_PASSTHROUGH_COLOR_MAP_MONO_TO_RGBA_FB}; + XrPassthroughColorMapMonoToMonoFB monoMap_{XR_TYPE_PASSTHROUGH_COLOR_MAP_MONO_TO_MONO_FB}; +}; diff --git a/Samples/XrSamples/XrKeyboard/Src/xr_render_model_helper.h b/Samples/XrSamples/XrKeyboard/Src/xr_render_model_helper.h new file mode 100755 index 0000000..3d566de --- /dev/null +++ b/Samples/XrSamples/XrKeyboard/Src/xr_render_model_helper.h @@ -0,0 +1,178 @@ +/************************************************************************************************ +Filename : xr_render_model_helper.h +Content : Helper Inteface for openxr render_model extensions +Created : April 2021 +Authors : Federico Schliemann +Language : C++ +Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved. +************************************************************************************************/ +#pragma once + +#include "xr_helper.h" + +class XrRenderModelHelper : public XrHelper { + public: + XrRenderModelHelper(XrInstance instance) : XrHelper(instance) { + /// Hook up extensions for device settings + oxr(xrGetInstanceProcAddr( + instance, + "xrEnumerateRenderModelPathsFB", + (PFN_xrVoidFunction*)(&xrEnumerateRenderModelPathsFB_))); + oxr(xrGetInstanceProcAddr( + instance, + "xrGetRenderModelPropertiesFB", + (PFN_xrVoidFunction*)(&xrGetRenderModelPropertiesFB_))); + oxr(xrGetInstanceProcAddr( + instance, "xrLoadRenderModelFB", (PFN_xrVoidFunction*)(&xrLoadRenderModelFB_))); + } + + ~XrRenderModelHelper() override { + xrEnumerateRenderModelPathsFB_ = nullptr; + xrGetRenderModelPropertiesFB_ = nullptr; + xrLoadRenderModelFB_ = nullptr; + }; + + /// XrHelper Interface + virtual bool SessionInit(XrSession session) override { + isIntialized_ = false; + session_ = session; + return true; + } + virtual bool SessionEnd() override { + session_ = XR_NULL_HANDLE; + return true; + } + virtual bool Update(XrSpace currentSpace, XrTime predictedDisplayTime) override { + OneTimeInitialize(); + return true; + } + + static std::vector RequiredExtensionNames() { + return {XR_FB_RENDER_MODEL_EXTENSION_NAME}; + } + + public: + /// Own interface + const std::vector& GetPaths() const { + return paths_; + } + const std::vector& GetProperties() const { + return properties_; + } + + std::vector LoadRenderModel(bool remote) { + std::vector buffer; + XrInstance instance = GetInstance(); + std::string strToCheck; + if (remote) { + strToCheck = "/model_fb/keyboard/remote"; + } else { + strToCheck = "/model_fb/keyboard/local"; + } + + for (const auto& p : paths_) { + char buf[256]; + uint32_t bufCount = 0; + // OpenXR two call pattern. First call gets buffer size, second call gets the buffer + // data + oxr(xrPathToString(instance, p.path, bufCount, &bufCount, nullptr)); + oxr(xrPathToString(instance, p.path, bufCount, &bufCount, &buf[0])); + std::string pathString = buf; + if (pathString.rfind(strToCheck, 0) == 0) { + XrRenderModelPropertiesFB prop{XR_TYPE_RENDER_MODEL_PROPERTIES_FB}; + XrResult result = xrGetRenderModelPropertiesFB_(session_, p.path, &prop); + if (result == XR_SUCCESS) { + if (prop.modelKey != XR_NULL_RENDER_MODEL_KEY_FB) { + XrRenderModelLoadInfoFB loadInfo = {XR_TYPE_RENDER_MODEL_LOAD_INFO_FB}; + loadInfo.modelKey = prop.modelKey; + + XrRenderModelBufferFB rmb{XR_TYPE_RENDER_MODEL_BUFFER_FB}; + rmb.next = nullptr; + rmb.bufferCapacityInput = 0; + rmb.buffer = nullptr; + if (oxr(xrLoadRenderModelFB_(session_, &loadInfo, &rmb))) { + XRLOG( + "XrRenderModelHelper: loading modelKey %u size %u ", + prop.modelKey, + rmb.bufferCountOutput); + buffer.resize(rmb.bufferCountOutput); + rmb.buffer = (uint8_t*)buffer.data(); + rmb.bufferCapacityInput = rmb.bufferCountOutput; + if (!oxr(xrLoadRenderModelFB_(session_, &loadInfo, &rmb))) { + XRLOG( + "XrRenderModelHelper: FAILED to load modelKey %u on pass 2", + prop.modelKey); + buffer.resize(0); + } else { + XRLOG( + "XrRenderModelHelper: loaded modelKey %u buffer size is %u", + prop.modelKey, + buffer.size()); + return buffer; + } + } else { + XRLOG( + "XrRenderModelHelper: FAILED to load modelKey %u on pass 1", + prop.modelKey); + } + } + } else { + XRLOG( + "XrRenderModelHelper: FAILED to load prop for path '%s'", + pathString.c_str()); + } + } + } + + return buffer; + } + + private: + void OneTimeInitialize() { + if (isIntialized_) + return; + /// Enumerate available models + XrInstance instance = GetInstance(); + if (xrEnumerateRenderModelPathsFB_) { + /// query path count + uint32_t pathCount = 0; + oxr(xrEnumerateRenderModelPathsFB_(session_, pathCount, &pathCount, nullptr)); + if (pathCount > 0) { + XRLOG("XrRenderModelHelper: found %u models ", pathCount); + paths_.resize(pathCount, { XR_TYPE_RENDER_MODEL_PATH_INFO_FB }); + /// Fill in the path data + oxr(xrEnumerateRenderModelPathsFB_(session_, pathCount, &pathCount, &paths_[0])); + /// Print paths for debug purpose + for (const auto& p : paths_) { + char buf[256]; + uint32_t bufCount = 0; + oxr(xrPathToString(instance, p.path, bufCount, &bufCount, nullptr)); + oxr(xrPathToString(instance, p.path, bufCount, &bufCount, &buf[0])); + XRLOG("XrRenderModelHelper: path=%u `%s`", (uint32_t)p.path, &buf[0]); + } + /// Get properties + for (const auto& p : paths_) { + XrRenderModelPropertiesFB prop{XR_TYPE_RENDER_MODEL_PROPERTIES_FB}; + XrResult result = xrGetRenderModelPropertiesFB_(session_, p.path, &prop); + if (result == XR_SUCCESS) { + properties_.push_back(prop); + } + } + } + } + isIntialized_ = true; + } + + private: + /// Session cache + XrSession session_ = XR_NULL_HANDLE; + /// RenderModel - extension functions + PFN_xrEnumerateRenderModelPathsFB xrEnumerateRenderModelPathsFB_ = nullptr; + PFN_xrGetRenderModelPropertiesFB xrGetRenderModelPropertiesFB_ = nullptr; + PFN_xrLoadRenderModelFB xrLoadRenderModelFB_ = nullptr; + /// RenderModel - data buffers + std::vector paths_; + std::vector properties_; + /// Lifetime + bool isIntialized_ = false; +}; diff --git a/Samples/XrSamples/XrKeyboard/assets/assets.txt b/Samples/XrSamples/XrKeyboard/assets/assets.txt new file mode 100644 index 0000000..2cc30f7 --- /dev/null +++ b/Samples/XrSamples/XrKeyboard/assets/assets.txt @@ -0,0 +1 @@ +This file is a placeholder. diff --git a/Samples/XrSamples/XrKeyboard/assets/k830.png b/Samples/XrSamples/XrKeyboard/assets/k830.png new file mode 100644 index 0000000..cb218f9 Binary files /dev/null and b/Samples/XrSamples/XrKeyboard/assets/k830.png differ diff --git a/Samples/XrSamples/XrKeyboard/assets/k830_art.glb b/Samples/XrSamples/XrKeyboard/assets/k830_art.glb new file mode 100644 index 0000000..1caef01 Binary files /dev/null and b/Samples/XrSamples/XrKeyboard/assets/k830_art.glb differ diff --git a/Samples/XrSamples/XrKeyboard/assets/panel.ktx b/Samples/XrSamples/XrKeyboard/assets/panel.ktx new file mode 100644 index 0000000..deb13e8 Binary files /dev/null and b/Samples/XrSamples/XrKeyboard/assets/panel.ktx differ diff --git a/Samples/XrSamples/XrKeyboard/java/MainActivity.java b/Samples/XrSamples/XrKeyboard/java/MainActivity.java new file mode 100644 index 0000000..c568474 --- /dev/null +++ b/Samples/XrSamples/XrKeyboard/java/MainActivity.java @@ -0,0 +1,28 @@ +// Copyright (c) Facebook Technologies, LLC and its affiliates. All Rights reserved. +package com.oculus.sdk.xrkeyboard; + +/** + * When using NativeActivity, we currently need to handle loading of dependent shared libraries + * manually before a shared library that depends on them is loaded, since there is not currently a + * way to specify a shared library dependency for NativeActivity via the manifest meta-data. + * + *

The simplest method for doing so is to subclass NativeActivity with an empty activity that + * calls System.loadLibrary on the dependent libraries, which is unfortunate when the goal is to + * write a pure native C/C++ only Android activity. + * + *

A native-code only solution is to load the dependent libraries dynamically using dlopen(). + * However, there are a few considerations, see: + * https://groups.google.com/forum/#!msg/android-ndk/l2E2qh17Q6I/wj6s_6HSjaYJ + * + *

1. Only call dlopen() if you're sure it will succeed as the bionic dynamic linker will + * remember if dlopen failed and will not re-try a dlopen on the same lib a second time. + * + *

2. Must remember what libraries have already been loaded to avoid infinitely looping when + * libraries have circular dependencies. + */ +public class MainActivity extends android.app.NativeActivity { + static { + System.loadLibrary("openxr_loader"); + System.loadLibrary("xrkeyboard"); + } +} diff --git a/Samples/XrSamples/XrKeyboard/res/values/strings.xml b/Samples/XrSamples/XrKeyboard/res/values/strings.xml new file mode 100644 index 0000000..65cc995 --- /dev/null +++ b/Samples/XrSamples/XrKeyboard/res/values/strings.xml @@ -0,0 +1,4 @@ + + + Xr Keyboard Sample + diff --git a/Samples/XrSamples/XrPassthrough/CMakeLists.txt b/Samples/XrSamples/XrPassthrough/CMakeLists.txt new file mode 100755 index 0000000..4ba3275 --- /dev/null +++ b/Samples/XrSamples/XrPassthrough/CMakeLists.txt @@ -0,0 +1,35 @@ +# Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved. + +project(xrpassthrough) + +if(NOT TARGET OpenXR::openxr_loader) + find_package(OpenXR REQUIRED) +endif() + +file(GLOB_RECURSE SRC_FILES + Src/*.c + Src/*.cpp +) + +if(ANDROID) + add_library(${PROJECT_NAME} MODULE ${SRC_FILES}) + target_link_libraries(${PROJECT_NAME} PRIVATE samplecommon_gl) + set_target_properties(${PROJECT_NAME} PROPERTIES LINK_FLAGS "-u ANativeActivity_onCreate") +elseif(WIN32) + add_executable(${PROJECT_NAME} ${SRC_FILES}) + add_custom_command(TARGET ${PROJECT_NAME} PRE_BUILD + COMMAND "${CMAKE_COMMAND}" -E copy_directory + "${CMAKE_CURRENT_LIST_DIR}/assets" + "$/assets" + VERBATIM) + + add_custom_command(TARGET ${PROJECT_NAME} PRE_BUILD + COMMAND "${CMAKE_COMMAND}" -E copy_directory + "${CMAKE_SOURCE_DIR}/SampleXrFramework/res/raw" + "$/font/res/raw" + VERBATIM) + target_link_libraries(${PROJECT_NAME} PRIVATE samplecommon_gl) +endif() + +# Common across platforms +target_include_directories(${PROJECT_NAME} PRIVATE Src) diff --git a/Samples/XrSamples/XrPassthrough/Projects/Android/AndroidManifest.xml b/Samples/XrSamples/XrPassthrough/Projects/Android/AndroidManifest.xml new file mode 100755 index 0000000..e8fbfd7 --- /dev/null +++ b/Samples/XrSamples/XrPassthrough/Projects/Android/AndroidManifest.xml @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Samples/XrSamples/XrPassthrough/Projects/Android/build.bat b/Samples/XrSamples/XrPassthrough/Projects/Android/build.bat new file mode 100755 index 0000000..facf79f --- /dev/null +++ b/Samples/XrSamples/XrPassthrough/Projects/Android/build.bat @@ -0,0 +1,29 @@ +@rem Only edit the master copy of this file in SDK_ROOT/bin/scripts/build/perproject + +@setlocal enableextensions enabledelayedexpansion + +@if not exist "build.gradle" @echo Build script must be executed from project directory. & goto :Abort + +@set P=.. + +:TryAgain + +@rem @echo P = %P% + +@if exist "%P%\bin\scripts\build\build.py.bat" goto :Found + +@if exist "%P%\bin\scripts\build" @echo "Could not find build.py.bat" & goto :Abort + +@set P=%P%\.. + +@goto :TryAgain + +:Found + +@set P=%P%\bin\scripts\build +@call %P%\build.py.bat %1 %2 %3 %4 %5 +@goto :End + +:Abort + +:End diff --git a/Samples/XrSamples/XrPassthrough/Projects/Android/build.gradle b/Samples/XrSamples/XrPassthrough/Projects/Android/build.gradle new file mode 100755 index 0000000..1ba2889 --- /dev/null +++ b/Samples/XrSamples/XrPassthrough/Projects/Android/build.gradle @@ -0,0 +1,78 @@ +// Top-level build file where you can add configuration options common to all sub-projects/modules. +buildscript { + repositories { + google() + mavenCentral() + } + dependencies { + classpath "com.android.tools.build:gradle:7.0.3" + } +} + +repositories { + google() + mavenCentral() +} + +apply plugin: 'com.android.application' + +android { + compileSdk 32 + + defaultConfig { + applicationId "com.oculus.xrpassthrough" + minSdk 26 + targetSdk 32 + versionCode 1 + versionName "1.0" + + // override app plugin abiFilters for 64-bit support + externalNativeBuild { + ndk { + abiFilters 'arm64-v8a' + } + ndkBuild { + abiFilters 'arm64-v8a' + } + cmake { + targets "xrpassthrough" + } + } + } + + sourceSets { + main { + manifest.srcFile 'AndroidManifest.xml' + java.srcDirs = ['../../java'] + assets.srcDirs = ['../../assets'] + res.srcDirs = ['../../res'] + } + } + + buildTypes { + debug { + debuggable true + } + + release { + debuggable false + } + } + + externalNativeBuild { + cmake { + path file('../../../../CMakeLists.txt') + } + } + + // Enable prefab support for the OpenXR AAR + buildFeatures { + prefab true + } +} + +dependencies { + // Package/application AndroidManifest.xml properties, plus headers and libraries + // exposed to CMake + implementation 'org.khronos.openxr:openxr_loader_for_android:1.1.36' +} diff --git a/Samples/XrSamples/XrPassthrough/Projects/Android/build.py b/Samples/XrSamples/XrPassthrough/Projects/Android/build.py new file mode 100755 index 0000000..d4b6e58 --- /dev/null +++ b/Samples/XrSamples/XrPassthrough/Projects/Android/build.py @@ -0,0 +1,35 @@ +#!/usr/bin/python +# (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +# This first bit of code is common bootstrapping code +# to determine the SDK root, and to set up the import +# path for additional python code. + +# begin bootstrap +import os +import sys + + +def init(): + root = os.path.realpath(os.path.dirname(os.path.realpath(__file__))) + os.chdir(root) # make sure we are always executing from the project directory + while os.path.isdir(os.path.join(root, "bin/scripts/build")) == False: + root = os.path.realpath(os.path.join(root, "..")) + if ( + len(root) <= 5 + ): # Should catch both Posix and Windows root directories (e.g. '/' and 'C:\') + print("Unable to find SDK root. Exiting.") + sys.exit(1) + root = os.path.abspath(root) + os.environ["OCULUS_SDK_PATH"] = root + sys.path.append(root + "/bin/scripts/build") + + +init() +import ovrbuild + +ovrbuild.init() +# end bootstrap + + +ovrbuild.build() diff --git a/Samples/XrSamples/XrPassthrough/Projects/Android/gradle.properties b/Samples/XrSamples/XrPassthrough/Projects/Android/gradle.properties new file mode 100644 index 0000000..3e927b1 --- /dev/null +++ b/Samples/XrSamples/XrPassthrough/Projects/Android/gradle.properties @@ -0,0 +1,21 @@ +# Project-wide Gradle settings. +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. More details, visit +# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects +# org.gradle.parallel=true +# AndroidX package structure to make it clearer which packages are bundled with the +# Android operating system, and which are packaged with your app's APK +# https://developer.android.com/topic/libraries/support-library/androidx-rn +android.useAndroidX=true +# Enables namespacing of each library's R class so that its R class includes only the +# resources declared in the library itself and none from the library's dependencies, +# thereby reducing the size of the R class for that library +android.nonTransitiveRClass=true \ No newline at end of file diff --git a/Samples/XrSamples/XrPassthrough/Projects/Android/gradle/wrapper/gradle-wrapper.jar b/Samples/XrSamples/XrPassthrough/Projects/Android/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..7454180 Binary files /dev/null and b/Samples/XrSamples/XrPassthrough/Projects/Android/gradle/wrapper/gradle-wrapper.jar differ diff --git a/Samples/XrSamples/XrPassthrough/Projects/Android/gradle/wrapper/gradle-wrapper.properties b/Samples/XrSamples/XrPassthrough/Projects/Android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..ffed3a2 --- /dev/null +++ b/Samples/XrSamples/XrPassthrough/Projects/Android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/Samples/XrSamples/XrPassthrough/Projects/Android/gradlew b/Samples/XrSamples/XrPassthrough/Projects/Android/gradlew new file mode 100755 index 0000000..a69d9cb --- /dev/null +++ b/Samples/XrSamples/XrPassthrough/Projects/Android/gradlew @@ -0,0 +1,240 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# 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 +# +# https://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. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit + +APP_NAME="Gradle" +APP_BASE_NAME=${0##*/} + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/Samples/XrSamples/XrPassthrough/Projects/Android/gradlew.bat b/Samples/XrSamples/XrPassthrough/Projects/Android/gradlew.bat new file mode 100755 index 0000000..f127cfd --- /dev/null +++ b/Samples/XrSamples/XrPassthrough/Projects/Android/gradlew.bat @@ -0,0 +1,91 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/Samples/XrSamples/XrPassthrough/Projects/Android/settings.gradle b/Samples/XrSamples/XrPassthrough/Projects/Android/settings.gradle new file mode 100755 index 0000000..2973d06 --- /dev/null +++ b/Samples/XrSamples/XrPassthrough/Projects/Android/settings.gradle @@ -0,0 +1 @@ +rootProject.name = "XrPassthrough" diff --git a/Samples/XrSamples/XrPassthrough/README.md b/Samples/XrSamples/XrPassthrough/README.md new file mode 100644 index 0000000..6871af3 --- /dev/null +++ b/Samples/XrSamples/XrPassthrough/README.md @@ -0,0 +1,11 @@ +# OpenXR Passthrough Sample + +## Overview +Passthrough is a method that allows users to view their physical environment through a light-blocking VR headset. The `XR_FB_passthrough` feature allows: +* An application to request that passthrough be composited with the application content. +* An application to specify the compositing and blending rules between passthrough and VR content. +* An application to apply styles, such as color mapping and edge rendering, to passthrough. +* An application to provide a geometry to be used in place of the user’s physical environment. Camera images will be projected onto the surface provided by the application. In some cases where a part of the environment, such as a desk, can be approximated well, this provides a better visual experience. + +## The Sample +This sample demonstrates the use of both still and animated styles, as well as selective and projected passthrough. diff --git a/Samples/XrSamples/XrPassthrough/Src/XrPassthrough.cpp b/Samples/XrSamples/XrPassthrough/Src/XrPassthrough.cpp new file mode 100755 index 0000000..047ad4e --- /dev/null +++ b/Samples/XrSamples/XrPassthrough/Src/XrPassthrough.cpp @@ -0,0 +1,1514 @@ +/************************************************************************************ + +Filename : XrPassthrough.cpp +Content : This sample uses the Android NativeActivity class. +Created : +Authors : + +Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved. + +*************************************************************************************/ + +#include +#include +#include +#include // for memset +#include +#include + +#include + +#if defined(ANDROID) +#include +#include +#include // for prctl( PR_SET_NAME ) +#include +#include // for native window JNI +#include +#else +#include +#endif // defined(ANDROID) + +#include + +#include "XrPassthrough.h" +#include "XrPassthroughInput.h" +#include "XrPassthroughGl.h" + +using namespace OVR; + +#if !defined(EGL_OPENGL_ES3_BIT_KHR) +#define EGL_OPENGL_ES3_BIT_KHR 0x0040 +#endif + +#define OVR_LOG_TAG "XrPassthrough" + +#if !defined(XR_USE_GRAPHICS_API_OPENGL_ES) && !defined(XR_USE_GRAPHICS_API_OPENGL) +#error A graphics backend must be defined! +#elif defined(XR_USE_GRAPHICS_API_OPENGL_ES) && defined(XR_USE_GRAPHICS_API_OPENGL) +#error Only one graphics backend shall be defined! +#endif + +#if defined(ANDROID) +#define ALOGE(...) __android_log_print(ANDROID_LOG_ERROR, OVR_LOG_TAG, __VA_ARGS__) +#define ALOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, OVR_LOG_TAG, __VA_ARGS__) +#else +#define ALOGE(...) \ + printf("ERROR: "); \ + printf(__VA_ARGS__); \ + printf("\n") +#define ALOGV(...) \ + printf("VERBOSE: "); \ + printf(__VA_ARGS__); \ + printf("\n") +#endif + +static const int CPU_LEVEL = 2; +static const int GPU_LEVEL = 3; +static const int NUM_MULTI_SAMPLES = 4; + +/* +================================================================================ + +OpenXR Utility Functions + +================================================================================ +*/ + +XrInstance instance; +void OXR_CheckErrors(XrResult result, const char* function, bool failOnError) { + if (XR_FAILED(result)) { + char errorBuffer[XR_MAX_RESULT_STRING_SIZE]; + xrResultToString(instance, result, errorBuffer); + if (failOnError) { + ALOGE("OpenXR error: %s: %s\n", function, errorBuffer); + } else { + ALOGV("OpenXR error: %s: %s\n", function, errorBuffer); + } + } +} + +#define DECL_PFN(pfn) PFN_##pfn pfn = nullptr +#define INIT_PFN(pfn) OXR(xrGetInstanceProcAddr(instance, #pfn, (PFN_xrVoidFunction*)(&pfn))) + +// FB_passthrough sample begin +DECL_PFN(xrCreatePassthroughFB); +DECL_PFN(xrDestroyPassthroughFB); +DECL_PFN(xrPassthroughStartFB); +DECL_PFN(xrPassthroughPauseFB); +DECL_PFN(xrCreatePassthroughLayerFB); +DECL_PFN(xrDestroyPassthroughLayerFB); +DECL_PFN(xrPassthroughLayerSetStyleFB); +DECL_PFN(xrPassthroughLayerPauseFB); +DECL_PFN(xrPassthroughLayerResumeFB); +DECL_PFN(xrCreateTriangleMeshFB); +DECL_PFN(xrDestroyTriangleMeshFB); +DECL_PFN(xrTriangleMeshGetVertexBufferFB); +DECL_PFN(xrTriangleMeshGetIndexBufferFB); +DECL_PFN(xrTriangleMeshBeginUpdateFB); +DECL_PFN(xrTriangleMeshEndUpdateFB); +DECL_PFN(xrCreateGeometryInstanceFB); +DECL_PFN(xrDestroyGeometryInstanceFB); +DECL_PFN(xrGeometryInstanceSetTransformFB); +// FB_passthrough sample end + +/* +================================================================================ + +Egl Utility Functions + +================================================================================ +*/ + +#if defined(XR_USE_GRAPHICS_API_OPENGL_ES) +static const char* EglErrorString(const EGLint error) { + switch (error) { + case EGL_SUCCESS: + return "EGL_SUCCESS"; + case EGL_NOT_INITIALIZED: + return "EGL_NOT_INITIALIZED"; + case EGL_BAD_ACCESS: + return "EGL_BAD_ACCESS"; + case EGL_BAD_ALLOC: + return "EGL_BAD_ALLOC"; + case EGL_BAD_ATTRIBUTE: + return "EGL_BAD_ATTRIBUTE"; + case EGL_BAD_CONTEXT: + return "EGL_BAD_CONTEXT"; + case EGL_BAD_CONFIG: + return "EGL_BAD_CONFIG"; + case EGL_BAD_CURRENT_SURFACE: + return "EGL_BAD_CURRENT_SURFACE"; + case EGL_BAD_DISPLAY: + return "EGL_BAD_DISPLAY"; + case EGL_BAD_SURFACE: + return "EGL_BAD_SURFACE"; + case EGL_BAD_MATCH: + return "EGL_BAD_MATCH"; + case EGL_BAD_PARAMETER: + return "EGL_BAD_PARAMETER"; + case EGL_BAD_NATIVE_PIXMAP: + return "EGL_BAD_NATIVE_PIXMAP"; + case EGL_BAD_NATIVE_WINDOW: + return "EGL_BAD_NATIVE_WINDOW"; + case EGL_CONTEXT_LOST: + return "EGL_CONTEXT_LOST"; + default: + return "unknown"; + } +} + +void Egl::Clear() { + MajorVersion = 0; + MinorVersion = 0; + Display = 0; + Config = 0; + TinySurface = EGL_NO_SURFACE; + MainSurface = EGL_NO_SURFACE; + Context = EGL_NO_CONTEXT; +} + +void Egl::CreateContext(const Egl* shareEgl) { + if (Display != 0) { + return; + } + + Display = eglGetDisplay(EGL_DEFAULT_DISPLAY); + ALOGV(" eglInitialize( Display, &MajorVersion, &MinorVersion )"); + eglInitialize(Display, &MajorVersion, &MinorVersion); + // Do NOT use eglChooseConfig, because the Android EGL code pushes in multisample + // flags in eglChooseConfig if the user has selected the "force 4x MSAA" option in + // settings, and that is completely wasted for our warp target. + const int MAX_CONFIGS = 1024; + EGLConfig configs[MAX_CONFIGS]; + EGLint numConfigs = 0; + if (eglGetConfigs(Display, configs, MAX_CONFIGS, &numConfigs) == EGL_FALSE) { + ALOGE(" eglGetConfigs() failed: %s", EglErrorString(eglGetError())); + return; + } + const EGLint configAttribs[] = { + EGL_RED_SIZE, + 8, + EGL_GREEN_SIZE, + 8, + EGL_BLUE_SIZE, + 8, + EGL_ALPHA_SIZE, + 8, // need alpha for the multi-pass timewarp compositor + EGL_DEPTH_SIZE, + 0, + EGL_STENCIL_SIZE, + 0, + EGL_SAMPLES, + 0, + EGL_NONE}; + Config = 0; + for (int i = 0; i < numConfigs; i++) { + EGLint value = 0; + + eglGetConfigAttrib(Display, configs[i], EGL_RENDERABLE_TYPE, &value); + if ((value & EGL_OPENGL_ES3_BIT_KHR) != EGL_OPENGL_ES3_BIT_KHR) { + continue; + } + + // The pbuffer config also needs to be compatible with normal window rendering + // so it can share textures with the window context. + eglGetConfigAttrib(Display, configs[i], EGL_SURFACE_TYPE, &value); + if ((value & (EGL_WINDOW_BIT | EGL_PBUFFER_BIT)) != (EGL_WINDOW_BIT | EGL_PBUFFER_BIT)) { + continue; + } + + int j = 0; + for (; configAttribs[j] != EGL_NONE; j += 2) { + eglGetConfigAttrib(Display, configs[i], configAttribs[j], &value); + if (value != configAttribs[j + 1]) { + break; + } + } + if (configAttribs[j] == EGL_NONE) { + Config = configs[i]; + break; + } + } + if (Config == 0) { + ALOGE(" eglChooseConfig() failed: %s", EglErrorString(eglGetError())); + return; + } + EGLint contextAttribs[] = {EGL_CONTEXT_CLIENT_VERSION, 3, EGL_NONE}; + ALOGV(" Context = eglCreateContext( Display, Config, EGL_NO_CONTEXT, contextAttribs )"); + Context = eglCreateContext( + Display, + Config, + (shareEgl != nullptr) ? shareEgl->Context : EGL_NO_CONTEXT, + contextAttribs); + if (Context == EGL_NO_CONTEXT) { + ALOGE(" eglCreateContext() failed: %s", EglErrorString(eglGetError())); + return; + } + const EGLint surfaceAttribs[] = {EGL_WIDTH, 16, EGL_HEIGHT, 16, EGL_NONE}; + ALOGV(" TinySurface = eglCreatePbufferSurface( Display, Config, surfaceAttribs )"); + TinySurface = eglCreatePbufferSurface(Display, Config, surfaceAttribs); + if (TinySurface == EGL_NO_SURFACE) { + ALOGE(" eglCreatePbufferSurface() failed: %s", EglErrorString(eglGetError())); + eglDestroyContext(Display, Context); + Context = EGL_NO_CONTEXT; + return; + } + ALOGV(" eglMakeCurrent( Display, TinySurface, TinySurface, Context )"); + if (eglMakeCurrent(Display, TinySurface, TinySurface, Context) == EGL_FALSE) { + ALOGE(" eglMakeCurrent() failed: %s", EglErrorString(eglGetError())); + eglDestroySurface(Display, TinySurface); + eglDestroyContext(Display, Context); + Context = EGL_NO_CONTEXT; + return; + } +} + +void Egl::DestroyContext() { + if (Display != 0) { + ALOGE(" eglMakeCurrent( Display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT )"); + if (eglMakeCurrent(Display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT) == EGL_FALSE) { + ALOGE(" eglMakeCurrent() failed: %s", EglErrorString(eglGetError())); + } + } + if (Context != EGL_NO_CONTEXT) { + ALOGE(" eglDestroyContext( Display, Context )"); + if (eglDestroyContext(Display, Context) == EGL_FALSE) { + ALOGE(" eglDestroyContext() failed: %s", EglErrorString(eglGetError())); + } + Context = EGL_NO_CONTEXT; + } + if (TinySurface != EGL_NO_SURFACE) { + ALOGE(" eglDestroySurface( Display, TinySurface )"); + if (eglDestroySurface(Display, TinySurface) == EGL_FALSE) { + ALOGE(" eglDestroySurface() failed: %s", EglErrorString(eglGetError())); + } + TinySurface = EGL_NO_SURFACE; + } + if (Display != 0) { + ALOGE(" eglTerminate( Display )"); + if (eglTerminate(Display) == EGL_FALSE) { + ALOGE(" eglTerminate() failed: %s", EglErrorString(eglGetError())); + } + Display = 0; + } +} + +#elif defined(XR_USE_GRAPHICS_API_OPENGL) + +#if defined(WIN32) +// Favor the high performance NVIDIA or AMD GPUs +extern "C" { +// http://developer.download.nvidia.com/devzone/devcenter/gamegraphics/files/OptimusRenderingPolicies.pdf +__declspec(dllexport) DWORD NvOptimusEnablement = 0x00000001; +// https://gpuopen.com/learn/amdpowerxpressrequesthighperformance/ +__declspec(dllexport) DWORD AmdPowerXpressRequestHighPerformance = 0x00000001; +} +#endif // defined(WIN32) + +void Egl::Clear() { + hDC = 0; + hGLRC = 0; +} + +void Egl::CreateContext(const Egl*) { + ovrGl_CreateContext_Windows(&hDC, &hGLRC); +} + +void Egl::DestroyContext() { + ovrGl_DestroyContext_Windows(); +} + +#endif + +void App::Clear() { +#if defined(XR_USE_PLATFORM_ANDROID) + Resumed = false; +#endif // defined(XR_USE_PLATFORM_ANDROID) + ShouldExit = false; + Focused = false; + Instance = XR_NULL_HANDLE; + Session = XR_NULL_HANDLE; + ViewportConfig = {}; + for (int i = 0; i < NUM_EYES; i++) { + ViewConfigurationView[i] = {}; + } + SystemId = XR_NULL_SYSTEM_ID; + HeadSpace = XR_NULL_HANDLE; + LocalSpace = XR_NULL_HANDLE; + StageSpace = XR_NULL_HANDLE; + SessionActive = false; + SwapInterval = 1; + for (int i = 0; i < MaxLayerCount; i++) { + Layers[i] = {}; + } + LayerCount = 0; + CpuLevel = 2; + GpuLevel = 2; + MainThreadTid = 0; + RenderThreadTid = 0; + TouchPadDownLastFrame = false; + + egl.Clear(); + appRenderer.Clear(); +} + +void App::HandleSessionStateChanges(XrSessionState state) { + if (state == XR_SESSION_STATE_READY) { +#if defined(XR_USE_PLATFORM_ANDROID) + assert(Resumed); +#endif // defined(XR_USE_PLATFORM_ANDROID) + assert(SessionActive == false); + + XrSessionBeginInfo sessionBeginInfo = {XR_TYPE_SESSION_BEGIN_INFO}; + sessionBeginInfo.primaryViewConfigurationType = ViewportConfig.viewConfigurationType; + + XrResult result; + OXR(result = xrBeginSession(Session, &sessionBeginInfo)); + + SessionActive = (result == XR_SUCCESS); + +#if defined(XR_USE_PLATFORM_ANDROID) + // Set session state once we have entered VR mode and have a valid session object. + if (SessionActive) { + XrPerfSettingsLevelEXT cpuPerfLevel = XR_PERF_SETTINGS_LEVEL_SUSTAINED_HIGH_EXT; + switch (CpuLevel) { + case 0: + cpuPerfLevel = XR_PERF_SETTINGS_LEVEL_POWER_SAVINGS_EXT; + break; + case 1: + cpuPerfLevel = XR_PERF_SETTINGS_LEVEL_SUSTAINED_LOW_EXT; + break; + case 2: + cpuPerfLevel = XR_PERF_SETTINGS_LEVEL_SUSTAINED_HIGH_EXT; + break; + case 3: + cpuPerfLevel = XR_PERF_SETTINGS_LEVEL_BOOST_EXT; + break; + default: + ALOGE("Invalid CPU level %d", CpuLevel); + break; + } + + XrPerfSettingsLevelEXT gpuPerfLevel = XR_PERF_SETTINGS_LEVEL_SUSTAINED_HIGH_EXT; + switch (GpuLevel) { + case 0: + gpuPerfLevel = XR_PERF_SETTINGS_LEVEL_POWER_SAVINGS_EXT; + break; + case 1: + gpuPerfLevel = XR_PERF_SETTINGS_LEVEL_SUSTAINED_LOW_EXT; + break; + case 2: + gpuPerfLevel = XR_PERF_SETTINGS_LEVEL_SUSTAINED_HIGH_EXT; + break; + case 3: + gpuPerfLevel = XR_PERF_SETTINGS_LEVEL_BOOST_EXT; + break; + default: + ALOGE("Invalid GPU level %d", GpuLevel); + break; + } + + PFN_xrPerfSettingsSetPerformanceLevelEXT pfnPerfSettingsSetPerformanceLevelEXT = NULL; + OXR(xrGetInstanceProcAddr( + Instance, + "xrPerfSettingsSetPerformanceLevelEXT", + (PFN_xrVoidFunction*)(&pfnPerfSettingsSetPerformanceLevelEXT))); + + OXR(pfnPerfSettingsSetPerformanceLevelEXT( + Session, XR_PERF_SETTINGS_DOMAIN_CPU_EXT, cpuPerfLevel)); + OXR(pfnPerfSettingsSetPerformanceLevelEXT( + Session, XR_PERF_SETTINGS_DOMAIN_GPU_EXT, gpuPerfLevel)); + + PFN_xrSetAndroidApplicationThreadKHR pfnSetAndroidApplicationThreadKHR = NULL; + OXR(xrGetInstanceProcAddr( + Instance, + "xrSetAndroidApplicationThreadKHR", + (PFN_xrVoidFunction*)(&pfnSetAndroidApplicationThreadKHR))); + + OXR(pfnSetAndroidApplicationThreadKHR( + Session, XR_ANDROID_THREAD_TYPE_APPLICATION_MAIN_KHR, MainThreadTid)); + OXR(pfnSetAndroidApplicationThreadKHR( + Session, XR_ANDROID_THREAD_TYPE_RENDERER_MAIN_KHR, RenderThreadTid)); + } +#endif // defined(XR_USE_PLATFORM_ANDROID) + } else if (state == XR_SESSION_STATE_STOPPING) { +#if defined(XR_USE_PLATFORM_ANDROID) + assert(Resumed == false); +#endif // defined(XR_USE_PLATFORM_ANDROID) + assert(SessionActive); + OXR(xrEndSession(Session)); + SessionActive = false; + } +} + +void App::HandleXrEvents() { + XrEventDataBuffer eventDataBuffer = {}; + + // Poll for events + for (;;) { + XrEventDataBaseHeader* baseEventHeader = (XrEventDataBaseHeader*)(&eventDataBuffer); + baseEventHeader->type = XR_TYPE_EVENT_DATA_BUFFER; + baseEventHeader->next = NULL; + XrResult r; + OXR(r = xrPollEvent(Instance, &eventDataBuffer)); + if (r != XR_SUCCESS) { + break; + } + + switch (baseEventHeader->type) { + case XR_TYPE_EVENT_DATA_EVENTS_LOST: + ALOGV("xrPollEvent: received XR_TYPE_EVENT_DATA_EVENTS_LOST event"); + break; + case XR_TYPE_EVENT_DATA_INSTANCE_LOSS_PENDING: + ALOGV("xrPollEvent: received XR_TYPE_EVENT_DATA_INSTANCE_LOSS_PENDING event"); + break; + case XR_TYPE_EVENT_DATA_INTERACTION_PROFILE_CHANGED: + ALOGV("xrPollEvent: received XR_TYPE_EVENT_DATA_INTERACTION_PROFILE_CHANGED event"); + break; + case XR_TYPE_EVENT_DATA_PERF_SETTINGS_EXT: { +#if defined(XR_USE_PLATFORM_ANDROID) + const XrEventDataPerfSettingsEXT* perf_settings_event = + (XrEventDataPerfSettingsEXT*)(baseEventHeader); + ALOGV( + "xrPollEvent: received XR_TYPE_EVENT_DATA_PERF_SETTINGS_EXT event: type %d subdomain %d : level %d -> level %d", + perf_settings_event->type, + perf_settings_event->subDomain, + perf_settings_event->fromLevel, + perf_settings_event->toLevel); +#endif // defined(XR_USE_PLATFORM_ANDROID) + } break; + case XR_TYPE_EVENT_DATA_REFERENCE_SPACE_CHANGE_PENDING: + ALOGV( + "xrPollEvent: received XR_TYPE_EVENT_DATA_REFERENCE_SPACE_CHANGE_PENDING event"); + break; + case XR_TYPE_EVENT_DATA_SESSION_STATE_CHANGED: { + const XrEventDataSessionStateChanged* session_state_changed_event = + (XrEventDataSessionStateChanged*)(baseEventHeader); + ALOGV( + "xrPollEvent: received XR_TYPE_EVENT_DATA_SESSION_STATE_CHANGED: %d for session %p at time %f", + session_state_changed_event->state, + (void*)session_state_changed_event->session, + FromXrTime(session_state_changed_event->time)); + + switch (session_state_changed_event->state) { + case XR_SESSION_STATE_FOCUSED: + Focused = true; + break; + case XR_SESSION_STATE_VISIBLE: + Focused = false; + break; + case XR_SESSION_STATE_READY: + case XR_SESSION_STATE_STOPPING: + HandleSessionStateChanges(session_state_changed_event->state); + break; + case XR_SESSION_STATE_EXITING: + ShouldExit = true; + break; + default: + break; + } + } break; + default: + ALOGV("xrPollEvent: Unknown event"); + break; + } + } +} + +#if defined(XR_USE_PLATFORM_ANDROID) +/* +================================================================================ + +Native Activity + +================================================================================ +*/ + +/** + * Process the next main command. + */ +static void app_handle_cmd(struct android_app* androidApp, int32_t cmd) { + App& app = *(App*)androidApp->userData; + + switch (cmd) { + // There is no APP_CMD_CREATE. The ANativeActivity creates the + // application thread from onCreate(). The application thread + // then calls android_main(). + case APP_CMD_START: { + ALOGV("onStart()"); + ALOGV(" APP_CMD_START"); + break; + } + case APP_CMD_RESUME: { + ALOGV("onResume()"); + ALOGV(" APP_CMD_RESUME"); + app.Resumed = true; + break; + } + case APP_CMD_PAUSE: { + ALOGV("onPause()"); + ALOGV(" APP_CMD_PAUSE"); + app.Resumed = false; + break; + } + case APP_CMD_STOP: { + ALOGV("onStop()"); + ALOGV(" APP_CMD_STOP"); + break; + } + case APP_CMD_DESTROY: { + ALOGV("onDestroy()"); + ALOGV(" APP_CMD_DESTROY"); + app.Clear(); + break; + } + case APP_CMD_INIT_WINDOW: { + ALOGV("surfaceCreated()"); + ALOGV(" APP_CMD_INIT_WINDOW"); + break; + } + case APP_CMD_TERM_WINDOW: { + ALOGV("surfaceDestroyed()"); + ALOGV(" APP_CMD_TERM_WINDOW"); + break; + } + } +} +#endif // defined(XR_USE_PLATFORM_ANDROID) + +void UpdateStageBounds(App& app) { + XrExtent2Df stageBounds = {}; + + XrResult result; + OXR(result = xrGetReferenceSpaceBoundsRect( + app.Session, XR_REFERENCE_SPACE_TYPE_STAGE, &stageBounds)); + if (result != XR_SUCCESS) { + ALOGV("Stage bounds query failed: using small defaults"); + stageBounds.width = 1.0f; + stageBounds.height = 1.0f; + } + + app.StageBounds = Vector3f(stageBounds.width * 0.5f, 1.0f, stageBounds.height * 0.5f); +} + +/** + * This is the main entry point of a native application that is using + * android_native_app_glue. It runs in its own thread, with its own + * event loop for receiving input events and doing other things. + */ +#if defined(XR_USE_PLATFORM_ANDROID) +void android_main(struct android_app* androidApp) { +#else +int main() { +#endif +#if defined(XR_USE_PLATFORM_ANDROID) + ALOGV("----------------------------------------------------------------"); + ALOGV("android_app_entry()"); + ALOGV(" android_main()"); + + JNIEnv* Env; + (*androidApp->activity->vm).AttachCurrentThread(&Env, nullptr); + + // Note that AttachCurrentThread will reset the thread name. + prctl(PR_SET_NAME, (long)"OVR::Main", 0, 0, 0); +#endif // defined(XR_USE_PLATFORM_ANDROID) + + App app; + app.Clear(); + +#if defined(XR_USE_PLATFORM_ANDROID) + PFN_xrInitializeLoaderKHR xrInitializeLoaderKHR; + xrGetInstanceProcAddr( + XR_NULL_HANDLE, "xrInitializeLoaderKHR", (PFN_xrVoidFunction*)&xrInitializeLoaderKHR); + if (xrInitializeLoaderKHR != NULL) { + XrLoaderInitInfoAndroidKHR loaderInitializeInfoAndroid = {XR_TYPE_LOADER_INIT_INFO_ANDROID_KHR}; + loaderInitializeInfoAndroid.applicationVM = androidApp->activity->vm; + loaderInitializeInfoAndroid.applicationContext = androidApp->activity->clazz; + xrInitializeLoaderKHR((XrLoaderInitInfoBaseHeaderKHR*)&loaderInitializeInfoAndroid); + } +#endif // defined(XR_USE_PLATFORM_ANDROID) + + // Log available layers. + { + XrResult result; + + PFN_xrEnumerateApiLayerProperties xrEnumerateApiLayerProperties; + OXR(result = xrGetInstanceProcAddr( + XR_NULL_HANDLE, + "xrEnumerateApiLayerProperties", + (PFN_xrVoidFunction*)&xrEnumerateApiLayerProperties)); + if (result != XR_SUCCESS) { + ALOGE("Failed to get xrEnumerateApiLayerProperties function pointer."); + exit(1); + } + + uint32_t layerCount = 0; + OXR(xrEnumerateApiLayerProperties(0, &layerCount, NULL)); + std::vector layerProperties(layerCount, {XR_TYPE_API_LAYER_PROPERTIES}); + OXR(xrEnumerateApiLayerProperties(layerCount, &layerCount, layerProperties.data())); + + for (const auto& layer : layerProperties) { + ALOGV("Found layer %s", layer.layerName); + } + } + + // Check that the extensions required are present. + const char* const requiredExtensionNames[] = { +#if defined(XR_USE_GRAPHICS_API_OPENGL_ES) + XR_KHR_OPENGL_ES_ENABLE_EXTENSION_NAME, +#elif defined(XR_USE_GRAPHICS_API_OPENGL) + XR_KHR_OPENGL_ENABLE_EXTENSION_NAME, +#endif +#if defined(XR_USE_PLATFORM_ANDROID) + XR_EXT_PERFORMANCE_SETTINGS_EXTENSION_NAME, + XR_KHR_ANDROID_THREAD_SETTINGS_EXTENSION_NAME, +#endif // defined(XR_USE_PLATFORM_ANDROID) + XR_FB_PASSTHROUGH_EXTENSION_NAME, + XR_FB_TRIANGLE_MESH_EXTENSION_NAME + }; + const uint32_t numRequiredExtensions = + sizeof(requiredExtensionNames) / sizeof(requiredExtensionNames[0]); + + // Check the list of required extensions against what is supported by the runtime. + { + uint32_t numOutputExtensions = 0; + OXR(xrEnumerateInstanceExtensionProperties(nullptr, 0, &numOutputExtensions, nullptr)); + ALOGV("xrEnumerateInstanceExtensionProperties found %u extension(s).", numOutputExtensions); + + auto extensionProperties = + std::vector(numOutputExtensions, {XR_TYPE_EXTENSION_PROPERTIES}); + + OXR(xrEnumerateInstanceExtensionProperties( + NULL, numOutputExtensions, &numOutputExtensions, extensionProperties.data())); + for (uint32_t i = 0; i < numOutputExtensions; i++) { + ALOGV("Extension #%d = '%s'.", i, extensionProperties[i].extensionName); + } + + for (uint32_t i = 0; i < numRequiredExtensions; i++) { + bool found = false; + for (uint32_t j = 0; j < numOutputExtensions; j++) { + if (!strcmp(requiredExtensionNames[i], extensionProperties[j].extensionName)) { + ALOGV("Found required extension %s", requiredExtensionNames[i]); + found = true; + break; + } + } + if (!found) { + ALOGE("Failed to find required extension %s", requiredExtensionNames[i]); + exit(1); + } + } + } + + // Create the OpenXR instance. + XrApplicationInfo appInfo = {}; + strcpy(appInfo.applicationName, "XrPassthrough"); + appInfo.applicationVersion = 0; + strcpy(appInfo.engineName, "Oculus Mobile Sample"); + appInfo.engineVersion = 0; + appInfo.apiVersion = XR_MAKE_VERSION(1, 0, 34); + + XrInstanceCreateInfo instanceCreateInfo = {XR_TYPE_INSTANCE_CREATE_INFO}; + instanceCreateInfo.createFlags = 0; + instanceCreateInfo.applicationInfo = appInfo; + instanceCreateInfo.enabledApiLayerCount = 0; + instanceCreateInfo.enabledApiLayerNames = NULL; + instanceCreateInfo.enabledExtensionCount = numRequiredExtensions; + instanceCreateInfo.enabledExtensionNames = requiredExtensionNames; + + XrResult initResult; + OXR(initResult = xrCreateInstance(&instanceCreateInfo, &app.Instance)); + if (initResult != XR_SUCCESS) { + ALOGE("Failed to create XR app.Instance: %d.", initResult); + exit(1); + } + // Set the global used in macros + instance = app.Instance; + + XrInstanceProperties instanceInfo = {XR_TYPE_INSTANCE_PROPERTIES}; + OXR(xrGetInstanceProperties(app.Instance, &instanceInfo)); + ALOGV( + "Runtime %s: Version : %u.%u.%u", + instanceInfo.runtimeName, + XR_VERSION_MAJOR(instanceInfo.runtimeVersion), + XR_VERSION_MINOR(instanceInfo.runtimeVersion), + XR_VERSION_PATCH(instanceInfo.runtimeVersion)); + + XrSystemGetInfo systemGetInfo = {XR_TYPE_SYSTEM_GET_INFO}; + systemGetInfo.formFactor = XR_FORM_FACTOR_HEAD_MOUNTED_DISPLAY; + + XrSystemId systemId; + OXR(initResult = xrGetSystem(app.Instance, &systemGetInfo, &systemId)); + if (initResult != XR_SUCCESS) { + if (initResult == XR_ERROR_FORM_FACTOR_UNAVAILABLE) { + ALOGE("Failed to get system; the specified form factor is not available. Is your headset connected?"); + } else { + ALOGE("xrGetSystem failed, error %d", initResult); + } + exit(1); + } + + XrSystemProperties systemProperties = {XR_TYPE_SYSTEM_PROPERTIES}; + OXR(xrGetSystemProperties(app.Instance, systemId, &systemProperties)); + + ALOGV( + "System Properties: Name=%s VendorId=%x", + systemProperties.systemName, + systemProperties.vendorId); + ALOGV( + "System Graphics Properties: MaxWidth=%d MaxHeight=%d MaxLayers=%d", + systemProperties.graphicsProperties.maxSwapchainImageWidth, + systemProperties.graphicsProperties.maxSwapchainImageHeight, + systemProperties.graphicsProperties.maxLayerCount); + ALOGV( + "System Tracking Properties: OrientationTracking=%s PositionTracking=%s", + systemProperties.trackingProperties.orientationTracking ? "True" : "False", + systemProperties.trackingProperties.positionTracking ? "True" : "False"); + + assert(MaxLayerCount <= systemProperties.graphicsProperties.maxLayerCount); + + // Get the graphics requirements. +#if defined(XR_USE_GRAPHICS_API_OPENGL_ES) + PFN_xrGetOpenGLESGraphicsRequirementsKHR pfnGetOpenGLESGraphicsRequirementsKHR = NULL; + OXR(xrGetInstanceProcAddr( + app.Instance, + "xrGetOpenGLESGraphicsRequirementsKHR", + (PFN_xrVoidFunction*)(&pfnGetOpenGLESGraphicsRequirementsKHR))); + + XrGraphicsRequirementsOpenGLESKHR graphicsRequirements = {XR_TYPE_GRAPHICS_REQUIREMENTS_OPENGL_ES_KHR}; + OXR(pfnGetOpenGLESGraphicsRequirementsKHR(app.Instance, systemId, &graphicsRequirements)); +#elif defined(XR_USE_GRAPHICS_API_OPENGL) + PFN_xrGetOpenGLGraphicsRequirementsKHR pfnGetOpenGLGraphicsRequirementsKHR = NULL; + OXR(xrGetInstanceProcAddr( + app.Instance, + "xrGetOpenGLGraphicsRequirementsKHR", + (PFN_xrVoidFunction*)(&pfnGetOpenGLGraphicsRequirementsKHR))); + + XrGraphicsRequirementsOpenGLKHR graphicsRequirements = {XR_TYPE_GRAPHICS_REQUIREMENTS_OPENGL_KHR}; + OXR(pfnGetOpenGLGraphicsRequirementsKHR(app.Instance, systemId, &graphicsRequirements)); +#endif + + // Create the EGL Context + app.egl.CreateContext(nullptr); + + // Check the graphics requirements. + int eglMajor = 0; + int eglMinor = 0; + glGetIntegerv(GL_MAJOR_VERSION, &eglMajor); + glGetIntegerv(GL_MINOR_VERSION, &eglMinor); + const XrVersion eglVersion = XR_MAKE_VERSION(eglMajor, eglMinor, 0); + if (eglVersion < graphicsRequirements.minApiVersionSupported || + eglVersion > graphicsRequirements.maxApiVersionSupported) { + ALOGE("GLES version %d.%d not supported", eglMajor, eglMinor); + exit(0); + } + + app.CpuLevel = CPU_LEVEL; + app.GpuLevel = GPU_LEVEL; +#if defined(ANDROID) + app.MainThreadTid = gettid(); +#else + app.MainThreadTid = (int)std::hash{}(std::this_thread::get_id()); +#endif + + app.SystemId = systemId; + + // FB_passthrough sample begin + INIT_PFN(xrCreatePassthroughFB); + INIT_PFN(xrDestroyPassthroughFB); + INIT_PFN(xrPassthroughStartFB); + INIT_PFN(xrPassthroughPauseFB); + INIT_PFN(xrCreatePassthroughLayerFB); + INIT_PFN(xrDestroyPassthroughLayerFB); + INIT_PFN(xrPassthroughLayerSetStyleFB); + INIT_PFN(xrPassthroughLayerPauseFB); + INIT_PFN(xrPassthroughLayerResumeFB); + INIT_PFN(xrCreateTriangleMeshFB); + INIT_PFN(xrDestroyTriangleMeshFB); + INIT_PFN(xrTriangleMeshGetVertexBufferFB); + INIT_PFN(xrTriangleMeshGetIndexBufferFB); + INIT_PFN(xrTriangleMeshBeginUpdateFB); + INIT_PFN(xrTriangleMeshEndUpdateFB); + INIT_PFN(xrCreateGeometryInstanceFB); + INIT_PFN(xrDestroyGeometryInstanceFB); + INIT_PFN(xrGeometryInstanceSetTransformFB); + // FB_passthrough sample end + + // Create the OpenXR Session. +#if defined(XR_USE_GRAPHICS_API_OPENGL_ES) + XrGraphicsBindingOpenGLESAndroidKHR graphicsBinding = {XR_TYPE_GRAPHICS_BINDING_OPENGL_ES_ANDROID_KHR}; + graphicsBinding.display = app.egl.Display; + graphicsBinding.config = app.egl.Config; + graphicsBinding.context = app.egl.Context; +#elif defined(XR_USE_GRAPHICS_API_OPENGL) + XrGraphicsBindingOpenGLWin32KHR graphicsBinding = {XR_TYPE_GRAPHICS_BINDING_OPENGL_WIN32_KHR}; + graphicsBinding.hDC = app.egl.hDC; + graphicsBinding.hGLRC = app.egl.hGLRC; +#endif + + XrSessionCreateInfo sessionCreateInfo = {XR_TYPE_SESSION_CREATE_INFO}; + sessionCreateInfo.next = &graphicsBinding; + sessionCreateInfo.createFlags = 0; + sessionCreateInfo.systemId = app.SystemId; + + OXR(initResult = xrCreateSession(app.Instance, &sessionCreateInfo, &app.Session)); + if (initResult != XR_SUCCESS) { + ALOGE("Failed to create XR session: %d.", initResult); + exit(1); + } + + // App only supports the primary stereo view config. + const XrViewConfigurationType supportedViewConfigType = + XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO; + + // Enumerate the viewport configurations. + uint32_t viewportConfigTypeCount = 0; + OXR(xrEnumerateViewConfigurations( + app.Instance, app.SystemId, 0, &viewportConfigTypeCount, NULL)); + + auto viewportConfigurationTypes = new XrViewConfigurationType[viewportConfigTypeCount]; + + OXR(xrEnumerateViewConfigurations( + app.Instance, + app.SystemId, + viewportConfigTypeCount, + &viewportConfigTypeCount, + viewportConfigurationTypes)); + + ALOGV("Available Viewport Configuration Types: %d", viewportConfigTypeCount); + + for (uint32_t i = 0; i < viewportConfigTypeCount; i++) { + const XrViewConfigurationType viewportConfigType = viewportConfigurationTypes[i]; + + ALOGV( + "Viewport configuration type %d : %s", + viewportConfigType, + viewportConfigType == supportedViewConfigType ? "Selected" : ""); + + XrViewConfigurationProperties viewportConfig = {XR_TYPE_VIEW_CONFIGURATION_PROPERTIES}; + OXR(xrGetViewConfigurationProperties( + app.Instance, app.SystemId, viewportConfigType, &viewportConfig)); + ALOGV( + "FovMutable=%s ConfigurationType %d", + viewportConfig.fovMutable ? "true" : "false", + viewportConfig.viewConfigurationType); + + uint32_t viewCount; + OXR(xrEnumerateViewConfigurationViews( + app.Instance, app.SystemId, viewportConfigType, 0, &viewCount, NULL)); + + if (viewCount > 0) { + auto elements = new XrViewConfigurationView[viewCount]; + + for (uint32_t e = 0; e < viewCount; e++) { + elements[e].type = XR_TYPE_VIEW_CONFIGURATION_VIEW; + elements[e].next = NULL; + } + + OXR(xrEnumerateViewConfigurationViews( + app.Instance, app.SystemId, viewportConfigType, viewCount, &viewCount, elements)); + + // Log the view config info for each view type for debugging purposes. + for (uint32_t e = 0; e < viewCount; e++) { + const XrViewConfigurationView* element = &elements[e]; + (void)element; + + ALOGV( + "Viewport [%d]: Recommended Width=%d Height=%d SampleCount=%d", + e, + element->recommendedImageRectWidth, + element->recommendedImageRectHeight, + element->recommendedSwapchainSampleCount); + + ALOGV( + "Viewport [%d]: Max Width=%d Height=%d SampleCount=%d", + e, + element->maxImageRectWidth, + element->maxImageRectHeight, + element->maxSwapchainSampleCount); + } + + // Cache the view config properties for the selected config type. + if (viewportConfigType == supportedViewConfigType) { + assert(viewCount == NUM_EYES); + for (uint32_t e = 0; e < viewCount; e++) { + app.ViewConfigurationView[e] = elements[e]; + } + } + + delete[] elements; + } else { + ALOGE("Empty viewport configuration type: %d", viewCount); + } + } + + delete[] viewportConfigurationTypes; + + // Get the viewport configuration info for the chosen viewport configuration type. + app.ViewportConfig.type = XR_TYPE_VIEW_CONFIGURATION_PROPERTIES; + + OXR(xrGetViewConfigurationProperties( + app.Instance, app.SystemId, supportedViewConfigType, &app.ViewportConfig)); + + bool stageSupported = false; + + uint32_t numOutputSpaces = 0; + OXR(xrEnumerateReferenceSpaces(app.Session, 0, &numOutputSpaces, NULL)); + + auto referenceSpaces = new XrReferenceSpaceType[numOutputSpaces]; + + OXR(xrEnumerateReferenceSpaces( + app.Session, numOutputSpaces, &numOutputSpaces, referenceSpaces)); + + for (uint32_t i = 0; i < numOutputSpaces; i++) { + if (referenceSpaces[i] == XR_REFERENCE_SPACE_TYPE_STAGE) { + stageSupported = true; + break; + } + } + + delete[] referenceSpaces; + + // Create a space to the first path + XrReferenceSpaceCreateInfo spaceCreateInfo = {XR_TYPE_REFERENCE_SPACE_CREATE_INFO}; + spaceCreateInfo.referenceSpaceType = XR_REFERENCE_SPACE_TYPE_VIEW; + spaceCreateInfo.poseInReferenceSpace.orientation.w = 1.0f; + OXR(xrCreateReferenceSpace(app.Session, &spaceCreateInfo, &app.HeadSpace)); + + spaceCreateInfo.referenceSpaceType = XR_REFERENCE_SPACE_TYPE_LOCAL; + OXR(xrCreateReferenceSpace(app.Session, &spaceCreateInfo, &app.LocalSpace)); + + if (stageSupported) { + spaceCreateInfo.referenceSpaceType = XR_REFERENCE_SPACE_TYPE_STAGE; + spaceCreateInfo.poseInReferenceSpace.position.y = 0.0f; + OXR(xrCreateReferenceSpace(app.Session, &spaceCreateInfo, &app.StageSpace)); + ALOGV("Created stage space"); + } + + XrView projections[NUM_EYES]; + for (int eye = 0; eye < NUM_EYES; eye++) { + projections[eye] = XrView{XR_TYPE_VIEW}; + } + + GLenum format = GL_SRGB8_ALPHA8; + int width = app.ViewConfigurationView[0].recommendedImageRectWidth; + int height = app.ViewConfigurationView[0].recommendedImageRectHeight; + + XrSwapchainCreateInfo swapChainCreateInfo = {XR_TYPE_SWAPCHAIN_CREATE_INFO}; + swapChainCreateInfo.usageFlags = + XR_SWAPCHAIN_USAGE_SAMPLED_BIT | XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT; + swapChainCreateInfo.format = format; + swapChainCreateInfo.sampleCount = 1; + swapChainCreateInfo.width = width; + swapChainCreateInfo.height = height; + swapChainCreateInfo.faceCount = 1; + swapChainCreateInfo.arraySize = 2; + swapChainCreateInfo.mipCount = 1; + + // Create the swapchain. + OXR(xrCreateSwapchain(app.Session, &swapChainCreateInfo, &app.ColorSwapChain)); + OXR(xrEnumerateSwapchainImages(app.ColorSwapChain, 0, &app.SwapChainLength, nullptr)); +#if defined(XR_USE_GRAPHICS_API_OPENGL_ES) + auto images = new XrSwapchainImageOpenGLESKHR[app.SwapChainLength]; + // Populate the swapchain image array. + for (uint32_t i = 0; i < app.SwapChainLength; i++) { + images[i] = {XR_TYPE_SWAPCHAIN_IMAGE_OPENGL_ES_KHR}; + } +#elif defined(XR_USE_GRAPHICS_API_OPENGL) + auto images = new XrSwapchainImageOpenGLKHR[app.SwapChainLength]; + // Populate the swapchain image array. + for (uint32_t i = 0; i < app.SwapChainLength; i++) { + images[i] = {XR_TYPE_SWAPCHAIN_IMAGE_OPENGL_KHR}; + } +#endif + + OXR(xrEnumerateSwapchainImages( + app.ColorSwapChain, + app.SwapChainLength, + &app.SwapChainLength, + (XrSwapchainImageBaseHeader*)images)); + + auto colorTextures = new GLuint[app.SwapChainLength]; + for (uint32_t i = 0; i < app.SwapChainLength; i++) { + colorTextures[i] = GLuint(images[i].image); + } + + app.appRenderer.Create( + format, width, height, NUM_MULTI_SAMPLES, app.SwapChainLength, colorTextures); + + delete[] images; + delete[] colorTextures; + + AppInput_init(app); + + // FB_passthrough sample begin + // Create passthrough objects + XrPassthroughFB passthrough = XR_NULL_HANDLE; + XrPassthroughLayerFB passthroughLayer = XR_NULL_HANDLE; + XrPassthroughLayerFB reconPassthroughLayer = XR_NULL_HANDLE; + XrPassthroughLayerFB geomPassthroughLayer = XR_NULL_HANDLE; + XrGeometryInstanceFB geomInstance = XR_NULL_HANDLE; + { + XrPassthroughCreateInfoFB ptci = {XR_TYPE_PASSTHROUGH_CREATE_INFO_FB}; + XrResult result; + OXR(result = xrCreatePassthroughFB(app.Session, &ptci, &passthrough)); + + if (XR_SUCCEEDED(result)) { + XrPassthroughLayerCreateInfoFB plci = {XR_TYPE_PASSTHROUGH_LAYER_CREATE_INFO_FB}; + plci.passthrough = passthrough; + plci.purpose = XR_PASSTHROUGH_LAYER_PURPOSE_RECONSTRUCTION_FB; + OXR(xrCreatePassthroughLayerFB(app.Session, &plci, &reconPassthroughLayer)); + } + + if (XR_SUCCEEDED(result)) { + XrPassthroughLayerCreateInfoFB plci = {XR_TYPE_PASSTHROUGH_LAYER_CREATE_INFO_FB}; + plci.passthrough = passthrough; + plci.purpose = XR_PASSTHROUGH_LAYER_PURPOSE_PROJECTED_FB; + OXR(xrCreatePassthroughLayerFB(app.Session, &plci, &geomPassthroughLayer)); + + const XrVector3f verts[] = {{0, 0, 0}, {1, 0, 0}, {0, 1, 0}, {1, 1, 0}}; + const uint32_t indexes[] = {0, 1, 2, 2, 1, 3}; + XrTriangleMeshCreateInfoFB tmci = {XR_TYPE_TRIANGLE_MESH_CREATE_INFO_FB}; + tmci.vertexCount = 4; + tmci.vertexBuffer = &verts[0]; + tmci.triangleCount = 2; + tmci.indexBuffer = &indexes[0]; + + XrTriangleMeshFB mesh = XR_NULL_HANDLE; + OXR(xrCreateTriangleMeshFB(app.Session, &tmci, &mesh)); + + XrGeometryInstanceCreateInfoFB gici = {XR_TYPE_GEOMETRY_INSTANCE_CREATE_INFO_FB}; + gici.layer = geomPassthroughLayer; + gici.mesh = mesh; + gici.baseSpace = app.LocalSpace; + gici.pose.orientation.w = 1.0f; + gici.scale = {1.0f, 1.0f, 1.0f}; + OXR(xrCreateGeometryInstanceFB(app.Session, &gici, &geomInstance)); + } + } + // FB_passthrough sample end + +#if defined(XR_USE_PLATFORM_ANDROID) + androidApp->userData = &app; + androidApp->onAppCmd = app_handle_cmd; +#endif // defined(XR_USE_PLATFORM_ANDROID) + + bool stageBoundsDirty = true; + + int frameCount = -1; + int framesCyclePaused = 0; + bool cyclePaused = false; + + constexpr int framesPerMode = 400; + + enum Mode { + Mode_Passthrough_Basic = 0, + Mode_Passthrough_DynamicRamp = 1, + Mode_Passthrough_GreenRampYellowEdges = 2, + Mode_Passthrough_Masked = 3, + Mode_Passthrough_ProjQuad = 4, + Mode_Passthrough_Stopped = 5, + Mode_NumModes = 6 + }; + + float clearColor[4] = {0.0f, 0.0f, 0.0f, 0.2f}; + +#if defined(XR_USE_PLATFORM_ANDROID) + while (androidApp->destroyRequested == 0) +#else + while (true) +#endif + { + frameCount++; + +#if defined(XR_USE_PLATFORM_ANDROID) + // Read all pending events. + for (;;) { + int events; + struct android_poll_source* source; + // If the timeout is zero, returns immediately without blocking. + // If the timeout is negative, waits indefinitely until an event appears. + const int timeoutMilliseconds = (app.Resumed == false && app.SessionActive == false && + androidApp->destroyRequested == 0) + ? -1 + : 0; + if (ALooper_pollAll(timeoutMilliseconds, NULL, &events, (void**)&source) < 0) { + break; + } + + // Process this event. + if (source != NULL) { + source->process(androidApp, source); + } + } +#elif defined(XR_USE_PLATFORM_WIN32) + MSG msg; + while (::PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) > 0) { + if (msg.message == WM_QUIT) { + app.ShouldExit = true; + } else { + ::TranslateMessage(&msg); + ::DispatchMessage(&msg); + } + } +#endif // defined(XR_USE_PLATFORM_ANDROID) + + app.HandleXrEvents(); + + if (app.ShouldExit) { + break; + } + + if (app.SessionActive == false) { + frameCount = -1; + framesCyclePaused = 0; + continue; + } + + AppInput_syncActions(app); + if (boolState.type != 0 && boolState.changedSinceLastSync == XR_TRUE && + boolState.currentState != XR_FALSE) { + cyclePaused = !cyclePaused; + } + + if (cyclePaused) { + clearColor[0] = 0.3f; + framesCyclePaused++; + } else { + clearColor[0] = 0.0f; + } + app.appRenderer.scene.SetClearColor(clearColor); + + // Create the scene if not yet created. + // The scene is created here to be able to show a loading icon. + if (!app.appRenderer.scene.IsCreated()) { + // Create the scene. + app.appRenderer.scene.Create(); + } + + if (stageBoundsDirty) { + UpdateStageBounds(app); + stageBoundsDirty = false; + } + + // NOTE: OpenXR does not use the concept of frame indices. Instead, + // XrWaitFrame returns the predicted display time. + XrFrameWaitInfo waitFrameInfo = {XR_TYPE_FRAME_WAIT_INFO}; + + XrFrameState frameState = {XR_TYPE_FRAME_STATE}; + + OXR(xrWaitFrame(app.Session, &waitFrameInfo, &frameState)); + + // Get the HMD pose, predicted for the middle of the time period during which + // the new eye images will be displayed. The number of frames predicted ahead + // depends on the pipeline depth of the engine and the synthesis rate. + // The better the prediction, the less black will be pulled in at the edges. + XrFrameBeginInfo beginFrameDesc = {XR_TYPE_FRAME_BEGIN_INFO}; + OXR(xrBeginFrame(app.Session, &beginFrameDesc)); + + XrPosef xfLocalFromHead; + { + XrSpaceLocation loc = {XR_TYPE_SPACE_LOCATION}; + OXR(xrLocateSpace( + app.HeadSpace, app.LocalSpace, frameState.predictedDisplayTime, &loc)); + xfLocalFromHead = loc.pose; + } + + XrViewState viewState = {XR_TYPE_VIEW_STATE}; + + XrViewLocateInfo projectionInfo = {XR_TYPE_VIEW_LOCATE_INFO}; + projectionInfo.viewConfigurationType = app.ViewportConfig.viewConfigurationType; + projectionInfo.displayTime = frameState.predictedDisplayTime; + projectionInfo.space = app.HeadSpace; + + uint32_t projectionCapacityInput = NUM_EYES; + uint32_t projectionCountOutput = projectionCapacityInput; + + OXR(xrLocateViews( + app.Session, + &projectionInfo, + &viewState, + projectionCapacityInput, + &projectionCountOutput, + projections)); + + // update input information + XrSpace controllerSpace[] = { + leftControllerAimSpace, + leftControllerGripSpace, + rightControllerAimSpace, + rightControllerGripSpace, + }; + + bool controllerActive[] = {leftControllerActive, rightControllerActive}; + for (int i = 0; i < 4; i++) { + if (controllerActive[i >> 1]) { + XrSpaceLocation loc = {XR_TYPE_SPACE_LOCATION}; + OXR(xrLocateSpace( + controllerSpace[i], app.LocalSpace, frameState.predictedDisplayTime, &loc)); + app.appRenderer.scene.trackedController[i].Active = + (loc.locationFlags & XR_SPACE_LOCATION_POSITION_VALID_BIT) != 0; + app.appRenderer.scene.trackedController[i].Pose = OvrFromXr(loc.pose); + } else { + app.appRenderer.scene.trackedController[i].Clear(); + } + } + + // FB_passthrough sample begin + // Cycle through passthrough operation / display modes + const Mode prevMode = Mode( + ((frameCount - framesCyclePaused + (framesPerMode * Mode_NumModes) - 1) / + framesPerMode) % + Mode_NumModes); + const Mode mode = Mode(((frameCount - framesCyclePaused) / framesPerMode) % Mode_NumModes); + + if (mode != prevMode) { + // Unset any sticky state from the previous mode + switch (prevMode) { + case Mode_Passthrough_Basic: + OXR(xrPassthroughLayerPauseFB(passthroughLayer)); + break; + case Mode_Passthrough_DynamicRamp: + OXR(xrPassthroughLayerPauseFB(passthroughLayer)); + break; + case Mode_Passthrough_GreenRampYellowEdges: + OXR(xrPassthroughLayerPauseFB(passthroughLayer)); + break; + case Mode_Passthrough_Masked: + OXR(xrPassthroughLayerPauseFB(passthroughLayer)); + clearColor[3] = 0.2f; + break; + case Mode_Passthrough_ProjQuad: + OXR(xrPassthroughLayerPauseFB(passthroughLayer)); + break; + case Mode_Passthrough_Stopped: + OXR(xrPassthroughStartFB(passthrough)); + break; + default: + break; + } + XrPassthroughStyleFB style{XR_TYPE_PASSTHROUGH_STYLE_FB}; + switch (mode) { + case Mode_Passthrough_Basic: + passthroughLayer = reconPassthroughLayer; + OXR(xrPassthroughLayerResumeFB(passthroughLayer)); + style.textureOpacityFactor = 0.5f; + style.edgeColor = {0.0f, 0.0f, 0.0f, 0.0f}; + OXR(xrPassthroughLayerSetStyleFB(passthroughLayer, &style)); + break; + case Mode_Passthrough_DynamicRamp: + passthroughLayer = reconPassthroughLayer; + OXR(xrPassthroughLayerResumeFB(passthroughLayer)); + style.textureOpacityFactor = 0.5f; + style.edgeColor = {0.0f, 0.0f, 0.0f, 0.0f}; + OXR(xrPassthroughLayerSetStyleFB(passthroughLayer, &style)); + break; + case Mode_Passthrough_GreenRampYellowEdges: { + passthroughLayer = reconPassthroughLayer; + OXR(xrPassthroughLayerResumeFB(passthroughLayer)); + // Create a color map which maps each input value to a green ramp + XrPassthroughColorMapMonoToRgbaFB colorMap = { + XR_TYPE_PASSTHROUGH_COLOR_MAP_MONO_TO_RGBA_FB}; + for (int i = 0; i < XR_PASSTHROUGH_COLOR_MAP_MONO_SIZE_FB; ++i) { + float colorValue = i / 255.0f; + colorMap.textureColorMap[i] = {0.0f, colorValue, 0.0f, 1.0f}; + } + style.textureOpacityFactor = 0.5f; + style.edgeColor = {1.0f, 1.0f, 0.0f, 0.5f}; + style.next = &colorMap; + OXR(xrPassthroughLayerSetStyleFB(passthroughLayer, &style)); + } break; + case Mode_Passthrough_Masked: + passthroughLayer = reconPassthroughLayer; + OXR(xrPassthroughLayerResumeFB(passthroughLayer)); + clearColor[3] = 1.0f; + style.textureOpacityFactor = 0.5f; + style.edgeColor = {0.0f, 0.0f, 0.0f, 0.0f}; + OXR(xrPassthroughLayerSetStyleFB(passthroughLayer, &style)); + break; + case Mode_Passthrough_ProjQuad: + passthroughLayer = geomPassthroughLayer; + OXR(xrPassthroughLayerResumeFB(passthroughLayer)); + break; + case Mode_Passthrough_Stopped: + OXR(xrPassthroughPauseFB(passthrough)); + break; + default: + break; + } + } + // per frame style adjustments for DynamicRamp + if (mode == Mode_Passthrough_DynamicRamp) { + const float frac = + ((frameCount - framesCyclePaused) % framesPerMode) / (framesPerMode - 1.0f); + // phase shifting color map + XrPassthroughColorMapMonoToRgbaFB colorMap = { + XR_TYPE_PASSTHROUGH_COLOR_MAP_MONO_TO_RGBA_FB}; + for (int i = 0; i < XR_PASSTHROUGH_COLOR_MAP_MONO_SIZE_FB; ++i) { + float v = i / 64.0f; + v -= floor(v) - 0.5f; + v = fabsf(v); + v = v * v; + int idx = (i + int(4 * frac * XR_PASSTHROUGH_COLOR_MAP_MONO_SIZE_FB)) % + XR_PASSTHROUGH_COLOR_MAP_MONO_SIZE_FB; + colorMap.textureColorMap[idx] = {v, v, v, 1.0f}; + } + XrPassthroughStyleFB style{XR_TYPE_PASSTHROUGH_STYLE_FB}; + style.textureOpacityFactor = 0.5f; + style.edgeColor = {0.0f, 0.0f, 0.0f, 0.0f}; + style.next = &colorMap; + OXR(xrPassthroughLayerSetStyleFB(passthroughLayer, &style)); + } else if (mode == Mode_Passthrough_ProjQuad && leftControllerGripSpace != XR_NULL_HANDLE) { + XrGeometryInstanceTransformFB git = {XR_TYPE_GEOMETRY_INSTANCE_TRANSFORM_FB}; + git.baseSpace = leftControllerGripSpace; + git.time = frameState.predictedDisplayTime; + // Approximate ring orientation relative to grip + Quatf rx(Vector3f(1, 0, 0), DegreeToRad(-20.0f)); + Quatf ry(Vector3f(0, 1, 0), DegreeToRad(-11.0f)); + Quatf rz(Vector3f(0, 0, 1), DegreeToRad(0.0f)); + Quatf r = rz * ry * rx; + git.pose.orientation = {r.x, r.y, r.z, r.w}; + git.pose.position = {0.05f, -0.15f, -0.05f}; + git.scale = {0.6f, 0.3f, 1.0f}; + OXR(xrGeometryInstanceSetTransformFB(geomInstance, &git)); + } + // FB_passthrough sample end + + AppRenderer::FrameIn frameIn; + uint32_t chainIndex = 0; + XrSwapchainImageAcquireInfo acquireInfo = {XR_TYPE_SWAPCHAIN_IMAGE_ACQUIRE_INFO, NULL}; + OXR(xrAcquireSwapchainImage(app.ColorSwapChain, &acquireInfo, &chainIndex)); + frameIn.SwapChainIndex = int(chainIndex); + + XrPosef xfLocalFromEye[NUM_EYES]; + + for (int eye = 0; eye < NUM_EYES; eye++) { + XrPosef xfHeadFromEye = projections[eye].pose; + XrPosef_Multiply(&xfLocalFromEye[eye], &xfLocalFromHead, &xfHeadFromEye); + + XrPosef xfEyeFromLocal; + XrPosef_Invert(&xfEyeFromLocal, &xfLocalFromEye[eye]); + + XrMatrix4x4f viewMat{}; + XrMatrix4x4f_CreateFromRigidTransform(&viewMat, &xfEyeFromLocal); + + const XrFovf fov = projections[eye].fov; + XrMatrix4x4f projMat; + XrMatrix4x4f_CreateProjectionFov(&projMat, GRAPHICS_OPENGL_ES, fov, 0.1f, 0.0f); + + frameIn.View[eye] = OvrFromXr(viewMat); + frameIn.Proj[eye] = OvrFromXr(projMat); + } + + if (app.StageSpace != XR_NULL_HANDLE) { + XrSpaceLocation loc = {XR_TYPE_SPACE_LOCATION}; + OXR(xrLocateSpace( + app.StageSpace, app.LocalSpace, frameState.predictedDisplayTime, &loc)); + XrPosef xfLocalFromStage = loc.pose; + + frameIn.HasStage = true; + frameIn.StagePose = OvrFromXr(xfLocalFromStage); + frameIn.StageScale = app.StageBounds; + } else { + frameIn.HasStage = false; + } + + XrSwapchainImageWaitInfo waitInfo = {XR_TYPE_SWAPCHAIN_IMAGE_WAIT_INFO}; + waitInfo.timeout = 1000000000; /* timeout in nanoseconds */ + XrResult res = xrWaitSwapchainImage(app.ColorSwapChain, &waitInfo); + int retry = 0; + while (res == XR_TIMEOUT_EXPIRED) { + res = xrWaitSwapchainImage(app.ColorSwapChain, &waitInfo); + retry++; + ALOGV( + " Retry xrWaitSwapchainImage %d times due to XR_TIMEOUT_EXPIRED (duration %f seconds)", + retry, + waitInfo.timeout * (1E-9)); + } + + app.appRenderer.RenderFrame(frameIn); + + XrSwapchainImageReleaseInfo releaseInfo = {XR_TYPE_SWAPCHAIN_IMAGE_RELEASE_INFO, NULL}; + OXR(xrReleaseSwapchainImage(app.ColorSwapChain, &releaseInfo)); + + // Set-up the compositor layers for this frame. + // NOTE: Multiple independent layers are allowed, but they need to be added + // in a depth consistent order. + + XrCompositionLayerProjectionView proj_views[2] = {}; + + app.LayerCount = 0; + memset(app.Layers, 0, sizeof(CompositionLayerUnion) * MaxLayerCount); + + // FB_passthrough sample begin + // passthrough layer is backmost layer (if available) + if (passthroughLayer != XR_NULL_HANDLE) { + XrCompositionLayerPassthroughFB passthrough_layer = {XR_TYPE_COMPOSITION_LAYER_PASSTHROUGH_FB}; + passthrough_layer.layerHandle = passthroughLayer; + passthrough_layer.flags = XR_COMPOSITION_LAYER_BLEND_TEXTURE_SOURCE_ALPHA_BIT; + passthrough_layer.space = XR_NULL_HANDLE; + app.Layers[app.LayerCount++].Passthrough = passthrough_layer; + } + // FB_passthrough sample end + + XrCompositionLayerProjection proj_layer = {XR_TYPE_COMPOSITION_LAYER_PROJECTION}; + proj_layer.layerFlags = XR_COMPOSITION_LAYER_BLEND_TEXTURE_SOURCE_ALPHA_BIT; + proj_layer.layerFlags |= XR_COMPOSITION_LAYER_CORRECT_CHROMATIC_ABERRATION_BIT; + proj_layer.layerFlags |= XR_COMPOSITION_LAYER_UNPREMULTIPLIED_ALPHA_BIT; + proj_layer.space = app.LocalSpace; + proj_layer.viewCount = NUM_EYES; + proj_layer.views = proj_views; + + for (int eye = 0; eye < NUM_EYES; eye++) { + XrCompositionLayerProjectionView& proj_view = proj_views[eye]; + proj_view = {XR_TYPE_COMPOSITION_LAYER_PROJECTION_VIEW}; + proj_view.pose = xfLocalFromEye[eye]; + proj_view.fov = projections[eye].fov; + + proj_view.subImage.swapchain = app.ColorSwapChain; + proj_view.subImage.imageRect.offset.x = 0; + proj_view.subImage.imageRect.offset.y = 0; + proj_view.subImage.imageRect.extent.width = width; + proj_view.subImage.imageRect.extent.height = height; + proj_view.subImage.imageArrayIndex = eye; + } + + app.Layers[app.LayerCount++].Projection = proj_layer; + + // Compose the layers for this frame. + const XrCompositionLayerBaseHeader* layers[MaxLayerCount] = {}; + for (int i = 0; i < app.LayerCount; i++) { + layers[i] = (const XrCompositionLayerBaseHeader*)&app.Layers[i]; + } + + XrFrameEndInfo endFrameInfo = {XR_TYPE_FRAME_END_INFO}; + endFrameInfo.displayTime = frameState.predictedDisplayTime; + endFrameInfo.environmentBlendMode = XR_ENVIRONMENT_BLEND_MODE_OPAQUE; + endFrameInfo.layerCount = app.LayerCount; + endFrameInfo.layers = layers; + + OXR(xrEndFrame(app.Session, &endFrameInfo)); + } + + app.appRenderer.Destroy(); + + AppInput_shutdown(); + + OXR(xrDestroySwapchain(app.ColorSwapChain)); + OXR(xrDestroySpace(app.HeadSpace)); + OXR(xrDestroySpace(app.LocalSpace)); + // StageSpace is optional. + if (app.StageSpace != XR_NULL_HANDLE) { + OXR(xrDestroySpace(app.StageSpace)); + } + OXR(xrDestroySession(app.Session)); + + app.egl.DestroyContext(); + + OXR(xrDestroyInstance(app.Instance)); + +#if defined(XR_USE_PLATFORM_ANDROID) + (*androidApp->activity->vm).DetachCurrentThread(); +#endif // defined(XR_USE_PLATFORM_ANDROID) +} diff --git a/Samples/XrSamples/XrPassthrough/Src/XrPassthrough.h b/Samples/XrSamples/XrPassthrough/Src/XrPassthrough.h new file mode 100755 index 0000000..39dee90 --- /dev/null +++ b/Samples/XrSamples/XrPassthrough/Src/XrPassthrough.h @@ -0,0 +1,144 @@ +#pragma once + +#if defined(ANDROID) +#include +#include +#include +#include +#define XR_USE_GRAPHICS_API_OPENGL_ES 1 +#define XR_USE_PLATFORM_ANDROID 1 +#else +#include "unknwn.h" +#include "Render/GlWrapperWin32.h" +#define XR_USE_GRAPHICS_API_OPENGL 1 +#define XR_USE_PLATFORM_WIN32 1 +#endif // defined(ANDROID) + +#include +#include +#include + +#include "XrPassthroughGl.h" + +void OXR_CheckErrors(XrResult result, const char* function, bool failOnError); +#define OXR(func) OXR_CheckErrors(func, #func, true); + +inline OVR::Matrix4f OvrFromXr(const XrMatrix4x4f& x) { + return OVR::Matrix4f( + x.m[0x0], + x.m[0x1], + x.m[0x2], + x.m[0x3], + x.m[0x4], + x.m[0x5], + x.m[0x6], + x.m[0x7], + x.m[0x8], + x.m[0x9], + x.m[0xa], + x.m[0xb], + x.m[0xc], + x.m[0xd], + x.m[0xe], + x.m[0xf]); +} + +inline OVR::Quatf OvrFromXr(const XrQuaternionf& q) { + return OVR::Quatf(q.x, q.y, q.z, q.w); +} + +inline OVR::Vector3f OvrFromXr(const XrVector3f& v) { + return OVR::Vector3f(v.x, v.y, v.z); +} + +inline OVR::Posef OvrFromXr(const XrPosef& p) { + return OVR::Posef(OvrFromXr(p.orientation), OvrFromXr(p.position)); +} + +/* +================================================================================ + +Egl + +================================================================================ +*/ + +struct Egl { + void Clear(); + void CreateContext(const Egl* shareEgl); + void DestroyContext(); +#if defined(XR_USE_GRAPHICS_API_OPENGL_ES) + EGLint MajorVersion; + EGLint MinorVersion; + EGLDisplay Display; + EGLConfig Config; + EGLSurface TinySurface; + EGLSurface MainSurface; + EGLContext Context; +#elif defined(XR_USE_GRAPHICS_API_OPENGL) + HDC hDC; + HGLRC hGLRC; +#endif +}; + +/* +================================================================================ + +App + +================================================================================ +*/ + +union CompositionLayerUnion { + XrCompositionLayerProjection Projection; + XrCompositionLayerQuad Quad; + XrCompositionLayerCylinderKHR Cylinder; + XrCompositionLayerCubeKHR Cube; + XrCompositionLayerEquirectKHR Equirect; + // FB_passthrough sample begin + XrCompositionLayerPassthroughFB Passthrough; + // FB_passthrough sample end +}; + +enum { MaxLayerCount = 16 }; + +struct App { + void Clear(); + void HandleSessionStateChanges(XrSessionState state); + void HandleXrEvents(); + + Egl egl; + +#if defined(XR_USE_PLATFORM_ANDROID) + bool Resumed; +#endif // defined(XR_USE_PLATFORM_ANDROID) + bool ShouldExit; + bool Focused; + + XrInstance Instance; + XrSession Session; + XrViewConfigurationProperties ViewportConfig; + XrViewConfigurationView ViewConfigurationView[NUM_EYES]; + XrSystemId SystemId; + XrSpace HeadSpace; + XrSpace LocalSpace; + XrSpace StageSpace; + bool SessionActive; + + int SwapInterval; + int CpuLevel; + int GpuLevel; + // These threads will be marked as performance threads. + int MainThreadTid; + int RenderThreadTid; + CompositionLayerUnion Layers[MaxLayerCount]; + int LayerCount; + + bool TouchPadDownLastFrame; + + XrSwapchain ColorSwapChain; + uint32_t SwapChainLength; + OVR::Vector3f StageBounds; + // Provided by XrPassthroughGl, which is not aware of VrApi or OpenXR + AppRenderer appRenderer; +}; diff --git a/Samples/XrSamples/XrPassthrough/Src/XrPassthroughGl.cpp b/Samples/XrSamples/XrPassthrough/Src/XrPassthroughGl.cpp new file mode 100755 index 0000000..c180aae --- /dev/null +++ b/Samples/XrSamples/XrPassthrough/Src/XrPassthroughGl.cpp @@ -0,0 +1,1182 @@ +/************************************************************************************ + +Filename : XrPassthroughGl.cpp +Content : This sample is derived from MagicCarpetXr. + When used in room scale mode, it draws a "carpet" under the + user to indicate where it is safe to walk around. +Created : November, 2018 +Authors : Cass Everitt + +Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved. + +*************************************************************************************/ + +#include +#include +#include +#include + +#if defined(ANDROID) +#include +#include +#include // for prctl( PR_SET_NAME ) +#include +#include // for native window JNI +#include +#endif // defined(ANDROID) + +#include +#include + +#if defined(ANDROID) +#include + +#include +#include +#include +#endif // defined(ANDROID) + +#include "XrPassthroughGl.h" + +using namespace OVR; + +// EXT_texture_border_clamp +#ifndef GL_CLAMP_TO_BORDER +#define GL_CLAMP_TO_BORDER 0x812D +#endif + +#ifndef GL_TEXTURE_BORDER_COLOR +#define GL_TEXTURE_BORDER_COLOR 0x1004 +#endif + +#ifndef GL_FRAMEBUFFER_SRGB_EXT +#define GL_FRAMEBUFFER_SRGB_EXT 0x8DB9 +#endif + +#if !defined(GL_EXT_multisampled_render_to_texture) +typedef void(GL_APIENTRY* PFNGLRENDERBUFFERSTORAGEMULTISAMPLEEXTPROC)( + GLenum target, + GLsizei samples, + GLenum internalformat, + GLsizei width, + GLsizei height); +typedef void(GL_APIENTRY* PFNGLFRAMEBUFFERTEXTURE2DMULTISAMPLEEXTPROC)( + GLenum target, + GLenum attachment, + GLenum textarget, + GLuint texture, + GLint level, + GLsizei samples); +#endif + +#if !defined(GL_OVR_multiview) +typedef void(GL_APIENTRY* PFNGLFRAMEBUFFERTEXTUREMULTIVIEWOVRPROC)( + GLenum target, + GLenum attachment, + GLuint texture, + GLint level, + GLint baseViewIndex, + GLsizei numViews); +#endif + +#if !defined(GL_OVR_multiview_multisampled_render_to_texture) +typedef void(GL_APIENTRY* PFNGLFRAMEBUFFERTEXTUREMULTISAMPLEMULTIVIEWOVRPROC)( + GLenum target, + GLenum attachment, + GLuint texture, + GLint level, + GLsizei samples, + GLint baseViewIndex, + GLsizei numViews); +#endif + +#define DEBUG 1 +#define OVR_LOG_TAG "XrPassthroughGl" + +#if defined(ANDROID) +#define ALOGE(...) __android_log_print(ANDROID_LOG_ERROR, OVR_LOG_TAG, __VA_ARGS__) +#if DEBUG +#define ALOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, OVR_LOG_TAG, __VA_ARGS__) +#else +#define ALOGV(...) +#endif +#else +#define ALOGE(...) \ + printf("ERROR: "); \ + printf(__VA_ARGS__); \ + printf("\n") +#define ALOGV(...) \ + printf("VERBOSE: "); \ + printf(__VA_ARGS__); \ + printf("\n") +#endif + +/* +================================================================================ + +OpenGL-ES Utility Functions + +================================================================================ +*/ + +namespace { +struct OpenGLExtensions_t { + bool multi_view; // GL_OVR_multiview, GL_OVR_multiview2 + bool EXT_texture_border_clamp; // GL_EXT_texture_border_clamp, GL_OES_texture_border_clamp + bool EXT_sRGB_write_control; +}; + +OpenGLExtensions_t glExtensions; +} // namespace + +static void EglInitExtensions() { + glExtensions = {}; + const char* allExtensions = (const char*)glGetString(GL_EXTENSIONS); + if (allExtensions != nullptr) { + glExtensions.multi_view = strstr(allExtensions, "GL_OVR_multiview2") && + strstr(allExtensions, "GL_OVR_multiview_multisampled_render_to_texture"); + + glExtensions.EXT_texture_border_clamp = + strstr(allExtensions, "GL_EXT_texture_border_clamp") || + strstr(allExtensions, "GL_OES_texture_border_clamp"); + glExtensions.EXT_sRGB_write_control = strstr(allExtensions, "GL_EXT_sRGB_write_control"); + } +} + +static const char* GlFrameBufferStatusString(GLenum status) { + switch (status) { + case GL_FRAMEBUFFER_UNDEFINED: + return "GL_FRAMEBUFFER_UNDEFINED"; + case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: + return "GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT"; + case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: + return "GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT"; + case GL_FRAMEBUFFER_UNSUPPORTED: + return "GL_FRAMEBUFFER_UNSUPPORTED"; + case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE: + return "GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE"; + default: + return "unknown"; + } +} + +#ifdef CHECK_GL_ERRORS + +static const char* GlErrorString(GLenum error) { + switch (error) { + case GL_NO_ERROR: + return "GL_NO_ERROR"; + case GL_INVALID_ENUM: + return "GL_INVALID_ENUM"; + case GL_INVALID_VALUE: + return "GL_INVALID_VALUE"; + case GL_INVALID_OPERATION: + return "GL_INVALID_OPERATION"; + case GL_INVALID_FRAMEBUFFER_OPERATION: + return "GL_INVALID_FRAMEBUFFER_OPERATION"; + case GL_OUT_OF_MEMORY: + return "GL_OUT_OF_MEMORY"; + default: + return "unknown"; + } +} + +static void GLCheckErrors(int line) { + for (int i = 0; i < 10; i++) { + const GLenum error = glGetError(); + if (error == GL_NO_ERROR) { + break; + } + ALOGE("GL error on line %d: %s", line, GlErrorString(error)); + } +} + +#define GL(func) \ + func; \ + GLCheckErrors(__LINE__); + +#else // CHECK_GL_ERRORS + +#define GL(func) func; + +#endif // CHECK_GL_ERRORS + +/* +================================================================================ + +Geometry + +================================================================================ +*/ + +enum VertexAttributeLocation { + VERTEX_ATTRIBUTE_LOCATION_POSITION, + VERTEX_ATTRIBUTE_LOCATION_COLOR, + VERTEX_ATTRIBUTE_LOCATION_UV, + VERTEX_ATTRIBUTE_LOCATION_TRANSFORM +}; + +struct VertexAttribute { + enum VertexAttributeLocation location; + const char* name; +}; + +static VertexAttribute ProgramVertexAttributes[] = { + {VERTEX_ATTRIBUTE_LOCATION_POSITION, "vertexPosition"}, + {VERTEX_ATTRIBUTE_LOCATION_COLOR, "vertexColor"}, + {VERTEX_ATTRIBUTE_LOCATION_UV, "vertexUv"}, + {VERTEX_ATTRIBUTE_LOCATION_TRANSFORM, "vertexTransform"}}; + +void Geometry::Clear() { + VertexBuffer = 0; + IndexBuffer = 0; + VertexArrayObject = 0; + VertexCount = 0; + IndexCount = 0; + for (int i = 0; i < MAX_VERTEX_ATTRIB_POINTERS; i++) { + memset(&VertexAttribs[i], 0, sizeof(VertexAttribs[i])); + VertexAttribs[i].Index = -1; + } +} + +void Geometry::CreateBox() { + struct CubeVertices { + float positions[8][4]; + unsigned char colors[8][4]; + }; + + const CubeVertices cubeVertices = { + // positions + {{-1.0f, -1.0f, -1.0f, 1.0f}, + {1.0f, -1.0f, -1.0f, 1.0f}, + {-1.0f, 1.0f, -1.0f, 1.0f}, + {1.0f, 1.0f, -1.0f, 1.0f}, + + {-1.0f, -1.0f, 1.0f, 1.0f}, + {1.0f, -1.0f, 1.0f, 1.0f}, + {-1.0f, 1.0f, 1.0f, 1.0f}, + {1.0f, 1.0f, 1.0f, 1.0f}}, + // colors + { + {255, 0, 0, 255}, + {250, 255, 0, 255}, + {250, 0, 255, 255}, + {255, 255, 0, 255}, + {255, 0, 0, 255}, + {250, 255, 0, 255}, + {250, 0, 255, 255}, + {255, 255, 0, 255}, + }, + }; + + // 6------7 + // /| /| + // 2-+----3 | + // | | | | + // | 4----+-5 + // |/ |/ + // 0------1 + + const unsigned short cubeIndices[36] = {0, 1, 3, 0, 3, 2, + + 5, 4, 6, 5, 6, 7, + + 4, 0, 2, 4, 2, 6, + + 1, 5, 7, 1, 7, 3, + + 4, 5, 1, 4, 1, 0, + + 2, 3, 7, 2, 7, 6}; + + VertexCount = 8; + IndexCount = 36; + + VertexAttribs[0].Index = VERTEX_ATTRIBUTE_LOCATION_POSITION; + VertexAttribs[0].Size = 4; + VertexAttribs[0].Type = GL_FLOAT; + VertexAttribs[0].Normalized = false; + VertexAttribs[0].Stride = sizeof(cubeVertices.positions[0]); + VertexAttribs[0].Pointer = (const GLvoid*)offsetof(CubeVertices, positions); + + VertexAttribs[1].Index = VERTEX_ATTRIBUTE_LOCATION_COLOR; + VertexAttribs[1].Size = 4; + VertexAttribs[1].Type = GL_UNSIGNED_BYTE; + VertexAttribs[1].Normalized = true; + VertexAttribs[1].Stride = sizeof(cubeVertices.colors[0]); + VertexAttribs[1].Pointer = (const GLvoid*)offsetof(CubeVertices, colors); + + GL(glGenBuffers(1, &VertexBuffer)); + GL(glBindBuffer(GL_ARRAY_BUFFER, VertexBuffer)); + GL(glBufferData(GL_ARRAY_BUFFER, sizeof(cubeVertices), &cubeVertices, GL_STATIC_DRAW)); + GL(glBindBuffer(GL_ARRAY_BUFFER, 0)); + + GL(glGenBuffers(1, &IndexBuffer)); + GL(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IndexBuffer)); + GL(glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(cubeIndices), cubeIndices, GL_STATIC_DRAW)); + GL(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)); +} + +void Geometry::CreateAxes() { + struct AxesVertices { + float positions[6][3]; + unsigned char colors[6][4]; + }; + + static const AxesVertices axesVertices = { + // positions + {{0, 0, 0}, {1, 0, 0}, {0, 0, 0}, {0, 1, 0}, {0, 0, 0}, {0, 0, 1}}, + // colors + {{255, 0, 0, 255}, + {255, 0, 0, 255}, + {0, 255, 0, 255}, + {0, 255, 0, 255}, + {0, 0, 255, 255}, + {0, 0, 255, 255}}, + }; + + static const unsigned short axesIndices[6] = { + 0, + 1, // x axis - red + 2, + 3, // y axis - green + 4, + 5 // z axis - blue + }; + + VertexCount = 6; + IndexCount = 6; + + VertexAttribs[0].Index = VERTEX_ATTRIBUTE_LOCATION_POSITION; + VertexAttribs[0].Size = 3; + VertexAttribs[0].Type = GL_FLOAT; + VertexAttribs[0].Normalized = false; + VertexAttribs[0].Stride = sizeof(axesVertices.positions[0]); + VertexAttribs[0].Pointer = (const GLvoid*)offsetof(AxesVertices, positions); + + VertexAttribs[1].Index = VERTEX_ATTRIBUTE_LOCATION_COLOR; + VertexAttribs[1].Size = 4; + VertexAttribs[1].Type = GL_UNSIGNED_BYTE; + VertexAttribs[1].Normalized = true; + VertexAttribs[1].Stride = sizeof(axesVertices.colors[0]); + VertexAttribs[1].Pointer = (const GLvoid*)offsetof(AxesVertices, colors); + + GL(glGenBuffers(1, &VertexBuffer)); + GL(glBindBuffer(GL_ARRAY_BUFFER, VertexBuffer)); + GL(glBufferData(GL_ARRAY_BUFFER, sizeof(axesVertices), &axesVertices, GL_STATIC_DRAW)); + GL(glBindBuffer(GL_ARRAY_BUFFER, 0)); + + GL(glGenBuffers(1, &IndexBuffer)); + GL(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IndexBuffer)); + GL(glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(axesIndices), axesIndices, GL_STATIC_DRAW)); + GL(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)); +} + +void Geometry::CreateStage() { + static const float stageVertices[12] = { + -1.0f, -1.0f, 0.0f, 1.0f, -1.0f, 0.0f, -1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f}; + + static const unsigned short stageIndices[6] = {0, 1, 2, 2, 1, 3}; + + VertexCount = 4; + IndexCount = 6; + + VertexAttribs[0].Index = VERTEX_ATTRIBUTE_LOCATION_POSITION; + VertexAttribs[0].Size = 3; + VertexAttribs[0].Type = GL_FLOAT; + VertexAttribs[0].Normalized = false; + VertexAttribs[0].Stride = 3 * sizeof(float); + VertexAttribs[0].Pointer = (const GLvoid*)0; + + GL(glGenBuffers(1, &VertexBuffer)); + GL(glBindBuffer(GL_ARRAY_BUFFER, VertexBuffer)); + GL(glBufferData(GL_ARRAY_BUFFER, sizeof(stageVertices), stageVertices, GL_STATIC_DRAW)); + GL(glBindBuffer(GL_ARRAY_BUFFER, 0)); + + GL(glGenBuffers(1, &IndexBuffer)); + GL(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IndexBuffer)); + GL(glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(stageIndices), stageIndices, GL_STATIC_DRAW)); + GL(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)); +} + +void Geometry::Destroy() { + GL(glDeleteBuffers(1, &IndexBuffer)); + GL(glDeleteBuffers(1, &VertexBuffer)); + + Clear(); +} + +void Geometry::CreateVAO() { + GL(glGenVertexArrays(1, &VertexArrayObject)); + GL(glBindVertexArray(VertexArrayObject)); + + GL(glBindBuffer(GL_ARRAY_BUFFER, VertexBuffer)); + + for (int i = 0; i < MAX_VERTEX_ATTRIB_POINTERS; i++) { + if (VertexAttribs[i].Index != -1) { + GL(glEnableVertexAttribArray(VertexAttribs[i].Index)); + GL(glVertexAttribPointer( + VertexAttribs[i].Index, + VertexAttribs[i].Size, + VertexAttribs[i].Type, + VertexAttribs[i].Normalized, + VertexAttribs[i].Stride, + VertexAttribs[i].Pointer)); + } + } + + GL(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IndexBuffer)); + + GL(glBindVertexArray(0)); +} + +void Geometry::DestroyVAO() { + GL(glDeleteVertexArrays(1, &VertexArrayObject)); +} + +/* +================================================================================ + +Program + +================================================================================ +*/ + +struct Uniform { + enum Index { + MODEL_MATRIX, + VIEW_ID, + SCENE_MATRICES, + }; + enum Type { + VECTOR4, + MATRIX4X4, + INTEGER, + BUFFER, + }; + + Index index; + Type type; + const char* name; +}; + +static Uniform ProgramUniforms[] = { + {Uniform::Index::MODEL_MATRIX, Uniform::Type::MATRIX4X4, "ModelMatrix"}, + {Uniform::Index::VIEW_ID, Uniform::Type::INTEGER, "ViewID"}, + {Uniform::Index::SCENE_MATRICES, Uniform::Type::BUFFER, "SceneMatrices"}, +}; + +void Program::Clear() { + Program = 0; + VertexShader = 0; + FragmentShader = 0; + memset(UniformLocation, 0, sizeof(UniformLocation)); + memset(UniformBinding, 0, sizeof(UniformBinding)); + memset(Textures, 0, sizeof(Textures)); +} + +static const char* programVersion = "#version 300 es\n"; + +bool Program::Create(const char* vertexSource, const char* fragmentSource) { + GLint r; + + GL(VertexShader = glCreateShader(GL_VERTEX_SHADER)); + + const char* vertexSources[3] = {programVersion, "", vertexSource}; + GL(glShaderSource(VertexShader, 3, vertexSources, 0)); + GL(glCompileShader(VertexShader)); + GL(glGetShaderiv(VertexShader, GL_COMPILE_STATUS, &r)); + if (r == GL_FALSE) { + GLchar msg[4096]; + GL(glGetShaderInfoLog(VertexShader, sizeof(msg), 0, msg)); + ALOGE("%s\n%s\n", vertexSource, msg); + return false; + } + + const char* fragmentSources[2] = {programVersion, fragmentSource}; + GL(FragmentShader = glCreateShader(GL_FRAGMENT_SHADER)); + GL(glShaderSource(FragmentShader, 2, fragmentSources, 0)); + GL(glCompileShader(FragmentShader)); + GL(glGetShaderiv(FragmentShader, GL_COMPILE_STATUS, &r)); + if (r == GL_FALSE) { + GLchar msg[4096]; + GL(glGetShaderInfoLog(FragmentShader, sizeof(msg), 0, msg)); + ALOGE("%s\n%s\n", fragmentSource, msg); + return false; + } + + GL(Program = glCreateProgram()); + GL(glAttachShader(Program, VertexShader)); + GL(glAttachShader(Program, FragmentShader)); + + // Bind the vertex attribute locations. + for (size_t i = 0; i < sizeof(ProgramVertexAttributes) / sizeof(ProgramVertexAttributes[0]); + i++) { + GL(glBindAttribLocation( + Program, ProgramVertexAttributes[i].location, ProgramVertexAttributes[i].name)); + } + + GL(glLinkProgram(Program)); + GL(glGetProgramiv(Program, GL_LINK_STATUS, &r)); + if (r == GL_FALSE) { + GLchar msg[4096]; + GL(glGetProgramInfoLog(Program, sizeof(msg), 0, msg)); + ALOGE("Linking program failed: %s\n", msg); + return false; + } + + int numBufferBindings = 0; + + memset(UniformLocation, -1, sizeof(UniformLocation)); + for (size_t i = 0; i < sizeof(ProgramUniforms) / sizeof(ProgramUniforms[0]); i++) { + const int uniformIndex = ProgramUniforms[i].index; + if (ProgramUniforms[i].type == Uniform::Type::BUFFER) { + GL(UniformLocation[uniformIndex] = + glGetUniformBlockIndex(Program, ProgramUniforms[i].name)); + UniformBinding[uniformIndex] = numBufferBindings++; + GL(glUniformBlockBinding( + Program, UniformLocation[uniformIndex], UniformBinding[uniformIndex])); + } else { + GL(UniformLocation[uniformIndex] = + glGetUniformLocation(Program, ProgramUniforms[i].name)); + UniformBinding[uniformIndex] = UniformLocation[uniformIndex]; + } + } + + GL(glUseProgram(Program)); + + // Get the texture locations. + for (int i = 0; i < MAX_PROGRAM_TEXTURES; i++) { + char name[32]; + sprintf(name, "Texture%i", i); + Textures[i] = glGetUniformLocation(Program, name); + if (Textures[i] != -1) { + GL(glUniform1i(Textures[i], i)); + } + } + + GL(glUseProgram(0)); + + return true; +} + +void Program::Destroy() { + if (Program != 0) { + GL(glDeleteProgram(Program)); + Program = 0; + } + if (VertexShader != 0) { + GL(glDeleteShader(VertexShader)); + VertexShader = 0; + } + if (FragmentShader != 0) { + GL(glDeleteShader(FragmentShader)); + FragmentShader = 0; + } +} + +static const char VERTEX_SHADER[] = + "#define NUM_VIEWS 2\n" + "#define VIEW_ID gl_ViewID_OVR\n" + "#extension GL_OVR_multiview2 : require\n" + "layout(num_views=NUM_VIEWS) in;\n" + "in vec3 vertexPosition;\n" + "in vec4 vertexColor;\n" + "uniform mat4 ModelMatrix;\n" + "uniform SceneMatrices\n" + "{\n" + " uniform mat4 ViewMatrix[NUM_VIEWS];\n" + " uniform mat4 ProjectionMatrix[NUM_VIEWS];\n" + "} sm;\n" + "out vec4 fragmentColor;\n" + "void main()\n" + "{\n" + " gl_Position = sm.ProjectionMatrix[VIEW_ID] * ( sm.ViewMatrix[VIEW_ID] * ( ModelMatrix * ( vec4( vertexPosition, 1.0 ) ) ) );\n" + " fragmentColor = vertexColor;\n" + "}\n"; + +static const char FRAGMENT_SHADER[] = + "in lowp vec4 fragmentColor;\n" + "out lowp vec4 outColor;\n" + "void main()\n" + "{\n" + " outColor = fragmentColor;\n" + "}\n"; + +static const char STAGE_VERTEX_SHADER[] = + "#define NUM_VIEWS 2\n" + "#define VIEW_ID gl_ViewID_OVR\n" + "#extension GL_OVR_multiview2 : require\n" + "layout(num_views=NUM_VIEWS) in;\n" + "in vec3 vertexPosition;\n" + "uniform mat4 ModelMatrix;\n" + "uniform SceneMatrices\n" + "{\n" + " uniform mat4 ViewMatrix[NUM_VIEWS];\n" + " uniform mat4 ProjectionMatrix[NUM_VIEWS];\n" + "} sm;\n" + "void main()\n" + "{\n" + " gl_Position = sm.ProjectionMatrix[VIEW_ID] * ( sm.ViewMatrix[VIEW_ID] * ( ModelMatrix * ( vec4( vertexPosition, 1.0 ) ) ) );\n" + "}\n"; + +static const char STAGE_FRAGMENT_SHADER[] = + "out lowp vec4 outColor;\n" + "void main()\n" + "{\n" + " outColor = vec4( 0.5, 0.5, 1.0, 0.5 );\n" + "}\n"; + +static const char CIRCLE_VERTEX_SHADER[] = + "#define NUM_VIEWS 2\n" + "#define VIEW_ID gl_ViewID_OVR\n" + "#extension GL_OVR_multiview2 : require\n" + "layout(num_views=NUM_VIEWS) in;\n" + "in vec3 vertexPosition;\n" + "in vec4 vertexColor;\n" + "uniform mat4 ModelMatrix;\n" + "uniform SceneMatrices\n" + "{\n" + " uniform mat4 ViewMatrix[NUM_VIEWS];\n" + " uniform mat4 ProjectionMatrix[NUM_VIEWS];\n" + "} sm;\n" + "out highp vec3 fragPosEye;\n" + "out highp vec3 circleCenter;\n" + "void main()\n" + "{\n" + " gl_Position = sm.ProjectionMatrix[VIEW_ID] * ( sm.ViewMatrix[VIEW_ID] * ( ModelMatrix * ( vec4( vertexPosition, 1.0 ) ) ) );\n" + " fragPosEye = (sm.ViewMatrix[VIEW_ID] * ( ModelMatrix * ( vec4( vertexPosition, 1.0 ) ) ) ).xyz;\n" + " circleCenter = (sm.ViewMatrix[VIEW_ID] * ( ModelMatrix * ( vec4( 0.0, 0.0, 0.0, 1.0 ) ) ) ).xyz;\n" + "}\n"; + +static const char CIRCLE_FRAGMENT_SHADER[] = + "in highp vec3 fragPosEye;\n" + "in highp vec3 circleCenter;\n" + "out highp vec4 outColor;\n" + "void main()\n" + "{\n" + " highp vec3 dir = fragPosEye / length(fragPosEye);\n" + " highp float a = dot(dir, dir);\n" + " highp float b = 2.0 * dot(dir, -circleCenter);\n" + " highp float r = 0.2;\n" + " highp float c = dot(circleCenter, circleCenter) - r * r;\n" + " highp float discr = b * b - 4.0 * a * c;\n" + " if (discr < 0.0) { discard; }\n" + " highp float t = (-b - sqrt(discr)) / (2.0 * a);\n" + " highp vec3 n = (t * dir) - circleCenter;\n" + " n = n / length(n);\n" + " highp float ndote = dot(n, -dir);\n" + " highp float alpha = 0.5 + 0.5 * smoothstep(0.35, 0.5, 1.0 - ndote);\n" + " outColor = vec4(1.0, 0.0, 0.0, alpha);\n" + "}\n"; + +/* +================================================================================ + +Framebuffer + +================================================================================ +*/ + +void Framebuffer::Clear() { + Width = 0; + Height = 0; + Multisamples = 0; + SwapChainLength = 0; + Elements = nullptr; +} + +static void* GlGetExtensionProc(const char* functionName) { +#if defined(ANDROID) + return (void*)eglGetProcAddress(functionName); +#elif defined(WIN32) + return (void*)wglGetProcAddress(functionName); +#else + static_assert(false); +#endif +} + +bool Framebuffer::Create( + const GLenum colorFormat, + const int width, + const int height, + const int multisamples, + const int swapChainLength, + GLuint* colorTextures) { + PFNGLFRAMEBUFFERTEXTUREMULTIVIEWOVRPROC glFramebufferTextureMultiviewOVR = + (PFNGLFRAMEBUFFERTEXTUREMULTIVIEWOVRPROC)GlGetExtensionProc( + "glFramebufferTextureMultiviewOVR"); + PFNGLFRAMEBUFFERTEXTUREMULTISAMPLEMULTIVIEWOVRPROC glFramebufferTextureMultisampleMultiviewOVR = + (PFNGLFRAMEBUFFERTEXTUREMULTISAMPLEMULTIVIEWOVRPROC)GlGetExtensionProc( + "glFramebufferTextureMultisampleMultiviewOVR"); + + Width = width; + Height = height; + Multisamples = multisamples; + SwapChainLength = swapChainLength; + + Elements = new Element[SwapChainLength]; + + for (int i = 0; i < SwapChainLength; i++) { + Element& el = Elements[i]; + // Create the color buffer texture. + el.ColorTexture = colorTextures[i]; + GLenum colorTextureTarget = GL_TEXTURE_2D_ARRAY; + GL(glBindTexture(colorTextureTarget, el.ColorTexture)); + GL(glTexParameteri(colorTextureTarget, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER)); + GL(glTexParameteri(colorTextureTarget, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER)); + GLfloat borderColor[] = {0.0f, 0.0f, 0.0f, 0.0f}; + GL(glTexParameterfv(colorTextureTarget, GL_TEXTURE_BORDER_COLOR, borderColor)); + GL(glTexParameteri(colorTextureTarget, GL_TEXTURE_MIN_FILTER, GL_LINEAR)); + GL(glTexParameteri(colorTextureTarget, GL_TEXTURE_MAG_FILTER, GL_LINEAR)); + GL(glBindTexture(colorTextureTarget, 0)); + + // Create the depth buffer texture. + GL(glGenTextures(1, &el.DepthTexture)); + GL(glBindTexture(GL_TEXTURE_2D_ARRAY, el.DepthTexture)); + GL(glTexStorage3D(GL_TEXTURE_2D_ARRAY, 1, GL_DEPTH_COMPONENT24, width, height, 2)); + GL(glBindTexture(GL_TEXTURE_2D_ARRAY, 0)); + + // Create the frame buffer. + GL(glGenFramebuffers(1, &el.FrameBufferObject)); + GL(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, el.FrameBufferObject)); + if (multisamples > 1 && (glFramebufferTextureMultisampleMultiviewOVR != nullptr)) { + GL(glFramebufferTextureMultisampleMultiviewOVR( + GL_DRAW_FRAMEBUFFER, + GL_DEPTH_ATTACHMENT, + el.DepthTexture, + 0 /* level */, + multisamples /* samples */, + 0 /* baseViewIndex */, + 2 /* numViews */)); + GL(glFramebufferTextureMultisampleMultiviewOVR( + GL_DRAW_FRAMEBUFFER, + GL_COLOR_ATTACHMENT0, + el.ColorTexture, + 0 /* level */, + multisamples /* samples */, + 0 /* baseViewIndex */, + 2 /* numViews */)); + } else if (glFramebufferTextureMultiviewOVR != nullptr) { + GL(glFramebufferTextureMultiviewOVR( + GL_DRAW_FRAMEBUFFER, + GL_DEPTH_ATTACHMENT, + el.DepthTexture, + 0 /* level */, + 0 /* baseViewIndex */, + 2 /* numViews */)); + GL(glFramebufferTextureMultiviewOVR( + GL_DRAW_FRAMEBUFFER, + GL_COLOR_ATTACHMENT0, + el.ColorTexture, + 0 /* level */, + 0 /* baseViewIndex */, + 2 /* numViews */)); + } + + GL(GLenum renderFramebufferStatus = glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER)); + GL(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0)); + if (renderFramebufferStatus != GL_FRAMEBUFFER_COMPLETE) { + ALOGE( + "Incomplete frame buffer object: %s", + GlFrameBufferStatusString(renderFramebufferStatus)); + return false; + } + } + + return true; +} + +void Framebuffer::Destroy() { + for (int i = 0; i < SwapChainLength; i++) { + Element& el = Elements[i]; + GL(glDeleteFramebuffers(1, &el.FrameBufferObject)); + GL(glDeleteTextures(1, &el.DepthTexture)); + } + delete[] Elements; + Clear(); +} + +void Framebuffer::Bind(int element) { + GL(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, Elements[element].FrameBufferObject)); +} + +void Framebuffer::Unbind() { + GL(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0)); +} + +void Framebuffer::Resolve() { + // Discard the depth buffer, so the tiler won't need to write it back out to memory. + const GLenum depthAttachment[1] = {GL_DEPTH_ATTACHMENT}; + glInvalidateFramebuffer(GL_DRAW_FRAMEBUFFER, 1, depthAttachment); + + // We now let the resolve happen implicitly. +} + +/* +================================================================================ + +TrackedController + +================================================================================ +*/ + +void TrackedController::Clear() { + Active = false; + Pose = OVR::Posef::Identity(); +} + +/* +================================================================================ + +Scene + +================================================================================ +*/ + +void Scene::SetClearColor(const float* c) { + for (int i = 0; i < 4; i++) { + ClearColor[i] = c[i]; + } +} + +void Scene::Clear() { + CreatedScene = false; + CreatedVAOs = false; + SceneMatrices = 0; + + StageProgram.Clear(); + Stage.Clear(); + AxesProgram.Clear(); + Axes.Clear(); + BoxProgram.Clear(); + Box.Clear(); + CircleProgram.Clear(); +} + +bool Scene::IsCreated() { + return CreatedScene; +} + +void Scene::CreateVAOs() { + if (!CreatedVAOs) { + // Stage + Stage.CreateVAO(); + + // Axes + Axes.CreateVAO(); + + // Box + Box.CreateVAO(); + + CreatedVAOs = true; + } +} + +void Scene::DestroyVAOs() { + if (CreatedVAOs) { + Stage.DestroyVAO(); + Axes.DestroyVAO(); + Box.DestroyVAO(); + + CreatedVAOs = false; + } +} + +void Scene::Create() { + // Setup the scene matrices. + GL(glGenBuffers(1, &SceneMatrices)); + GL(glBindBuffer(GL_UNIFORM_BUFFER, SceneMatrices)); + GL(glBufferData( + GL_UNIFORM_BUFFER, + 2 * sizeof(Matrix4f) /* 2 view matrices */ + + 2 * sizeof(Matrix4f) /* 2 projection matrices */, + nullptr, + GL_STATIC_DRAW)); + GL(glBindBuffer(GL_UNIFORM_BUFFER, 0)); + + // Stage + if (!StageProgram.Create(STAGE_VERTEX_SHADER, STAGE_FRAGMENT_SHADER)) { + ALOGE("Failed to compile stage program"); + } + Stage.CreateStage(); + + // Axes + if (!AxesProgram.Create(VERTEX_SHADER, FRAGMENT_SHADER)) { + ALOGE("Failed to compile axes program"); + } + Axes.CreateAxes(); + + // Box + if (!BoxProgram.Create(VERTEX_SHADER, FRAGMENT_SHADER)) { + ALOGE("Failed to compile box program"); + } + Box.CreateBox(); + + // Circle + if (!CircleProgram.Create(CIRCLE_VERTEX_SHADER, CIRCLE_FRAGMENT_SHADER)) { + ALOGE("Failed to compile circle program"); + } + + CreatedScene = true; + + CreateVAOs(); + float c[] = {0.3, 0.3, 0.3, 0.0}; + SetClearColor(c); +} + +void Scene::Destroy() { + DestroyVAOs(); + + GL(glDeleteBuffers(1, &SceneMatrices)); + StageProgram.Destroy(); + Stage.Destroy(); + AxesProgram.Destroy(); + Axes.Destroy(); + BoxProgram.Destroy(); + Box.Destroy(); + CircleProgram.Destroy(); + CreatedScene = false; +} + +/* +================================================================================ + +AppRenderer + +================================================================================ +*/ + +void AppRenderer::Clear() { + framebuffer.Clear(); + scene.Clear(); +} + +void AppRenderer::Create( + GLenum format, + int width, + int height, + int numMultiSamples, + int swapChainLength, + GLuint* colorTextures) { + EglInitExtensions(); + framebuffer.Create(format, width, height, numMultiSamples, swapChainLength, colorTextures); + if (glExtensions.EXT_sRGB_write_control) { + // This app was originally written with the presumption that + // its swapchains and compositor front buffer were RGB. + // In order to have the colors the same now that its compositing + // to an sRGB front buffer, we have to write to an sRGB swapchain + // but with the linear->sRGB conversion disabled on write. + GL(glDisable(GL_FRAMEBUFFER_SRGB_EXT)); + } +} + +void AppRenderer::Destroy() { + framebuffer.Destroy(); +} + +void AppRenderer::RenderFrame(AppRenderer::FrameIn frameIn) { + // Update the scene matrices. + GL(glBindBuffer(GL_UNIFORM_BUFFER, scene.SceneMatrices)); + GL(Matrix4f* sceneMatrices = (Matrix4f*)glMapBufferRange( + GL_UNIFORM_BUFFER, + 0, + 4 * sizeof(Matrix4f) /* 2 view + 2 proj matrices */, + GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT)); + + if (sceneMatrices != nullptr) { + memcpy((char*)sceneMatrices, &frameIn.View, 4 * sizeof(Matrix4f)); + } + + GL(glUnmapBuffer(GL_UNIFORM_BUFFER)); + GL(glBindBuffer(GL_UNIFORM_BUFFER, 0)); + + // Render the eye images. + framebuffer.Bind(frameIn.SwapChainIndex); + + GL(glEnable(GL_SCISSOR_TEST)); + GL(glDepthMask(GL_TRUE)); + GL(glEnable(GL_DEPTH_TEST)); + GL(glDepthFunc(GL_LEQUAL)); + GL(glEnable(GL_CULL_FACE)); + GL(glCullFace(GL_BACK)); + GL(glDisable(GL_BLEND)); + GL(glViewport(0, 0, framebuffer.Width, framebuffer.Height)); + GL(glScissor(0, 0, framebuffer.Width, framebuffer.Height)); + GL(glClearColor( + scene.ClearColor[0], scene.ClearColor[1], scene.ClearColor[2], scene.ClearColor[3])); + GL(glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)); + + GL(glLineWidth(3.0)); + // "tracking space" axes (could be LOCAL or LOCAL_FLOOR) + GL(glUseProgram(scene.AxesProgram.Program)); + GL(glBindBufferBase( + GL_UNIFORM_BUFFER, + scene.AxesProgram.UniformBinding[Uniform::Index::SCENE_MATRICES], + scene.SceneMatrices)); + if (scene.AxesProgram.UniformLocation[Uniform::Index::VIEW_ID] >= + 0) // NOTE: will not be present when multiview path is enabled. + { + GL(glUniform1i(scene.AxesProgram.UniformLocation[Uniform::Index::VIEW_ID], 0)); + } + if (scene.AxesProgram.UniformLocation[Uniform::Index::MODEL_MATRIX] >= 0) { + const Matrix4f scale = Matrix4f::Scaling(0.1, 0.1, 0.1); + GL(glUniformMatrix4fv( + scene.AxesProgram.UniformLocation[Uniform::Index::MODEL_MATRIX], + 1, + GL_TRUE, + &scale.M[0][0])); + } + GL(glBindVertexArray(scene.Axes.VertexArrayObject)); + GL(glDrawElements(GL_LINES, scene.Axes.IndexCount, GL_UNSIGNED_SHORT, nullptr)); + GL(glBindVertexArray(0)); + GL(glUseProgram(0)); + + if (frameIn.HasStage) { + // stage axes + GL(glUseProgram(scene.AxesProgram.Program)); + GL(glBindBufferBase( + GL_UNIFORM_BUFFER, + scene.AxesProgram.UniformBinding[Uniform::Index::SCENE_MATRICES], + scene.SceneMatrices)); + if (scene.AxesProgram.UniformLocation[Uniform::Index::VIEW_ID] >= + 0) // NOTE: will not be present when multiview path is enabled. + { + GL(glUniform1i(scene.AxesProgram.UniformLocation[Uniform::Index::VIEW_ID], 0)); + } + if (scene.AxesProgram.UniformLocation[Uniform::Index::MODEL_MATRIX] >= 0) { + const Matrix4f scale = Matrix4f::Scaling(0.5, 0.5, 0.5); + const Matrix4f stagePoseMat = Matrix4f(frameIn.StagePose); + const Matrix4f m1 = stagePoseMat * scale; + GL(glUniformMatrix4fv( + scene.AxesProgram.UniformLocation[Uniform::Index::MODEL_MATRIX], + 1, + GL_TRUE, + &m1.M[0][0])); + } + GL(glBindVertexArray(scene.Axes.VertexArrayObject)); + GL(glDrawElements(GL_LINES, scene.Axes.IndexCount, GL_UNSIGNED_SHORT, nullptr)); + GL(glBindVertexArray(0)); + GL(glUseProgram(0)); + } + + { + // Controllers + auto& prg = scene.BoxProgram; + auto& geo = scene.Box; + GL(glUseProgram(prg.Program)); + GL(glBindVertexArray(geo.VertexArrayObject)); + GL(glBindBufferBase( + GL_UNIFORM_BUFFER, + prg.UniformBinding[Uniform::Index::SCENE_MATRICES], + scene.SceneMatrices)); + if (prg.UniformLocation[Uniform::Index::VIEW_ID] >= + 0) // NOTE: will not be present when multiview path is enabled. + { + GL(glUniform1i(prg.UniformLocation[Uniform::Index::VIEW_ID], 0)); + } + GL(glDepthMask(GL_TRUE)); + GL(glEnable(GL_DEPTH_TEST)); + GL(glDepthFunc(GL_LEQUAL)); + GL(glDisable(GL_CULL_FACE)); + GL(glEnable(GL_BLEND)); + GL(glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); + for (int i = 0; i < 4; i++) { + if (scene.trackedController[i].Active == false) { + continue; + } + Matrix4f pose(scene.trackedController[i].Pose); + Matrix4f scale; + if (i & 1) { + scale = Matrix4f::Scaling(0.03f, 0.03f, 0.03f); + } else { + scale = Matrix4f::Scaling(0.02f, 0.02f, 0.06f); + } + Matrix4f model = pose * scale; + glUniformMatrix4fv( + prg.UniformLocation[Uniform::Index::MODEL_MATRIX], 1, GL_TRUE, &model.M[0][0]); + GL(glDrawElements(GL_TRIANGLES, geo.IndexCount, GL_UNSIGNED_SHORT, NULL)); + } + GL(glBindVertexArray(0)); + GL(glUseProgram(0)); + } + if (scene.ClearColor[3] == 1.0) { + // Controller alpha circles + auto& prg = scene.CircleProgram; + auto& geo = scene.Box; + GL(glUseProgram(prg.Program)); + GL(glBindVertexArray(geo.VertexArrayObject)); + GL(glBindBufferBase( + GL_UNIFORM_BUFFER, + prg.UniformBinding[Uniform::Index::SCENE_MATRICES], + scene.SceneMatrices)); + if (prg.UniformLocation[Uniform::Index::VIEW_ID] >= + 0) // NOTE: will not be present when multiview path is enabled. + { + GL(glUniform1i(prg.UniformLocation[Uniform::Index::VIEW_ID], 0)); + } + GL(glDisable(GL_DEPTH_TEST)); + GL(glEnable(GL_CULL_FACE)); + GL(glEnable(GL_BLEND)); + GL(glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_TRUE)); + GL(glBlendEquation(GL_MIN)); + GL(glBlendFunc(GL_ONE, GL_ONE)); + for (int i = 1; i < 4; i += 2) { + if (scene.trackedController[i].Active == false) { + continue; + } + Matrix4f pose(scene.trackedController[i].Pose); + Matrix4f scale; + scale = Matrix4f::Scaling(0.2f, 0.2f, 0.2f); + Matrix4f model = pose * scale; + glUniformMatrix4fv( + prg.UniformLocation[Uniform::Index::MODEL_MATRIX], 1, GL_TRUE, &model.M[0][0]); + GL(glDrawElements(GL_TRIANGLES, geo.IndexCount, GL_UNSIGNED_SHORT, NULL)); + } + GL(glBlendEquation(GL_FUNC_ADD)); + GL(glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE)); + GL(glBindVertexArray(0)); + GL(glUseProgram(0)); + } + + if (frameIn.HasStage) { + // Stage + GL(glUseProgram(scene.StageProgram.Program)); + GL(glBindBufferBase( + GL_UNIFORM_BUFFER, + scene.StageProgram.UniformBinding[Uniform::Index::SCENE_MATRICES], + scene.SceneMatrices)); + if (scene.StageProgram.UniformLocation[Uniform::Index::VIEW_ID] >= + 0) // NOTE: will not be present when multiview path is enabled. + { + GL(glUniform1i(scene.StageProgram.UniformLocation[Uniform::Index::VIEW_ID], 0)); + } + if (scene.StageProgram.UniformLocation[Uniform::Index::MODEL_MATRIX] >= 0) { + const Matrix4f rotateVtoH = Matrix4f::RotationX(-M_PI / 2.0f); + const Matrix4f stageScaleMat = Matrix4f::Scaling(frameIn.StageScale); + const Matrix4f stagePoseMat = Matrix4f(frameIn.StagePose); + const Matrix4f m2 = stagePoseMat * stageScaleMat * rotateVtoH; + GL(glUniformMatrix4fv( + scene.StageProgram.UniformLocation[Uniform::Index::MODEL_MATRIX], + 1, + GL_TRUE, + &m2.M[0][0])); + } + GL(glEnable(GL_DEPTH_TEST)); + GL(glDepthMask(GL_FALSE)); + GL(glEnable(GL_DEPTH_TEST)); + GL(glDepthFunc(GL_LEQUAL)); + GL(glDisable(GL_CULL_FACE)); + GL(glEnable(GL_BLEND)); + GL(glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); + GL(glBindVertexArray(scene.Stage.VertexArrayObject)); + GL(glDrawElements(GL_TRIANGLES, scene.Stage.IndexCount, GL_UNSIGNED_SHORT, nullptr)); + GL(glDepthMask(GL_TRUE)); + GL(glDisable(GL_BLEND)); + GL(glBindVertexArray(0)); + GL(glUseProgram(0)); + } + + framebuffer.Resolve(); + framebuffer.Unbind(); +} diff --git a/Samples/XrSamples/XrPassthrough/Src/XrPassthroughGl.h b/Samples/XrSamples/XrPassthrough/Src/XrPassthroughGl.h new file mode 100755 index 0000000..dd56783 --- /dev/null +++ b/Samples/XrSamples/XrPassthrough/Src/XrPassthroughGl.h @@ -0,0 +1,133 @@ +#pragma once + +#if defined(ANDROID) +#include +#else +#include "Render/GlWrapperWin32.h" +#endif // defined(ANDROID) + +#include "OVR_Math.h" + +#ifndef NUM_EYES +#define NUM_EYES 2 +#endif + +struct Geometry { + void Clear(); + void CreateAxes(); + void CreateStage(); + void CreateBox(); + void Destroy(); + void CreateVAO(); + void DestroyVAO(); + static constexpr int MAX_VERTEX_ATTRIB_POINTERS = 3; + struct VertexAttribPointer { + GLint Index; + GLint Size; + GLenum Type; + GLboolean Normalized; + GLsizei Stride; + const GLvoid* Pointer; + }; + GLuint VertexBuffer; + GLuint IndexBuffer; + GLuint VertexArrayObject; + int VertexCount; + int IndexCount; + VertexAttribPointer VertexAttribs[MAX_VERTEX_ATTRIB_POINTERS]; +}; + +struct Program { + static constexpr int MAX_PROGRAM_UNIFORMS = 8; + static constexpr int MAX_PROGRAM_TEXTURES = 8; + + void Clear(); + bool Create(const char* vertexSource, const char* fragmentSource); + void Destroy(); + GLuint Program; + GLuint VertexShader; + GLuint FragmentShader; + // These will be -1 if not used by the program. + GLint UniformLocation[MAX_PROGRAM_UNIFORMS]; // ProgramUniforms[].name + GLint UniformBinding[MAX_PROGRAM_UNIFORMS]; // ProgramUniforms[].name + GLint Textures[MAX_PROGRAM_TEXTURES]; // Texture%i +}; + +struct Framebuffer { + void Clear(); + bool Create( + const GLenum colorFormat, + const int width, + const int height, + const int multisamples, + const int swapChainLength, + GLuint* colorTextures); + void Destroy(); + void Bind(int element); + void Unbind(); + void Resolve(); + int Width; + int Height; + int Multisamples; + int SwapChainLength; + struct Element { + GLuint ColorTexture; + GLuint DepthTexture; + GLuint FrameBufferObject; + }; + Element* Elements; +}; + +struct TrackedController { + void Clear(); + bool Active; + OVR::Posef Pose; +}; + +struct Scene { + void Clear(); + void Create(); + void Destroy(); + bool IsCreated(); + void SetClearColor(const float* c); + void CreateVAOs(); + void DestroyVAOs(); + bool CreatedScene; + bool CreatedVAOs; + GLuint SceneMatrices; + Program StageProgram; + Geometry Stage; + Program AxesProgram; + Geometry Axes; + Program BoxProgram; + Geometry Box; + Program CircleProgram; + float ClearColor[4]; + TrackedController trackedController[4]; // left aim, left grip, right aim, right grip +}; + +struct AppRenderer { + void Clear(); + void Create( + GLenum format, + int width, + int height, + int numMultiSamples, + int swapChainLength, + GLuint* colorTextures); + void Destroy(); + + struct FrameIn { + int SwapChainIndex; + OVR::Matrix4f View[NUM_EYES]; + OVR::Matrix4f Proj[NUM_EYES]; + bool HasStage; + OVR::Posef StagePose; + OVR::Vector3f StageScale; + }; + + void RenderFrame(FrameIn frameIn); + + Framebuffer framebuffer; + Scene scene; +}; diff --git a/Samples/XrSamples/XrPassthrough/Src/XrPassthroughInput.cpp b/Samples/XrSamples/XrPassthrough/Src/XrPassthroughInput.cpp new file mode 100755 index 0000000..d8cb426 --- /dev/null +++ b/Samples/XrSamples/XrPassthrough/Src/XrPassthroughInput.cpp @@ -0,0 +1,244 @@ +/************************************************************************************ + +Filename : XrPassthrough.cpp +Content : This sample uses the Android NativeActivity class. +Created : +Authors : + +Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved. + +*************************************************************************************/ + +#include +#include +#include +#include // for memset +#include +#include + +#if defined(ANDROID) +#include +#include +#include // for native window JNI +#include +#endif // defined(ANDROID) + +#include + +#include + +#include "XrPassthrough.h" +#include "XrPassthroughInput.h" + +using namespace OVR; + +#if !defined(EGL_OPENGL_ES3_BIT_KHR) +#define EGL_OPENGL_ES3_BIT_KHR 0x0040 +#endif + +#define DEBUG 1 +#define LOG_TAG "XrPassthrough" + +#if defined(ANDROID) +#define ALOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__) +#define ALOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__) +#else +#include +#define ALOGE(...) \ + printf("ERROR: "); \ + printf(__VA_ARGS__); \ + printf("\n") +#define ALOGV(...) \ + printf("VERBOSE: "); \ + printf(__VA_ARGS__); \ + printf("\n") +#endif + +namespace { + +XrActionSet +CreateActionSet(XrInstance instance, int priority, const char* name, const char* localizedName) { + XrActionSetCreateInfo asci = {XR_TYPE_ACTION_SET_CREATE_INFO}; + asci.priority = priority; + strcpy(asci.actionSetName, name); + strcpy(asci.localizedActionSetName, localizedName); + XrActionSet actionSet = XR_NULL_HANDLE; + OXR(xrCreateActionSet(instance, &asci, &actionSet)); + return actionSet; +} + +XrAction CreateAction( + XrActionSet actionSet, + XrActionType type, + const char* actionName, + const char* localizedName, + int countSubactionPaths = 0, + XrPath* subactionPaths = nullptr) { + ALOGV("CreateAction %s, %" PRIi32, actionName, countSubactionPaths); + + XrActionCreateInfo aci = {XR_TYPE_ACTION_CREATE_INFO}; + aci.actionType = type; + if (countSubactionPaths > 0) { + aci.countSubactionPaths = countSubactionPaths; + aci.subactionPaths = subactionPaths; + } + strcpy(aci.actionName, actionName); + strcpy(aci.localizedActionName, localizedName ? localizedName : actionName); + XrAction action = XR_NULL_HANDLE; + OXR(xrCreateAction(actionSet, &aci, &action)); + return action; +} + +XrActionSuggestedBinding +ActionSuggestedBinding(App& app, XrAction action, const char* bindingString) { + XrActionSuggestedBinding asb; + asb.action = action; + XrPath bindingPath; + OXR(xrStringToPath(app.Instance, bindingString, &bindingPath)); + asb.binding = bindingPath; + return asb; +} + +XrSpace CreateActionSpace(App& app, XrAction poseAction, XrPath subactionPath) { + XrActionSpaceCreateInfo asci = {XR_TYPE_ACTION_SPACE_CREATE_INFO}; + asci.action = poseAction; + asci.poseInActionSpace.orientation.w = 1.0f; + asci.subactionPath = subactionPath; + XrSpace actionSpace = XR_NULL_HANDLE; + OXR(xrCreateActionSpace(app.Session, &asci, &actionSpace)); + return actionSpace; +} + +XrActionStateBoolean GetActionStateBoolean(App& app, XrAction action) { + XrActionStateGetInfo getInfo = {XR_TYPE_ACTION_STATE_GET_INFO}; + getInfo.action = action; + + XrActionStateBoolean state = {XR_TYPE_ACTION_STATE_BOOLEAN}; + + OXR(xrGetActionStateBoolean(app.Session, &getInfo, &state)); + return state; +} + +bool ActionPoseIsActive(App& app, XrAction action, XrPath subactionPath) { + XrActionStateGetInfo getInfo = {XR_TYPE_ACTION_STATE_GET_INFO}; + getInfo.action = action; + getInfo.subactionPath = subactionPath; + + XrActionStatePose state = {XR_TYPE_ACTION_STATE_POSE}; + OXR(xrGetActionStatePose(app.Session, &getInfo, &state)); + return state.isActive != XR_FALSE; +} + +XrActionSet runningActionSet = XR_NULL_HANDLE; + +XrAction aimPoseAction = XR_NULL_HANDLE; +XrAction gripPoseAction = XR_NULL_HANDLE; + +XrAction boolAction = XR_NULL_HANDLE; +XrPath leftHandPath = XR_NULL_PATH; +XrPath rightHandPath = XR_NULL_PATH; +} // namespace + +XrActionStateBoolean boolState; + +bool leftControllerActive = false; +bool rightControllerActive = false; + +XrSpace leftControllerAimSpace = XR_NULL_HANDLE; +XrSpace rightControllerAimSpace = XR_NULL_HANDLE; +XrSpace leftControllerGripSpace = XR_NULL_HANDLE; +XrSpace rightControllerGripSpace = XR_NULL_HANDLE; + +void AppInput_init(App& app) { + // Actions + runningActionSet = + CreateActionSet(app.Instance, 1, "running_action_set", "Action Set used on main loop"); + boolAction = CreateAction(runningActionSet, XR_ACTION_TYPE_BOOLEAN_INPUT, "toggle", "Toggle"); + + OXR(xrStringToPath(app.Instance, "/user/hand/left", &leftHandPath)); + OXR(xrStringToPath(app.Instance, "/user/hand/right", &rightHandPath)); + XrPath handSubactionPaths[2] = {leftHandPath, rightHandPath}; + + aimPoseAction = CreateAction( + runningActionSet, XR_ACTION_TYPE_POSE_INPUT, "aim_pose", nullptr, 2, handSubactionPaths); + + gripPoseAction = CreateAction( + runningActionSet, XR_ACTION_TYPE_POSE_INPUT, "grip_pose", nullptr, 2, handSubactionPaths); + + XrPath interactionProfilePath = XR_NULL_PATH; + + OXR(xrStringToPath( + app.Instance, "/interaction_profiles/oculus/touch_controller", &interactionProfilePath)); + + // Action creation + { + // Map bindings + + std::vector bindings; + bindings.push_back( + ActionSuggestedBinding(app, boolAction, "/user/hand/left/input/trigger")); + bindings.push_back( + ActionSuggestedBinding(app, boolAction, "/user/hand/right/input/trigger")); + bindings.push_back( + ActionSuggestedBinding(app, aimPoseAction, "/user/hand/left/input/aim/pose")); + bindings.push_back( + ActionSuggestedBinding(app, aimPoseAction, "/user/hand/right/input/aim/pose")); + bindings.push_back( + ActionSuggestedBinding(app, gripPoseAction, "/user/hand/left/input/grip/pose")); + bindings.push_back( + ActionSuggestedBinding(app, gripPoseAction, "/user/hand/right/input/grip/pose")); + + XrInteractionProfileSuggestedBinding suggestedBindings = {XR_TYPE_INTERACTION_PROFILE_SUGGESTED_BINDING}; + suggestedBindings.interactionProfile = interactionProfilePath; + suggestedBindings.suggestedBindings = &bindings[0]; + suggestedBindings.countSuggestedBindings = bindings.size(); + OXR(xrSuggestInteractionProfileBindings(app.Instance, &suggestedBindings)); + + // Attach to session + XrSessionActionSetsAttachInfo attachInfo = {XR_TYPE_SESSION_ACTION_SETS_ATTACH_INFO}; + attachInfo.countActionSets = 1; + attachInfo.actionSets = &runningActionSet; + OXR(xrAttachSessionActionSets(app.Session, &attachInfo)); + } +} + +void AppInput_shutdown() { + if (leftControllerAimSpace != XR_NULL_HANDLE) { + OXR(xrDestroySpace(leftControllerAimSpace)); + OXR(xrDestroySpace(rightControllerAimSpace)); + OXR(xrDestroySpace(leftControllerGripSpace)); + OXR(xrDestroySpace(rightControllerGripSpace)); + leftControllerAimSpace = XR_NULL_HANDLE; + rightControllerAimSpace = XR_NULL_HANDLE; + leftControllerGripSpace = XR_NULL_HANDLE; + rightControllerGripSpace = XR_NULL_HANDLE; + } +} + +void AppInput_syncActions(App& app) { + // sync action data + XrActiveActionSet activeActionSet = {}; + activeActionSet.actionSet = runningActionSet; + activeActionSet.subactionPath = XR_NULL_PATH; + + XrActionsSyncInfo syncInfo = {XR_TYPE_ACTIONS_SYNC_INFO}; + syncInfo.countActiveActionSets = 1; + syncInfo.activeActionSets = &activeActionSet; + OXR(xrSyncActions(app.Session, &syncInfo)); + + // query input action states + XrActionStateGetInfo getInfo = {XR_TYPE_ACTION_STATE_GET_INFO}; + getInfo.subactionPath = XR_NULL_PATH; + + boolState = GetActionStateBoolean(app, boolAction); + + if (leftControllerAimSpace == XR_NULL_HANDLE && app.SessionActive == true) { + leftControllerAimSpace = CreateActionSpace(app, aimPoseAction, leftHandPath); + rightControllerAimSpace = CreateActionSpace(app, aimPoseAction, rightHandPath); + leftControllerGripSpace = CreateActionSpace(app, gripPoseAction, leftHandPath); + rightControllerGripSpace = CreateActionSpace(app, gripPoseAction, rightHandPath); + } + + leftControllerActive = ActionPoseIsActive(app, aimPoseAction, leftHandPath); + rightControllerActive = ActionPoseIsActive(app, aimPoseAction, rightHandPath); +} diff --git a/Samples/XrSamples/XrPassthrough/Src/XrPassthroughInput.h b/Samples/XrSamples/XrPassthrough/Src/XrPassthroughInput.h new file mode 100755 index 0000000..78b881f --- /dev/null +++ b/Samples/XrSamples/XrPassthrough/Src/XrPassthroughInput.h @@ -0,0 +1,34 @@ +#pragma once + +#if defined(ANDROID) +#include +#include +#include +#include + +#define XR_USE_GRAPHICS_API_OPENGL_ES 1 +#define XR_USE_PLATFORM_ANDROID 1 +#else +#include "unknwn.h" +#include "Render/GlWrapperWin32.h" +#define XR_USE_GRAPHICS_API_OPENGL 1 +#define XR_USE_PLATFORM_WIN32 1 +#endif // defined(ANDROID) + +#include +#include +#include + +void AppInput_init(App& app); +void AppInput_shutdown(); +void AppInput_syncActions(App& app); + +extern XrActionStateBoolean boolState; + +extern bool leftControllerActive; +extern bool rightControllerActive; + +extern XrSpace leftControllerAimSpace; +extern XrSpace rightControllerAimSpace; +extern XrSpace leftControllerGripSpace; +extern XrSpace rightControllerGripSpace; diff --git a/Samples/XrSamples/XrPassthrough/assets/donotedelete.txt b/Samples/XrSamples/XrPassthrough/assets/donotedelete.txt new file mode 100644 index 0000000..e69de29 diff --git a/Samples/XrSamples/XrPassthrough/java/com/oculus/NativeActivity.java b/Samples/XrSamples/XrPassthrough/java/com/oculus/NativeActivity.java new file mode 100644 index 0000000..e8992db --- /dev/null +++ b/Samples/XrSamples/XrPassthrough/java/com/oculus/NativeActivity.java @@ -0,0 +1,28 @@ +// Copyright (c) Facebook Technologies, LLC and its affiliates. All Rights reserved. +package com.oculus; + +/** + * When using NativeActivity, we currently need to handle loading of dependent shared libraries + * manually before a shared library that depends on them is loaded, since there is not currently a + * way to specify a shared library dependency for NativeActivity via the manifest meta-data. + * + *

The simplest method for doing so is to subclass NativeActivity with an empty activity that + * calls System.loadLibrary on the dependent libraries, which is unfortunate when the goal is to + * write a pure native C/C++ only Android activity. + * + *

A native-code only solution is to load the dependent libraries dynamically using dlopen(). + * However, there are a few considerations, see: + * https://groups.google.com/forum/#!msg/android-ndk/l2E2qh17Q6I/wj6s_6HSjaYJ + * + *

1. Only call dlopen() if you're sure it will succeed as the bionic dynamic linker will + * remember if dlopen failed and will not re-try a dlopen on the same lib a second time. + * + *

2. Must remember what libraries have already been loaded to avoid infinitely looping when + * libraries have circular dependencies. + */ +public class NativeActivity extends android.app.NativeActivity { + static { + System.loadLibrary("openxr_loader"); + System.loadLibrary("xrpassthrough"); + } +} diff --git a/Samples/XrSamples/XrPassthroughOcclusion/CMakeLists.txt b/Samples/XrSamples/XrPassthroughOcclusion/CMakeLists.txt new file mode 100755 index 0000000..7358d6e --- /dev/null +++ b/Samples/XrSamples/XrPassthroughOcclusion/CMakeLists.txt @@ -0,0 +1,35 @@ +# Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved. + +project(xrpassthroughocclusion) + +if(NOT TARGET OpenXR::openxr_loader) + find_package(OpenXR REQUIRED) +endif() + +file(GLOB_RECURSE SRC_FILES + Src/*.c + Src/*.cpp +) + +if(ANDROID) + add_library(${PROJECT_NAME} MODULE ${SRC_FILES}) + target_link_libraries(${PROJECT_NAME} PRIVATE samplecommon_gl) + set_target_properties(${PROJECT_NAME} PROPERTIES LINK_FLAGS "-u ANativeActivity_onCreate") +elseif(WIN32) + add_executable(${PROJECT_NAME} ${SRC_FILES}) + add_custom_command(TARGET ${PROJECT_NAME} PRE_BUILD + COMMAND "${CMAKE_COMMAND}" -E copy_directory + "${CMAKE_CURRENT_LIST_DIR}/assets" + "$/assets" + VERBATIM) + + add_custom_command(TARGET ${PROJECT_NAME} PRE_BUILD + COMMAND "${CMAKE_COMMAND}" -E copy_directory + "${CMAKE_SOURCE_DIR}/SampleXrFramework/res/raw" + "$/font/res/raw" + VERBATIM) + target_link_libraries(${PROJECT_NAME} PRIVATE samplecommon_gl) +endif() + +# Common across platforms +target_include_directories(${PROJECT_NAME} PRIVATE Src) diff --git a/Samples/XrSamples/XrPassthroughOcclusion/Projects/Android/AndroidManifest.xml b/Samples/XrSamples/XrPassthroughOcclusion/Projects/Android/AndroidManifest.xml new file mode 100755 index 0000000..9b0d700 --- /dev/null +++ b/Samples/XrSamples/XrPassthroughOcclusion/Projects/Android/AndroidManifest.xml @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Samples/XrSamples/XrPassthroughOcclusion/Projects/Android/build.bat b/Samples/XrSamples/XrPassthroughOcclusion/Projects/Android/build.bat new file mode 100755 index 0000000..facf79f --- /dev/null +++ b/Samples/XrSamples/XrPassthroughOcclusion/Projects/Android/build.bat @@ -0,0 +1,29 @@ +@rem Only edit the master copy of this file in SDK_ROOT/bin/scripts/build/perproject + +@setlocal enableextensions enabledelayedexpansion + +@if not exist "build.gradle" @echo Build script must be executed from project directory. & goto :Abort + +@set P=.. + +:TryAgain + +@rem @echo P = %P% + +@if exist "%P%\bin\scripts\build\build.py.bat" goto :Found + +@if exist "%P%\bin\scripts\build" @echo "Could not find build.py.bat" & goto :Abort + +@set P=%P%\.. + +@goto :TryAgain + +:Found + +@set P=%P%\bin\scripts\build +@call %P%\build.py.bat %1 %2 %3 %4 %5 +@goto :End + +:Abort + +:End diff --git a/Samples/XrSamples/XrPassthroughOcclusion/Projects/Android/build.gradle b/Samples/XrSamples/XrPassthroughOcclusion/Projects/Android/build.gradle new file mode 100755 index 0000000..9eed2b8 --- /dev/null +++ b/Samples/XrSamples/XrPassthroughOcclusion/Projects/Android/build.gradle @@ -0,0 +1,78 @@ +// Top-level build file where you can add configuration options common to all sub-projects/modules. +buildscript { + repositories { + google() + mavenCentral() + } + dependencies { + classpath "com.android.tools.build:gradle:7.0.3" + } +} + +repositories { + google() + mavenCentral() +} + +apply plugin: 'com.android.application' + +android { + compileSdk 32 + + defaultConfig { + applicationId "com.oculus.xrpassthroughocclusion" + minSdk 26 + targetSdk 32 + versionCode 1 + versionName "1.0" + + // override app plugin abiFilters for 64-bit support + externalNativeBuild { + ndk { + abiFilters 'arm64-v8a' + } + ndkBuild { + abiFilters 'arm64-v8a' + } + cmake { + targets "xrpassthroughocclusion" + } + } + } + + sourceSets { + main { + manifest.srcFile 'AndroidManifest.xml' + java.srcDirs = ['../../java'] + assets.srcDirs = ['../../assets'] + res.srcDirs = ['../../res'] + } + } + + buildTypes { + debug { + debuggable true + } + + release { + debuggable false + } + } + + externalNativeBuild { + cmake { + path file('../../../../CMakeLists.txt') + } + } + + // Enable prefab support for the OpenXR AAR + buildFeatures { + prefab true + } +} + +dependencies { + // Package/application AndroidManifest.xml properties, plus headers and libraries + // exposed to CMake + implementation 'org.khronos.openxr:openxr_loader_for_android:1.1.36' +} diff --git a/Samples/XrSamples/XrPassthroughOcclusion/Projects/Android/build.py b/Samples/XrSamples/XrPassthroughOcclusion/Projects/Android/build.py new file mode 100755 index 0000000..d4b6e58 --- /dev/null +++ b/Samples/XrSamples/XrPassthroughOcclusion/Projects/Android/build.py @@ -0,0 +1,35 @@ +#!/usr/bin/python +# (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +# This first bit of code is common bootstrapping code +# to determine the SDK root, and to set up the import +# path for additional python code. + +# begin bootstrap +import os +import sys + + +def init(): + root = os.path.realpath(os.path.dirname(os.path.realpath(__file__))) + os.chdir(root) # make sure we are always executing from the project directory + while os.path.isdir(os.path.join(root, "bin/scripts/build")) == False: + root = os.path.realpath(os.path.join(root, "..")) + if ( + len(root) <= 5 + ): # Should catch both Posix and Windows root directories (e.g. '/' and 'C:\') + print("Unable to find SDK root. Exiting.") + sys.exit(1) + root = os.path.abspath(root) + os.environ["OCULUS_SDK_PATH"] = root + sys.path.append(root + "/bin/scripts/build") + + +init() +import ovrbuild + +ovrbuild.init() +# end bootstrap + + +ovrbuild.build() diff --git a/Samples/XrSamples/XrPassthroughOcclusion/Projects/Android/gradle.properties b/Samples/XrSamples/XrPassthroughOcclusion/Projects/Android/gradle.properties new file mode 100644 index 0000000..3e927b1 --- /dev/null +++ b/Samples/XrSamples/XrPassthroughOcclusion/Projects/Android/gradle.properties @@ -0,0 +1,21 @@ +# Project-wide Gradle settings. +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. More details, visit +# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects +# org.gradle.parallel=true +# AndroidX package structure to make it clearer which packages are bundled with the +# Android operating system, and which are packaged with your app's APK +# https://developer.android.com/topic/libraries/support-library/androidx-rn +android.useAndroidX=true +# Enables namespacing of each library's R class so that its R class includes only the +# resources declared in the library itself and none from the library's dependencies, +# thereby reducing the size of the R class for that library +android.nonTransitiveRClass=true \ No newline at end of file diff --git a/Samples/XrSamples/XrPassthroughOcclusion/Projects/Android/gradle/wrapper/gradle-wrapper.jar b/Samples/XrSamples/XrPassthroughOcclusion/Projects/Android/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..7454180 Binary files /dev/null and b/Samples/XrSamples/XrPassthroughOcclusion/Projects/Android/gradle/wrapper/gradle-wrapper.jar differ diff --git a/Samples/XrSamples/XrPassthroughOcclusion/Projects/Android/gradle/wrapper/gradle-wrapper.properties b/Samples/XrSamples/XrPassthroughOcclusion/Projects/Android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..ffed3a2 --- /dev/null +++ b/Samples/XrSamples/XrPassthroughOcclusion/Projects/Android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/Samples/XrSamples/XrPassthroughOcclusion/Projects/Android/gradlew b/Samples/XrSamples/XrPassthroughOcclusion/Projects/Android/gradlew new file mode 100755 index 0000000..a69d9cb --- /dev/null +++ b/Samples/XrSamples/XrPassthroughOcclusion/Projects/Android/gradlew @@ -0,0 +1,240 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# 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 +# +# https://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. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit + +APP_NAME="Gradle" +APP_BASE_NAME=${0##*/} + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/Samples/XrSamples/XrPassthroughOcclusion/Projects/Android/gradlew.bat b/Samples/XrSamples/XrPassthroughOcclusion/Projects/Android/gradlew.bat new file mode 100755 index 0000000..f127cfd --- /dev/null +++ b/Samples/XrSamples/XrPassthroughOcclusion/Projects/Android/gradlew.bat @@ -0,0 +1,91 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/Samples/XrSamples/XrPassthroughOcclusion/Projects/Android/settings.gradle b/Samples/XrSamples/XrPassthroughOcclusion/Projects/Android/settings.gradle new file mode 100755 index 0000000..af11d22 --- /dev/null +++ b/Samples/XrSamples/XrPassthroughOcclusion/Projects/Android/settings.gradle @@ -0,0 +1 @@ +rootProject.name = "XrPassthroughOcclusion" diff --git a/Samples/XrSamples/XrPassthroughOcclusion/README.md b/Samples/XrSamples/XrPassthroughOcclusion/README.md new file mode 100644 index 0000000..09ed7cc --- /dev/null +++ b/Samples/XrSamples/XrPassthroughOcclusion/README.md @@ -0,0 +1,7 @@ +# OpenXR Passthrough Occlusion Sample + +## Overview +The `XR_META_environment_depth` extension allows the application to request depth maps of the real-world environment surrounding the headset and perform passthrough occlusion based on that. Occlusion is one of the mixed reality (MR) software features that support the hiding or masking of virtual/augmented objects behind real-world objects (e.g., furniture, limbs, people). In MR, occlusions are essential for rendering believable visual interactions between virtual and physical content. They are one of the key components in enabling a coherent sense of reality for users. When a virtual object hides behind a physical object, but the application cannot render it appropriately, the believability breaks and the user experience suffers. + +## The Sample +This native sample app demonstrates Passthrough and "hard occlusions" implemented using the Depth API. It allows the user to move a cube attached to both the left and right controller and observe how it gets occluded by the real world around the user. diff --git a/Samples/XrSamples/XrPassthroughOcclusion/Src/XrPassthroughOcclusion.cpp b/Samples/XrSamples/XrPassthroughOcclusion/Src/XrPassthroughOcclusion.cpp new file mode 100755 index 0000000..f9e7c62 --- /dev/null +++ b/Samples/XrSamples/XrPassthroughOcclusion/Src/XrPassthroughOcclusion.cpp @@ -0,0 +1,1478 @@ +/************************************************************************************ + +Filename : XrPassthroughOcclusion.cpp +Content : This sample uses the Android NativeActivity class. + +Copyright : Copyright (c) Meta Platforms, Inc. and affiliates. All rights reserved. + +*************************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(ANDROID) +#include +#include +#include // for prctl( PR_SET_NAME ) +#include +#include // for native window JNI +#include +#else +#include +#endif // defined(ANDROID) + +#include "XrPassthroughOcclusion.h" +#include "XrPassthroughOcclusionInput.h" +#include "XrPassthroughOcclusionGl.h" + +using namespace OVR; + +#if !defined(EGL_OPENGL_ES3_BIT_KHR) +#define EGL_OPENGL_ES3_BIT_KHR 0x0040 +#endif + +#define OVR_LOG_TAG "XrPassthroughOcclusion" + +#if !defined(XR_USE_GRAPHICS_API_OPENGL_ES) && !defined(XR_USE_GRAPHICS_API_OPENGL) +#error A graphics backend must be defined! +#elif defined(XR_USE_GRAPHICS_API_OPENGL_ES) && defined(XR_USE_GRAPHICS_API_OPENGL) +#error Only one graphics backend shall be defined! +#endif + +#if defined(ANDROID) +#define ALOGE(...) __android_log_print(ANDROID_LOG_ERROR, OVR_LOG_TAG, __VA_ARGS__) +#define ALOGW(...) __android_log_print(ANDROID_LOG_WARN, OVR_LOG_TAG, __VA_ARGS__) +#define ALOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, OVR_LOG_TAG, __VA_ARGS__) +#else +#define ALOGE(...) \ + printf("ERROR: "); \ + printf(__VA_ARGS__); \ + printf("\n") +#define ALOGV(...) \ + printf("VERBOSE: "); \ + printf(__VA_ARGS__); \ + printf("\n") +#endif + +namespace { + +constexpr int kNumEyes = 2; + +// The GL driver on Quest devices does not resolve depth for multisampled buffers. +// Must either use VK or avoid using multisampling. +constexpr int kNumMultiSamples = 1; + +// Near and far plane values. Must be consistent between the inputs to the +// projection matrix computation and depth submission. +constexpr float kProjectionNearZ = 0.1f; +constexpr float kProjectionFarZ = 10.0f; + +} // namespace + +/* +================================================================================ + +OpenXR Utility Functions + +================================================================================ +*/ + +XrInstance instance; +void OXR_CheckErrors(XrResult result, const char* function, bool failOnError) { + if (XR_FAILED(result)) { + char errorBuffer[XR_MAX_RESULT_STRING_SIZE]; + xrResultToString(instance, result, errorBuffer); + if (failOnError) { + ALOGE("OpenXR error: %s: %s\n", function, errorBuffer); + std::abort(); + } else { + ALOGV("OpenXR error: %s: %s\n", function, errorBuffer); + } + } +} + +// Utility function which appends an OpenXR structure to the end of the `next` chain +// of another OpenXR structure. +template +void appendToNextChain(TChain& chainRoot, TAppend& toAppend) { + XrBaseOutStructure* node = reinterpret_cast(&chainRoot); + XrBaseOutStructure* toAppendPtr = reinterpret_cast(&toAppend); + while (node->next != nullptr) { + node = node->next; + } + node->next = toAppendPtr; +} + +#define DECL_PFN(pfn) PFN_##pfn pfn = nullptr +#define INIT_PFN(pfn) OXR(xrGetInstanceProcAddr(instance, #pfn, (PFN_xrVoidFunction*)(&pfn))) + +DECL_PFN(xrCreatePassthroughFB); +DECL_PFN(xrDestroyPassthroughFB); +DECL_PFN(xrPassthroughStartFB); +DECL_PFN(xrPassthroughPauseFB); +DECL_PFN(xrCreatePassthroughLayerFB); +DECL_PFN(xrDestroyPassthroughLayerFB); +DECL_PFN(xrPassthroughLayerSetStyleFB); +DECL_PFN(xrPassthroughLayerPauseFB); +DECL_PFN(xrPassthroughLayerResumeFB); + +DECL_PFN(xrCreateEnvironmentDepthProviderMETA); +DECL_PFN(xrDestroyEnvironmentDepthProviderMETA); +DECL_PFN(xrStartEnvironmentDepthProviderMETA); +DECL_PFN(xrStopEnvironmentDepthProviderMETA); +DECL_PFN(xrCreateEnvironmentDepthSwapchainMETA); +DECL_PFN(xrDestroyEnvironmentDepthSwapchainMETA); +DECL_PFN(xrEnumerateEnvironmentDepthSwapchainImagesMETA); +DECL_PFN(xrGetEnvironmentDepthSwapchainStateMETA); +DECL_PFN(xrAcquireEnvironmentDepthImageMETA); +DECL_PFN(xrSetEnvironmentDepthHandRemovalMETA); + +/* +================================================================================ + +Environment Depth View Conversion Functions + +================================================================================ +*/ + +OVR::Matrix3f MakePinholeProjectionMatrix(const XrFovf& cameraFovAngles) { + const float tanLeft = std::tan(-cameraFovAngles.angleLeft); + const float tanRight = std::tan(cameraFovAngles.angleRight); + const float tanUp = std::tan(cameraFovAngles.angleUp); + const float tanDown = std::tan(-cameraFovAngles.angleDown); + + const float tanAngleWidth = tanRight + tanLeft; + const float tanAngleHeight = tanUp + tanDown; + + OVR::Matrix3f m = OVR::Matrix3f::Identity(); + m(0, 0) = 1.0f / tanAngleWidth; + m(1, 1) = 1.0f / tanAngleHeight; + m(0, 2) = -tanLeft / tanAngleWidth; + m(1, 2) = -tanDown / tanAngleHeight; + m(2, 2) = -1.0; + + return m; +} + +OVR::Matrix3f MakePinholeUnprojectionMatrix(const XrFovf& cameraFovAngles) { + const float tanLeft = std::tan(-cameraFovAngles.angleLeft); + const float tanRight = std::tan(cameraFovAngles.angleRight); + const float tanUp = std::tan(cameraFovAngles.angleUp); + const float tanDown = std::tan(-cameraFovAngles.angleDown); + + OVR::Matrix3f m = OVR::Matrix3f::Identity(); + m(0, 0) = tanRight + tanLeft; + m(1, 1) = tanUp + tanDown; + m(0, 2) = -tanLeft; + m(1, 2) = -tanDown; + m(2, 2) = -1.0; + + return m; +} + +OVR::Matrix3f MakeQuadFromNormalizedCoordTransform(const OVR::Vector2f& quadSize) { + OVR::Matrix3f T_Quad_Normalized; + T_Quad_Normalized(0, 0) = quadSize.x; + T_Quad_Normalized(1, 1) = quadSize.y; + T_Quad_Normalized(2, 2) = 1.0; + return T_Quad_Normalized; +} + +OVR::Matrix3f MakeNormalizedFromQuadCoordTransform(const OVR::Vector2f& quadSize) { + OVR::Matrix3f T_Normalized_Quad; + T_Normalized_Quad(0, 0) = 1.0 / quadSize.x; + T_Normalized_Quad(1, 1) = 1.0 / quadSize.y; + T_Normalized_Quad(2, 2) = 1.0; + return T_Normalized_Quad; +} + +OVR::Quatf OvrFromXrQuaternion(const XrQuaternionf& xrOrientation) { + return OVR::Quatf(xrOrientation.x, xrOrientation.y, xrOrientation.z, xrOrientation.w); +} + +OVR::Matrix3f MakeDestFromSourceMapping( + const OVR::Vector2f& destSize, + const XrFovf& destFov, + const XrPosef& xfLocalFromDestCamera, + const OVR::Vector2f& sourceSize, + const XrFovf& sourceFov, + const XrPosef& xfLocalFromSourceCamera) { + // Unprojection of points in source image to bearing vectors in the camera. + const OVR::Matrix3f T_SourceCamera_SourceNormCoord = MakePinholeUnprojectionMatrix(sourceFov); + + // Projection of points from the dest camera to the image. + const OVR::Matrix3f T_DestNormCoord_DestCamera = MakePinholeProjectionMatrix(destFov); + + // Construct quaternions from rotation components of the two transformation from views. + const OVR::Quatf Q_LocalFromDestCamera = OvrFromXrQuaternion(xfLocalFromDestCamera.orientation); + const OVR::Quatf Q_LocalFromSourceCamera = + OvrFromXrQuaternion(xfLocalFromSourceCamera.orientation); + + // Rotation between the views. + const OVR::Matrix3f R_DestCamera_SourceCamera = + OVR::Matrix3f(Q_LocalFromDestCamera).Transposed() * OVR::Matrix3f(Q_LocalFromSourceCamera); + + // Map [0, 1]x[0, 1] to [0, width]x[0, height]. + const OVR::Matrix3f T_DestCoord_DestNormCoord = MakeQuadFromNormalizedCoordTransform(destSize); + + // Map [0, width]x[0, height] to [0, 1]x[0, 1]. + const OVR::Matrix3f T_SourceNormCoord_SourceCoord = + MakeNormalizedFromQuadCoordTransform(sourceSize); + + // Put it all together. + const OVR::Matrix3f T_DestCoord_SourceCoord = T_DestCoord_DestNormCoord * + T_DestNormCoord_DestCamera * R_DestCamera_SourceCamera * T_SourceCamera_SourceNormCoord * + T_SourceNormCoord_SourceCoord; + return T_DestCoord_SourceCoord; +} + +/* +================================================================================ + +Egl Utility Functions + +================================================================================ +*/ + +#if defined(XR_USE_GRAPHICS_API_OPENGL_ES) +static const char* EglErrorString(const EGLint error) { + switch (error) { + case EGL_SUCCESS: + return "EGL_SUCCESS"; + case EGL_NOT_INITIALIZED: + return "EGL_NOT_INITIALIZED"; + case EGL_BAD_ACCESS: + return "EGL_BAD_ACCESS"; + case EGL_BAD_ALLOC: + return "EGL_BAD_ALLOC"; + case EGL_BAD_ATTRIBUTE: + return "EGL_BAD_ATTRIBUTE"; + case EGL_BAD_CONTEXT: + return "EGL_BAD_CONTEXT"; + case EGL_BAD_CONFIG: + return "EGL_BAD_CONFIG"; + case EGL_BAD_CURRENT_SURFACE: + return "EGL_BAD_CURRENT_SURFACE"; + case EGL_BAD_DISPLAY: + return "EGL_BAD_DISPLAY"; + case EGL_BAD_SURFACE: + return "EGL_BAD_SURFACE"; + case EGL_BAD_MATCH: + return "EGL_BAD_MATCH"; + case EGL_BAD_PARAMETER: + return "EGL_BAD_PARAMETER"; + case EGL_BAD_NATIVE_PIXMAP: + return "EGL_BAD_NATIVE_PIXMAP"; + case EGL_BAD_NATIVE_WINDOW: + return "EGL_BAD_NATIVE_WINDOW"; + case EGL_CONTEXT_LOST: + return "EGL_CONTEXT_LOST"; + default: + return "unknown"; + } +} + +void Egl::CreateContext(const Egl* shareEgl) { + if (Display != 0) { + return; + } + + Display = eglGetDisplay(EGL_DEFAULT_DISPLAY); + ALOGV(" eglInitialize( Display, &MajorVersion, &MinorVersion )"); + eglInitialize(Display, &MajorVersion, &MinorVersion); + // Do NOT use eglChooseConfig, because the Android EGL code pushes in multisample + // flags in eglChooseConfig if the user has selected the "force 4x MSAA" option in + // settings, and that is completely wasted for our warp target. + const int MAX_CONFIGS = 1024; + EGLConfig configs[MAX_CONFIGS]; + EGLint numConfigs = 0; + if (eglGetConfigs(Display, configs, MAX_CONFIGS, &numConfigs) == EGL_FALSE) { + ALOGE(" eglGetConfigs() failed: %s", EglErrorString(eglGetError())); + return; + } + const EGLint configAttribs[] = { + EGL_RED_SIZE, + 8, + EGL_GREEN_SIZE, + 8, + EGL_BLUE_SIZE, + 8, + EGL_ALPHA_SIZE, + 8, // need alpha for the multi-pass timewarp compositor + EGL_DEPTH_SIZE, + 0, + EGL_STENCIL_SIZE, + 0, + EGL_SAMPLES, + 0, + EGL_NONE}; + Config = 0; + for (int i = 0; i < numConfigs; i++) { + EGLint value = 0; + + eglGetConfigAttrib(Display, configs[i], EGL_RENDERABLE_TYPE, &value); + if ((value & EGL_OPENGL_ES3_BIT_KHR) != EGL_OPENGL_ES3_BIT_KHR) { + continue; + } + + // The pbuffer config also needs to be compatible with normal window rendering + // so it can share textures with the window context. + eglGetConfigAttrib(Display, configs[i], EGL_SURFACE_TYPE, &value); + if ((value & (EGL_WINDOW_BIT | EGL_PBUFFER_BIT)) != (EGL_WINDOW_BIT | EGL_PBUFFER_BIT)) { + continue; + } + + int j = 0; + for (; configAttribs[j] != EGL_NONE; j += 2) { + eglGetConfigAttrib(Display, configs[i], configAttribs[j], &value); + if (value != configAttribs[j + 1]) { + break; + } + } + if (configAttribs[j] == EGL_NONE) { + Config = configs[i]; + break; + } + } + if (Config == 0) { + ALOGE(" eglChooseConfig() failed: %s", EglErrorString(eglGetError())); + return; + } + EGLint contextAttribs[] = {EGL_CONTEXT_CLIENT_VERSION, 3, EGL_NONE}; + ALOGV(" Context = eglCreateContext( Display, Config, EGL_NO_CONTEXT, contextAttribs )"); + Context = eglCreateContext( + Display, + Config, + (shareEgl != nullptr) ? shareEgl->Context : EGL_NO_CONTEXT, + contextAttribs); + if (Context == EGL_NO_CONTEXT) { + ALOGE(" eglCreateContext() failed: %s", EglErrorString(eglGetError())); + return; + } + const EGLint surfaceAttribs[] = {EGL_WIDTH, 16, EGL_HEIGHT, 16, EGL_NONE}; + ALOGV(" TinySurface = eglCreatePbufferSurface( Display, Config, surfaceAttribs )"); + TinySurface = eglCreatePbufferSurface(Display, Config, surfaceAttribs); + if (TinySurface == EGL_NO_SURFACE) { + ALOGE(" eglCreatePbufferSurface() failed: %s", EglErrorString(eglGetError())); + eglDestroyContext(Display, Context); + Context = EGL_NO_CONTEXT; + return; + } + ALOGV(" eglMakeCurrent( Display, TinySurface, TinySurface, Context )"); + if (eglMakeCurrent(Display, TinySurface, TinySurface, Context) == EGL_FALSE) { + ALOGE(" eglMakeCurrent() failed: %s", EglErrorString(eglGetError())); + eglDestroySurface(Display, TinySurface); + eglDestroyContext(Display, Context); + Context = EGL_NO_CONTEXT; + return; + } +} + +void Egl::DestroyContext() { + if (Display != 0) { + ALOGE(" eglMakeCurrent( Display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT )"); + if (eglMakeCurrent(Display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT) == EGL_FALSE) { + ALOGE(" eglMakeCurrent() failed: %s", EglErrorString(eglGetError())); + } + } + if (Context != EGL_NO_CONTEXT) { + ALOGE(" eglDestroyContext( Display, Context )"); + if (eglDestroyContext(Display, Context) == EGL_FALSE) { + ALOGE(" eglDestroyContext() failed: %s", EglErrorString(eglGetError())); + } + Context = EGL_NO_CONTEXT; + } + if (TinySurface != EGL_NO_SURFACE) { + ALOGE(" eglDestroySurface( Display, TinySurface )"); + if (eglDestroySurface(Display, TinySurface) == EGL_FALSE) { + ALOGE(" eglDestroySurface() failed: %s", EglErrorString(eglGetError())); + } + TinySurface = EGL_NO_SURFACE; + } + if (Display != 0) { + ALOGE(" eglTerminate( Display )"); + if (eglTerminate(Display) == EGL_FALSE) { + ALOGE(" eglTerminate() failed: %s", EglErrorString(eglGetError())); + } + Display = 0; + } +} + +#elif defined(XR_USE_GRAPHICS_API_OPENGL) + +#if defined(WIN32) +// Favor the high performance NVIDIA or AMD GPUs +extern "C" { +// http://developer.download.nvidia.com/devzone/devcenter/gamegraphics/files/OptimusRenderingPolicies.pdf +__declspec(dllexport) DWORD NvOptimusEnablement = 0x00000001; +// https://gpuopen.com/learn/amdpowerxpressrequesthighperformance/ +__declspec(dllexport) DWORD AmdPowerXpressRequestHighPerformance = 0x00000001; +} +#endif // defined(WIN32) + +void Egl::CreateContext(const Egl*) { + ovrGl_CreateContext_Windows(&hDC, &hGLRC); +} + +void Egl::DestroyContext() { + ovrGl_DestroyContext_Windows(); +} + +#endif + +void App::HandleSessionStateChanges(XrSessionState state) { + if (state == XR_SESSION_STATE_READY) { +#if defined(XR_USE_PLATFORM_ANDROID) + assert(Resumed); +#endif // defined(XR_USE_PLATFORM_ANDROID) + assert(SessionActive == false); + + XrSessionBeginInfo sessionBeginInfo = {XR_TYPE_SESSION_BEGIN_INFO}; + sessionBeginInfo.primaryViewConfigurationType = ViewportConfig.viewConfigurationType; + + XrResult result; + OXR(result = xrBeginSession(Session, &sessionBeginInfo)); + + SessionActive = (result == XR_SUCCESS); + +#if defined(XR_USE_PLATFORM_ANDROID) + // Set session state once we have entered VR mode and have a valid session object. + if (SessionActive) { + XrPerfSettingsLevelEXT cpuPerfLevel = XR_PERF_SETTINGS_LEVEL_SUSTAINED_HIGH_EXT; + switch (CpuLevel) { + case 0: + cpuPerfLevel = XR_PERF_SETTINGS_LEVEL_POWER_SAVINGS_EXT; + break; + case 1: + cpuPerfLevel = XR_PERF_SETTINGS_LEVEL_SUSTAINED_LOW_EXT; + break; + case 2: + cpuPerfLevel = XR_PERF_SETTINGS_LEVEL_SUSTAINED_HIGH_EXT; + break; + case 3: + cpuPerfLevel = XR_PERF_SETTINGS_LEVEL_BOOST_EXT; + break; + default: + ALOGE("Invalid CPU level %d", CpuLevel); + break; + } + + XrPerfSettingsLevelEXT gpuPerfLevel = XR_PERF_SETTINGS_LEVEL_SUSTAINED_HIGH_EXT; + switch (GpuLevel) { + case 0: + gpuPerfLevel = XR_PERF_SETTINGS_LEVEL_POWER_SAVINGS_EXT; + break; + case 1: + gpuPerfLevel = XR_PERF_SETTINGS_LEVEL_SUSTAINED_LOW_EXT; + break; + case 2: + gpuPerfLevel = XR_PERF_SETTINGS_LEVEL_SUSTAINED_HIGH_EXT; + break; + case 3: + gpuPerfLevel = XR_PERF_SETTINGS_LEVEL_BOOST_EXT; + break; + default: + ALOGE("Invalid GPU level %d", GpuLevel); + break; + } + + PFN_xrPerfSettingsSetPerformanceLevelEXT pfnPerfSettingsSetPerformanceLevelEXT = NULL; + OXR(xrGetInstanceProcAddr( + Instance, + "xrPerfSettingsSetPerformanceLevelEXT", + (PFN_xrVoidFunction*)(&pfnPerfSettingsSetPerformanceLevelEXT))); + + OXR(pfnPerfSettingsSetPerformanceLevelEXT( + Session, XR_PERF_SETTINGS_DOMAIN_CPU_EXT, cpuPerfLevel)); + OXR(pfnPerfSettingsSetPerformanceLevelEXT( + Session, XR_PERF_SETTINGS_DOMAIN_GPU_EXT, gpuPerfLevel)); + + PFN_xrSetAndroidApplicationThreadKHR pfnSetAndroidApplicationThreadKHR = NULL; + OXR(xrGetInstanceProcAddr( + Instance, + "xrSetAndroidApplicationThreadKHR", + (PFN_xrVoidFunction*)(&pfnSetAndroidApplicationThreadKHR))); + + OXR(pfnSetAndroidApplicationThreadKHR( + Session, XR_ANDROID_THREAD_TYPE_APPLICATION_MAIN_KHR, MainThreadTid)); + OXR(pfnSetAndroidApplicationThreadKHR( + Session, XR_ANDROID_THREAD_TYPE_RENDERER_MAIN_KHR, RenderThreadTid)); + } +#endif // defined(XR_USE_PLATFORM_ANDROID) + } else if (state == XR_SESSION_STATE_STOPPING) { +#if defined(XR_USE_PLATFORM_ANDROID) + assert(Resumed == false); +#endif // defined(XR_USE_PLATFORM_ANDROID) + assert(SessionActive); + OXR(xrEndSession(Session)); + SessionActive = false; + } +} + +void App::HandleXrEvents() { + XrEventDataBuffer eventDataBuffer = {}; + + // Poll for events + for (;;) { + XrEventDataBaseHeader* baseEventHeader = (XrEventDataBaseHeader*)(&eventDataBuffer); + baseEventHeader->type = XR_TYPE_EVENT_DATA_BUFFER; + baseEventHeader->next = NULL; + XrResult r; + OXR(r = xrPollEvent(Instance, &eventDataBuffer)); + if (r != XR_SUCCESS) { + break; + } + + switch (baseEventHeader->type) { + case XR_TYPE_EVENT_DATA_EVENTS_LOST: + ALOGV("xrPollEvent: received XR_TYPE_EVENT_DATA_EVENTS_LOST event"); + break; + case XR_TYPE_EVENT_DATA_INSTANCE_LOSS_PENDING: + ALOGV("xrPollEvent: received XR_TYPE_EVENT_DATA_INSTANCE_LOSS_PENDING event"); + break; + case XR_TYPE_EVENT_DATA_INTERACTION_PROFILE_CHANGED: + ALOGV("xrPollEvent: received XR_TYPE_EVENT_DATA_INTERACTION_PROFILE_CHANGED event"); + break; + case XR_TYPE_EVENT_DATA_PERF_SETTINGS_EXT: { +#if defined(XR_USE_PLATFORM_ANDROID) + const XrEventDataPerfSettingsEXT* perf_settings_event = + (XrEventDataPerfSettingsEXT*)(baseEventHeader); + ALOGV( + "xrPollEvent: received XR_TYPE_EVENT_DATA_PERF_SETTINGS_EXT event: type %d subdomain %d : level %d -> level %d", + perf_settings_event->type, + perf_settings_event->subDomain, + perf_settings_event->fromLevel, + perf_settings_event->toLevel); +#endif // defined(XR_USE_PLATFORM_ANDROID) + } break; + case XR_TYPE_EVENT_DATA_REFERENCE_SPACE_CHANGE_PENDING: + ALOGV( + "xrPollEvent: received XR_TYPE_EVENT_DATA_REFERENCE_SPACE_CHANGE_PENDING event"); + break; + case XR_TYPE_EVENT_DATA_SESSION_STATE_CHANGED: { + const XrEventDataSessionStateChanged* session_state_changed_event = + (XrEventDataSessionStateChanged*)(baseEventHeader); + ALOGV( + "xrPollEvent: received XR_TYPE_EVENT_DATA_SESSION_STATE_CHANGED: %d for session %p at time %f", + session_state_changed_event->state, + (void*)session_state_changed_event->session, + FromXrTime(session_state_changed_event->time)); + + switch (session_state_changed_event->state) { + case XR_SESSION_STATE_FOCUSED: + Focused = true; + break; + case XR_SESSION_STATE_VISIBLE: + Focused = false; + break; + case XR_SESSION_STATE_READY: + case XR_SESSION_STATE_STOPPING: + HandleSessionStateChanges(session_state_changed_event->state); + break; + case XR_SESSION_STATE_EXITING: + ShouldExit = true; + break; + default: + break; + } + } break; + default: + ALOGV("xrPollEvent: Unknown event"); + break; + } + } +} + +#if defined(XR_USE_PLATFORM_ANDROID) +/* +================================================================================ + +Native Activity + +================================================================================ +*/ + +/** + * Process the next main command. + */ +static void app_handle_cmd(struct android_app* androidApp, int32_t cmd) { + App& app = *(App*)androidApp->userData; + + switch (cmd) { + // There is no APP_CMD_CREATE. The ANativeActivity creates the + // application thread from onCreate(). The application thread + // then calls android_main(). + case APP_CMD_START: { + ALOGV("onStart()"); + ALOGV(" APP_CMD_START"); + break; + } + case APP_CMD_RESUME: { + ALOGV("onResume()"); + ALOGV(" APP_CMD_RESUME"); + app.Resumed = true; + break; + } + case APP_CMD_PAUSE: { + ALOGV("onPause()"); + ALOGV(" APP_CMD_PAUSE"); + app.Resumed = false; + break; + } + case APP_CMD_STOP: { + ALOGV("onStop()"); + ALOGV(" APP_CMD_STOP"); + break; + } + case APP_CMD_DESTROY: { + ALOGV("onDestroy()"); + ALOGV(" APP_CMD_DESTROY"); + break; + } + case APP_CMD_INIT_WINDOW: { + ALOGV("surfaceCreated()"); + ALOGV(" APP_CMD_INIT_WINDOW"); + break; + } + case APP_CMD_TERM_WINDOW: { + ALOGV("surfaceDestroyed()"); + ALOGV(" APP_CMD_TERM_WINDOW"); + break; + } + } +} + +bool CheckUseScenePermission(JNIEnv* env, jobject activityObject); + +#endif // defined(XR_USE_PLATFORM_ANDROID) + +/** + * This is the main entry point of a native application that is using + * android_native_app_glue. It runs in its own thread, with its own + * event loop for receiving input events and doing other things. + */ +#if defined(XR_USE_PLATFORM_ANDROID) +void android_main(struct android_app* androidApp) { +#else +int main() { +#endif +#if defined(XR_USE_PLATFORM_ANDROID) + ALOGV("----------------------------------------------------------------"); + ALOGV("android_app_entry()"); + ALOGV(" android_main()"); + + JNIEnv* Env; + (*androidApp->activity->vm).AttachCurrentThread(&Env, nullptr); + + // Note that AttachCurrentThread will reset the thread name. + prctl(PR_SET_NAME, (long)"OVR::Main", 0, 0, 0); +#endif // defined(XR_USE_PLATFORM_ANDROID) + + App app; + +#if defined(XR_USE_PLATFORM_ANDROID) + PFN_xrInitializeLoaderKHR xrInitializeLoaderKHR; + xrGetInstanceProcAddr( + XR_NULL_HANDLE, "xrInitializeLoaderKHR", (PFN_xrVoidFunction*)&xrInitializeLoaderKHR); + if (xrInitializeLoaderKHR != NULL) { + XrLoaderInitInfoAndroidKHR loaderInitializeInfoAndroid = { + XR_TYPE_LOADER_INIT_INFO_ANDROID_KHR}; + loaderInitializeInfoAndroid.applicationVM = androidApp->activity->vm; + loaderInitializeInfoAndroid.applicationContext = androidApp->activity->clazz; + xrInitializeLoaderKHR((XrLoaderInitInfoBaseHeaderKHR*)&loaderInitializeInfoAndroid); + } +#endif // defined(XR_USE_PLATFORM_ANDROID) + + // Log available layers. + { + XrResult result; + + PFN_xrEnumerateApiLayerProperties xrEnumerateApiLayerProperties; + OXR(result = xrGetInstanceProcAddr( + XR_NULL_HANDLE, + "xrEnumerateApiLayerProperties", + (PFN_xrVoidFunction*)&xrEnumerateApiLayerProperties)); + if (result != XR_SUCCESS) { + ALOGE("Failed to get xrEnumerateApiLayerProperties function pointer."); + exit(1); + } + + uint32_t layerCount = 0; + OXR(xrEnumerateApiLayerProperties(0, &layerCount, NULL)); + std::vector layerProperties( + layerCount, {XR_TYPE_API_LAYER_PROPERTIES}); + OXR(xrEnumerateApiLayerProperties(layerCount, &layerCount, layerProperties.data())); + + for (const auto& layer : layerProperties) { + ALOGV("Found layer %s", layer.layerName); + } + } + + // Check that the extensions required are present. + const char* const requiredExtensionNames[] = { +#if defined(XR_USE_GRAPHICS_API_OPENGL_ES) + XR_KHR_OPENGL_ES_ENABLE_EXTENSION_NAME, +#elif defined(XR_USE_GRAPHICS_API_OPENGL) + XR_KHR_OPENGL_ENABLE_EXTENSION_NAME, +#endif +#if defined(XR_USE_PLATFORM_ANDROID) + XR_EXT_PERFORMANCE_SETTINGS_EXTENSION_NAME, + XR_KHR_ANDROID_THREAD_SETTINGS_EXTENSION_NAME, +#endif // defined(XR_USE_PLATFORM_ANDROID) + XR_FB_PASSTHROUGH_EXTENSION_NAME, + XR_META_ENVIRONMENT_DEPTH_EXTENSION_NAME + }; + const uint32_t numRequiredExtensions = + sizeof(requiredExtensionNames) / sizeof(requiredExtensionNames[0]); + + // Check the list of required extensions against what is supported by the runtime. + { + uint32_t numOutputExtensions = 0; + OXR(xrEnumerateInstanceExtensionProperties(nullptr, 0, &numOutputExtensions, nullptr)); + ALOGV("xrEnumerateInstanceExtensionProperties found %u extension(s).", numOutputExtensions); + + auto extensionProperties = + std::vector(numOutputExtensions, {XR_TYPE_EXTENSION_PROPERTIES}); + + OXR(xrEnumerateInstanceExtensionProperties( + NULL, numOutputExtensions, &numOutputExtensions, extensionProperties.data())); + for (uint32_t i = 0; i < numOutputExtensions; i++) { + ALOGV("Extension #%d = '%s'.", i, extensionProperties[i].extensionName); + } + + for (uint32_t i = 0; i < numRequiredExtensions; i++) { + bool found = false; + for (uint32_t j = 0; j < numOutputExtensions; j++) { + if (!strcmp(requiredExtensionNames[i], extensionProperties[j].extensionName)) { + ALOGV("Found required extension %s", requiredExtensionNames[i]); + found = true; + break; + } + } + if (!found) { + ALOGE("Failed to find required extension %s", requiredExtensionNames[i]); + exit(1); + } + } + } + + // Create the OpenXR instance. + XrApplicationInfo appInfo = {}; + strcpy(appInfo.applicationName, "XrPassthroughOcclusion"); + appInfo.applicationVersion = 0; + strcpy(appInfo.engineName, "Oculus Mobile Sample"); + appInfo.engineVersion = 0; + appInfo.apiVersion = XR_MAKE_VERSION(1, 0, 34); + + XrInstanceCreateInfo instanceCreateInfo = {XR_TYPE_INSTANCE_CREATE_INFO}; + instanceCreateInfo.createFlags = 0; + instanceCreateInfo.applicationInfo = appInfo; + instanceCreateInfo.enabledApiLayerCount = 0; + instanceCreateInfo.enabledApiLayerNames = NULL; + instanceCreateInfo.enabledExtensionCount = numRequiredExtensions; + instanceCreateInfo.enabledExtensionNames = requiredExtensionNames; + +#if defined(XR_USE_PLATFORM_ANDROID) + if (!CheckUseScenePermission(Env, androidApp->activity->clazz)) { + ALOGW( + "com.oculus.USE_SCENE permission should be requested before creation of OpenXR instance. " + "Application will not function correctly without it."); + } else { + ALOGV("com.oculus.USE_SCENE permission WAS granted"); + } +#endif // defined(XR_USE_PLATFORM_ANDROID) + + XrResult initResult; + OXR(initResult = xrCreateInstance(&instanceCreateInfo, &app.Instance)); + if (initResult != XR_SUCCESS) { + ALOGE("Failed to create XR app.Instance: %d.", initResult); + exit(1); + } + // Set the global used in macros + instance = app.Instance; + + XrInstanceProperties instanceInfo = {XR_TYPE_INSTANCE_PROPERTIES}; + OXR(xrGetInstanceProperties(app.Instance, &instanceInfo)); + ALOGV( + "Runtime %s: Version : %u.%u.%u", + instanceInfo.runtimeName, + XR_VERSION_MAJOR(instanceInfo.runtimeVersion), + XR_VERSION_MINOR(instanceInfo.runtimeVersion), + XR_VERSION_PATCH(instanceInfo.runtimeVersion)); + + XrSystemGetInfo systemGetInfo = {XR_TYPE_SYSTEM_GET_INFO}; + systemGetInfo.formFactor = XR_FORM_FACTOR_HEAD_MOUNTED_DISPLAY; + + XrSystemId systemId; + OXR(initResult = xrGetSystem(app.Instance, &systemGetInfo, &systemId)); + if (initResult != XR_SUCCESS) { + if (initResult == XR_ERROR_FORM_FACTOR_UNAVAILABLE) { + ALOGE("Failed to get system; the specified form factor is not available. Is your headset connected?"); + } else { + ALOGE("xrGetSystem failed, error %d", initResult); + } + exit(1); + } + + XrSystemProperties systemProperties = {XR_TYPE_SYSTEM_PROPERTIES}; + OXR(xrGetSystemProperties(app.Instance, systemId, &systemProperties)); + + ALOGV( + "System Properties: Name=%s VendorId=%x", + systemProperties.systemName, + systemProperties.vendorId); + ALOGV( + "System Graphics Properties: MaxWidth=%d MaxHeight=%d MaxLayers=%d", + systemProperties.graphicsProperties.maxSwapchainImageWidth, + systemProperties.graphicsProperties.maxSwapchainImageHeight, + systemProperties.graphicsProperties.maxLayerCount); + ALOGV( + "System Tracking Properties: OrientationTracking=%s PositionTracking=%s", + systemProperties.trackingProperties.orientationTracking ? "True" : "False", + systemProperties.trackingProperties.positionTracking ? "True" : "False"); + + assert(App::kMaxLayerCount <= systemProperties.graphicsProperties.maxLayerCount); + + // Get the graphics requirements. +#if defined(XR_USE_GRAPHICS_API_OPENGL_ES) + PFN_xrGetOpenGLESGraphicsRequirementsKHR pfnGetOpenGLESGraphicsRequirementsKHR = NULL; + OXR(xrGetInstanceProcAddr( + app.Instance, + "xrGetOpenGLESGraphicsRequirementsKHR", + (PFN_xrVoidFunction*)(&pfnGetOpenGLESGraphicsRequirementsKHR))); + + XrGraphicsRequirementsOpenGLESKHR graphicsRequirements = { + XR_TYPE_GRAPHICS_REQUIREMENTS_OPENGL_ES_KHR}; + OXR(pfnGetOpenGLESGraphicsRequirementsKHR(app.Instance, systemId, &graphicsRequirements)); +#elif defined(XR_USE_GRAPHICS_API_OPENGL) + PFN_xrGetOpenGLGraphicsRequirementsKHR pfnGetOpenGLGraphicsRequirementsKHR = NULL; + OXR(xrGetInstanceProcAddr( + app.Instance, + "xrGetOpenGLGraphicsRequirementsKHR", + (PFN_xrVoidFunction*)(&pfnGetOpenGLGraphicsRequirementsKHR))); + + XrGraphicsRequirementsOpenGLKHR graphicsRequirements = { + XR_TYPE_GRAPHICS_REQUIREMENTS_OPENGL_KHR}; + OXR(pfnGetOpenGLGraphicsRequirementsKHR(app.Instance, systemId, &graphicsRequirements)); +#endif + + // Create the EGL Context + app.egl.CreateContext(nullptr); + + // Check the graphics requirements. + int eglMajor = 0; + int eglMinor = 0; + glGetIntegerv(GL_MAJOR_VERSION, &eglMajor); + glGetIntegerv(GL_MINOR_VERSION, &eglMinor); + const XrVersion eglVersion = XR_MAKE_VERSION(eglMajor, eglMinor, 0); + if (eglVersion < graphicsRequirements.minApiVersionSupported || + eglVersion > graphicsRequirements.maxApiVersionSupported) { + ALOGE("GLES version %d.%d not supported", eglMajor, eglMinor); + exit(0); + } + +#if defined(ANDROID) + app.MainThreadTid = gettid(); +#else + app.MainThreadTid = (int)std::hash{}(std::this_thread::get_id()); +#endif + + app.SystemId = systemId; + + INIT_PFN(xrCreatePassthroughFB); + INIT_PFN(xrDestroyPassthroughFB); + INIT_PFN(xrPassthroughStartFB); + INIT_PFN(xrPassthroughPauseFB); + INIT_PFN(xrCreatePassthroughLayerFB); + INIT_PFN(xrDestroyPassthroughLayerFB); + INIT_PFN(xrPassthroughLayerSetStyleFB); + INIT_PFN(xrPassthroughLayerPauseFB); + INIT_PFN(xrPassthroughLayerResumeFB); + + INIT_PFN(xrCreateEnvironmentDepthProviderMETA); + INIT_PFN(xrDestroyEnvironmentDepthProviderMETA); + INIT_PFN(xrStartEnvironmentDepthProviderMETA); + INIT_PFN(xrStopEnvironmentDepthProviderMETA); + INIT_PFN(xrCreateEnvironmentDepthSwapchainMETA); + INIT_PFN(xrGetEnvironmentDepthSwapchainStateMETA); + INIT_PFN(xrDestroyEnvironmentDepthSwapchainMETA); + INIT_PFN(xrEnumerateEnvironmentDepthSwapchainImagesMETA); + INIT_PFN(xrAcquireEnvironmentDepthImageMETA); + INIT_PFN(xrSetEnvironmentDepthHandRemovalMETA); + + // Create the OpenXR Session. +#if defined(XR_USE_GRAPHICS_API_OPENGL_ES) + XrGraphicsBindingOpenGLESAndroidKHR graphicsBinding = { + XR_TYPE_GRAPHICS_BINDING_OPENGL_ES_ANDROID_KHR}; + graphicsBinding.display = app.egl.Display; + graphicsBinding.config = app.egl.Config; + graphicsBinding.context = app.egl.Context; +#elif defined(XR_USE_GRAPHICS_API_OPENGL) + XrGraphicsBindingOpenGLWin32KHR graphicsBinding = {XR_TYPE_GRAPHICS_BINDING_OPENGL_WIN32_KHR}; + graphicsBinding.hDC = app.egl.hDC; + graphicsBinding.hGLRC = app.egl.hGLRC; +#endif + + XrSessionCreateInfo sessionCreateInfo = {XR_TYPE_SESSION_CREATE_INFO}; + sessionCreateInfo.next = &graphicsBinding; + sessionCreateInfo.createFlags = 0; + sessionCreateInfo.systemId = app.SystemId; + + OXR(initResult = xrCreateSession(app.Instance, &sessionCreateInfo, &app.Session)); + if (initResult != XR_SUCCESS) { + ALOGE("Failed to create XR session: %d.", initResult); + exit(1); + } + + // App only supports the primary stereo view config. + const XrViewConfigurationType supportedViewConfigType = + XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO; + + // Enumerate the viewport configurations. + uint32_t viewportConfigTypeCount = 0; + OXR(xrEnumerateViewConfigurations( + app.Instance, app.SystemId, 0, &viewportConfigTypeCount, NULL)); + + auto viewportConfigurationTypes = new XrViewConfigurationType[viewportConfigTypeCount]; + + OXR(xrEnumerateViewConfigurations( + app.Instance, + app.SystemId, + viewportConfigTypeCount, + &viewportConfigTypeCount, + viewportConfigurationTypes)); + + ALOGV("Available Viewport Configuration Types: %d", viewportConfigTypeCount); + + for (uint32_t i = 0; i < viewportConfigTypeCount; i++) { + const XrViewConfigurationType viewportConfigType = viewportConfigurationTypes[i]; + + ALOGV( + "Viewport configuration type %d : %s", + viewportConfigType, + viewportConfigType == supportedViewConfigType ? "Selected" : ""); + + XrViewConfigurationProperties viewportConfig = {XR_TYPE_VIEW_CONFIGURATION_PROPERTIES}; + OXR(xrGetViewConfigurationProperties( + app.Instance, app.SystemId, viewportConfigType, &viewportConfig)); + ALOGV( + "FovMutable=%s ConfigurationType %d", + viewportConfig.fovMutable ? "true" : "false", + viewportConfig.viewConfigurationType); + + uint32_t viewCount; + OXR(xrEnumerateViewConfigurationViews( + app.Instance, app.SystemId, viewportConfigType, 0, &viewCount, NULL)); + + if (viewCount > 0) { + auto elements = new XrViewConfigurationView[viewCount]; + + for (uint32_t e = 0; e < viewCount; e++) { + elements[e].type = XR_TYPE_VIEW_CONFIGURATION_VIEW; + elements[e].next = NULL; + } + + OXR(xrEnumerateViewConfigurationViews( + app.Instance, app.SystemId, viewportConfigType, viewCount, &viewCount, elements)); + + // Log the view config info for each view type for debugging purposes. + for (uint32_t e = 0; e < viewCount; e++) { + const XrViewConfigurationView* element = &elements[e]; + (void)element; + + ALOGV( + "Viewport [%d]: Recommended Width=%d Height=%d SampleCount=%d", + e, + element->recommendedImageRectWidth, + element->recommendedImageRectHeight, + element->recommendedSwapchainSampleCount); + + ALOGV( + "Viewport [%d]: Max Width=%d Height=%d SampleCount=%d", + e, + element->maxImageRectWidth, + element->maxImageRectHeight, + element->maxSwapchainSampleCount); + } + + // Cache the view config properties for the selected config type. + if (viewportConfigType == supportedViewConfigType) { + assert(viewCount == kNumEyes); + for (uint32_t e = 0; e < viewCount; e++) { + app.ViewConfigurationView[e] = elements[e]; + } + } + + delete[] elements; + } else { + ALOGE("Empty viewport configuration type: %d", viewCount); + } + } + + delete[] viewportConfigurationTypes; + + // Get the viewport configuration info for the chosen viewport configuration type. + app.ViewportConfig.type = XR_TYPE_VIEW_CONFIGURATION_PROPERTIES; + + OXR(xrGetViewConfigurationProperties( + app.Instance, app.SystemId, supportedViewConfigType, &app.ViewportConfig)); + + // Create a space to the first path + XrReferenceSpaceCreateInfo spaceCreateInfo = {XR_TYPE_REFERENCE_SPACE_CREATE_INFO}; + spaceCreateInfo.referenceSpaceType = XR_REFERENCE_SPACE_TYPE_VIEW; + spaceCreateInfo.poseInReferenceSpace.orientation.w = 1.0f; + OXR(xrCreateReferenceSpace(app.Session, &spaceCreateInfo, &app.HeadSpace)); + + spaceCreateInfo.referenceSpaceType = XR_REFERENCE_SPACE_TYPE_LOCAL; + OXR(xrCreateReferenceSpace(app.Session, &spaceCreateInfo, &app.LocalSpace)); + + XrView projections[kNumEyes]; + for (int eye = 0; eye < kNumEyes; eye++) { + projections[eye] = XrView{XR_TYPE_VIEW}; + } + +#if defined(XR_USE_GRAPHICS_API_OPENGL_ES) + using SwapchainImageType = XrSwapchainImageOpenGLESKHR; + static constexpr auto kSwapChainImageType = XR_TYPE_SWAPCHAIN_IMAGE_OPENGL_ES_KHR; +#elif defined(XR_USE_GRAPHICS_API_OPENGL) + using SwapchainImageType = XrSwapchainImageOpenGLKHR; + static constexpr auto kSwapChainImageType = XR_TYPE_SWAPCHAIN_IMAGE_OPENGL_KHR; +#endif + + GLenum format = GL_SRGB8_ALPHA8; + int width = app.ViewConfigurationView[0].recommendedImageRectWidth; + int height = app.ViewConfigurationView[0].recommendedImageRectHeight; + + XrSwapchainCreateInfo swapChainCreateInfo = {XR_TYPE_SWAPCHAIN_CREATE_INFO}; + swapChainCreateInfo.usageFlags = + XR_SWAPCHAIN_USAGE_SAMPLED_BIT | XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT; + swapChainCreateInfo.format = format; + swapChainCreateInfo.sampleCount = 1; + swapChainCreateInfo.width = width; + swapChainCreateInfo.height = height; + swapChainCreateInfo.faceCount = 1; + swapChainCreateInfo.arraySize = 2; + swapChainCreateInfo.mipCount = 1; + + OXR(xrCreateSwapchain(app.Session, &swapChainCreateInfo, &app.ColorSwapchain)); + OXR(xrEnumerateSwapchainImages(app.ColorSwapchain, 0, &app.SwapchainLength, nullptr)); + + std::vector colorImages(app.SwapchainLength); + for (uint32_t i = 0; i < app.SwapchainLength; ++i) { + colorImages[i] = {kSwapChainImageType}; + } + + OXR(xrEnumerateSwapchainImages( + app.ColorSwapchain, + app.SwapchainLength, + &app.SwapchainLength, + (XrSwapchainImageBaseHeader*)colorImages.data())); + + std::vector colorTextures(app.SwapchainLength); + for (uint32_t i = 0; i < app.SwapchainLength; ++i) { + colorTextures[i] = GLuint(colorImages[i].image); + } + + app.appRenderer.Create( + format, width, height, kNumMultiSamples, app.SwapchainLength, colorTextures.data()); + + AppInput_init(app); + + // Create passthrough objects + XrPassthroughFB passthrough = XR_NULL_HANDLE; + XrPassthroughLayerFB passthroughLayer = XR_NULL_HANDLE; + { + XrPassthroughCreateInfoFB ptci = {XR_TYPE_PASSTHROUGH_CREATE_INFO_FB}; + XrResult result; + OXR(result = xrCreatePassthroughFB(app.Session, &ptci, &passthrough)); + + if (XR_SUCCEEDED(result)) { + XrPassthroughLayerCreateInfoFB plci = {XR_TYPE_PASSTHROUGH_LAYER_CREATE_INFO_FB}; + plci.passthrough = passthrough; + plci.purpose = XR_PASSTHROUGH_LAYER_PURPOSE_RECONSTRUCTION_FB; + OXR(xrCreatePassthroughLayerFB(app.Session, &plci, &passthroughLayer)); + } + } + + OXR(xrPassthroughStartFB(passthrough)); + OXR(xrPassthroughLayerResumeFB(passthroughLayer)); + + // Create the environment depth provider. + const XrEnvironmentDepthProviderCreateInfoMETA environmentDepthProviderCreateInfo{ + /*type=*/XR_TYPE_ENVIRONMENT_DEPTH_PROVIDER_CREATE_INFO_META, + /*next=*/nullptr, + /*createFlags=*/0}; + OXR(xrCreateEnvironmentDepthProviderMETA( + app.Session, &environmentDepthProviderCreateInfo, &app.EnvironmentDepthProvider)); + + // Create the depth swapchain. + XrEnvironmentDepthSwapchainCreateInfoMETA environmentDepthSwapchainCreateInfo{ + XR_TYPE_ENVIRONMENT_DEPTH_SWAPCHAIN_CREATE_INFO_META}; + + OXR(xrCreateEnvironmentDepthSwapchainMETA( + app.EnvironmentDepthProvider, + &environmentDepthSwapchainCreateInfo, + &app.EnvironmentDepthSwapchain)); + + XrEnvironmentDepthSwapchainStateMETA environmentDepthSwapchainState{ + XR_TYPE_ENVIRONMENT_DEPTH_SWAPCHAIN_STATE_META}; + OXR(xrGetEnvironmentDepthSwapchainStateMETA( + app.EnvironmentDepthSwapchain, &environmentDepthSwapchainState)); + uint32_t environmentDepthSwapChainLength = 0; + OXR(xrEnumerateEnvironmentDepthSwapchainImagesMETA( + app.EnvironmentDepthSwapchain, 0, &environmentDepthSwapChainLength, nullptr)); + + // Populate the swapchain image array. + std::vector environmentDepthImages(environmentDepthSwapChainLength); + for (uint32_t i = 0; i < environmentDepthSwapChainLength; ++i) { + environmentDepthImages[i] = {kSwapChainImageType}; + } + + OXR(xrEnumerateEnvironmentDepthSwapchainImagesMETA( + app.EnvironmentDepthSwapchain, + environmentDepthSwapChainLength, + &environmentDepthSwapChainLength, + (XrSwapchainImageBaseHeader*)environmentDepthImages.data())); + + std::vector environmentDepthTextures(environmentDepthSwapChainLength); + for (uint32_t i = 0; i < environmentDepthSwapChainLength; ++i) { + environmentDepthTextures[i] = GLuint(environmentDepthImages[i].image); + } + + OXR(xrStartEnvironmentDepthProviderMETA(app.EnvironmentDepthProvider)); + +#if defined(XR_USE_PLATFORM_ANDROID) + androidApp->userData = &app; + androidApp->onAppCmd = app_handle_cmd; +#endif // defined(XR_USE_PLATFORM_ANDROID) + +#if defined(XR_USE_PLATFORM_ANDROID) + while (androidApp->destroyRequested == 0) +#else + while (true) +#endif + { +#if defined(XR_USE_PLATFORM_ANDROID) + // Read all pending events. + for (;;) { + int events; + struct android_poll_source* source; + // If the timeout is zero, returns immediately without blocking. + // If the timeout is negative, waits indefinitely until an event appears. + const int timeoutMilliseconds = (app.Resumed == false && app.SessionActive == false && + androidApp->destroyRequested == 0) + ? -1 + : 0; + if (ALooper_pollAll(timeoutMilliseconds, NULL, &events, (void**)&source) < 0) { + break; + } + + // Process this event. + if (source != NULL) { + source->process(androidApp, source); + } + } +#elif defined(XR_USE_PLATFORM_WIN32) + MSG msg; + while (::PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) > 0) { + if (msg.message == WM_QUIT) { + app.ShouldExit = true; + } else { + ::TranslateMessage(&msg); + ::DispatchMessage(&msg); + } + } +#endif // defined(XR_USE_PLATFORM_ANDROID) + + app.HandleXrEvents(); + + if (app.ShouldExit) { + break; + } + + AppInput_syncActions(app); + + // Create the scene if not yet created. + // The scene is created here to be able to show a loading icon. + if (!app.appRenderer.scene.IsCreated()) { + // Create the scene. + app.appRenderer.scene.Create(); + } + + // xrWaitFrame() is going to fail if session is not active + if (!app.SessionActive) { + continue; + } + + // NOTE: OpenXR does not use the concept of frame indices. Instead, + // XrWaitFrame returns the predicted display time. + XrFrameWaitInfo waitFrameInfo = {XR_TYPE_FRAME_WAIT_INFO}; + + XrFrameState frameState = {XR_TYPE_FRAME_STATE}; + + OXR(xrWaitFrame(app.Session, &waitFrameInfo, &frameState)); + + // Get the HMD pose, predicted for the middle of the time period during which + // the new eye images will be displayed. The number of frames predicted ahead + // depends on the pipeline depth of the engine and the synthesis rate. + // The better the prediction, the less black will be pulled in at the edges. + XrFrameBeginInfo beginFrameDesc = {XR_TYPE_FRAME_BEGIN_INFO}; + OXR(xrBeginFrame(app.Session, &beginFrameDesc)); + + XrPosef xfLocalFromHead; + { + XrSpaceLocation loc = {XR_TYPE_SPACE_LOCATION}; + OXR(xrLocateSpace( + app.HeadSpace, app.LocalSpace, frameState.predictedDisplayTime, &loc)); + xfLocalFromHead = loc.pose; + } + + XrViewState viewState = {XR_TYPE_VIEW_STATE}; + + XrViewLocateInfo projectionInfo = {XR_TYPE_VIEW_LOCATE_INFO}; + projectionInfo.viewConfigurationType = app.ViewportConfig.viewConfigurationType; + projectionInfo.displayTime = frameState.predictedDisplayTime; + projectionInfo.space = app.HeadSpace; + + uint32_t projectionCapacityInput = kNumEyes; + uint32_t projectionCountOutput = projectionCapacityInput; + + OXR(xrLocateViews( + app.Session, + &projectionInfo, + &viewState, + projectionCapacityInput, + &projectionCountOutput, + projections)); + + // update input information + std::vector controllerSpaces; + if (leftControllerActive) { + controllerSpaces.push_back(leftControllerAimSpace); + } + if (rightControllerActive) { + controllerSpaces.push_back(rightControllerAimSpace); + } + app.appRenderer.scene.TrackedControllers.clear(); + for (const XrSpace controllerSpace : controllerSpaces) { + XrSpaceLocation loc{XR_TYPE_SPACE_LOCATION}; + OXR(xrLocateSpace( + controllerSpace, app.LocalSpace, frameState.predictedDisplayTime, &loc)); + app.appRenderer.scene.TrackedControllers.push_back({}); + app.appRenderer.scene.TrackedControllers.back().Pose = OvrFromXr(loc.pose); + } + + AppRenderer::FrameIn frameIn; + uint32_t chainIndex = 0; + XrSwapchainImageAcquireInfo acquireInfo = {XR_TYPE_SWAPCHAIN_IMAGE_ACQUIRE_INFO, NULL}; + OXR(xrAcquireSwapchainImage(app.ColorSwapchain, &acquireInfo, &chainIndex)); + + frameIn.SwapChainIndex = int(chainIndex); + frameIn.ScreenNearZ = kProjectionNearZ; + frameIn.ScreenFarZ = kProjectionFarZ; + + XrPosef xfLocalFromEye[kNumEyes]; + + for (int eye = 0; eye < kNumEyes; eye++) { + XrPosef xfHeadFromEye = projections[eye].pose; + XrPosef_Multiply(&xfLocalFromEye[eye], &xfLocalFromHead, &xfHeadFromEye); + + XrPosef xfEyeFromLocal; + XrPosef_Invert(&xfEyeFromLocal, &xfLocalFromEye[eye]); + + XrMatrix4x4f viewMat{}; + XrMatrix4x4f_CreateFromRigidTransform(&viewMat, &xfEyeFromLocal); + + const XrFovf fov = projections[eye].fov; + XrMatrix4x4f projectionMat; + XrMatrix4x4f_CreateProjectionFov( + &projectionMat, GRAPHICS_OPENGL_ES, fov, kProjectionNearZ, kProjectionFarZ); + + frameIn.View[eye] = OvrFromXr(viewMat); + frameIn.Proj[eye] = OvrFromXr(projectionMat); + } + + XrSwapchainImageWaitInfo waitInfo = {XR_TYPE_SWAPCHAIN_IMAGE_WAIT_INFO}; + waitInfo.timeout = 1000000000; /* timeout in nanoseconds */ + XrResult res = xrWaitSwapchainImage(app.ColorSwapchain, &waitInfo); + int retry = 0; + while (res == XR_TIMEOUT_EXPIRED) { + res = xrWaitSwapchainImage(app.ColorSwapchain, &waitInfo); + retry++; + ALOGV( + " Retry xrWaitSwapchainImage %d times due to XR_TIMEOUT_EXPIRED (duration %f seconds)", + retry, + waitInfo.timeout * (1E-9)); + } + + XrEnvironmentDepthImageAcquireInfoMETA environmentDepthAcquireInfo{ + XR_TYPE_ENVIRONMENT_DEPTH_IMAGE_ACQUIRE_INFO_META}; + environmentDepthAcquireInfo.space = app.LocalSpace; + environmentDepthAcquireInfo.displayTime = frameState.predictedDisplayTime; + XrEnvironmentDepthImageMETA environmentDepthImage{XR_TYPE_ENVIRONMENT_DEPTH_IMAGE_META}; + environmentDepthImage.views[0].type = XR_TYPE_ENVIRONMENT_DEPTH_IMAGE_VIEW_META; + environmentDepthImage.views[1].type = XR_TYPE_ENVIRONMENT_DEPTH_IMAGE_VIEW_META; + + const XrResult acquireResult = xrAcquireEnvironmentDepthImageMETA( + app.EnvironmentDepthProvider, &environmentDepthAcquireInfo, &environmentDepthImage); + if (acquireResult == XR_SUCCESS) { + frameIn.HasDepth = true; + frameIn.DepthTexture = + environmentDepthTextures.at(environmentDepthImage.swapchainIndex); + frameIn.DepthNearZ = environmentDepthImage.nearZ; + frameIn.DepthFarZ = environmentDepthImage.farZ; + + for (int eye = 0; eye < kNumEyes; ++eye) { + const XrPosef xfLocalFromDepthEye = environmentDepthImage.views[eye].pose; + XrPosef xfDepthEyeFromLocal; + XrPosef_Invert(&xfDepthEyeFromLocal, &xfLocalFromDepthEye); + XrMatrix4x4f viewMat; + XrMatrix4x4f_CreateFromRigidTransform(&viewMat, &xfDepthEyeFromLocal); + XrMatrix4x4f projectionMat; + XrMatrix4x4f_CreateProjectionFov( + &projectionMat, + GRAPHICS_OPENGL_ES, + environmentDepthImage.views[eye].fov, + environmentDepthImage.nearZ, + std::isfinite(environmentDepthImage.farZ) ? environmentDepthImage.farZ : 0); + + frameIn.DepthViewMatrices[eye] = OvrFromXr(viewMat); + frameIn.DepthProjectionMatrices[eye] = OvrFromXr(projectionMat); + } + } else { + ALOGE("No depth image received. Result = %d", acquireResult); + frameIn.HasDepth = false; + } + + app.appRenderer.RenderFrame(frameIn); + + XrSwapchainImageReleaseInfo releaseInfo = {XR_TYPE_SWAPCHAIN_IMAGE_RELEASE_INFO, NULL}; + OXR(xrReleaseSwapchainImage(app.ColorSwapchain, &releaseInfo)); + + // Set-up the compositor layers for this frame. + // NOTE: Multiple independent layers are allowed, but they need to be added + // in a depth consistent order. + + XrCompositionLayerProjectionView proj_views[2] = {}; + + app.LayerCount = 0; + memset(app.Layers, 0, sizeof(CompositionLayerUnion) * App::kMaxLayerCount); + + // passthrough layer is backmost layer (if available) + if (passthroughLayer != XR_NULL_HANDLE) { + XrCompositionLayerPassthroughFB passthrough_layer = { + XR_TYPE_COMPOSITION_LAYER_PASSTHROUGH_FB}; + passthrough_layer.layerHandle = passthroughLayer; + passthrough_layer.flags = XR_COMPOSITION_LAYER_BLEND_TEXTURE_SOURCE_ALPHA_BIT; + passthrough_layer.space = XR_NULL_HANDLE; + + app.Layers[app.LayerCount++].Passthrough = passthrough_layer; + } + + XrCompositionLayerProjection proj_layer = {XR_TYPE_COMPOSITION_LAYER_PROJECTION}; + proj_layer.layerFlags = XR_COMPOSITION_LAYER_BLEND_TEXTURE_SOURCE_ALPHA_BIT; + proj_layer.layerFlags |= XR_COMPOSITION_LAYER_CORRECT_CHROMATIC_ABERRATION_BIT; + proj_layer.layerFlags |= XR_COMPOSITION_LAYER_UNPREMULTIPLIED_ALPHA_BIT; + proj_layer.space = app.LocalSpace; + proj_layer.viewCount = kNumEyes; + proj_layer.views = proj_views; + + for (int eye = 0; eye < kNumEyes; eye++) { + XrCompositionLayerProjectionView& proj_view = proj_views[eye]; + proj_view = {XR_TYPE_COMPOSITION_LAYER_PROJECTION_VIEW}; + proj_view.pose = xfLocalFromEye[eye]; + proj_view.fov = projections[eye].fov; + + proj_view.subImage.swapchain = app.ColorSwapchain; + proj_view.subImage.imageRect.offset.x = 0; + proj_view.subImage.imageRect.offset.y = 0; + proj_view.subImage.imageRect.extent.width = width; + proj_view.subImage.imageRect.extent.height = height; + proj_view.subImage.imageArrayIndex = eye; + } + + app.Layers[app.LayerCount++].Projection = proj_layer; + + // Compose the layers for this frame. + const XrCompositionLayerBaseHeader* layers[App::kMaxLayerCount] = {}; + for (int i = 0; i < app.LayerCount; i++) { + layers[i] = (const XrCompositionLayerBaseHeader*)&app.Layers[i]; + } + + XrFrameEndInfo endFrameInfo = {XR_TYPE_FRAME_END_INFO}; + endFrameInfo.displayTime = frameState.predictedDisplayTime; + endFrameInfo.environmentBlendMode = XR_ENVIRONMENT_BLEND_MODE_OPAQUE; + endFrameInfo.layerCount = app.LayerCount; + endFrameInfo.layers = layers; + + OXR(xrEndFrame(app.Session, &endFrameInfo)); + } + + OXR(xrStopEnvironmentDepthProviderMETA(app.EnvironmentDepthProvider)); + OXR(xrDestroyEnvironmentDepthProviderMETA(app.EnvironmentDepthProvider)); + + OXR(xrPassthroughPauseFB(passthrough)); + OXR(xrDestroyPassthroughFB(passthrough)); + + app.appRenderer.Destroy(); + + AppInput_shutdown(); + + OXR(xrDestroySwapchain(app.ColorSwapchain)); + OXR(xrDestroyEnvironmentDepthSwapchainMETA(app.EnvironmentDepthSwapchain)); + OXR(xrDestroySpace(app.HeadSpace)); + OXR(xrDestroySpace(app.LocalSpace)); + OXR(xrDestroySession(app.Session)); + + app.egl.DestroyContext(); + + OXR(xrDestroyInstance(app.Instance)); + +#if defined(XR_USE_PLATFORM_ANDROID) + (*androidApp->activity->vm).DetachCurrentThread(); +#endif // defined(XR_USE_PLATFORM_ANDROID) +} + +#if defined(XR_USE_PLATFORM_ANDROID) + +template +Result JNI_CheckResult(Result result, const char* function, JNIEnv* env) { + if constexpr (std::is_pointer::value) { + if (result == nullptr) { + ALOGE("JNI function failed %s", function); + abort(); + } + } + if (env->ExceptionCheck() == JNI_TRUE) { + ALOGE("JNI function caused a java exception %s", function); + abort(); + } + return result; +} + +bool CheckUseScenePermission(JNIEnv* env, jobject activityObject) { +#define JNI_CHECK_RESULT(func) JNI_CheckResult(func, #func, env); + jstring strPermission = JNI_CHECK_RESULT(env->NewStringUTF("com.oculus.permission.USE_SCENE")); + jclass clsActivity = JNI_CHECK_RESULT(env->FindClass("android/app/Activity")); + jmethodID methodCheckSelfPermission = JNI_CHECK_RESULT( + env->GetMethodID(clsActivity, "checkSelfPermission", "(Ljava/lang/String;)I")); + jint intPermissionResult = JNI_CHECK_RESULT( + env->CallIntMethod(activityObject, methodCheckSelfPermission, strPermission)); + jclass clsPackageManager = + JNI_CHECK_RESULT(env->FindClass("android/content/pm/PackageManager")); + jfieldID fidPermissionGranted = + JNI_CHECK_RESULT(env->GetStaticFieldID(clsPackageManager, "PERMISSION_GRANTED", "I")); + jint intPermissionGranted = + JNI_CHECK_RESULT(env->GetStaticIntField(clsPackageManager, fidPermissionGranted)); + env->DeleteLocalRef(strPermission); + return intPermissionResult == intPermissionGranted; +#undef JNI_CHECK_RESULT +} + +#endif // defined(XR_USE_PLATFORM_ANDROID) diff --git a/Samples/XrSamples/XrPassthroughOcclusion/Src/XrPassthroughOcclusion.h b/Samples/XrSamples/XrPassthroughOcclusion/Src/XrPassthroughOcclusion.h new file mode 100755 index 0000000..17b3512 --- /dev/null +++ b/Samples/XrSamples/XrPassthroughOcclusion/Src/XrPassthroughOcclusion.h @@ -0,0 +1,148 @@ +#pragma once + +#if defined(ANDROID) +#include +#include +#include +#include +#define XR_USE_GRAPHICS_API_OPENGL_ES 1 +#define XR_USE_PLATFORM_ANDROID 1 +#else +#include "unknwn.h" +#include "Render/GlWrapperWin32.h" +#define XR_USE_GRAPHICS_API_OPENGL 1 +#define XR_USE_PLATFORM_WIN32 1 +#endif // defined(ANDROID) + +#include +#include +#include + +#include "XrPassthroughOcclusionGl.h" + +void OXR_CheckErrors(XrResult result, const char* function, bool failOnError); +#define OXR(func) OXR_CheckErrors(func, #func, true); + +inline OVR::Matrix4f OvrFromXr(const XrMatrix4x4f& x) { + return OVR::Matrix4f( + x.m[0x0], + x.m[0x1], + x.m[0x2], + x.m[0x3], + x.m[0x4], + x.m[0x5], + x.m[0x6], + x.m[0x7], + x.m[0x8], + x.m[0x9], + x.m[0xa], + x.m[0xb], + x.m[0xc], + x.m[0xd], + x.m[0xe], + x.m[0xf]); +} + +inline OVR::Quatf OvrFromXr(const XrQuaternionf& q) { + return OVR::Quatf(q.x, q.y, q.z, q.w); +} + +inline OVR::Vector3f OvrFromXr(const XrVector3f& v) { + return OVR::Vector3f(v.x, v.y, v.z); +} + +inline OVR::Posef OvrFromXr(const XrPosef& p) { + return OVR::Posef(OvrFromXr(p.orientation), OvrFromXr(p.position)); +} + +/* +================================================================================ + +Egl + +================================================================================ +*/ + +class Egl { + public: + Egl() = default; + + void CreateContext(const Egl* shareEgl); + void DestroyContext(); +#if defined(XR_USE_GRAPHICS_API_OPENGL_ES) + EGLint MajorVersion = 0; + EGLint MinorVersion = 0; + EGLDisplay Display = 0; + EGLConfig Config = 0; + EGLSurface TinySurface = EGL_NO_SURFACE; + EGLSurface MainSurface = EGL_NO_SURFACE; + EGLContext Context = EGL_NO_CONTEXT; +#elif defined(XR_USE_GRAPHICS_API_OPENGL) + HDC hDC = 0; + HGLRC hGLRC = 0; +#endif +}; + +/* +================================================================================ + +App + +================================================================================ +*/ + +union CompositionLayerUnion { + XrCompositionLayerProjection Projection; + XrCompositionLayerQuad Quad; + XrCompositionLayerCylinderKHR Cylinder; + XrCompositionLayerCubeKHR Cube; + XrCompositionLayerEquirectKHR Equirect; + XrCompositionLayerPassthroughFB Passthrough; +}; + +class App { + public: + static constexpr int kNumEyes = 2; + static constexpr int kMaxLayerCount = 16; + + App() = default; + + void HandleSessionStateChanges(XrSessionState state); + void HandleXrEvents(); + + Egl egl; + +#if defined(XR_USE_PLATFORM_ANDROID) + bool Resumed = false; +#endif // defined(XR_USE_PLATFORM_ANDROID) + bool ShouldExit = false; + bool Focused = false; + + XrInstance Instance = XR_NULL_HANDLE; + XrSession Session = XR_NULL_HANDLE; + XrViewConfigurationProperties ViewportConfig = {}; + XrViewConfigurationView ViewConfigurationView[kNumEyes] = {}; + XrSystemId SystemId = XR_NULL_SYSTEM_ID; + XrSpace HeadSpace = XR_NULL_HANDLE; + XrSpace LocalSpace = XR_NULL_HANDLE; + XrSpace StageSpace = XR_NULL_HANDLE; + bool SessionActive = false; + + int SwapInterval = 1; + int CpuLevel = 2; + int GpuLevel = 3; + // These threads will be marked as performance threads. + int MainThreadTid = 0; + int RenderThreadTid = 0; + CompositionLayerUnion Layers[kMaxLayerCount] = {}; + int LayerCount = 0; + XrSwapchain ColorSwapchain = XR_NULL_HANDLE; + uint32_t SwapchainLength = 0; + + // Environment Depth Provider. + XrEnvironmentDepthProviderMETA EnvironmentDepthProvider = XR_NULL_HANDLE; + XrEnvironmentDepthSwapchainMETA EnvironmentDepthSwapchain = XR_NULL_HANDLE; + + // Provided by XrPassthroughOcclusionGl, which is not aware of VrApi or OpenXR. + AppRenderer appRenderer; +}; diff --git a/Samples/XrSamples/XrPassthroughOcclusion/Src/XrPassthroughOcclusionGl.cpp b/Samples/XrSamples/XrPassthroughOcclusion/Src/XrPassthroughOcclusionGl.cpp new file mode 100755 index 0000000..21457ab --- /dev/null +++ b/Samples/XrSamples/XrPassthroughOcclusion/Src/XrPassthroughOcclusionGl.cpp @@ -0,0 +1,993 @@ +/************************************************************************************ + +Filename : XrPassthroughOcclusionGl.cpp +Content : This sample is derived from XrPassthrough. + Used to demonstrate occlusions in passthrough + +Copyright : Copyright (c) Meta Platforms, Inc. and affiliates. All rights reserved. + +*************************************************************************************/ + +#include "XrPassthroughOcclusionGl.h" + +#if defined(ANDROID) +#include +#include +#include // for prctl( PR_SET_NAME ) +#include +#include // for native window JNI +#include +#endif // defined(ANDROID) + +#include +#include +#include + +#if defined(ANDROID) +#include + +#include +#include +#include +#endif // defined(ANDROID) + +using namespace OVR; + +// EXT_texture_border_clamp +#ifndef GL_CLAMP_TO_BORDER +#define GL_CLAMP_TO_BORDER 0x812D +#endif + +#ifndef GL_TEXTURE_BORDER_COLOR +#define GL_TEXTURE_BORDER_COLOR 0x1004 +#endif + +#ifndef GL_FRAMEBUFFER_SRGB_EXT +#define GL_FRAMEBUFFER_SRGB_EXT 0x8DB9 +#endif + +#if !defined(GL_OVR_multiview) +typedef void(GL_APIENTRY* PFNGLFRAMEBUFFERTEXTUREMULTIVIEWOVRPROC)( + GLenum target, + GLenum attachment, + GLuint texture, + GLint level, + GLint baseViewIndex, + GLsizei numViews); +#endif + +#if !defined(GL_OVR_multiview_multisampled_render_to_texture) +typedef void(GL_APIENTRY* PFNGLFRAMEBUFFERTEXTUREMULTISAMPLEMULTIVIEWOVRPROC)( + GLenum target, + GLenum attachment, + GLuint texture, + GLint level, + GLsizei samples, + GLint baseViewIndex, + GLsizei numViews); +#endif + +#define DEBUG 1 +#define OVR_LOG_TAG "XrPassthroughOcclusionGl" + +#if defined(ANDROID) +#define ALOGE(...) __android_log_print(ANDROID_LOG_ERROR, OVR_LOG_TAG, __VA_ARGS__) +#if DEBUG +#define ALOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, OVR_LOG_TAG, __VA_ARGS__) +#else +#define ALOGV(...) +#endif +#else +#define ALOGE(...) \ + printf("ERROR: "); \ + printf(__VA_ARGS__); \ + printf("\n") +#define ALOGV(...) \ + printf("VERBOSE: "); \ + printf(__VA_ARGS__); \ + printf("\n") +#endif + +/* +================================================================================ + +OpenGL-ES Utility Functions + +================================================================================ +*/ + +namespace { +struct OpenGLExtensions_t { + bool multi_view; // GL_OVR_multiview, GL_OVR_multiview2 + bool EXT_texture_border_clamp; // GL_EXT_texture_border_clamp, GL_OES_texture_border_clamp + bool EXT_sRGB_write_control; +}; + +OpenGLExtensions_t glExtensions; + +void EglInitExtensions() { + glExtensions = {}; + const char* allExtensions = (const char*)glGetString(GL_EXTENSIONS); + if (allExtensions != nullptr) { + glExtensions.multi_view = strstr(allExtensions, "GL_OVR_multiview2") && + strstr(allExtensions, "GL_OVR_multiview_multisampled_render_to_texture"); + + glExtensions.EXT_texture_border_clamp = + strstr(allExtensions, "GL_EXT_texture_border_clamp") || + strstr(allExtensions, "GL_OES_texture_border_clamp"); + glExtensions.EXT_sRGB_write_control = strstr(allExtensions, "GL_EXT_sRGB_write_control"); + } +} + +const char* GlFrameBufferStatusString(GLenum status) { + switch (status) { + case GL_FRAMEBUFFER_UNDEFINED: + return "GL_FRAMEBUFFER_UNDEFINED"; + case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: + return "GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT"; + case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: + return "GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT"; + case GL_FRAMEBUFFER_UNSUPPORTED: + return "GL_FRAMEBUFFER_UNSUPPORTED"; + case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE: + return "GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE"; + default: + return "unknown"; + } +} + +#define CHECK_GL_ERRORS +#ifdef CHECK_GL_ERRORS + +const char* GlErrorString(GLenum error) { + switch (error) { + case GL_NO_ERROR: + return "GL_NO_ERROR"; + case GL_INVALID_ENUM: + return "GL_INVALID_ENUM"; + case GL_INVALID_VALUE: + return "GL_INVALID_VALUE"; + case GL_INVALID_OPERATION: + return "GL_INVALID_OPERATION"; + case GL_INVALID_FRAMEBUFFER_OPERATION: + return "GL_INVALID_FRAMEBUFFER_OPERATION"; + case GL_OUT_OF_MEMORY: + return "GL_OUT_OF_MEMORY"; + default: + return "unknown"; + } +} + +void GLCheckErrors(int line) { + for (int i = 0; i < 10; i++) { + const GLenum error = glGetError(); + if (error == GL_NO_ERROR) { + break; + } + ALOGE("GL error on line %d: %s", line, GlErrorString(error)); + } +} + +#define GL(func) \ + func; \ + GLCheckErrors(__LINE__); + +#else // CHECK_GL_ERRORS + +#define GL(func) func; + +#endif // CHECK_GL_ERRORS + +} // namespace + +/* +================================================================================ + +Geometry + +================================================================================ +*/ + +enum VertexAttributeLocation { + VERTEX_ATTRIBUTE_LOCATION_POSITION, + VERTEX_ATTRIBUTE_LOCATION_COLOR, + VERTEX_ATTRIBUTE_LOCATION_UV, + VERTEX_ATTRIBUTE_LOCATION_TRANSFORM +}; + +struct VertexAttribute { + enum VertexAttributeLocation location; + const char* name; +}; + +static VertexAttribute ProgramVertexAttributes[] = { + {VERTEX_ATTRIBUTE_LOCATION_POSITION, "vertexPosition"}, + {VERTEX_ATTRIBUTE_LOCATION_COLOR, "vertexColor"}, + {VERTEX_ATTRIBUTE_LOCATION_UV, "vertexUv"}, + {VERTEX_ATTRIBUTE_LOCATION_TRANSFORM, "vertexTransform"}}; + +void Geometry::CreateBox() { + struct CubeVertices { + float positions[8][4]; + unsigned char colors[8][4]; + }; + + const CubeVertices cubeVertices = { + // positions + {{-1.0f, -1.0f, -1.0f, 1.0f}, + {1.0f, -1.0f, -1.0f, 1.0f}, + {-1.0f, 1.0f, -1.0f, 1.0f}, + {1.0f, 1.0f, -1.0f, 1.0f}, + + {-1.0f, -1.0f, 1.0f, 1.0f}, + {1.0f, -1.0f, 1.0f, 1.0f}, + {-1.0f, 1.0f, 1.0f, 1.0f}, + {1.0f, 1.0f, 1.0f, 1.0f}}, + // colors + { + {255, 0, 0, 255}, + {250, 255, 0, 255}, + {250, 0, 255, 255}, + {255, 255, 0, 255}, + {255, 0, 0, 255}, + {250, 255, 0, 255}, + {250, 0, 255, 255}, + {255, 255, 0, 255}, + }, + }; + + // 6------7 + // /| /| + // 2-+----3 | + // | | | | + // | 4----+-5 + // |/ |/ + // 0------1 + + const unsigned short cubeIndices[36] = {0, 1, 3, 0, 3, 2, + + 5, 4, 6, 5, 6, 7, + + 4, 0, 2, 4, 2, 6, + + 1, 5, 7, 1, 7, 3, + + 4, 5, 1, 4, 1, 0, + + 2, 3, 7, 2, 7, 6}; + + VertexCount = 8; + IndexCount = 36; + + VertexAttribs.resize(2); + + VertexAttribs[0].Index = VERTEX_ATTRIBUTE_LOCATION_POSITION; + VertexAttribs[0].Size = 4; + VertexAttribs[0].Type = GL_FLOAT; + VertexAttribs[0].Normalized = false; + VertexAttribs[0].Stride = sizeof(cubeVertices.positions[0]); + VertexAttribs[0].Pointer = (const GLvoid*)offsetof(CubeVertices, positions); + + VertexAttribs[1].Index = VERTEX_ATTRIBUTE_LOCATION_COLOR; + VertexAttribs[1].Size = 4; + VertexAttribs[1].Type = GL_UNSIGNED_BYTE; + VertexAttribs[1].Normalized = true; + VertexAttribs[1].Stride = sizeof(cubeVertices.colors[0]); + VertexAttribs[1].Pointer = (const GLvoid*)offsetof(CubeVertices, colors); + + GL(glGenBuffers(1, &VertexBuffer)); + GL(glBindBuffer(GL_ARRAY_BUFFER, VertexBuffer)); + GL(glBufferData(GL_ARRAY_BUFFER, sizeof(cubeVertices), &cubeVertices, GL_STATIC_DRAW)); + GL(glBindBuffer(GL_ARRAY_BUFFER, 0)); + + GL(glGenBuffers(1, &IndexBuffer)); + GL(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IndexBuffer)); + GL(glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(cubeIndices), cubeIndices, GL_STATIC_DRAW)); + GL(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)); + + CreateVAO(); +} + +void Geometry::CreateAxes() { + struct AxesVertices { + float positions[6][3]; + unsigned char colors[6][4]; + }; + + static const AxesVertices axesVertices = { + // positions + {{0, 0, 0}, {1, 0, 0}, {0, 0, 0}, {0, 1, 0}, {0, 0, 0}, {0, 0, 1}}, + // colors + {{255, 0, 0, 255}, + {255, 0, 0, 255}, + {0, 255, 0, 255}, + {0, 255, 0, 255}, + {0, 0, 255, 255}, + {0, 0, 255, 255}}, + }; + + static const unsigned short axesIndices[6] = { + 0, + 1, // x axis - red + 2, + 3, // y axis - green + 4, + 5 // z axis - blue + }; + + VertexCount = 6; + IndexCount = 6; + + VertexAttribs.resize(2); + + VertexAttribs[0].Index = VERTEX_ATTRIBUTE_LOCATION_POSITION; + VertexAttribs[0].Size = 3; + VertexAttribs[0].Type = GL_FLOAT; + VertexAttribs[0].Normalized = false; + VertexAttribs[0].Stride = sizeof(axesVertices.positions[0]); + VertexAttribs[0].Pointer = (const GLvoid*)offsetof(AxesVertices, positions); + + VertexAttribs[1].Index = VERTEX_ATTRIBUTE_LOCATION_COLOR; + VertexAttribs[1].Size = 4; + VertexAttribs[1].Type = GL_UNSIGNED_BYTE; + VertexAttribs[1].Normalized = true; + VertexAttribs[1].Stride = sizeof(axesVertices.colors[0]); + VertexAttribs[1].Pointer = (const GLvoid*)offsetof(AxesVertices, colors); + + GL(glGenBuffers(1, &VertexBuffer)); + GL(glBindBuffer(GL_ARRAY_BUFFER, VertexBuffer)); + GL(glBufferData(GL_ARRAY_BUFFER, sizeof(axesVertices), &axesVertices, GL_STATIC_DRAW)); + GL(glBindBuffer(GL_ARRAY_BUFFER, 0)); + + GL(glGenBuffers(1, &IndexBuffer)); + GL(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IndexBuffer)); + GL(glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(axesIndices), axesIndices, GL_STATIC_DRAW)); + GL(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)); + + CreateVAO(); +} + +void Geometry::CreatePlane() { + struct MappedVertices { + float Positions[8]; + float Uvs[8]; + }; + const MappedVertices mappedVertices = { + {-1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, 1.0f, 1.0f}, + {0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f}}; + static const unsigned short kPlaneIndices[6] = {0, 1, 2, 2, 1, 3}; + + VertexCount = 4; + IndexCount = 6; + + VertexAttribs.resize(2); + + VertexAttribs[0].Index = VERTEX_ATTRIBUTE_LOCATION_POSITION; + VertexAttribs[0].Size = 2; + VertexAttribs[0].Type = GL_FLOAT; + VertexAttribs[0].Normalized = false; + VertexAttribs[0].Stride = 2 * sizeof(float); + VertexAttribs[0].Pointer = reinterpret_cast(offsetof(MappedVertices, Positions)); + + VertexAttribs[1].Index = VERTEX_ATTRIBUTE_LOCATION_UV; + VertexAttribs[1].Size = 2; + VertexAttribs[1].Type = GL_FLOAT; + VertexAttribs[1].Normalized = false; + VertexAttribs[1].Stride = 2 * sizeof(float); + VertexAttribs[1].Pointer = reinterpret_cast(offsetof(MappedVertices, Uvs)); + + GL(glGenBuffers(1, &VertexBuffer)); + GL(glBindBuffer(GL_ARRAY_BUFFER, VertexBuffer)); + GL(glBufferData(GL_ARRAY_BUFFER, sizeof(mappedVertices), &mappedVertices, GL_STATIC_DRAW)); + GL(glBindBuffer(GL_ARRAY_BUFFER, 0)); + + GL(glGenBuffers(1, &IndexBuffer)); + GL(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IndexBuffer)); + GL(glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(kPlaneIndices), kPlaneIndices, GL_STATIC_DRAW)); + GL(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)); + + CreateVAO(); +} + +void Geometry::Destroy() { + GL(glDeleteBuffers(1, &IndexBuffer)); + GL(glDeleteBuffers(1, &VertexBuffer)); + + GL(glDeleteVertexArrays(1, &VertexArrayObject)); + + VertexBuffer = 0; + IndexBuffer = 0; + VertexArrayObject = 0; + VertexCount = 0; + IndexCount = 0; + + VertexAttribs.clear(); +} + +void Geometry::CreateVAO() { + GL(glGenVertexArrays(1, &VertexArrayObject)); + GL(glBindVertexArray(VertexArrayObject)); + + GL(glBindBuffer(GL_ARRAY_BUFFER, VertexBuffer)); + + for (const auto& vertexAttrib : VertexAttribs) { + GL(glEnableVertexAttribArray(vertexAttrib.Index)); + GL(glVertexAttribPointer( + vertexAttrib.Index, + vertexAttrib.Size, + vertexAttrib.Type, + vertexAttrib.Normalized, + vertexAttrib.Stride, + vertexAttrib.Pointer)); + } + + GL(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IndexBuffer)); + GL(glBindVertexArray(0)); +} + +/* +================================================================================ + +Program + +================================================================================ +*/ + +struct Uniform { + enum Index { + MODEL_MATRIX, + SCENE_MATRICES, + DEPTH_VIEW_MATRICES, + DEPTH_PROJECTION_MATRICES, + }; + enum Type { + UNIFORM, + BUFFER, + }; + + Index index; + Type type; + const char* name; +}; + +static Uniform ProgramUniforms[] = { + {Uniform::Index::MODEL_MATRIX, Uniform::Type::UNIFORM, "ModelMatrix"}, + {Uniform::Index::SCENE_MATRICES, Uniform::Type::BUFFER, "SceneMatrices"}, + {Uniform::Index::DEPTH_VIEW_MATRICES, Uniform::Type::UNIFORM, "DepthViewMatrix"}, + {Uniform::Index::DEPTH_PROJECTION_MATRICES, Uniform::Type::UNIFORM, "DepthProjectionMatrix"}, +}; + +static const char* programVersion = "#version 300 es\n"; + +bool Program::Create(const char* vertexSource, const char* fragmentSource) { + GLint r; + + GL(VertexShader = glCreateShader(GL_VERTEX_SHADER)); + + const char* vertexSources[3] = {programVersion, "", vertexSource}; + GL(glShaderSource(VertexShader, 3, vertexSources, 0)); + GL(glCompileShader(VertexShader)); + GL(glGetShaderiv(VertexShader, GL_COMPILE_STATUS, &r)); + if (r == GL_FALSE) { + GLchar msg[4096]; + GL(glGetShaderInfoLog(VertexShader, sizeof(msg), 0, msg)); + ALOGE("%s\n", vertexSource); + ALOGE("ERROR: %s\n", msg); + return false; + } + + const char* fragmentSources[2] = {programVersion, fragmentSource}; + GL(FragmentShader = glCreateShader(GL_FRAGMENT_SHADER)); + GL(glShaderSource(FragmentShader, 2, fragmentSources, 0)); + GL(glCompileShader(FragmentShader)); + GL(glGetShaderiv(FragmentShader, GL_COMPILE_STATUS, &r)); + if (r == GL_FALSE) { + GLchar msg[4096]; + GL(glGetShaderInfoLog(FragmentShader, sizeof(msg), 0, msg)); + ALOGE("%s\n", fragmentSource); + ALOGE("ERROR: %s\n", msg); + return false; + } + + GL(Program_ = glCreateProgram()); + GL(glAttachShader(Program_, VertexShader)); + GL(glAttachShader(Program_, FragmentShader)); + + // Bind the vertex attribute locations. + for (size_t i = 0; i < sizeof(ProgramVertexAttributes) / sizeof(ProgramVertexAttributes[0]); + i++) { + GL(glBindAttribLocation( + Program_, ProgramVertexAttributes[i].location, ProgramVertexAttributes[i].name)); + } + + GL(glLinkProgram(Program_)); + GL(glGetProgramiv(Program_, GL_LINK_STATUS, &r)); + if (r == GL_FALSE) { + GLchar msg[4096]; + GL(glGetProgramInfoLog(Program_, sizeof(msg), 0, msg)); + ALOGE("Linking program failed: %s\n", msg); + return false; + } + + int numBufferBindings = 0; + + UniformLocation.clear(); + UniformBinding.clear(); + + for (size_t i = 0; i < sizeof(ProgramUniforms) / sizeof(ProgramUniforms[0]); i++) { + const int uniformIndex = ProgramUniforms[i].index; + if (ProgramUniforms[i].type == Uniform::Type::BUFFER) { + const GLint blockIndex = glGetUniformBlockIndex(Program_, ProgramUniforms[i].name); + if (blockIndex >= 0) { + UniformLocation[uniformIndex] = blockIndex; + UniformBinding[uniformIndex] = numBufferBindings++; + GL(glUniformBlockBinding( + Program_, UniformLocation[uniformIndex], UniformBinding[uniformIndex])); + } + } else { + const GLint uniformLocation = glGetUniformLocation(Program_, ProgramUniforms[i].name); + if (uniformLocation >= 0) { + UniformLocation[uniformIndex] = uniformLocation; + UniformBinding[uniformIndex] = uniformLocation; + } + } + } + + GL(glUseProgram(Program_)); + + // Get the texture locations. + constexpr int kMaxTextures = 8; + for (int i = 0; i < kMaxTextures; i++) { + char name[32]; + sprintf(name, "Texture%i", i); + const GLint location = glGetUniformLocation(Program_, name); + if (location != -1) { + Textures[i] = location; + GL(glUniform1i(location, i)); + } + } + + GL(glUseProgram(0)); + + return true; +} + +void Program::Destroy() { + if (Program_ != 0) { + GL(glDeleteProgram(Program_)); + Program_ = 0; + } + if (VertexShader != 0) { + GL(glDeleteShader(VertexShader)); + VertexShader = 0; + } + if (FragmentShader != 0) { + GL(glDeleteShader(FragmentShader)); + FragmentShader = 0; + } + + UniformLocation.clear(); + UniformBinding.clear(); + Textures.clear(); +} + +int Program::GetUniformLocationOrDie(int uniformId) const { + const auto it = UniformLocation.find(uniformId); + if (it == UniformLocation.end()) { + ALOGE("Could not find uniform location %d", uniformId); + std::abort(); + } + return it->second; +} + +int Program::GetUniformBindingOrDie(int uniformId) const { + const auto it = UniformBinding.find(uniformId); + if (it == UniformBinding.end()) { + ALOGE("Could not find uniform binding %d", uniformId); + std::abort(); + } + return it->second; +} + +static const char SIX_DOF_VERTEX_SHADER[] = R"( + #define NUM_VIEWS 2 + #define VIEW_ID gl_ViewID_OVR + #extension GL_OVR_multiview2 : require + layout(num_views=NUM_VIEWS) in; + in vec3 vertexPosition; + in vec4 vertexColor; + uniform mat4 ModelMatrix; + uniform SceneMatrices + { + uniform mat4 ViewMatrix[NUM_VIEWS]; + uniform mat4 ProjectionMatrix[NUM_VIEWS]; + } sm; + out vec4 fragmentColor; + out vec4 cubeWorldPosition; + void main() { + cubeWorldPosition = ModelMatrix * vec4(vertexPosition, 1.0f); + gl_Position = sm.ProjectionMatrix[VIEW_ID] * sm.ViewMatrix[VIEW_ID] * cubeWorldPosition; + fragmentColor = vertexColor; + } +)"; + +static const char SIX_DOF_FRAGMENT_SHADER[] = R"( + #define NUM_VIEWS 2 + #define VIEW_ID gl_ViewID_OVR + #extension GL_OVR_multiview2 : require + #extension GL_ARB_shading_language_420pack : enable + in lowp vec4 fragmentColor; + in lowp vec4 cubeWorldPosition; + uniform highp mat4 DepthViewMatrix[NUM_VIEWS]; + uniform highp mat4 DepthProjectionMatrix[NUM_VIEWS]; + layout(binding = 0) uniform highp sampler2DArray EnvironmentDepthTexture; + out lowp vec4 outColor; + void main() { + // Transform from world space to depth camera space using 6-DOF matrix + highp vec4 cubeDepthCameraPosition = DepthProjectionMatrix[VIEW_ID] * DepthViewMatrix[VIEW_ID] * cubeWorldPosition; + + // 3D point --> Homogeneous Coordinates --> Normalized Coordinates in [0,1] + highp vec2 cubeDepthCameraPositionHC = cubeDepthCameraPosition.xy / cubeDepthCameraPosition.w; + cubeDepthCameraPositionHC = cubeDepthCameraPositionHC * 0.5f + 0.5f; + + // Sample from Environment Depth API texture + highp vec3 depthViewCoord = vec3(cubeDepthCameraPositionHC, VIEW_ID); + highp float depthViewEyeZ = texture(EnvironmentDepthTexture, depthViewCoord).r; + + // Get virtual object depth + highp float cubeDepth = cubeDepthCameraPosition.z / cubeDepthCameraPosition.w; + cubeDepth = cubeDepth * 0.5f + 0.5f; + + // Test virtual object depth with environment depth. + // If the virtual object is further away (occluded) output a transparent color so real scene content from PT layer is displayed. + outColor = fragmentColor; + if (cubeDepth < depthViewEyeZ) { + outColor.a = 1.0f; // fully opaque + } + else { + outColor = vec4(0.0f, 0.0f, 0.0f, 0.0f); // invisible + } + + gl_FragDepth = cubeDepth; + } +)"; + +/* +================================================================================ + +Framebuffer + +================================================================================ +*/ + +static void* GlGetExtensionProc(const char* functionName) { +#if defined(ANDROID) + return (void*)eglGetProcAddress(functionName); +#elif defined(WIN32) + return (void*)wglGetProcAddress(functionName); +#else + static_assert(false); +#endif +} + +bool Framebuffer::Create( + const GLenum colorFormat, + const int width, + const int height, + const int multisamples, + const int swapChainLength, + GLuint* colorTextures) { + PFNGLFRAMEBUFFERTEXTUREMULTIVIEWOVRPROC glFramebufferTextureMultiviewOVR = + (PFNGLFRAMEBUFFERTEXTUREMULTIVIEWOVRPROC)GlGetExtensionProc( + "glFramebufferTextureMultiviewOVR"); + PFNGLFRAMEBUFFERTEXTUREMULTISAMPLEMULTIVIEWOVRPROC glFramebufferTextureMultisampleMultiviewOVR = + (PFNGLFRAMEBUFFERTEXTUREMULTISAMPLEMULTIVIEWOVRPROC)GlGetExtensionProc( + "glFramebufferTextureMultisampleMultiviewOVR"); + + Width = width; + Height = height; + Multisamples = multisamples; + + Elements.clear(); + Elements.resize(swapChainLength); + + for (int i = 0; i < swapChainLength; i++) { + Element& el = Elements[i]; + // Create the color buffer texture. + el.ColorTexture = colorTextures[i]; + GL(glBindTexture(GL_TEXTURE_2D_ARRAY, el.ColorTexture)); + GL(glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER)); + GL(glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER)); + GLfloat borderColor[] = {0.0f, 0.0f, 0.0f, 0.0f}; + GL(glTexParameterfv(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_BORDER_COLOR, borderColor)); + GL(glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_LINEAR)); + GL(glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_LINEAR)); + GL(glBindTexture(GL_TEXTURE_2D_ARRAY, 0)); + + // Create the depth buffer texture. + GL(glGenTextures(1, &el.DepthTexture)); + GL(glBindTexture(GL_TEXTURE_2D_ARRAY, el.DepthTexture)); + GL(glTexStorage3D(GL_TEXTURE_2D_ARRAY, 1, GL_DEPTH_COMPONENT24, width, height, 2)); + GL(glBindTexture(GL_TEXTURE_2D_ARRAY, 0)); + + // Create the frame buffer. + GL(glGenFramebuffers(1, &el.FrameBufferObject)); + GL(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, el.FrameBufferObject)); + if (multisamples > 1 && (glFramebufferTextureMultisampleMultiviewOVR != nullptr)) { + GL(glFramebufferTextureMultisampleMultiviewOVR( + GL_DRAW_FRAMEBUFFER, + GL_DEPTH_ATTACHMENT, + el.DepthTexture, + 0 /* level */, + multisamples /* samples */, + 0 /* baseViewIndex */, + 2 /* numViews */)); + GL(glFramebufferTextureMultisampleMultiviewOVR( + GL_DRAW_FRAMEBUFFER, + GL_COLOR_ATTACHMENT0, + el.ColorTexture, + 0 /* level */, + multisamples /* samples */, + 0 /* baseViewIndex */, + 2 /* numViews */)); + } else if (glFramebufferTextureMultiviewOVR != nullptr) { + GL(glFramebufferTextureMultiviewOVR( + GL_DRAW_FRAMEBUFFER, + GL_DEPTH_ATTACHMENT, + el.DepthTexture, + 0 /* level */, + 0 /* baseViewIndex */, + 2 /* numViews */)); + GL(glFramebufferTextureMultiviewOVR( + GL_DRAW_FRAMEBUFFER, + GL_COLOR_ATTACHMENT0, + el.ColorTexture, + 0 /* level */, + 0 /* baseViewIndex */, + 2 /* numViews */)); + } + + GL(GLenum renderFramebufferStatus = glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER)); + GL(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0)); + if (renderFramebufferStatus != GL_FRAMEBUFFER_COMPLETE) { + ALOGE( + "Incomplete frame buffer object: %s", + GlFrameBufferStatusString(renderFramebufferStatus)); + Elements.clear(); + return false; + } + } + return true; +} + +void Framebuffer::Destroy() { + for (size_t i = 0; i < Elements.size(); i++) { + Element& el = Elements[i]; + GL(glDeleteFramebuffers(1, &el.FrameBufferObject)); + GL(glDeleteTextures(1, &el.DepthTexture)); + } + Elements.clear(); + Width = 0; + Height = 0; + Multisamples = 0; +} + +void Framebuffer::Bind(int element) { + // No overflow as soon Elements gets resized by swapChainLength parameter in Create(). + if (element < 0 || element >= static_cast(Elements.size())) { + ALOGE("Framebuffer index out of bounds."); + std::abort(); + } + GL(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, Elements[element].FrameBufferObject)); +} + +void Framebuffer::Unbind() { + GL(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0)); +} + +void Framebuffer::Resolve() { + // Discard the depth buffer, so the tiler won't need to write it back out to memory. + const GLenum depthAttachment[1] = {GL_DEPTH_ATTACHMENT}; + glInvalidateFramebuffer(GL_DRAW_FRAMEBUFFER, 1, depthAttachment); + + // We now let the resolve happen implicitly. +} + +/* +================================================================================ + +Scene + +================================================================================ +*/ + +bool Scene::IsCreated() { + return CreatedScene; +} + +void Scene::Create() { + // Setup the scene matrices. + GL(glGenBuffers(1, &SceneMatrices)); + GL(glBindBuffer(GL_UNIFORM_BUFFER, SceneMatrices)); + GL(glBufferData( + GL_UNIFORM_BUFFER, + 2 * sizeof(Matrix4f) /* 2 view matrices */ + + 2 * sizeof(Matrix4f) /* 2 projection matrices */, + nullptr, + GL_STATIC_DRAW)); + GL(glBindBuffer(GL_UNIFORM_BUFFER, 0)); + + if (!BoxDepthSpaceOcclusionProgram.Create(SIX_DOF_VERTEX_SHADER, SIX_DOF_FRAGMENT_SHADER)) { + ALOGE("Failed to compile depth space occlusion box program"); + } + Box.CreateBox(); + + CreatedScene = true; +} + +void Scene::Destroy() { + GL(glDeleteBuffers(1, &SceneMatrices)); + BoxDepthSpaceOcclusionProgram.Destroy(); + Box.Destroy(); + CreatedScene = false; +} + +/* +================================================================================ + +AppRenderer + +================================================================================ +*/ + +void AppRenderer::Create( + GLenum format, + int width, + int height, + int numMultiSamples, + int swapChainLength, + GLuint* colorTextures) { + EglInitExtensions(); + if (!framebuffer.Create( + format, width, height, numMultiSamples, swapChainLength, colorTextures)) { + ALOGE("Failed to create framebuffer"); + std::abort(); + } + if (glExtensions.EXT_sRGB_write_control) { + // This app was originally written with the presumption that + // its swapchains and compositor front buffer were RGB. + // In order to have the colors the same now that its compositing + // to an sRGB front buffer, we have to write to an sRGB swapchain + // but with the linear->sRGB conversion disabled on write. + GL(glDisable(GL_FRAMEBUFFER_SRGB_EXT)); + } + IsCreated = true; +} + +void AppRenderer::Destroy() { + framebuffer.Destroy(); + scene.Destroy(); + IsCreated = false; +} + +void AppRenderer::RenderFrame(const FrameIn& frameIn) { + if (!IsCreated) { + std::abort(); + } + + // Update the scene matrices. + GL(glBindBuffer(GL_UNIFORM_BUFFER, scene.SceneMatrices)); + GL(Matrix4f* sceneMatrices = (Matrix4f*)glMapBufferRange( + GL_UNIFORM_BUFFER, + 0, + 4 * sizeof(Matrix4f) /* 2 view + 2 proj matrices */, + GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT)); + + if (sceneMatrices != nullptr) { + std::memcpy(reinterpret_cast(sceneMatrices), &frameIn.View[0], 2 * sizeof(Matrix4f)); + std::memcpy( + reinterpret_cast(sceneMatrices) + 2 * sizeof(Matrix4f), + &frameIn.Proj[0], + 2 * sizeof(Matrix4f)); + } + + GL(glUnmapBuffer(GL_UNIFORM_BUFFER)); + GL(glBindBuffer(GL_UNIFORM_BUFFER, 0)); + + // Render the eye images. + framebuffer.Bind(frameIn.SwapChainIndex); + + GL(glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE)); + GL(glDepthMask(GL_TRUE)); + GL(glEnable(GL_SCISSOR_TEST)); + GL(glEnable(GL_DEPTH_TEST)); + GL(glDepthFunc(GL_LEQUAL)); + GL(glDisable(GL_CULL_FACE)); + GL(glDisable(GL_BLEND)); + + GL(glViewport(0, 0, framebuffer.GetWidth(), framebuffer.GetHeight())); + GL(glScissor(0, 0, framebuffer.GetWidth(), framebuffer.GetHeight())); + + GL(glClearColor(0.0, 0.0, 0.0, 0.0)); + GL(glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)); + + RenderScene(frameIn); + + framebuffer.Resolve(); + framebuffer.Unbind(); +} + +void AppRenderer::RenderScene(const FrameIn& frameIn) { + GL(glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE)); + GL(glDepthMask(GL_TRUE)); + GL(glEnable(GL_DEPTH_TEST)); + GL(glDepthFunc(GL_LEQUAL)); + GL(glDisable(GL_CULL_FACE)); + GL(glDisable(GL_BLEND)); + GL(glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); + + // Controllers + GL(glUseProgram(scene.BoxDepthSpaceOcclusionProgram.GetProgramId())); + GL(glBindVertexArray(scene.Box.GetVertexArrayObject())); + + constexpr size_t kDepthMatrixSize = 4 * 4 * sizeof(float); + float viewDataBlock[4 * 4 * 2]; + float projectionDataBlock[4 * 4 * 2]; + std::memcpy( + reinterpret_cast(viewDataBlock), + &frameIn.DepthViewMatrices[0].M[0][0], + kDepthMatrixSize); + std::memcpy( + reinterpret_cast(viewDataBlock) + kDepthMatrixSize, + &frameIn.DepthViewMatrices[1].M[0][0], + kDepthMatrixSize); + std::memcpy( + reinterpret_cast(projectionDataBlock), + &frameIn.DepthProjectionMatrices[0].M[0][0], + kDepthMatrixSize); + std::memcpy( + reinterpret_cast(projectionDataBlock) + kDepthMatrixSize, + &frameIn.DepthProjectionMatrices[1].M[0][0], + kDepthMatrixSize); + + GL(glUniformMatrix4fv( + scene.BoxDepthSpaceOcclusionProgram.GetUniformLocationOrDie( + Uniform::Index::DEPTH_VIEW_MATRICES), + 2, + GL_FALSE, + viewDataBlock)); + GL(glUniformMatrix4fv( + scene.BoxDepthSpaceOcclusionProgram.GetUniformLocationOrDie( + Uniform::Index::DEPTH_PROJECTION_MATRICES), + 2, + GL_FALSE, + projectionDataBlock)); + + GL(glBindTexture(GL_TEXTURE_2D_ARRAY, frameIn.DepthTexture)); + GL(glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)); + GL(glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)); + GL(glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_NEAREST)); + GL(glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_NEAREST)); + + GL(glBindBufferBase( + GL_UNIFORM_BUFFER, + scene.BoxDepthSpaceOcclusionProgram.GetUniformBindingOrDie(Uniform::Index::SCENE_MATRICES), + scene.SceneMatrices)); + for (const auto& trackedController : scene.TrackedControllers) { + const Matrix4f pose(trackedController.Pose); + const Matrix4f offset = Matrix4f::Translation(0, 0, -0.25); + const Matrix4f scale = Matrix4f::Scaling(0.1, 0.1, 0.1); + const Matrix4f model = pose * offset * scale; + glUniformMatrix4fv( + scene.BoxDepthSpaceOcclusionProgram.GetUniformLocationOrDie( + Uniform::Index::MODEL_MATRIX), + 1, + GL_TRUE, + &model.M[0][0]); + GL(glDrawElements(GL_TRIANGLES, scene.Box.GetIndexCount(), GL_UNSIGNED_SHORT, NULL)); + } + + GL(glBindVertexArray(0)); + GL(glBindTexture(GL_TEXTURE_2D_ARRAY, 0)); + GL(glUseProgram(0)); +} diff --git a/Samples/XrSamples/XrPassthroughOcclusion/Src/XrPassthroughOcclusionGl.h b/Samples/XrSamples/XrPassthroughOcclusion/Src/XrPassthroughOcclusionGl.h new file mode 100755 index 0000000..16fb054 --- /dev/null +++ b/Samples/XrSamples/XrPassthroughOcclusion/Src/XrPassthroughOcclusionGl.h @@ -0,0 +1,184 @@ +#pragma once + +#include +#include +#include + +#if defined(ANDROID) +#include +#else +#include "Render/GlWrapperWin32.h" +#endif // defined(ANDROID) + +#include "OVR_Math.h" + +class Geometry { + public: + Geometry() = default; + + void CreateAxes(); + void CreatePlane(); + void CreateBox(); + + void Destroy(); + + int GetIndexCount() const { + return IndexCount; + } + GLuint GetVertexArrayObject() const { + return VertexArrayObject; + } + + private: + struct VertexAttribPointer { + GLint Index = 0; + GLint Size = 0; + GLenum Type = 0; + GLboolean Normalized = false; + GLsizei Stride = 0; + const GLvoid* Pointer = nullptr; + }; + + void CreateVAO(); + + GLuint VertexArrayObject = 0; + GLuint VertexBuffer = 0; + GLuint IndexBuffer = 0; + int VertexCount = 0; + int IndexCount = 0; + std::vector VertexAttribs; +}; + +class Program { + public: + Program() = default; + + bool Create(const char* vertexSource, const char* fragmentSource); + void Destroy(); + + int GetProgramId() const { + return Program_; + } + + int GetUniformLocationOrDie(int uniformId) const; + int GetUniformBindingOrDie(int uniformId) const; + + private: + GLuint Program_ = 0; + GLuint VertexShader = 0; + GLuint FragmentShader = 0; + + // These will be -1 if not used by the program. + std::unordered_map UniformLocation; + std::unordered_map UniformBinding; + std::unordered_map Textures; +}; + +class Framebuffer { + public: + Framebuffer() = default; + + bool Create( + GLenum colorFormat, + int width, + int height, + int multisamples, + int swapChainLength, + GLuint* colorTextures); + void Destroy(); + + void Bind(int element); + void Unbind(); + void Resolve(); + + int GetWidth() const { + return Width; + } + int GetHeight() const { + return Height; + } + + private: + struct Element { + GLuint ColorTexture = 0; + GLuint DepthTexture = 0; + GLuint FrameBufferObject = 0; + }; + + int Width = 0; + int Height = 0; + int Multisamples = 0; + std::vector Elements; +}; + +class Scene { + public: + struct TrackedController { + OVR::Posef Pose; + }; + + Scene() = default; + + void Create(); + void Destroy(); + + bool IsCreated(); + void SetClearColor(const float* c); + + std::vector TrackedControllers; + + GLuint SceneMatrices = 0; + + Program BoxDepthSpaceOcclusionProgram; + Geometry Box; + + private: + bool CreatedScene = false; +}; + +class AppRenderer { + public: + struct FrameIn { + static constexpr int kNumEyes = 2; + + int SwapChainIndex = 0; + OVR::Matrix4f View[kNumEyes]; + OVR::Matrix4f Proj[kNumEyes]; + + bool HasDepth = false; + + // Render target metadata: + float ScreenNearZ = 0.0f; + float ScreenFarZ = 0.0f; + + // Depth texture metadata: + GLuint DepthTexture = 0; + float DepthNearZ = 0.0f; + float DepthFarZ = 0.0f; + + // Depth space transform matrices: + OVR::Matrix4f DepthViewMatrices[kNumEyes]; + OVR::Matrix4f DepthProjectionMatrices[kNumEyes]; + }; + + AppRenderer() = default; + + void Create( + GLenum format, + int width, + int height, + int numMultiSamples, + int swapChainLength, + GLuint* colorTextures); + void Destroy(); + + void RenderFrame(const FrameIn& frameIn); + + Scene scene; + + private: + void RenderScene(const FrameIn& frameIn); + + bool IsCreated = false; + Framebuffer framebuffer; +}; diff --git a/Samples/XrSamples/XrPassthroughOcclusion/Src/XrPassthroughOcclusionInput.cpp b/Samples/XrSamples/XrPassthroughOcclusion/Src/XrPassthroughOcclusionInput.cpp new file mode 100755 index 0000000..57034ef --- /dev/null +++ b/Samples/XrSamples/XrPassthroughOcclusion/Src/XrPassthroughOcclusionInput.cpp @@ -0,0 +1,242 @@ +/************************************************************************************ + +Filename : XrPassthroughOcclusionInput.cpp +Content : This sample uses the Android NativeActivity class. + +Copyright : Copyright (c) Meta Platforms, Inc. and affiliates. All rights reserved. + +*************************************************************************************/ + +#include +#include +#include +#include // for memset +#include +#include + +#if defined(ANDROID) +#include +#include +#include // for native window JNI +#include +#endif // defined(ANDROID) + +#include + +#include + +#include "XrPassthroughOcclusion.h" +#include "XrPassthroughOcclusionInput.h" + +using namespace OVR; + +#if !defined(EGL_OPENGL_ES3_BIT_KHR) +#define EGL_OPENGL_ES3_BIT_KHR 0x0040 +#endif + +#define DEBUG 1 +#define LOG_TAG "XrPassthroughOcclusion" + +#if defined(ANDROID) +#define ALOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__) +#define ALOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__) +#else +#include +#define ALOGE(...) \ + printf("ERROR: "); \ + printf(__VA_ARGS__); \ + printf("\n") +#define ALOGV(...) \ + printf("VERBOSE: "); \ + printf(__VA_ARGS__); \ + printf("\n") +#endif + +namespace { + +XrActionSet +CreateActionSet(XrInstance instance, int priority, const char* name, const char* localizedName) { + XrActionSetCreateInfo asci = {XR_TYPE_ACTION_SET_CREATE_INFO}; + asci.priority = priority; + strcpy(asci.actionSetName, name); + strcpy(asci.localizedActionSetName, localizedName); + XrActionSet actionSet = XR_NULL_HANDLE; + OXR(xrCreateActionSet(instance, &asci, &actionSet)); + return actionSet; +} + +XrAction CreateAction( + XrActionSet actionSet, + XrActionType type, + const char* actionName, + const char* localizedName, + int countSubactionPaths = 0, + XrPath* subactionPaths = nullptr) { + ALOGV("CreateAction %s, %" PRIi32, actionName, countSubactionPaths); + + XrActionCreateInfo aci = {XR_TYPE_ACTION_CREATE_INFO}; + aci.actionType = type; + if (countSubactionPaths > 0) { + aci.countSubactionPaths = countSubactionPaths; + aci.subactionPaths = subactionPaths; + } + strcpy(aci.actionName, actionName); + strcpy(aci.localizedActionName, localizedName ? localizedName : actionName); + XrAction action = XR_NULL_HANDLE; + OXR(xrCreateAction(actionSet, &aci, &action)); + return action; +} + +XrActionSuggestedBinding +ActionSuggestedBinding(App& app, XrAction action, const char* bindingString) { + XrActionSuggestedBinding asb; + asb.action = action; + XrPath bindingPath; + OXR(xrStringToPath(app.Instance, bindingString, &bindingPath)); + asb.binding = bindingPath; + return asb; +} + +XrSpace CreateActionSpace(App& app, XrAction poseAction, XrPath subactionPath) { + XrActionSpaceCreateInfo asci = {XR_TYPE_ACTION_SPACE_CREATE_INFO}; + asci.action = poseAction; + asci.poseInActionSpace.orientation.w = 1.0f; + asci.subactionPath = subactionPath; + XrSpace actionSpace = XR_NULL_HANDLE; + OXR(xrCreateActionSpace(app.Session, &asci, &actionSpace)); + return actionSpace; +} + +XrActionStateBoolean GetActionStateBoolean(App& app, XrAction action) { + XrActionStateGetInfo getInfo = {XR_TYPE_ACTION_STATE_GET_INFO}; + getInfo.action = action; + + XrActionStateBoolean state = {XR_TYPE_ACTION_STATE_BOOLEAN}; + OXR(xrGetActionStateBoolean(app.Session, &getInfo, &state)); + return state; +} + +bool ActionPoseIsActive(App& app, XrAction action, XrPath subactionPath) { + XrActionStateGetInfo getInfo = {XR_TYPE_ACTION_STATE_GET_INFO}; + getInfo.action = action; + getInfo.subactionPath = subactionPath; + + XrActionStatePose state = {XR_TYPE_ACTION_STATE_POSE}; + OXR(xrGetActionStatePose(app.Session, &getInfo, &state)); + return state.isActive != XR_FALSE; +} + +XrActionSet runningActionSet = XR_NULL_HANDLE; + +XrAction aimPoseAction = XR_NULL_HANDLE; +XrAction gripPoseAction = XR_NULL_HANDLE; + +XrAction boolAction = XR_NULL_HANDLE; +XrPath leftHandPath = XR_NULL_PATH; +XrPath rightHandPath = XR_NULL_PATH; +} // namespace + +XrActionStateBoolean boolState; + +bool leftControllerActive = false; +bool rightControllerActive = false; + +XrSpace leftControllerAimSpace = XR_NULL_HANDLE; +XrSpace rightControllerAimSpace = XR_NULL_HANDLE; +XrSpace leftControllerGripSpace = XR_NULL_HANDLE; +XrSpace rightControllerGripSpace = XR_NULL_HANDLE; + +void AppInput_init(App& app) { + // Actions + runningActionSet = + CreateActionSet(app.Instance, 1, "running_action_set", "Action Set used on main loop"); + boolAction = CreateAction(runningActionSet, XR_ACTION_TYPE_BOOLEAN_INPUT, "toggle", "Toggle"); + + OXR(xrStringToPath(app.Instance, "/user/hand/left", &leftHandPath)); + OXR(xrStringToPath(app.Instance, "/user/hand/right", &rightHandPath)); + XrPath handSubactionPaths[2] = {leftHandPath, rightHandPath}; + + aimPoseAction = CreateAction( + runningActionSet, XR_ACTION_TYPE_POSE_INPUT, "aim_pose", nullptr, 2, handSubactionPaths); + + gripPoseAction = CreateAction( + runningActionSet, XR_ACTION_TYPE_POSE_INPUT, "grip_pose", nullptr, 2, handSubactionPaths); + + XrPath interactionProfilePath = XR_NULL_PATH; + + OXR(xrStringToPath( + app.Instance, "/interaction_profiles/oculus/touch_controller", &interactionProfilePath)); + + // Action creation + { + // Map bindings + + std::vector bindings; + bindings.push_back( + ActionSuggestedBinding(app, boolAction, "/user/hand/left/input/trigger")); + bindings.push_back( + ActionSuggestedBinding(app, boolAction, "/user/hand/right/input/trigger")); + bindings.push_back( + ActionSuggestedBinding(app, aimPoseAction, "/user/hand/left/input/aim/pose")); + bindings.push_back( + ActionSuggestedBinding(app, aimPoseAction, "/user/hand/right/input/aim/pose")); + bindings.push_back( + ActionSuggestedBinding(app, gripPoseAction, "/user/hand/left/input/grip/pose")); + bindings.push_back( + ActionSuggestedBinding(app, gripPoseAction, "/user/hand/right/input/grip/pose")); + + XrInteractionProfileSuggestedBinding suggestedBindings = { + XR_TYPE_INTERACTION_PROFILE_SUGGESTED_BINDING}; + suggestedBindings.interactionProfile = interactionProfilePath; + suggestedBindings.suggestedBindings = &bindings[0]; + suggestedBindings.countSuggestedBindings = bindings.size(); + OXR(xrSuggestInteractionProfileBindings(app.Instance, &suggestedBindings)); + + // Attach to session + XrSessionActionSetsAttachInfo attachInfo = {XR_TYPE_SESSION_ACTION_SETS_ATTACH_INFO}; + attachInfo.countActionSets = 1; + attachInfo.actionSets = &runningActionSet; + OXR(xrAttachSessionActionSets(app.Session, &attachInfo)); + } +} + +void AppInput_shutdown() { + if (leftControllerAimSpace != XR_NULL_HANDLE) { + OXR(xrDestroySpace(leftControllerAimSpace)); + OXR(xrDestroySpace(rightControllerAimSpace)); + OXR(xrDestroySpace(leftControllerGripSpace)); + OXR(xrDestroySpace(rightControllerGripSpace)); + leftControllerAimSpace = XR_NULL_HANDLE; + rightControllerAimSpace = XR_NULL_HANDLE; + leftControllerGripSpace = XR_NULL_HANDLE; + rightControllerGripSpace = XR_NULL_HANDLE; + } +} + +void AppInput_syncActions(App& app) { + // sync action data + XrActiveActionSet activeActionSet = {}; + activeActionSet.actionSet = runningActionSet; + activeActionSet.subactionPath = XR_NULL_PATH; + + XrActionsSyncInfo syncInfo = {XR_TYPE_ACTIONS_SYNC_INFO}; + syncInfo.countActiveActionSets = 1; + syncInfo.activeActionSets = &activeActionSet; + OXR(xrSyncActions(app.Session, &syncInfo)); + + // query input action states + XrActionStateGetInfo getInfo = {XR_TYPE_ACTION_STATE_GET_INFO}; + getInfo.subactionPath = XR_NULL_PATH; + + boolState = GetActionStateBoolean(app, boolAction); + + if (leftControllerAimSpace == XR_NULL_HANDLE && app.SessionActive == true) { + leftControllerAimSpace = CreateActionSpace(app, aimPoseAction, leftHandPath); + rightControllerAimSpace = CreateActionSpace(app, aimPoseAction, rightHandPath); + leftControllerGripSpace = CreateActionSpace(app, gripPoseAction, leftHandPath); + rightControllerGripSpace = CreateActionSpace(app, gripPoseAction, rightHandPath); + } + + leftControllerActive = ActionPoseIsActive(app, aimPoseAction, leftHandPath); + rightControllerActive = ActionPoseIsActive(app, aimPoseAction, rightHandPath); +} diff --git a/Samples/XrSamples/XrPassthroughOcclusion/Src/XrPassthroughOcclusionInput.h b/Samples/XrSamples/XrPassthroughOcclusion/Src/XrPassthroughOcclusionInput.h new file mode 100755 index 0000000..78b881f --- /dev/null +++ b/Samples/XrSamples/XrPassthroughOcclusion/Src/XrPassthroughOcclusionInput.h @@ -0,0 +1,34 @@ +#pragma once + +#if defined(ANDROID) +#include +#include +#include +#include + +#define XR_USE_GRAPHICS_API_OPENGL_ES 1 +#define XR_USE_PLATFORM_ANDROID 1 +#else +#include "unknwn.h" +#include "Render/GlWrapperWin32.h" +#define XR_USE_GRAPHICS_API_OPENGL 1 +#define XR_USE_PLATFORM_WIN32 1 +#endif // defined(ANDROID) + +#include +#include +#include + +void AppInput_init(App& app); +void AppInput_shutdown(); +void AppInput_syncActions(App& app); + +extern XrActionStateBoolean boolState; + +extern bool leftControllerActive; +extern bool rightControllerActive; + +extern XrSpace leftControllerAimSpace; +extern XrSpace rightControllerAimSpace; +extern XrSpace leftControllerGripSpace; +extern XrSpace rightControllerGripSpace; diff --git a/Samples/XrSamples/XrPassthroughOcclusion/assets/donotedelete.txt b/Samples/XrSamples/XrPassthroughOcclusion/assets/donotedelete.txt new file mode 100644 index 0000000..e69de29 diff --git a/Samples/XrSamples/XrPassthroughOcclusion/java/com/oculus/xrpassthroughocclusion/MainActivity.java b/Samples/XrSamples/XrPassthroughOcclusion/java/com/oculus/xrpassthroughocclusion/MainActivity.java new file mode 100644 index 0000000..1e16958 --- /dev/null +++ b/Samples/XrSamples/XrPassthroughOcclusion/java/com/oculus/xrpassthroughocclusion/MainActivity.java @@ -0,0 +1,101 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +package com.oculus.xrpassthroughocclusion; + +import android.content.Intent; +import android.content.pm.PackageManager; +import android.os.Build; +import android.os.Bundle; +import android.util.Log; +import java.util.HashSet; +import java.util.Set; + +public class MainActivity extends android.app.Activity { + public static final String APP_NAME = "XrPassthroughOcclusion"; + + /// Tag used for log messages. + public static final String TAG = APP_NAME; + + // Just adding these permissions to manifest does not work for Android >= 11. Requests have to + // be made explicitly in the code! + private static final String PERMISSIONS[] = {"com.oculus.permission.USE_SCENE"}; + + private static final String NATIVE_LIBRARIES[] = {"openxr_loader", "xrpassthroughocclusion"}; + private final int PERMISSION_REQUEST_CODE = 1; + + private Set mRequestedPermissions = new HashSet<>(); + + private Set getMissingPermissions() { + Set missingPermissions = new HashSet<>(); + for (String permission : PERMISSIONS) { + Log.d(TAG, String.format("Checking permission %s", permission)); + if (checkSelfPermission(permission) != PackageManager.PERMISSION_GRANTED) { + Log.d(TAG, String.format("Requesting permission %s", permission)); + missingPermissions.add(permission); + } + } + return missingPermissions; + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + // startNativeActivity() is called if/when all permissions are granted, otherwise app is + // terminated + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + Set missingPermissions = getMissingPermissions(); + if (missingPermissions.isEmpty()) { + startNativeActivity(); + } else { + mRequestedPermissions = missingPermissions; + requestPermissions( + mRequestedPermissions.toArray(new String[missingPermissions.size()]), + PERMISSION_REQUEST_CODE); + } + } + } + + @Override + public void onDestroy() { + Log.d(TAG, String.format("onDestroy() called")); + super.onDestroy(); + } + + private boolean allPermissionsGranted() { + return mRequestedPermissions.isEmpty(); + } + + @Override + public void onRequestPermissionsResult( + int requestCode, String permissions[], int[] grantResults) { + Log.d(TAG, "onRequestPermissionsResult() called"); + final int length = permissions.length; + if (requestCode == PERMISSION_REQUEST_CODE && length > 0) { + for (int i = 0; i < length; ++i) { + String permission = permissions[i]; + if (grantResults[i] == PackageManager.PERMISSION_GRANTED) { + Log.d(TAG, String.format("Permission %s granted", permission)); + mRequestedPermissions.remove(permission); + } else { + Log.d(TAG, String.format("Permission %s DENIED", permission)); + finish(); + return; + } + } + if (allPermissionsGranted()) { + startNativeActivity(); + } + } + } + + private void startNativeActivity() { + Log.d(TAG, "Starting native activity"); + + for (String libraryName : NATIVE_LIBRARIES) { + System.loadLibrary(libraryName); + } + Intent intent = new Intent(getApplicationContext(), MainNativeActivity.class); + startActivity(intent); + finish(); + } +} diff --git a/Samples/XrSamples/XrPassthroughOcclusion/java/com/oculus/xrpassthroughocclusion/MainNativeActivity.java b/Samples/XrSamples/XrPassthroughOcclusion/java/com/oculus/xrpassthroughocclusion/MainNativeActivity.java new file mode 100644 index 0000000..f976486 --- /dev/null +++ b/Samples/XrSamples/XrPassthroughOcclusion/java/com/oculus/xrpassthroughocclusion/MainNativeActivity.java @@ -0,0 +1,33 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +package com.oculus.xrpassthroughocclusion; + +import android.os.Bundle; +import android.util.Log; + +public class MainNativeActivity extends android.app.NativeActivity { + + @Override + public void onCreate(Bundle savedInstanceState) { + Log.d( + MainActivity.TAG, + String.format("MainNativeActivity.onCreate() called")); + super.onCreate(savedInstanceState); + } + + // Called from native code to safely quit app. + public void onNativeFinish() { + Log.d( + MainActivity.TAG, + String.format("MainNativeActivity finish called from native app.")); + finishAndRemoveTask(); + } + + @Override + public void onDestroy() { + Log.d( + MainActivity.TAG, + String.format("MainNativeActivity.onDestroy() called")); + super.onDestroy(); + } +} diff --git a/Samples/XrSamples/XrSceneModel/CMakeLists.txt b/Samples/XrSamples/XrSceneModel/CMakeLists.txt new file mode 100755 index 0000000..9f42449 --- /dev/null +++ b/Samples/XrSamples/XrSceneModel/CMakeLists.txt @@ -0,0 +1,35 @@ +# Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved. + +project(scenemodel) + +if(NOT TARGET OpenXR::openxr_loader) + find_package(OpenXR REQUIRED) +endif() + +file(GLOB_RECURSE SRC_FILES + Src/*.c + Src/*.cpp +) + +if(ANDROID) + add_library(${PROJECT_NAME} MODULE ${SRC_FILES}) + target_link_libraries(${PROJECT_NAME} PRIVATE samplecommon_gl) + set_target_properties(${PROJECT_NAME} PROPERTIES LINK_FLAGS "-u ANativeActivity_onCreate") +elseif(WIN32) + add_executable(${PROJECT_NAME} ${SRC_FILES}) + add_custom_command(TARGET ${PROJECT_NAME} PRE_BUILD + COMMAND "${CMAKE_COMMAND}" -E copy_directory + "${CMAKE_CURRENT_LIST_DIR}/assets" + "$/assets" + VERBATIM) + + add_custom_command(TARGET ${PROJECT_NAME} PRE_BUILD + COMMAND "${CMAKE_COMMAND}" -E copy_directory + "${CMAKE_SOURCE_DIR}/SampleXrFramework/res/raw" + "$/font/res/raw" + VERBATIM) + target_link_libraries(${PROJECT_NAME} PRIVATE samplecommon_gl) +endif() + +# Common across platforms +target_include_directories(${PROJECT_NAME} PRIVATE Src) diff --git a/Samples/XrSamples/XrSceneModel/Projects/Android/AndroidManifest.xml b/Samples/XrSamples/XrSceneModel/Projects/Android/AndroidManifest.xml new file mode 100755 index 0000000..7a72c8d --- /dev/null +++ b/Samples/XrSamples/XrSceneModel/Projects/Android/AndroidManifest.xml @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Samples/XrSamples/XrSceneModel/Projects/Android/build.bat b/Samples/XrSamples/XrSceneModel/Projects/Android/build.bat new file mode 100755 index 0000000..facf79f --- /dev/null +++ b/Samples/XrSamples/XrSceneModel/Projects/Android/build.bat @@ -0,0 +1,29 @@ +@rem Only edit the master copy of this file in SDK_ROOT/bin/scripts/build/perproject + +@setlocal enableextensions enabledelayedexpansion + +@if not exist "build.gradle" @echo Build script must be executed from project directory. & goto :Abort + +@set P=.. + +:TryAgain + +@rem @echo P = %P% + +@if exist "%P%\bin\scripts\build\build.py.bat" goto :Found + +@if exist "%P%\bin\scripts\build" @echo "Could not find build.py.bat" & goto :Abort + +@set P=%P%\.. + +@goto :TryAgain + +:Found + +@set P=%P%\bin\scripts\build +@call %P%\build.py.bat %1 %2 %3 %4 %5 +@goto :End + +:Abort + +:End diff --git a/Samples/XrSamples/XrSceneModel/Projects/Android/build.gradle b/Samples/XrSamples/XrSceneModel/Projects/Android/build.gradle new file mode 100755 index 0000000..ae238e6 --- /dev/null +++ b/Samples/XrSamples/XrSceneModel/Projects/Android/build.gradle @@ -0,0 +1,78 @@ +// Top-level build file where you can add configuration options common to all sub-projects/modules. +buildscript { + repositories { + google() + mavenCentral() + } + dependencies { + classpath "com.android.tools.build:gradle:7.0.3" + } +} + +repositories { + google() + mavenCentral() +} + +apply plugin: 'com.android.application' + +android { + compileSdk 32 + + defaultConfig { + applicationId "com.oculus.sdk.scenemodel" + minSdk 26 + targetSdk 32 + versionCode 1 + versionName "1.0" + + // override app plugin abiFilters for 64-bit support + externalNativeBuild { + ndk { + abiFilters 'arm64-v8a' + } + ndkBuild { + abiFilters 'arm64-v8a' + } + cmake { + targets "scenemodel" + } + } + } + + sourceSets { + main { + manifest.srcFile 'AndroidManifest.xml' + java.srcDirs = ['../../java'] + assets.srcDirs = ['../../assets'] + res.srcDirs = ['../../res'] + } + } + + buildTypes { + debug { + debuggable true + } + + release { + debuggable false + } + } + + externalNativeBuild { + cmake { + path file('../../../../CMakeLists.txt') + } + } + + // Enable prefab support for the OpenXR AAR + buildFeatures { + prefab true + } +} + +dependencies { + // Package/application AndroidManifest.xml properties, plus headers and libraries + // exposed to CMake + implementation 'org.khronos.openxr:openxr_loader_for_android:1.1.36' +} diff --git a/Samples/XrSamples/XrSceneModel/Projects/Android/build.py b/Samples/XrSamples/XrSceneModel/Projects/Android/build.py new file mode 100755 index 0000000..d4b6e58 --- /dev/null +++ b/Samples/XrSamples/XrSceneModel/Projects/Android/build.py @@ -0,0 +1,35 @@ +#!/usr/bin/python +# (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +# This first bit of code is common bootstrapping code +# to determine the SDK root, and to set up the import +# path for additional python code. + +# begin bootstrap +import os +import sys + + +def init(): + root = os.path.realpath(os.path.dirname(os.path.realpath(__file__))) + os.chdir(root) # make sure we are always executing from the project directory + while os.path.isdir(os.path.join(root, "bin/scripts/build")) == False: + root = os.path.realpath(os.path.join(root, "..")) + if ( + len(root) <= 5 + ): # Should catch both Posix and Windows root directories (e.g. '/' and 'C:\') + print("Unable to find SDK root. Exiting.") + sys.exit(1) + root = os.path.abspath(root) + os.environ["OCULUS_SDK_PATH"] = root + sys.path.append(root + "/bin/scripts/build") + + +init() +import ovrbuild + +ovrbuild.init() +# end bootstrap + + +ovrbuild.build() diff --git a/Samples/XrSamples/XrSceneModel/Projects/Android/gradle.properties b/Samples/XrSamples/XrSceneModel/Projects/Android/gradle.properties new file mode 100644 index 0000000..3e927b1 --- /dev/null +++ b/Samples/XrSamples/XrSceneModel/Projects/Android/gradle.properties @@ -0,0 +1,21 @@ +# Project-wide Gradle settings. +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. More details, visit +# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects +# org.gradle.parallel=true +# AndroidX package structure to make it clearer which packages are bundled with the +# Android operating system, and which are packaged with your app's APK +# https://developer.android.com/topic/libraries/support-library/androidx-rn +android.useAndroidX=true +# Enables namespacing of each library's R class so that its R class includes only the +# resources declared in the library itself and none from the library's dependencies, +# thereby reducing the size of the R class for that library +android.nonTransitiveRClass=true \ No newline at end of file diff --git a/Samples/XrSamples/XrSceneModel/Projects/Android/gradle/wrapper/gradle-wrapper.jar b/Samples/XrSamples/XrSceneModel/Projects/Android/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..7454180 Binary files /dev/null and b/Samples/XrSamples/XrSceneModel/Projects/Android/gradle/wrapper/gradle-wrapper.jar differ diff --git a/Samples/XrSamples/XrSceneModel/Projects/Android/gradle/wrapper/gradle-wrapper.properties b/Samples/XrSamples/XrSceneModel/Projects/Android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..ffed3a2 --- /dev/null +++ b/Samples/XrSamples/XrSceneModel/Projects/Android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/Samples/XrSamples/XrSceneModel/Projects/Android/gradlew b/Samples/XrSamples/XrSceneModel/Projects/Android/gradlew new file mode 100755 index 0000000..a69d9cb --- /dev/null +++ b/Samples/XrSamples/XrSceneModel/Projects/Android/gradlew @@ -0,0 +1,240 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# 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 +# +# https://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. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit + +APP_NAME="Gradle" +APP_BASE_NAME=${0##*/} + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/Samples/XrSamples/XrSceneModel/Projects/Android/gradlew.bat b/Samples/XrSamples/XrSceneModel/Projects/Android/gradlew.bat new file mode 100755 index 0000000..f127cfd --- /dev/null +++ b/Samples/XrSamples/XrSceneModel/Projects/Android/gradlew.bat @@ -0,0 +1,91 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/Samples/XrSamples/XrSceneModel/Projects/Android/settings.gradle b/Samples/XrSamples/XrSceneModel/Projects/Android/settings.gradle new file mode 100755 index 0000000..b77fad1 --- /dev/null +++ b/Samples/XrSamples/XrSceneModel/Projects/Android/settings.gradle @@ -0,0 +1 @@ +rootProject.name = "XrSceneModel" diff --git a/Samples/XrSamples/XrSceneModel/README.md b/Samples/XrSamples/XrSceneModel/README.md new file mode 100644 index 0000000..5978184 --- /dev/null +++ b/Samples/XrSamples/XrSceneModel/README.md @@ -0,0 +1,7 @@ +# OpenXR Scene Model Sample + +## Overview +The `XR_FB_scene` extension expands on the concept of spatial entities to include a method for spatial entities to represent rooms, objects, or other boundaries in a scene. + +## The Sample +The sample demonstrates a scene-aware experience, including the floor, walls, and furniture. diff --git a/Samples/XrSamples/XrSceneModel/Src/SceneModelGl.cpp b/Samples/XrSamples/XrSceneModel/Src/SceneModelGl.cpp new file mode 100755 index 0000000..8188c0a --- /dev/null +++ b/Samples/XrSamples/XrSceneModel/Src/SceneModelGl.cpp @@ -0,0 +1,1372 @@ +/************************************************************************************ + +Filename : SceneModelGl.cpp +Content : This sample is derived from VrCubeWorld_SurfaceView. + When used in room scale mode, it draws a "carpet" under the + user to indicate where it is safe to walk around. +Created : July, 2020 +Authors : Cass Everitt + +Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved. + +*************************************************************************************/ + +#include +#include +#include +#include + +#if defined(ANDROID) +#include +#include +#include // for prctl( PR_SET_NAME ) +#include +#include // for native window JNI +#include +#endif + +#include +#include + +#if defined(ANDROID) +#include + +#include +#include +#include +#endif + +#include "SceneModelGl.h" +#include "SceneModelHelpers.h" +#include "SceneModelShaders.h" + +using namespace OVR; + +// EXT_texture_border_clamp +#ifndef GL_CLAMP_TO_BORDER +#define GL_CLAMP_TO_BORDER 0x812D +#endif + +#ifndef GL_TEXTURE_BORDER_COLOR +#define GL_TEXTURE_BORDER_COLOR 0x1004 +#endif + +#ifndef GL_FRAMEBUFFER_SRGB_EXT +#define GL_FRAMEBUFFER_SRGB_EXT 0x8DB9 +#endif + +#if !defined(GL_EXT_multisampled_render_to_texture) +typedef void(GL_APIENTRY* PFNGLRENDERBUFFERSTORAGEMULTISAMPLEEXTPROC)( + GLenum target, + GLsizei samples, + GLenum internalformat, + GLsizei width, + GLsizei height); +typedef void(GL_APIENTRY* PFNGLFRAMEBUFFERTEXTURE2DMULTISAMPLEEXTPROC)( + GLenum target, + GLenum attachment, + GLenum textarget, + GLuint texture, + GLint level, + GLsizei samples); +#endif + +#if !defined(GL_OVR_multiview) +typedef void(GL_APIENTRY* PFNGLFRAMEBUFFERTEXTUREMULTIVIEWOVRPROC)( + GLenum target, + GLenum attachment, + GLuint texture, + GLint level, + GLint baseViewIndex, + GLsizei numViews); +#endif + +#if !defined(GL_OVR_multiview_multisampled_render_to_texture) +typedef void(GL_APIENTRY* PFNGLFRAMEBUFFERTEXTUREMULTISAMPLEMULTIVIEWOVRPROC)( + GLenum target, + GLenum attachment, + GLuint texture, + GLint level, + GLsizei samples, + GLint baseViewIndex, + GLsizei numViews); +#endif + +#define DEBUG 1 + +#if defined(ANDROID) +#define OVR_LOG_TAG "SceneModelGl" + +#define ALOGE(...) __android_log_print(ANDROID_LOG_ERROR, OVR_LOG_TAG, __VA_ARGS__) +#if DEBUG +#define ALOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, OVR_LOG_TAG, __VA_ARGS__) +#else +#define ALOGV(...) +#endif + +#else +#define ALOGE(...) \ + printf("ERROR: "); \ + printf(__VA_ARGS__); \ + printf("\n") +#define ALOGV(...) \ + printf("VERBOSE: "); \ + printf(__VA_ARGS__); \ + printf("\n") +#endif // defined(ANDROID) + +/* +================================================================================ + +OpenGL-ES Utility Functions + +================================================================================ +*/ + +namespace { +struct OpenGLExtensions_t { + bool multi_view; // GL_OVR_multiview, GL_OVR_multiview2 + bool EXT_texture_border_clamp; // GL_EXT_texture_border_clamp, GL_OES_texture_border_clamp + bool EXT_sRGB_write_control; +}; + +OpenGLExtensions_t glExtensions; + +Matrix4f ZOffsetTransform(const float zOffset) { + Matrix4f transform = Matrix4f::Identity(); + transform(2, 3) = zOffset; + return transform; +} + +} // namespace + +static void EglInitExtensions() { + glExtensions = {}; + const char* allExtensions = (const char*)glGetString(GL_EXTENSIONS); + if (allExtensions != nullptr) { + glExtensions.multi_view = strstr(allExtensions, "GL_OVR_multiview2") && + strstr(allExtensions, "GL_OVR_multiview_multisampled_render_to_texture"); + + glExtensions.EXT_texture_border_clamp = + strstr(allExtensions, "GL_EXT_texture_border_clamp") || + strstr(allExtensions, "GL_OES_texture_border_clamp"); + glExtensions.EXT_sRGB_write_control = strstr(allExtensions, "GL_EXT_sRGB_write_control"); + } +} + +static const char* GlFrameBufferStatusString(GLenum status) { + switch (status) { + case GL_FRAMEBUFFER_UNDEFINED: + return "GL_FRAMEBUFFER_UNDEFINED"; + case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: + return "GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT"; + case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: + return "GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT"; + case GL_FRAMEBUFFER_UNSUPPORTED: + return "GL_FRAMEBUFFER_UNSUPPORTED"; + case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE: + return "GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE"; + default: + return "unknown"; + } +} + +#ifdef CHECK_GL_ERRORS + +static const char* GlErrorString(GLenum error) { + switch (error) { + case GL_NO_ERROR: + return "GL_NO_ERROR"; + case GL_INVALID_ENUM: + return "GL_INVALID_ENUM"; + case GL_INVALID_VALUE: + return "GL_INVALID_VALUE"; + case GL_INVALID_OPERATION: + return "GL_INVALID_OPERATION"; + case GL_INVALID_FRAMEBUFFER_OPERATION: + return "GL_INVALID_FRAMEBUFFER_OPERATION"; + case GL_OUT_OF_MEMORY: + return "GL_OUT_OF_MEMORY"; + default: + return "unknown"; + } +} + +static void GLCheckErrors(int line) { + for (int i = 0; i < 10; i++) { + const GLenum error = glGetError(); + if (error == GL_NO_ERROR) { + break; + } + ALOGE("GL error on line %d: %s", line, GlErrorString(error)); + } +} + +#define GL(func) \ + func; \ + GLCheckErrors(__LINE__); + +#else // CHECK_GL_ERRORS + +#define GL(func) func; + +#endif // CHECK_GL_ERRORS + +/* +================================================================================ + +ovrGeometry + +================================================================================ +*/ + +enum VertexAttributeLocation { + VERTEX_ATTRIBUTE_LOCATION_POSITION, + VERTEX_ATTRIBUTE_LOCATION_COLOR, + VERTEX_ATTRIBUTE_LOCATION_UV +}; + +struct ovrVertexAttribute { + enum VertexAttributeLocation location; + const char* name; +}; + +static ovrVertexAttribute ProgramVertexAttributes[] = { + {VERTEX_ATTRIBUTE_LOCATION_POSITION, "vertexPosition"}, + {VERTEX_ATTRIBUTE_LOCATION_COLOR, "vertexColor"}, + {VERTEX_ATTRIBUTE_LOCATION_UV, "vertexUv"}}; + +void ovrGeometry::Clear() { + VertexBuffer_ = 0; + IndexBuffer_ = 0; + VertexArrayObject_ = 0; + for (int i = 0; i < MAX_VERTEX_ATTRIB_POINTERS; i++) { + memset(&VertexAttribs_[i], 0, sizeof(VertexAttribs_[i])); + VertexAttribs_[i].Index = -1; + } + + IsRenderable_ = false; +} + +void ovrGeometry::BindVAO() const { + GL(glBindVertexArray(VertexArrayObject_)); +} + +void ovrGeometry::CreateIndexBuffer(const std::vector& indices) { + if (IndexBuffer_ == 0) { + GL(glGenBuffers(1, &IndexBuffer_)); + } + GL(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IndexBuffer_)); + GL(glBufferData( + GL_ELEMENT_ARRAY_BUFFER, + indices.size() * sizeof(unsigned short), + indices.data(), + GL_STATIC_DRAW)); + GL(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)); + IndexCount_ = indices.size(); +} + +void ovrGeometry::CreateAxes() { + struct AxesVertices { + float positions[6][3]; + unsigned char colors[6][4]; + }; + + static const AxesVertices kAxesVertices = { + // positions + {{0, 0, 0}, {1, 0, 0}, {0, 0, 0}, {0, 1, 0}, {0, 0, 0}, {0, 0, 1}}, + // colors + {{255, 0, 0, 255}, + {255, 0, 0, 255}, + {0, 255, 0, 255}, + {0, 255, 0, 255}, + {0, 0, 255, 255}, + {0, 0, 255, 255}}, + }; + + VertexAttribs_[0].Index = VERTEX_ATTRIBUTE_LOCATION_POSITION; + VertexAttribs_[0].Size = 3; + VertexAttribs_[0].Type = GL_FLOAT; + VertexAttribs_[0].Normalized = false; + VertexAttribs_[0].Stride = sizeof(kAxesVertices.positions[0]); + VertexAttribs_[0].Pointer = (const GLvoid*)offsetof(AxesVertices, positions); + + VertexAttribs_[1].Index = VERTEX_ATTRIBUTE_LOCATION_COLOR; + VertexAttribs_[1].Size = 4; + VertexAttribs_[1].Type = GL_UNSIGNED_BYTE; + VertexAttribs_[1].Normalized = true; + VertexAttribs_[1].Stride = sizeof(kAxesVertices.colors[0]); + VertexAttribs_[1].Pointer = (const GLvoid*)offsetof(AxesVertices, colors); + + GL(glGenBuffers(1, &VertexBuffer_)); + GL(glBindBuffer(GL_ARRAY_BUFFER, VertexBuffer_)); + GL(glBufferData(GL_ARRAY_BUFFER, sizeof(kAxesVertices), &kAxesVertices, GL_STATIC_DRAW)); + GL(glBindBuffer(GL_ARRAY_BUFFER, 0)); + + static const std::vector indices = { + 0, + 1, // x axis - red + 2, + 3, // y axis - green + 4, + 5 // z axis - blue + }; + CreateIndexBuffer(indices); + + IsRenderable_ = true; +} + +void ovrGeometry::CreateStage() { + static const float stageVertices[12] = { + -1.0f, -1.0f, 0.0f, 1.0f, -1.0f, 0.0f, -1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f}; + + VertexAttribs_[0].Index = VERTEX_ATTRIBUTE_LOCATION_POSITION; + VertexAttribs_[0].Size = 3; + VertexAttribs_[0].Type = GL_FLOAT; + VertexAttribs_[0].Normalized = false; + VertexAttribs_[0].Stride = 3 * sizeof(float); + VertexAttribs_[0].Pointer = (const GLvoid*)0; + + GL(glGenBuffers(1, &VertexBuffer_)); + GL(glBindBuffer(GL_ARRAY_BUFFER, VertexBuffer_)); + GL(glBufferData(GL_ARRAY_BUFFER, sizeof(stageVertices), stageVertices, GL_STATIC_DRAW)); + GL(glBindBuffer(GL_ARRAY_BUFFER, 0)); + + static const std::vector indices = {0, 1, 2, 2, 1, 3}; + CreateIndexBuffer(indices); + + IsRenderable_ = true; +} + +void ovrGeometry::CreatePlane(const std::vector& vertices, const XrColor4f& color) { + if (vertices.size() < 3) { + IsRenderable_ = false; + return; + } + + struct PlaneVertex { + XrVector3f position; + XrColor4f color; + }; + + std::vector planeVertices; + planeVertices.reserve(vertices.size()); + for (const auto& vertex : vertices) { + planeVertices.emplace_back(PlaneVertex{vertex, color}); + } + VertexAttribs_[0].Index = VERTEX_ATTRIBUTE_LOCATION_POSITION; + VertexAttribs_[0].Size = 3; + VertexAttribs_[0].Type = GL_FLOAT; + VertexAttribs_[0].Normalized = false; + VertexAttribs_[0].Stride = sizeof(PlaneVertex); + VertexAttribs_[0].Pointer = (const GLvoid*)offsetof(PlaneVertex, position); + + VertexAttribs_[1].Index = VERTEX_ATTRIBUTE_LOCATION_COLOR; + VertexAttribs_[1].Size = 4; + VertexAttribs_[1].Type = GL_FLOAT; + VertexAttribs_[1].Normalized = false; + VertexAttribs_[1].Stride = sizeof(PlaneVertex); + VertexAttribs_[1].Pointer = (const GLvoid*)offsetof(PlaneVertex, color); + + if (VertexBuffer_ == 0) { + GL(glGenBuffers(1, &VertexBuffer_)); + } + GL(glBindBuffer(GL_ARRAY_BUFFER, VertexBuffer_)); + GL(glBufferData( + GL_ARRAY_BUFFER, + sizeof(PlaneVertex) * planeVertices.size(), + planeVertices.data(), + GL_STATIC_DRAW)); + GL(glBindBuffer(GL_ARRAY_BUFFER, 0)); + // Render only front side of the plane to make sure plane direction is correct. + std::vector indices; + const int numTriangles = vertices.size() - 2; + indices.reserve(numTriangles * 3); + for (int i = 0; i < numTriangles; ++i) { + indices.push_back(0); + indices.push_back(i + 1); + indices.push_back(i + 2); + } + CreateIndexBuffer(indices); + // VAO buffer needs to be created per each plane. + CreateVAO(); + + IsRenderable_ = true; +} + +void ovrGeometry::CreateVolume(const std::array& vertices, const XrColor4f& color) { + struct VolumeVertices { + XrVector3f positions[8]; + XrColor4f colors[8]; + }; + + const VolumeVertices volumeVertices = { + {vertices[0], + vertices[1], + vertices[2], + vertices[3], + vertices[4], + vertices[5], + vertices[6], + vertices[7]}, + {color, color, color, color, color, color, color, color}}; + VertexAttribs_[0].Index = VERTEX_ATTRIBUTE_LOCATION_POSITION; + VertexAttribs_[0].Size = 3; + VertexAttribs_[0].Type = GL_FLOAT; + VertexAttribs_[0].Normalized = false; + VertexAttribs_[0].Stride = sizeof(volumeVertices.positions[0]); + VertexAttribs_[0].Pointer = (const GLvoid*)offsetof(VolumeVertices, positions); + + VertexAttribs_[1].Index = VERTEX_ATTRIBUTE_LOCATION_COLOR; + VertexAttribs_[1].Size = 4; + VertexAttribs_[1].Type = GL_FLOAT; + VertexAttribs_[1].Normalized = false; + VertexAttribs_[1].Stride = sizeof(volumeVertices.colors[0]); + VertexAttribs_[1].Pointer = (const GLvoid*)offsetof(VolumeVertices, colors); + + GL(glGenBuffers(1, &VertexBuffer_)); + GL(glBindBuffer(GL_ARRAY_BUFFER, VertexBuffer_)); + GL(glBufferData(GL_ARRAY_BUFFER, sizeof(VolumeVertices), &volumeVertices, GL_STATIC_DRAW)); + GL(glBindBuffer(GL_ARRAY_BUFFER, 0)); + const std::vector indices = { + 0, 2, 1, 2, 0, 3, // bottom + 4, 6, 5, 6, 4, 7, // top + 0, 1, 4, 1, 5, 4, // front + 1, 2, 5, 2, 6, 5, // right + 2, 3, 6, 3, 7, 6, // back + 3, 0, 7, 0, 4, 7 // left + }; + CreateIndexBuffer(indices); + CreateVAO(); + + IsRenderable_ = true; +} + +void ovrGeometry::CreateMesh(const XrSpaceTriangleMeshMETA& mesh) { + VertexAttribs_[0].Index = VERTEX_ATTRIBUTE_LOCATION_POSITION; + VertexAttribs_[0].Size = 3; + VertexAttribs_[0].Type = GL_FLOAT; + VertexAttribs_[0].Normalized = false; + VertexAttribs_[0].Stride = sizeof(XrVector3f); + VertexAttribs_[0].Pointer = (const GLvoid*)0; + + GL(glGenBuffers(1, &VertexBuffer_)); + GL(glBindBuffer(GL_ARRAY_BUFFER, VertexBuffer_)); + GL(glBufferData(GL_ARRAY_BUFFER, mesh.vertexCountOutput * sizeof(XrVector3f), mesh.vertices, GL_STATIC_DRAW)); + GL(glBindBuffer(GL_ARRAY_BUFFER, 0)); + + // Decompose triangles into lines to draw the mesh as a wireframe model with GL_LINES. + std::vector indices; + indices.reserve(mesh.indexCountOutput * 2); + for (uint32_t i = 0; i < mesh.indexCountOutput; i += 3) { + for (uint32_t j = 0; j < 3; ++j) { + indices.push_back(mesh.indices[i + j]); + indices.push_back(mesh.indices[i + (j + 1) % 3]); + } + } + + GL(glGenBuffers(1, &IndexBuffer_)); + GL(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IndexBuffer_)); + GL(glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(uint32_t), indices.data(), GL_STATIC_DRAW)); + GL(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)); + IndexCount_ = indices.size(); + + CreateVAO(); + + IsRenderable_ = true; +} + +void ovrGeometry::Destroy() { + if (IndexBuffer_ != 0) { + GL(glDeleteBuffers(1, &IndexBuffer_)); + } + if (VertexBuffer_ != 0) { + GL(glDeleteBuffers(1, &VertexBuffer_)); + } + Clear(); +} + +void ovrGeometry::CreateVAO() { + if (VertexArrayObject_ == 0) { + GL(glGenVertexArrays(1, &VertexArrayObject_)); + } + GL(glBindVertexArray(VertexArrayObject_)); + + GL(glBindBuffer(GL_ARRAY_BUFFER, VertexBuffer_)); + + for (int i = 0; i < MAX_VERTEX_ATTRIB_POINTERS; i++) { + if (VertexAttribs_[i].Index != -1) { + GL(glEnableVertexAttribArray(VertexAttribs_[i].Index)); + GL(glVertexAttribPointer( + VertexAttribs_[i].Index, + VertexAttribs_[i].Size, + VertexAttribs_[i].Type, + VertexAttribs_[i].Normalized, + VertexAttribs_[i].Stride, + VertexAttribs_[i].Pointer)); + } + } + + GL(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IndexBuffer_)); + + GL(glBindVertexArray(0)); +} + +void ovrGeometry::DestroyVAO() { + GL(glDeleteVertexArrays(1, &VertexArrayObject_)); +} + +/* +================================================================================ + +ovrProgram + +================================================================================ +*/ + +struct ovrUniform { + enum Index { + MODEL_MATRIX, + VIEW_ID, + SCENE_MATRICES, + }; + enum Type { + VECTOR4, + MATRIX4X4, + INTEGER, + BUFFER, + }; + + Index index; + Type type; + const char* name; +}; + +static ovrUniform ProgramUniforms[] = { + {ovrUniform::Index::MODEL_MATRIX, ovrUniform::Type::MATRIX4X4, "ModelMatrix"}, + {ovrUniform::Index::VIEW_ID, ovrUniform::Type::INTEGER, "ViewID"}, + {ovrUniform::Index::SCENE_MATRICES, ovrUniform::Type::BUFFER, "SceneMatrices"}, +}; + +void ovrProgram::Clear() { + Program = 0; + VertexShader = 0; + FragmentShader = 0; + memset(UniformLocation, 0, sizeof(UniformLocation)); + memset(UniformBinding, 0, sizeof(UniformBinding)); + memset(Textures, 0, sizeof(Textures)); +} + +static const char* programVersion = "#version 300 es\n"; + +bool ovrProgram::Create(const char* vertexSource, const char* fragmentSource) { + GLint r; + + GL(VertexShader = glCreateShader(GL_VERTEX_SHADER)); + + const char* vertexSources[3] = {programVersion, "", vertexSource}; + GL(glShaderSource(VertexShader, 3, vertexSources, 0)); + GL(glCompileShader(VertexShader)); + GL(glGetShaderiv(VertexShader, GL_COMPILE_STATUS, &r)); + if (r == GL_FALSE) { + GLchar msg[4096]; + GL(glGetShaderInfoLog(VertexShader, sizeof(msg), 0, msg)); + ALOGE("vertex shader compile failed"); + ALOGE("%s\n%s\n", vertexSource, msg); + return false; + } + + const char* fragmentSources[2] = {programVersion, fragmentSource}; + GL(FragmentShader = glCreateShader(GL_FRAGMENT_SHADER)); + GL(glShaderSource(FragmentShader, 2, fragmentSources, 0)); + GL(glCompileShader(FragmentShader)); + GL(glGetShaderiv(FragmentShader, GL_COMPILE_STATUS, &r)); + if (r == GL_FALSE) { + GLchar msg[4096]; + GL(glGetShaderInfoLog(FragmentShader, sizeof(msg), 0, msg)); + ALOGE("fragment shader compile failed"); + ALOGE("%s\n%s\n", fragmentSource, msg); + return false; + } + + GL(Program = glCreateProgram()); + GL(glAttachShader(Program, VertexShader)); + GL(glAttachShader(Program, FragmentShader)); + + // Bind the vertex attribute locations. + for (size_t i = 0; i < sizeof(ProgramVertexAttributes) / sizeof(ProgramVertexAttributes[0]); + i++) { + GL(glBindAttribLocation( + Program, ProgramVertexAttributes[i].location, ProgramVertexAttributes[i].name)); + } + + GL(glLinkProgram(Program)); + GL(glGetProgramiv(Program, GL_LINK_STATUS, &r)); + if (r == GL_FALSE) { + GLchar msg[4096]; + GL(glGetProgramInfoLog(Program, sizeof(msg), 0, msg)); + ALOGE("Linking program failed: %s\n", msg); + return false; + } + + int numBufferBindings = 0; + + memset(UniformLocation, -1, sizeof(UniformLocation)); + for (size_t i = 0; i < sizeof(ProgramUniforms) / sizeof(ProgramUniforms[0]); i++) { + const int uniformIndex = ProgramUniforms[i].index; + if (ProgramUniforms[i].type == ovrUniform::Type::BUFFER) { + GL(UniformLocation[uniformIndex] = + glGetUniformBlockIndex(Program, ProgramUniforms[i].name)); + UniformBinding[uniformIndex] = numBufferBindings++; + GL(glUniformBlockBinding( + Program, UniformLocation[uniformIndex], UniformBinding[uniformIndex])); + } else { + GL(UniformLocation[uniformIndex] = + glGetUniformLocation(Program, ProgramUniforms[i].name)); + UniformBinding[uniformIndex] = UniformLocation[uniformIndex]; + } + } + + GL(glUseProgram(Program)); + + // Get the texture locations. + for (int i = 0; i < MAX_PROGRAM_TEXTURES; i++) { + char name[32]; + sprintf(name, "Texture%i", i); + Textures[i] = glGetUniformLocation(Program, name); + if (Textures[i] != -1) { + GL(glUniform1i(Textures[i], i)); + } + } + + GL(glUseProgram(0)); + + return true; +} + +void ovrProgram::Destroy() { + if (Program != 0) { + GL(glDeleteProgram(Program)); + Program = 0; + } + if (VertexShader != 0) { + GL(glDeleteShader(VertexShader)); + VertexShader = 0; + } + if (FragmentShader != 0) { + GL(glDeleteShader(FragmentShader)); + FragmentShader = 0; + } +} + +/* +================================================================================ + +ovrFramebuffer + +================================================================================ +*/ + +void ovrFramebuffer::Clear() { + Width = 0; + Height = 0; + Multisamples = 0; + SwapChainLength = 0; + Elements = nullptr; +} + +static void* GlGetExtensionProc(const char* functionName) { +#if defined(ANDROID) + return (void*)eglGetProcAddress(functionName); +#elif defined(WIN32) + return (void*)wglGetProcAddress(functionName); +#else + static_assert(false); +#endif +} + +bool ovrFramebuffer::Create( + const GLenum colorFormat, + const int width, + const int height, + const int multisamples, + const int swapChainLength, + GLuint* colorTextures) { + PFNGLFRAMEBUFFERTEXTUREMULTIVIEWOVRPROC glFramebufferTextureMultiviewOVR = + (PFNGLFRAMEBUFFERTEXTUREMULTIVIEWOVRPROC)GlGetExtensionProc( + "glFramebufferTextureMultiviewOVR"); + PFNGLFRAMEBUFFERTEXTUREMULTISAMPLEMULTIVIEWOVRPROC glFramebufferTextureMultisampleMultiviewOVR = + (PFNGLFRAMEBUFFERTEXTUREMULTISAMPLEMULTIVIEWOVRPROC)GlGetExtensionProc( + "glFramebufferTextureMultisampleMultiviewOVR"); + + Width = width; + Height = height; + Multisamples = multisamples; + SwapChainLength = swapChainLength; + + Elements = new Element[SwapChainLength]; + + for (int i = 0; i < SwapChainLength; i++) { + Element& el = Elements[i]; + // Create the color buffer texture. + el.ColorTexture = colorTextures[i]; + GLenum colorTextureTarget = GL_TEXTURE_2D_ARRAY; + GL(glBindTexture(colorTextureTarget, el.ColorTexture)); + GL(glTexParameteri(colorTextureTarget, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER)); + GL(glTexParameteri(colorTextureTarget, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER)); + GLfloat borderColor[] = {0.0f, 0.0f, 0.0f, 0.0f}; + GL(glTexParameterfv(colorTextureTarget, GL_TEXTURE_BORDER_COLOR, borderColor)); + GL(glTexParameteri(colorTextureTarget, GL_TEXTURE_MIN_FILTER, GL_LINEAR)); + GL(glTexParameteri(colorTextureTarget, GL_TEXTURE_MAG_FILTER, GL_LINEAR)); + GL(glBindTexture(colorTextureTarget, 0)); + + // Create the depth buffer texture. + GL(glGenTextures(1, &el.DepthTexture)); + GL(glBindTexture(GL_TEXTURE_2D_ARRAY, el.DepthTexture)); + GL(glTexStorage3D(GL_TEXTURE_2D_ARRAY, 1, GL_DEPTH_COMPONENT24, width, height, 2)); + GL(glBindTexture(GL_TEXTURE_2D_ARRAY, 0)); + + // Create the frame buffer. + GL(glGenFramebuffers(1, &el.FrameBufferObject)); + GL(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, el.FrameBufferObject)); + if (multisamples > 1 && (glFramebufferTextureMultisampleMultiviewOVR != nullptr)) { + GL(glFramebufferTextureMultisampleMultiviewOVR( + GL_DRAW_FRAMEBUFFER, + GL_DEPTH_ATTACHMENT, + el.DepthTexture, + 0 /* level */, + multisamples /* samples */, + 0 /* baseViewIndex */, + 2 /* numViews */)); + GL(glFramebufferTextureMultisampleMultiviewOVR( + GL_DRAW_FRAMEBUFFER, + GL_COLOR_ATTACHMENT0, + el.ColorTexture, + 0 /* level */, + multisamples /* samples */, + 0 /* baseViewIndex */, + 2 /* numViews */)); + } else if (glFramebufferTextureMultiviewOVR) { + GL(glFramebufferTextureMultiviewOVR( + GL_DRAW_FRAMEBUFFER, + GL_DEPTH_ATTACHMENT, + el.DepthTexture, + 0 /* level */, + 0 /* baseViewIndex */, + 2 /* numViews */)); + GL(glFramebufferTextureMultiviewOVR( + GL_DRAW_FRAMEBUFFER, + GL_COLOR_ATTACHMENT0, + el.ColorTexture, + 0 /* level */, + 0 /* baseViewIndex */, + 2 /* numViews */)); + } + + GL(GLenum renderFramebufferStatus = glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER)); + GL(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0)); + if (renderFramebufferStatus != GL_FRAMEBUFFER_COMPLETE) { + ALOGE( + "Incomplete frame buffer object: %s", + GlFrameBufferStatusString(renderFramebufferStatus)); + return false; + } + } + + return true; +} + +void ovrFramebuffer::Destroy() { + for (int i = 0; i < SwapChainLength; i++) { + Element& el = Elements[i]; + GL(glDeleteFramebuffers(1, &el.FrameBufferObject)); + GL(glDeleteTextures(1, &el.DepthTexture)); + } + delete[] Elements; + Clear(); +} + +void ovrFramebuffer::Bind(int element) { + GL(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, Elements[element].FrameBufferObject)); +} + +void ovrFramebuffer::Unbind() { + GL(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0)); +} + +void ovrFramebuffer::Resolve() { +#if defined(ANDROID) + // Discard the depth buffer, so the tiler won't need to write it back out to memory. + const GLenum depthAttachment[1] = {GL_DEPTH_ATTACHMENT}; + glInvalidateFramebuffer(GL_DRAW_FRAMEBUFFER, 1, depthAttachment); +#endif + + // We now let the resolve happen implicitly. +} + +/* +================================================================================ + +ovrPlane + +================================================================================ +*/ + +ovrPlane::ovrPlane(const XrSpace space) : Space(space) {} + +void ovrPlane::Update(const XrRect2Df& boundingBox2D, const XrColor4f& color) { + const auto& offset = boundingBox2D.offset; + const auto& extent = boundingBox2D.extent; + const std::vector vertices = { + XrVector3f{offset.x, offset.y, 0.0f}, + XrVector3f{offset.x + extent.width, offset.y, 0.0f}, + XrVector3f{offset.x + extent.width, offset.y + extent.height, 0.0f}, + XrVector3f{offset.x, offset.y + extent.height, 0.0f}}; + Geometry.CreatePlane(vertices, color); +} + +void ovrPlane::Update(const XrBoundary2DFB& boundary2D, const XrColor4f& color) { + std::vector vertices; + vertices.reserve(boundary2D.vertexCountOutput); + for (uint32_t i = 0; i < boundary2D.vertexCountOutput; ++i) { + vertices.push_back(XrVector3f{boundary2D.vertices[i].x, boundary2D.vertices[i].y, 0.0f}); + } + Geometry.CreatePlane(vertices, color); +} + +void ovrPlane::SetZOffset(const float zOffset) { + ZOffset = zOffset; +} + +void ovrPlane::SetPose(const XrPosef& T_World_Plane_Xr) { + T_World_Plane = FromXrPosef(T_World_Plane_Xr); + IsPoseSet_ = true; +} + +/* +================================================================================ + +ovrVolume + +================================================================================ +*/ + +ovrVolume::ovrVolume(const XrSpace space) : Space(space){}; + +void ovrVolume::Update(const XrRect3DfFB& boundingBox3D, const XrColor4f& color) { + const auto& offset = boundingBox3D.offset; + const auto& extent = boundingBox3D.extent; + const std::array vertices = { + XrVector3f{offset.x, offset.y, offset.z}, + XrVector3f{offset.x + extent.width, offset.y, offset.z}, + XrVector3f{offset.x + extent.width, offset.y + extent.height, offset.z}, + XrVector3f{offset.x, offset.y + extent.height, offset.z}, + XrVector3f{offset.x, offset.y, offset.z + extent.depth}, + XrVector3f{offset.x + extent.width, offset.y, offset.z + extent.depth}, + XrVector3f{offset.x + extent.width, offset.y + extent.height, offset.z + extent.depth}, + XrVector3f{offset.x, offset.y + extent.height, offset.z + extent.depth}}; + Geometry.CreateVolume(vertices, color); +} + +void ovrVolume::SetPose(const XrPosef& T_World_Volume_Xr) { + T_World_Volume = FromXrPosef(T_World_Volume_Xr); + IsPoseSet_ = true; +} + +/* +================================================================================ + +ovrMesh + +================================================================================ +*/ + +ovrMesh::ovrMesh(const XrSpace space) : Space(space){} + +void ovrMesh::Update(const XrSpaceTriangleMeshMETA& mesh) { + Geometry.CreateMesh(mesh); +} + +void ovrMesh::SetPose(const XrPosef& T_World_Mesh_Xr) { + T_World_Mesh = FromXrPosef(T_World_Mesh_Xr); + IsPoseSet_ = true; +} + +/* +================================================================================ + +ovrScene + +================================================================================ +*/ + +void ovrScene::SetClearColor(const float* c) { + for (int i = 0; i < 4; i++) { + ClearColor[i] = c[i]; + } +} + +void ovrScene::Clear() { + CreatedScene = false; + CreatedVAOs = false; + SceneMatrices = 0; + + StageProgram.Clear(); + Stage.Clear(); + AxesProgram.Clear(); + Axes.Clear(); + PlaneProgram.Clear(); + for (auto& plane : Planes) { + plane.Geometry.Clear(); + } + Planes.clear(); + + VolumeProgram.Clear(); + for (auto& volume : Volumes) { + volume.Geometry.Clear(); + } + Volumes.clear(); + + MeshProgram.Clear(); + for (auto& mesh : Meshes) { + mesh.Geometry.Clear(); + } + Meshes.clear(); +} + +bool ovrScene::IsCreated() { + return CreatedScene; +} + +void ovrScene::CreateVAOs() { + if (!CreatedVAOs) { + // Stage + Stage.CreateVAO(); + // Axes + Axes.CreateVAO(); + + CreatedVAOs = true; + } +} + +void ovrScene::DestroyVAOs() { + if (CreatedVAOs) { + Stage.DestroyVAO(); + Axes.DestroyVAO(); + + CreatedVAOs = false; + } +} + +void ovrScene::Create() { + // Setup the scene matrices. + GL(glGenBuffers(1, &SceneMatrices)); + GL(glBindBuffer(GL_UNIFORM_BUFFER, SceneMatrices)); + GL(glBufferData( + GL_UNIFORM_BUFFER, + 2 * sizeof(Matrix4f) /* 2 view matrices */ + + 2 * sizeof(Matrix4f) /* 2 projection matrices */, + nullptr, + GL_STATIC_DRAW)); + GL(glBindBuffer(GL_UNIFORM_BUFFER, 0)); + + // Stage + if (!StageProgram.Create(STAGE_VERTEX_SHADER, STAGE_FRAGMENT_SHADER)) { + ALOGE("Failed to compile stage program"); + } + Stage.CreateStage(); + + // Axes + if (!AxesProgram.Create(VERTEX_SHADER, FRAGMENT_SHADER)) { + ALOGE("Failed to compile axes program"); + } + Axes.CreateAxes(); + + // Planes + if (!PlaneProgram.Create(VERTEX_SHADER, FRAGMENT_SHADER)) { + ALOGE("Failed to compile plane program!"); + } + + // Volumes + if (!VolumeProgram.Create(VERTEX_SHADER, FRAGMENT_SHADER)) { + ALOGE("Failed to compile volume program!"); + } + + // Meshes + if (!MeshProgram.Create(VERTEX_SHADER, MESH_FRAGMENT_SHADER)) { + ALOGE("Failed to compile mesh program!"); + } + + CreatedScene = true; + + CreateVAOs(); + float c[] = {0.0, 0.0, 0.0, 0.0}; + SetClearColor(c); +} + +void ovrScene::Destroy() { + DestroyVAOs(); + + GL(glDeleteBuffers(1, &SceneMatrices)); + StageProgram.Destroy(); + Stage.Destroy(); + AxesProgram.Destroy(); + Axes.Destroy(); + + PlaneProgram.Destroy(); + for (auto& plane : Planes) { + plane.Geometry.DestroyVAO(); + plane.Geometry.Destroy(); + } + Planes.clear(); + + VolumeProgram.Destroy(); + for (auto& volume : Volumes) { + volume.Geometry.DestroyVAO(); + volume.Geometry.Destroy(); + } + Volumes.clear(); +} + +/* +================================================================================ + +ovrAppRenderer + +================================================================================ +*/ + +void ovrAppRenderer::Clear() { + Framebuffer.Clear(); + Scene.Clear(); +} + +void ovrAppRenderer::Create( + GLenum format, + int width, + int height, + int numMultiSamples, + int swapChainLength, + GLuint* colorTextures) { + EglInitExtensions(); + Framebuffer.Create(format, width, height, numMultiSamples, swapChainLength, colorTextures); + if (glExtensions.EXT_sRGB_write_control) { + // This app was originally written with the presumption that + // its swapchains and compositor front buffer were RGB. + // In order to have the colors the same now that its compositing + // to an sRGB front buffer, we have to write to an sRGB swapchain + // but with the linear->sRGB conversion disabled on write. + GL(glDisable(GL_FRAMEBUFFER_SRGB_EXT)); + } +} + +void ovrAppRenderer::Destroy() { + Framebuffer.Destroy(); +} + +void ovrAppRenderer::RenderFrame(const FrameIn& frameIn) { + // Update the scene matrices. + GL(glBindBuffer(GL_UNIFORM_BUFFER, Scene.SceneMatrices)); + GL(Matrix4f* sceneMatrices = (Matrix4f*)glMapBufferRange( + GL_UNIFORM_BUFFER, + 0, + 4 * sizeof(Matrix4f) /* 2 view + 2 proj matrices */, + GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT)); + + if (sceneMatrices != nullptr) { + memcpy((char*)sceneMatrices, &frameIn.View, 4 * sizeof(Matrix4f)); + } + + GL(glUnmapBuffer(GL_UNIFORM_BUFFER)); + GL(glBindBuffer(GL_UNIFORM_BUFFER, 0)); + + // Render the eye images. + Framebuffer.Bind(frameIn.SwapChainIndex); + + GL(glEnable(GL_SCISSOR_TEST)); + GL(glDepthMask(GL_TRUE)); + GL(glEnable(GL_DEPTH_TEST)); + GL(glDepthFunc(GL_LEQUAL)); + GL(glEnable(GL_CULL_FACE)); + GL(glCullFace(GL_BACK)); + GL(glDisable(GL_BLEND)); + GL(glViewport(0, 0, Framebuffer.Width, Framebuffer.Height)); + GL(glScissor(0, 0, Framebuffer.Width, Framebuffer.Height)); + GL(glClearColor( + Scene.ClearColor[0], Scene.ClearColor[1], Scene.ClearColor[2], Scene.ClearColor[3])); + GL(glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)); + + GL(glLineWidth(3.0)); + // "tracking space" axes (could be LOCAL or LOCAL_FLOOR) + GL(glUseProgram(Scene.AxesProgram.Program)); + GL(glBindBufferBase( + GL_UNIFORM_BUFFER, + Scene.AxesProgram.UniformBinding[ovrUniform::Index::SCENE_MATRICES], + Scene.SceneMatrices)); + if (Scene.AxesProgram.UniformLocation[ovrUniform::Index::VIEW_ID] >= + 0) // NOTE: will not be present when multiview path is enabled. + { + GL(glUniform1i(Scene.AxesProgram.UniformLocation[ovrUniform::Index::VIEW_ID], 0)); + } + if (Scene.AxesProgram.UniformLocation[ovrUniform::Index::MODEL_MATRIX] >= 0) { + const Matrix4f scale = Matrix4f::Scaling(0.1, 0.1, 0.1); + GL(glUniformMatrix4fv( + Scene.AxesProgram.UniformLocation[ovrUniform::Index::MODEL_MATRIX], + 1, + GL_TRUE, + &scale.M[0][0])); + } + Scene.Axes.BindVAO(); + GL(glDrawElements(GL_LINES, Scene.Axes.IndexCount(), GL_UNSIGNED_SHORT, nullptr)); + GL(glBindVertexArray(0)); + GL(glUseProgram(0)); + + if (frameIn.HasStage) { + // stage axes + GL(glUseProgram(Scene.AxesProgram.Program)); + GL(glBindBufferBase( + GL_UNIFORM_BUFFER, + Scene.AxesProgram.UniformBinding[ovrUniform::Index::SCENE_MATRICES], + Scene.SceneMatrices)); + if (Scene.AxesProgram.UniformLocation[ovrUniform::Index::VIEW_ID] >= + 0) // NOTE: will not be present when multiview path is enabled. + { + GL(glUniform1i(Scene.AxesProgram.UniformLocation[ovrUniform::Index::VIEW_ID], 0)); + } + if (Scene.AxesProgram.UniformLocation[ovrUniform::Index::MODEL_MATRIX] >= 0) { + const Matrix4f scale = Matrix4f::Scaling(0.5, 0.5, 0.5); + const Matrix4f stagePoseMat = Matrix4f(frameIn.StagePose); + const Matrix4f m1 = stagePoseMat * scale; + GL(glUniformMatrix4fv( + Scene.AxesProgram.UniformLocation[ovrUniform::Index::MODEL_MATRIX], + 1, + GL_TRUE, + &m1.M[0][0])); + } + Scene.Axes.BindVAO(); + GL(glDrawElements(GL_LINES, Scene.Axes.IndexCount(), GL_UNSIGNED_SHORT, nullptr)); + GL(glBindVertexArray(0)); + GL(glUseProgram(0)); + } + + if (frameIn.HasStage) { + // Stage + GL(glUseProgram(Scene.StageProgram.Program)); + GL(glBindBufferBase( + GL_UNIFORM_BUFFER, + Scene.StageProgram.UniformBinding[ovrUniform::Index::SCENE_MATRICES], + Scene.SceneMatrices)); + if (Scene.StageProgram.UniformLocation[ovrUniform::Index::VIEW_ID] >= + 0) // NOTE: will not be present when multiview path is enabled. + { + GL(glUniform1i(Scene.StageProgram.UniformLocation[ovrUniform::Index::VIEW_ID], 0)); + } + if (Scene.StageProgram.UniformLocation[ovrUniform::Index::MODEL_MATRIX] >= 0) { + const Matrix4f rotateVtoH = Matrix4f::RotationX(-M_PI / 2.0f); + const Matrix4f stageScaleMat = Matrix4f::Scaling(frameIn.StageScale); + const Matrix4f stagePoseMat = Matrix4f(frameIn.StagePose); + const Matrix4f m2 = stagePoseMat * stageScaleMat * rotateVtoH; + GL(glUniformMatrix4fv( + Scene.StageProgram.UniformLocation[ovrUniform::Index::MODEL_MATRIX], + 1, + GL_TRUE, + &m2.M[0][0])); + } + GL(glDepthMask(GL_FALSE)); + GL(glEnable(GL_DEPTH_TEST)); + GL(glDepthFunc(GL_LEQUAL)); + GL(glDisable(GL_CULL_FACE)); + GL(glEnable(GL_BLEND)); + GL(glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); + Scene.Stage.BindVAO(); + GL(glDrawElements(GL_TRIANGLES, Scene.Stage.IndexCount(), GL_UNSIGNED_SHORT, nullptr)); + GL(glDepthMask(GL_TRUE)); + GL(glDisable(GL_BLEND)); + GL(glBindVertexArray(0)); + GL(glUseProgram(0)); + + Framebuffer.Resolve(); + } + + // Render controllers, reusing axes shaders. + for (int i = 0; i < 2; ++i) { + if (frameIn.RenderController[i]) { + GL(glLineWidth(5.0)); + GL(glUseProgram(Scene.AxesProgram.Program)); + GL(glBindBufferBase( + GL_UNIFORM_BUFFER, + Scene.AxesProgram.UniformBinding[ovrUniform::Index::SCENE_MATRICES], + Scene.SceneMatrices)); + if (Scene.AxesProgram.UniformLocation[ovrUniform::Index::VIEW_ID] >= 0) { + GL(glUniform1i(Scene.AxesProgram.UniformLocation[ovrUniform::Index::VIEW_ID], 0)); + } + if (Scene.AxesProgram.UniformLocation[ovrUniform::Index::MODEL_MATRIX] >= 0) { + const Matrix4f transform = + Matrix4f(frameIn.ControllerPoses[i]) * Matrix4f::Scaling(0.1, 0.1, 0.1); + GL(glUniformMatrix4fv( + Scene.AxesProgram.UniformLocation[ovrUniform::Index::MODEL_MATRIX], + 1, + GL_TRUE, + &transform.M[0][0])); + } + Scene.Axes.BindVAO(); + GL(glDrawElements(GL_LINES, Scene.Axes.IndexCount(), GL_UNSIGNED_SHORT, nullptr)); + GL(glBindVertexArray(0)); + GL(glUseProgram(0)); + } + } + + // Render planes + GL(glUseProgram(Scene.PlaneProgram.Program)); + GL(glBindBufferBase( + GL_UNIFORM_BUFFER, + Scene.PlaneProgram.UniformBinding[ovrUniform::Index::SCENE_MATRICES], + Scene.SceneMatrices)); + if (Scene.PlaneProgram.UniformLocation[ovrUniform::Index::VIEW_ID] >= 0) { + // NOTE: will not be present when multiview path is enabled. + GL(glUniform1i(Scene.PlaneProgram.UniformLocation[ovrUniform::Index::VIEW_ID], 0)); + } + GL(glEnable(GL_BLEND)); + GL(glEnable(GL_CULL_FACE)); + GL(glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); + for (const auto& plane : Scene.Planes) { + if (!plane.IsRenderable()) { + continue; + } + if (Scene.PlaneProgram.UniformLocation[ovrUniform::Index::MODEL_MATRIX] >= 0) { + Matrix4f transform = Matrix4f(plane.T_World_Plane); + if (plane.ZOffset != 0.0f) { + transform *= ZOffsetTransform(plane.ZOffset); + } + GL(glUniformMatrix4fv( + Scene.PlaneProgram.UniformLocation[ovrUniform::Index::MODEL_MATRIX], + 1, + GL_TRUE, + &transform.M[0][0])); + } + plane.Geometry.BindVAO(); + GL(glDrawElements(GL_TRIANGLES, plane.Geometry.IndexCount(), GL_UNSIGNED_SHORT, nullptr)); + GL(glBindVertexArray(0)); + } + GL(glDisable(GL_CULL_FACE)); + GL(glDisable(GL_BLEND)); + GL(glUseProgram(0)); + + // Render plane pose as RGB axes if available. + GL(glLineWidth(5.0)); + GL(glUseProgram(Scene.AxesProgram.Program)); + GL(glBindBufferBase( + GL_UNIFORM_BUFFER, + Scene.AxesProgram.UniformBinding[ovrUniform::Index::SCENE_MATRICES], + Scene.SceneMatrices)); + if (Scene.AxesProgram.UniformLocation[ovrUniform::Index::VIEW_ID] >= 0) { + GL(glUniform1i(Scene.AxesProgram.UniformLocation[ovrUniform::Index::VIEW_ID], 0)); + } + Scene.Axes.BindVAO(); + for (const auto& plane : Scene.Planes) { + if (!plane.IsRenderable()) { + continue; + } + if (Scene.AxesProgram.UniformLocation[ovrUniform::Index::MODEL_MATRIX] >= 0) { + Matrix4f transform = Matrix4f(plane.T_World_Plane); + if (plane.ZOffset != 0.0f) { + transform *= ZOffsetTransform(plane.ZOffset); + } + transform *= Matrix4f::Scaling(0.1, 0.1, 0.1); + GL(glUniformMatrix4fv( + Scene.AxesProgram.UniformLocation[ovrUniform::Index::MODEL_MATRIX], + 1, + GL_TRUE, + &transform.M[0][0])); + GL(glDrawElements(GL_LINES, Scene.Axes.IndexCount(), GL_UNSIGNED_SHORT, nullptr)); + } + } + GL(glBindVertexArray(0)); + GL(glUseProgram(0)); + + // Render volumes + GL(glUseProgram(Scene.VolumeProgram.Program)); + GL(glBindBufferBase( + GL_UNIFORM_BUFFER, + Scene.VolumeProgram.UniformBinding[ovrUniform::Index::SCENE_MATRICES], + Scene.SceneMatrices)); + if (Scene.VolumeProgram.UniformLocation[ovrUniform::Index::VIEW_ID] >= 0) { + // NOTE: will not be present when multiview path is enabled. + GL(glUniform1i(Scene.VolumeProgram.UniformLocation[ovrUniform::Index::VIEW_ID], 0)); + } + GL(glEnable(GL_BLEND)); + GL(glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); + for (const auto& volume : Scene.Volumes) { + if (!volume.IsRenderable()) { + continue; + } + if (Scene.VolumeProgram.UniformLocation[ovrUniform::Index::MODEL_MATRIX] >= 0) { + const Matrix4f transform = Matrix4f(volume.T_World_Volume); + GL(glUniformMatrix4fv( + Scene.VolumeProgram.UniformLocation[ovrUniform::Index::MODEL_MATRIX], + 1, + GL_TRUE, + &transform.M[0][0])); + } + volume.Geometry.BindVAO(); + GL(glDrawElements(GL_TRIANGLES, volume.Geometry.IndexCount(), GL_UNSIGNED_SHORT, nullptr)); + GL(glBindVertexArray(0)); + } + GL(glDisable(GL_BLEND)); + GL(glUseProgram(0)); + + // Render the underlying anchor pose as RGB axes if available. + GL(glLineWidth(5.0)); + GL(glUseProgram(Scene.AxesProgram.Program)); + GL(glBindBufferBase( + GL_UNIFORM_BUFFER, + Scene.AxesProgram.UniformBinding[ovrUniform::Index::SCENE_MATRICES], + Scene.SceneMatrices)); + if (Scene.AxesProgram.UniformLocation[ovrUniform::Index::VIEW_ID] >= 0) { + GL(glUniform1i(Scene.AxesProgram.UniformLocation[ovrUniform::Index::VIEW_ID], 0)); + } + Scene.Axes.BindVAO(); + for (const auto& volume : Scene.Volumes) { + if (!volume.IsRenderable()) { + continue; + } + if (Scene.AxesProgram.UniformLocation[ovrUniform::Index::MODEL_MATRIX] >= 0) { + const Matrix4f transform = + Matrix4f(volume.T_World_Volume) * Matrix4f::Scaling(0.1, 0.1, 0.1); + GL(glUniformMatrix4fv( + Scene.AxesProgram.UniformLocation[ovrUniform::Index::MODEL_MATRIX], + 1, + GL_TRUE, + &transform.M[0][0])); + GL(glDrawElements(GL_LINES, Scene.Axes.IndexCount(), GL_UNSIGNED_SHORT, nullptr)); + } + } + GL(glBindVertexArray(0)); + GL(glUseProgram(0)); + + // Render meshes + GL(glLineWidth(2.0)); + GL(glUseProgram(Scene.MeshProgram.Program)); + GL(glBindBufferBase(GL_UNIFORM_BUFFER, Scene.MeshProgram.UniformBinding[ovrUniform::Index::SCENE_MATRICES], Scene.SceneMatrices)); + if (Scene.MeshProgram.UniformLocation[ovrUniform::Index::VIEW_ID] >= 0) { + GL(glUniform1i(Scene.MeshProgram.UniformLocation[ovrUniform::Index::VIEW_ID], 0)); + } + GL(glEnable(GL_BLEND)); + GL(glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); + for (const auto& mesh : Scene.Meshes) { + if (!mesh.IsRenderable()) { + continue; + } + if (Scene.MeshProgram.UniformLocation[ovrUniform::Index::MODEL_MATRIX] >= 0) { + const Matrix4f transform = Matrix4f(mesh.T_World_Mesh); + GL(glUniformMatrix4fv(Scene.MeshProgram.UniformLocation[ovrUniform::Index::MODEL_MATRIX], 1, GL_TRUE, &transform.M[0][0])); + } + mesh.Geometry.BindVAO(); + GL(glDrawElements(GL_LINES, mesh.Geometry.IndexCount(), GL_UNSIGNED_INT, nullptr)); + GL(glBindVertexArray(0)); + } + GL(glDisable(GL_BLEND)); + GL(glUseProgram(0)); + + Framebuffer.Unbind(); +} diff --git a/Samples/XrSamples/XrSceneModel/Src/SceneModelGl.h b/Samples/XrSamples/XrSceneModel/Src/SceneModelGl.h new file mode 100755 index 0000000..6c47a45 --- /dev/null +++ b/Samples/XrSamples/XrSceneModel/Src/SceneModelGl.h @@ -0,0 +1,264 @@ +#pragma once + +#include +#include + +#if defined(ANDROID) +#include +#include +#include + +#define XR_USE_GRAPHICS_API_OPENGL_ES 1 +#define XR_USE_PLATFORM_ANDROID 1 +#elif defined(WIN32) +#include "Render/GlWrapperWin32.h" + +#include +#define XR_USE_GRAPHICS_API_OPENGL 1 +#define XR_USE_PLATFORM_WIN32 1 +#endif + +#include +#include +#include + +#include "OVR_Math.h" + +#define NUM_EYES 2 + +struct ovrGeometry { + void Clear(); + void CreateAxes(); + void CreateStage(); + void CreatePlane(const std::vector& vertices, const XrColor4f& color); + void CreateVolume(const std::array& vertices, const XrColor4f& color); + void CreateMesh(const XrSpaceTriangleMeshMETA& mesh); + void Destroy(); + void CreateVAO(); + void DestroyVAO(); + + int IndexCount() const { + return IndexCount_; + } + + void BindVAO() const; + + bool IsRenderable() const { + return IsRenderable_; + } + + private: + static constexpr int MAX_VERTEX_ATTRIB_POINTERS = 3; + + struct VertexAttribPointer { + GLint Index; + GLint Size; + GLenum Type; + GLboolean Normalized; + GLsizei Stride; + const GLvoid* Pointer; + }; + + void CreateIndexBuffer(const std::vector& indices); + + int IndexCount_; + + VertexAttribPointer VertexAttribs_[MAX_VERTEX_ATTRIB_POINTERS]; + GLuint VertexBuffer_ = 0; + GLuint IndexBuffer_ = 0; + GLuint VertexArrayObject_ = 0; + + bool IsRenderable_ = false; +}; + +struct ovrProgram { + static constexpr int MAX_PROGRAM_UNIFORMS = 8; + static constexpr int MAX_PROGRAM_TEXTURES = 8; + + void Clear(); + bool Create(const char* vertexSource, const char* fragmentSource); + void Destroy(); + GLuint Program; + GLuint VertexShader; + GLuint FragmentShader; + // These will be -1 if not used by the program. + GLint UniformLocation[MAX_PROGRAM_UNIFORMS]; // ProgramUniforms[].name + GLint UniformBinding[MAX_PROGRAM_UNIFORMS]; // ProgramUniforms[].name + GLint Textures[MAX_PROGRAM_TEXTURES]; // Texture%i +}; + +struct ovrFramebuffer { + void Clear(); + bool Create( + const GLenum colorFormat, + const int width, + const int height, + const int multisamples, + const int swapChainLength, + GLuint* colorTextures); + void Destroy(); + void Bind(int element); + void Unbind(); + void Resolve(); + int Width; + int Height; + int Multisamples; + int SwapChainLength; + struct Element { + GLuint ColorTexture; + GLuint DepthTexture; + GLuint FrameBufferObject; + }; + Element* Elements; +}; + +struct ovrPlane { + explicit ovrPlane(const XrSpace space); + + void Update(const XrRect2Df& boundingBox2D, const XrColor4f& color); + + void Update(const XrBoundary2DFB& boundary2D, const XrColor4f& color); + + void SetPose(const XrPosef& T_World_Plane); + + void SetZOffset(const float zOffset); + + void SetVisible(const bool isVisible) { + IsVisible_ = isVisible; + } + + bool IsRenderable() const { + return IsVisible_ && IsPoseSet_ && Geometry.IsRenderable(); + } + + XrSpace Space; + OVR::Posef T_World_Plane; + ovrGeometry Geometry; + float ZOffset = 0.0f; // Z offset in the plane frame + + private: + bool IsVisible_ = true; + bool IsPoseSet_ = false; +}; + +struct ovrVolume { + explicit ovrVolume(const XrSpace space); + + void Update(const XrRect3DfFB& boundingBox3D, const XrColor4f& color); + + void SetPose(const XrPosef& T_World_Volume); + + void SetVisible(const bool isVisible) { + IsVisible_ = isVisible; + } + + bool IsRenderable() const { + return IsVisible_ && IsPoseSet_ && Geometry.IsRenderable(); + } + + XrSpace Space; + OVR::Posef T_World_Volume; + ovrGeometry Geometry; + + private: + bool IsVisible_ = true; + bool IsPoseSet_ = false; +}; + +struct ovrMesh { + explicit ovrMesh(const XrSpace space); + + void Update(const XrSpaceTriangleMeshMETA& mesh); + + void SetPose(const XrPosef& T_World_Mesh); + + void SetVisible(const bool isVisible) { + IsVisible_ = isVisible; + } + + bool IsRenderable() const { + return IsVisible_ && IsPoseSet_ && Geometry.IsRenderable(); + } + + XrSpace Space; + OVR::Posef T_World_Mesh; + ovrGeometry Geometry; + + private: + bool IsVisible_ = true; + bool IsPoseSet_ = false; +}; + +struct ovrScene { + public: + void Clear(); + void Create(); + void Destroy(); + bool IsCreated(); + void SetClearColor(const float* c); + void CreateVAOs(); + void DestroyVAOs(); + + void DestroyPlaneGeometries(); + void ClearPlaneGeometries(); + void DestroyVolumeGeometries(); + void ClearVolumeGeometries(); + + void AddPlaneToRender(); + + void UpdatePlaneToRender( + const int planeIndex, + const std::array& vertices, + const XrColor4f& color); + + void AddVolumeToRender(); + + void UpdateVolumeToRender( + const int volumeIndex, + const std::array& vertices, + const XrColor4f& color); + + bool CreatedScene; + bool CreatedVAOs; + GLuint SceneMatrices; + ovrProgram StageProgram; + ovrGeometry Stage; + ovrProgram AxesProgram; + ovrGeometry Axes; + ovrProgram PlaneProgram; + ovrProgram VolumeProgram; + ovrProgram MeshProgram; + float ClearColor[4]; + + std::vector Planes; + std::vector Volumes; + std::vector Meshes; +}; + +struct ovrAppRenderer { + void Clear(); + void Create( + GLenum format, + int width, + int height, + int numMultiSamples, + int swapChainLength, + GLuint* colorTextures); + void Destroy(); + + struct FrameIn { + int SwapChainIndex; + OVR::Matrix4f View[NUM_EYES]; + OVR::Matrix4f Proj[NUM_EYES]; + bool HasStage; + OVR::Posef StagePose; + OVR::Vector3f StageScale; + std::array RenderController; + std::array ControllerPoses; + }; + + void RenderFrame(const FrameIn& frameIn); + + ovrFramebuffer Framebuffer; + ovrScene Scene; +}; diff --git a/Samples/XrSamples/XrSceneModel/Src/SceneModelHelpers.h b/Samples/XrSamples/XrSceneModel/Src/SceneModelHelpers.h new file mode 100755 index 0000000..7c83ceb --- /dev/null +++ b/Samples/XrSamples/XrSceneModel/Src/SceneModelHelpers.h @@ -0,0 +1,86 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +#pragma once + +#include +#include + +inline XrVector3f ToXrVector3f(const OVR::Vector3f& s) { + XrVector3f r; + r.x = s.x; + r.y = s.y; + r.z = s.z; + return r; +} + +inline OVR::Vector3f FromXrVector3f(const XrVector3f& s) { + OVR::Vector3f r; + r.x = s.x; + r.y = s.y; + r.z = s.z; + return r; +} + +inline XrQuaternionf ToXrQuaternionf(const OVR::Quatf& s) { + XrQuaternionf r; + r.x = s.x; + r.y = s.y; + r.z = s.z; + r.w = s.w; + return r; +} + +inline OVR::Quatf FromXrQuaternionf(const XrQuaternionf& s) { + OVR::Quatf r; + r.x = s.x; + r.y = s.y; + r.z = s.z; + r.w = s.w; + return r; +} + +inline XrPosef ToXrPosef(const OVR::Posef& s) { + XrPosef r; + r.orientation = ToXrQuaternionf(s.Rotation); + r.position = ToXrVector3f(s.Translation); + return r; +} + +inline OVR::Posef FromXrPosef(const XrPosef& s) { + OVR::Posef r; + r.Rotation = FromXrQuaternionf(s.orientation); + r.Translation = FromXrVector3f(s.position); + return r; +} + +inline OVR::Matrix4f OvrFromXr(const XrMatrix4x4f& x) { + return OVR::Matrix4f( + x.m[0x0], + x.m[0x1], + x.m[0x2], + x.m[0x3], + x.m[0x4], + x.m[0x5], + x.m[0x6], + x.m[0x7], + x.m[0x8], + x.m[0x9], + x.m[0xa], + x.m[0xb], + x.m[0xc], + x.m[0xd], + x.m[0xe], + x.m[0xf]); +} + +inline OVR::Quatf OvrFromXr(const XrQuaternionf& q) { + return OVR::Quatf(q.x, q.y, q.z, q.w); +} + +inline OVR::Vector3f OvrFromXr(const XrVector3f& v) { + return OVR::Vector3f(v.x, v.y, v.z); +} + +inline OVR::Posef OvrFromXr(const XrPosef& p) { + return OVR::Posef(OvrFromXr(p.orientation), OvrFromXr(p.position)); +} diff --git a/Samples/XrSamples/XrSceneModel/Src/SceneModelShaders.h b/Samples/XrSamples/XrSceneModel/Src/SceneModelShaders.h new file mode 100755 index 0000000..ce09130 --- /dev/null +++ b/Samples/XrSamples/XrSceneModel/Src/SceneModelShaders.h @@ -0,0 +1,60 @@ +// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +#pragma once + +static const char VERTEX_SHADER[] = R"( + #define NUM_VIEWS 2 + #define VIEW_ID gl_ViewID_OVR + #extension GL_OVR_multiview2 : require + layout(num_views=NUM_VIEWS) in; + in vec3 vertexPosition; + in vec4 vertexColor; + uniform mat4 ModelMatrix; + uniform SceneMatrices { + uniform mat4 ViewMatrix[NUM_VIEWS]; + uniform mat4 ProjectionMatrix[NUM_VIEWS]; + } sm; + out vec4 fragmentColor; + void main() { + gl_Position = sm.ProjectionMatrix[VIEW_ID] * (sm.ViewMatrix[VIEW_ID] * (ModelMatrix * vec4(vertexPosition, 1.0))); + fragmentColor = vertexColor; + } +)"; + +static const char FRAGMENT_SHADER[] = R"( + in lowp vec4 fragmentColor; + out lowp vec4 outColor; + void main() { + outColor = fragmentColor; + } +)"; + +static const char STAGE_VERTEX_SHADER[] = R"( + #define NUM_VIEWS 2 + #define VIEW_ID gl_ViewID_OVR + #extension GL_OVR_multiview2 : require + layout(num_views=NUM_VIEWS) in; + in vec3 vertexPosition; + uniform mat4 ModelMatrix; + uniform SceneMatrices { + uniform mat4 ViewMatrix[NUM_VIEWS]; + uniform mat4 ProjectionMatrix[NUM_VIEWS]; + } sm; + void main() { + gl_Position = sm.ProjectionMatrix[VIEW_ID] * (sm.ViewMatrix[VIEW_ID] * (ModelMatrix * (vec4(vertexPosition, 1.0)))); + } +)"; + +static const char STAGE_FRAGMENT_SHADER[] = R"( + out lowp vec4 outColor; + void main() { + outColor = vec4(0.5, 0.5, 1.0, 0.5); + } +)"; + +static const char MESH_FRAGMENT_SHADER[] = R"( + out lowp vec4 outColor; + void main() { + outColor = vec4(1.0, 1.0, 1.0, 0.8); + } +)"; diff --git a/Samples/XrSamples/XrSceneModel/Src/SceneModelXr.cpp b/Samples/XrSamples/XrSceneModel/Src/SceneModelXr.cpp new file mode 100755 index 0000000..4073080 --- /dev/null +++ b/Samples/XrSamples/XrSceneModel/Src/SceneModelXr.cpp @@ -0,0 +1,2305 @@ +/************************************************************************************ + +Filename : SceneModelXr.cpp +Content : This sample uses the Android NativeActivity class. +Created : +Authors : + +Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved. + +*************************************************************************************/ + +#include +#include +#include +#include // for memset +#include +#include +#include +#include +#include + +#if defined(ANDROID) +#include +#include +#include // for prctl( PR_SET_NAME ) +#include +#include // for native window JNI +#include +#else +#include +#endif + +#include + +#include "SceneModelHelpers.h" +#include "SceneModelGl.h" +#include "SceneModelXr.h" +#include "SimpleXrInput.h" + +#include + + +#if defined(_WIN32) +// Favor the high performance NVIDIA or AMD GPUs +extern "C" { +// http://developer.download.nvidia.com/devzone/devcenter/gamegraphics/files/OptimusRenderingPolicies.pdf +__declspec(dllexport) DWORD NvOptimusEnablement = 0x00000001; +// https://gpuopen.com/learn/amdpowerxpressrequesthighperformance/ +__declspec(dllexport) DWORD AmdPowerXpressRequestHighPerformance = 0x00000001; +} +#endif // defined(_WIN32) + +using namespace OVR; + +#if !defined(EGL_OPENGL_ES3_BIT_KHR) +#define EGL_OPENGL_ES3_BIT_KHR 0x0040 +#endif + +#if defined(ANDROID) +#define DEBUG 1 +#define OVR_LOG_TAG "SceneModelXr" + +#define ALOGE(...) __android_log_print(ANDROID_LOG_ERROR, OVR_LOG_TAG, __VA_ARGS__) +#define ALOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, OVR_LOG_TAG, __VA_ARGS__) +#else +#include +#define ALOGE(...) \ + printf("ERROR: "); \ + printf(__VA_ARGS__); \ + printf("\n") +#define ALOGV(...) \ + printf("VERBOSE: "); \ + printf(__VA_ARGS__); \ + printf("\n") +#endif + +static const int CPU_LEVEL = 2; +static const int GPU_LEVEL = 3; +static const int NUM_MULTI_SAMPLES = 4; + +static const uint32_t MAX_PERSISTENT_SPACES = 100; + +union ovrCompositorLayer_Union { + XrCompositionLayerProjection Projection; + XrCompositionLayerQuad Quad; + XrCompositionLayerCylinderKHR Cylinder; + XrCompositionLayerCubeKHR Cube; + XrCompositionLayerEquirectKHR Equirect; + XrCompositionLayerPassthroughFB Passthrough; +}; + +enum { ovrMaxLayerCount = 16 }; + +// Forward declarations +XrInstance instance; + +/* +================================================================================ + +Hex and Binary Conversion Functions + +================================================================================ +*/ + +std::string bin2hex(const uint8_t* src, uint32_t size) { + std::string res; + res.reserve(size * 2); + const char hex[] = "0123456789ABCDEF"; + for (uint32_t i = 0; i < size; ++i) { + uint8_t c = src[i]; + res += hex[c >> 4]; + res += hex[c & 0xf]; + } + return res; +} + +void hex2bin(const std::string& hex, uint8_t* dst, uint32_t size) { + assert(hex.size() == size * 2); + for (uint32_t i = 0; i < size; ++i) { + dst[i] = 0; + for (uint32_t j = 0; j < 2; ++j) { + uint8_t c = hex[2 * i + j]; + if ('0' <= c && c <= '9') { + c -= '0'; + } else if ('A' <= c && c <= 'F') { + c -= 'A'; + c += 10; + } else { + assert(false); + } + dst[i] += c << (j == 0 ? 4 : 0); + } + } +} + +std::string uuidToHexString(const XrUuidEXT& uuid) { + return bin2hex(uuid.data, XR_UUID_SIZE_EXT); +} + +void hexStringToUuid(const std::string& hex, XrUuidEXT& uuid) { + hex2bin(hex, uuid.data, XR_UUID_SIZE_EXT); +} + +bool isValid(const XrUuidEXT& uuid) { + for (int i = 0; i < XR_UUID_SIZE_EXT; ++i) { + if (uuid.data[i] > 0) { + return true; + } + } + return false; +} + +static const std::map SemanticLabelToColorMap = { + {"TABLE", {1.0f, 0.0f, 0.0f, 0.2f}}, + {"COUCH", {0.0f, 1.0f, 0.0f, 0.2f}}, + {"FLOOR", {0.2f, 0.2f, 0.8f, 0.8f}}, + {"CEILING", {0.3f, 0.3f, 0.3f, 0.8f}}, + {"WALL_FACE", {0.5f, 0.5f, 0.0f, 0.8f}}, + {"INVISIBLE_WALL_FACE", {0.7f, 0.9f, 0.3f, 0.8f}}, + {"WINDOW_FRAME", {0.0f, 0.5f, 0.6f, 0.6f}}, + {"DOOR_FRAME", {0.0f, 0.2f, 0.2f, 0.6f}}, + {"STORAGE", {0.5f, 0.1f, 0.4f, 0.6f}}, + {"BED", {0.0f, 0.7f, 0.9f, 0.6f}}, + {"SCREEN", {0.4f, 0.4f, 0.4f, 0.8f}}, + {"LAMP", {0.9f, 0.9f, 0.9f, 0.8f}}, + {"PLANT", {0.1f, 0.9f, 0.2f, 0.6f}}, + {"WALL_ART", {1.0f, 0.6f, 0.0f, 0.6f}}, + {"OTHER", {1.0f, 0.0f, 1.0f, 0.2f}}}; + +XrColor4f GetColorForSemanticLabels(const std::string& labels) { + const XrColor4f defaultColor = {0.2f, 0.2f, 0.0f, 0.2f}; + if (labels.empty()) { + return defaultColor; + } + + // Find the color for the first semantic label + const std::string delimiter = ","; // Delimiter to separate labels + const std::string firstLabel = labels.substr(0, labels.find(delimiter)); + if (firstLabel.empty() || !SemanticLabelToColorMap.count(firstLabel)) { + return defaultColor; + } + + return SemanticLabelToColorMap.at(firstLabel); +} + +std::string BoundaryVisibilityToString(const XrBoundaryVisibilityMETA boundaryVisibility) { + switch (boundaryVisibility) { + case XR_BOUNDARY_VISIBILITY_NOT_SUPPRESSED_META: + return "Not suppressed"; + case XR_BOUNDARY_VISIBILITY_SUPPRESSED_META: + return "Suppressed"; + default: + return "Unknown"; + } +} + + +/* +================================================================================ + +OpenXR Utility Functions + +================================================================================ +*/ + +static void OXR_CheckErrors(XrResult result, const char* function, bool failOnError) { + if (XR_FAILED(result)) { + char errorBuffer[XR_MAX_RESULT_STRING_SIZE]; + xrResultToString(instance, result, errorBuffer); + if (failOnError) { + ALOGE("OpenXR error: %s: %s\n", function, errorBuffer); + } else { + ALOGV("OpenXR error: %s: %s\n", function, errorBuffer); + } + } +} + +#if defined(DEBUG) +#define OXR(func) OXR_CheckErrors(func, #func, true); +#else +#define OXR(func) OXR_CheckErrors(func, #func, false); +#endif + +/* +================================================================================ + +Egl Utility Functions + +================================================================================ +*/ + +#if defined(XR_USE_GRAPHICS_API_OPENGL_ES) +static const char* EglErrorString(const EGLint error) { + switch (error) { + case EGL_SUCCESS: + return "EGL_SUCCESS"; + case EGL_NOT_INITIALIZED: + return "EGL_NOT_INITIALIZED"; + case EGL_BAD_ACCESS: + return "EGL_BAD_ACCESS"; + case EGL_BAD_ALLOC: + return "EGL_BAD_ALLOC"; + case EGL_BAD_ATTRIBUTE: + return "EGL_BAD_ATTRIBUTE"; + case EGL_BAD_CONTEXT: + return "EGL_BAD_CONTEXT"; + case EGL_BAD_CONFIG: + return "EGL_BAD_CONFIG"; + case EGL_BAD_CURRENT_SURFACE: + return "EGL_BAD_CURRENT_SURFACE"; + case EGL_BAD_DISPLAY: + return "EGL_BAD_DISPLAY"; + case EGL_BAD_SURFACE: + return "EGL_BAD_SURFACE"; + case EGL_BAD_MATCH: + return "EGL_BAD_MATCH"; + case EGL_BAD_PARAMETER: + return "EGL_BAD_PARAMETER"; + case EGL_BAD_NATIVE_PIXMAP: + return "EGL_BAD_NATIVE_PIXMAP"; + case EGL_BAD_NATIVE_WINDOW: + return "EGL_BAD_NATIVE_WINDOW"; + case EGL_CONTEXT_LOST: + return "EGL_CONTEXT_LOST"; + default: + return "unknown"; + } +} +#endif + +/* +================================================================================ + +ovrEgl + +================================================================================ +*/ + +struct ovrEgl { + void Clear(); + void CreateContext(const ovrEgl* shareEgl); + void DestroyContext(); +#if defined(XR_USE_GRAPHICS_API_OPENGL_ES) + EGLint MajorVersion; + EGLint MinorVersion; + EGLDisplay Display; + EGLConfig Config; + EGLSurface TinySurface; + EGLSurface MainSurface; + EGLContext Context; +#elif defined(XR_USE_GRAPHICS_API_OPENGL) + HDC hDC; + HGLRC hGLRC; +#endif +}; + +void ovrEgl::Clear() { +#if defined(XR_USE_GRAPHICS_API_OPENGL_ES) + MajorVersion = 0; + MinorVersion = 0; + Display = 0; + Config = 0; + TinySurface = EGL_NO_SURFACE; + MainSurface = EGL_NO_SURFACE; + Context = EGL_NO_CONTEXT; +#elif defined(XR_USE_GRAPHICS_API_OPENGL) + hDC = 0; + hGLRC = 0; +#endif +} + +#if defined(XR_USE_GRAPHICS_API_OPENGL_ES) +void ovrEgl::CreateContext(const ovrEgl* shareEgl) { + if (Display != 0) { + return; + } + + Display = eglGetDisplay(EGL_DEFAULT_DISPLAY); + ALOGV(" eglInitialize( Display, &MajorVersion, &MinorVersion )"); + eglInitialize(Display, &MajorVersion, &MinorVersion); + // Do NOT use eglChooseConfig, because the Android EGL code pushes in multisample + // flags in eglChooseConfig if the user has selected the "force 4x MSAA" option in + // settings, and that is completely wasted for our warp target. + const int MAX_CONFIGS = 1024; + EGLConfig configs[MAX_CONFIGS]; + EGLint numConfigs = 0; + if (eglGetConfigs(Display, configs, MAX_CONFIGS, &numConfigs) == EGL_FALSE) { + ALOGE(" eglGetConfigs() failed: %s", EglErrorString(eglGetError())); + return; + } + const EGLint configAttribs[] = { + EGL_RED_SIZE, + 8, + EGL_GREEN_SIZE, + 8, + EGL_BLUE_SIZE, + 8, + EGL_ALPHA_SIZE, + 8, // need alpha for the multi-pass timewarp compositor + EGL_DEPTH_SIZE, + 0, + EGL_STENCIL_SIZE, + 0, + EGL_SAMPLES, + 0, + EGL_NONE}; + Config = 0; + for (int i = 0; i < numConfigs; i++) { + EGLint value = 0; + + eglGetConfigAttrib(Display, configs[i], EGL_RENDERABLE_TYPE, &value); + if ((value & EGL_OPENGL_ES3_BIT_KHR) != EGL_OPENGL_ES3_BIT_KHR) { + continue; + } + + // The pbuffer config also needs to be compatible with normal window rendering + // so it can share textures with the window context. + eglGetConfigAttrib(Display, configs[i], EGL_SURFACE_TYPE, &value); + if ((value & (EGL_WINDOW_BIT | EGL_PBUFFER_BIT)) != (EGL_WINDOW_BIT | EGL_PBUFFER_BIT)) { + continue; + } + + int j = 0; + for (; configAttribs[j] != EGL_NONE; j += 2) { + eglGetConfigAttrib(Display, configs[i], configAttribs[j], &value); + if (value != configAttribs[j + 1]) { + break; + } + } + if (configAttribs[j] == EGL_NONE) { + Config = configs[i]; + break; + } + } + if (Config == 0) { + ALOGE(" eglChooseConfig() failed: %s", EglErrorString(eglGetError())); + return; + } + EGLint contextAttribs[] = {EGL_CONTEXT_CLIENT_VERSION, 3, EGL_NONE}; + ALOGV(" Context = eglCreateContext( Display, Config, EGL_NO_CONTEXT, contextAttribs )"); + Context = eglCreateContext( + Display, + Config, + (shareEgl != nullptr) ? shareEgl->Context : EGL_NO_CONTEXT, + contextAttribs); + if (Context == EGL_NO_CONTEXT) { + ALOGE(" eglCreateContext() failed: %s", EglErrorString(eglGetError())); + return; + } + const EGLint surfaceAttribs[] = {EGL_WIDTH, 16, EGL_HEIGHT, 16, EGL_NONE}; + ALOGV(" TinySurface = eglCreatePbufferSurface( Display, Config, surfaceAttribs )"); + TinySurface = eglCreatePbufferSurface(Display, Config, surfaceAttribs); + if (TinySurface == EGL_NO_SURFACE) { + ALOGE(" eglCreatePbufferSurface() failed: %s", EglErrorString(eglGetError())); + eglDestroyContext(Display, Context); + Context = EGL_NO_CONTEXT; + return; + } + ALOGV(" eglMakeCurrent( Display, TinySurface, TinySurface, Context )"); + if (eglMakeCurrent(Display, TinySurface, TinySurface, Context) == EGL_FALSE) { + ALOGE(" eglMakeCurrent() failed: %s", EglErrorString(eglGetError())); + eglDestroySurface(Display, TinySurface); + eglDestroyContext(Display, Context); + Context = EGL_NO_CONTEXT; + return; + } +} + +void ovrEgl::DestroyContext() { + if (Display != 0) { + ALOGE(" eglMakeCurrent( Display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT )"); + if (eglMakeCurrent(Display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT) == EGL_FALSE) { + ALOGE(" eglMakeCurrent() failed: %s", EglErrorString(eglGetError())); + } + } + if (Context != EGL_NO_CONTEXT) { + ALOGE(" eglDestroyContext( Display, Context )"); + if (eglDestroyContext(Display, Context) == EGL_FALSE) { + ALOGE(" eglDestroyContext() failed: %s", EglErrorString(eglGetError())); + } + Context = EGL_NO_CONTEXT; + } + if (TinySurface != EGL_NO_SURFACE) { + ALOGE(" eglDestroySurface( Display, TinySurface )"); + if (eglDestroySurface(Display, TinySurface) == EGL_FALSE) { + ALOGE(" eglDestroySurface() failed: %s", EglErrorString(eglGetError())); + } + TinySurface = EGL_NO_SURFACE; + } + if (Display != 0) { + ALOGE(" eglTerminate( Display )"); + if (eglTerminate(Display) == EGL_FALSE) { + ALOGE(" eglTerminate() failed: %s", EglErrorString(eglGetError())); + } + Display = 0; + } +} + +#elif defined(XR_USE_GRAPHICS_API_OPENGL) + +void ovrEgl::CreateContext(const ovrEgl*) { + ovrGl_CreateContext_Windows(&hDC, &hGLRC); +} + +void ovrEgl::DestroyContext() { + ovrGl_DestroyContext_Windows(); +} + +#endif + +/* +================================================================================ + +ovrApp + +================================================================================ +*/ + +struct ovrExtensionFunctionPointers { + PFN_xrCreatePassthroughFB xrCreatePassthroughFB = nullptr; + PFN_xrDestroyPassthroughFB xrDestroyPassthroughFB = nullptr; + PFN_xrCreatePassthroughLayerFB xrCreatePassthroughLayerFB = nullptr; + PFN_xrDestroyPassthroughLayerFB xrDestroyPassthroughLayerFB = nullptr; + PFN_xrPassthroughLayerResumeFB xrPassthroughLayerResumeFB = nullptr; + PFN_xrPassthroughLayerPauseFB xrPassthroughLayerPauseFB = nullptr; + PFN_xrPassthroughLayerSetStyleFB xrPassthroughLayerSetStyleFB = nullptr; + PFN_xrPassthroughStartFB xrPassthroughStartFB = nullptr; + PFN_xrPassthroughPauseFB xrPassthroughPauseFB = nullptr; + PFN_xrEnumerateSpaceSupportedComponentsFB xrEnumerateSpaceSupportedComponentsFB = nullptr; + PFN_xrGetSpaceComponentStatusFB xrGetSpaceComponentStatusFB = nullptr; + PFN_xrSetSpaceComponentStatusFB xrSetSpaceComponentStatusFB = nullptr; + PFN_xrQuerySpacesFB xrQuerySpacesFB = nullptr; + PFN_xrRetrieveSpaceQueryResultsFB xrRetrieveSpaceQueryResultsFB = nullptr; + PFN_xrGetSpaceBoundingBox2DFB xrGetSpaceBoundingBox2DFB = nullptr; + PFN_xrGetSpaceBoundingBox3DFB xrGetSpaceBoundingBox3DFB = nullptr; + PFN_xrGetSpaceSemanticLabelsFB xrGetSpaceSemanticLabelsFB = nullptr; + PFN_xrGetSpaceBoundary2DFB xrGetSpaceBoundary2DFB = nullptr; + PFN_xrGetSpaceRoomLayoutFB xrGetSpaceRoomLayoutFB = nullptr; + PFN_xrGetSpaceContainerFB xrGetSpaceContainerFB = nullptr; + PFN_xrGetSpaceTriangleMeshMETA xrGetSpaceTriangleMeshMETA = nullptr; +#if defined(ANDROID) + PFN_xrRequestSceneCaptureFB xrRequestSceneCaptureFB = nullptr; +#endif + PFN_xrRequestBoundaryVisibilityMETA xrRequestBoundaryVisibilityMETA = nullptr; +}; + +struct ovrApp { + void Clear(); + void HandleSessionStateChanges(XrSessionState state); + void HandleXrEvents(); + bool IsComponentSupported(XrSpace space, XrSpaceComponentTypeFB type); + bool IsComponentEnabled(XrSpace space, XrSpaceComponentTypeFB type); + void CollectRoomLayoutUuids(XrSpace space, std::unordered_set& UuidSet); + void CollectSpaceContainerUuids(XrSpace space, std::unordered_set& UuidSet); + + ovrEgl Egl; +#if defined(XR_USE_PLATFORM_ANDROID) + ANativeWindow* NativeWindow; + bool Resumed; +#endif // defined(XR_USE_PLATFORM_ANDROID) + bool ShouldExit; + bool Focused; + + XrSession Session; + XrViewConfigurationProperties ViewportConfig; + XrViewConfigurationView ViewConfigurationView[NUM_EYES]; + XrSystemId SystemId; + XrSpace HeadSpace; + XrSpace LocalSpace; + XrSpace StageSpace; + bool SessionActive; + + ovrExtensionFunctionPointers FunPtrs; + + int SwapInterval; + int CpuLevel; + int GpuLevel; + // These threads will be marked as performance threads. + int MainThreadTid; + int RenderThreadTid; + ovrCompositorLayer_Union Layers[ovrMaxLayerCount]; + int LayerCount; + bool TouchPadDownLastFrame; + + enum class QueryType { + None, + QueryAll, + QueryAllBounded2DEnabled, + QueryAllRoomLayoutEnabled, + QueryByUuids, + }; + QueryType NextQueryType; + bool QueryAllAnchorsInRoom = true; + bool IsQueryComplete = true; + + enum class PlaneVisualizationMode { + BoundingBox = 0, + Boundary, + Count, // Not a valid enum + }; + PlaneVisualizationMode CurrentPlaneVisualizationMode = PlaneVisualizationMode::Boundary; + + enum class VisualizationMode { + VisualizeAll = 0, + VisualizePlanesAndVolumes, + VisualizeMeshes, + Count, // Not a valid enum + }; + VisualizationMode CurrentVisualizationMode = VisualizationMode::VisualizeAll; + + bool ClearScene = false; + + XrSwapchain ColorSwapChain; + uint32_t SwapChainLength; + OVR::Vector3f StageBounds; + // Provided by SceneModelGl, which is not aware of VrApi or OpenXR + ovrAppRenderer AppRenderer; + + std::unordered_set UuidSet; + + bool DisplayPassthrough = true; + XrPassthroughFB Passthrough = XR_NULL_HANDLE; + XrPassthroughLayerFB PassthroughLayer = XR_NULL_HANDLE; + + XrBoundaryVisibilityMETA CurrentBoundaryVisibility = XR_BOUNDARY_VISIBILITY_NOT_SUPPRESSED_META; + +}; + +void ovrApp::Clear() { +#if defined(XR_USE_PLATFORM_ANDROID) + NativeWindow = NULL; + Resumed = false; +#endif // defined(XR_USE_PLATFORM_ANDROID) + ShouldExit = false; + Focused = false; + instance = XR_NULL_HANDLE; + Session = XR_NULL_HANDLE; + ViewportConfig = {}; + for (int i = 0; i < NUM_EYES; i++) { + ViewConfigurationView[i] = {}; + } + SystemId = XR_NULL_SYSTEM_ID; + HeadSpace = XR_NULL_HANDLE; + LocalSpace = XR_NULL_HANDLE; + StageSpace = XR_NULL_HANDLE; + SessionActive = false; + SwapInterval = 1; + for (int i = 0; i < ovrMaxLayerCount; i++) { + Layers[i] = {}; + } + LayerCount = 0; + CpuLevel = 2; + GpuLevel = 2; + MainThreadTid = 0; + RenderThreadTid = 0; + TouchPadDownLastFrame = false; + NextQueryType = QueryType::None; + IsQueryComplete = true; + ClearScene = false; + + Egl.Clear(); + AppRenderer.Clear(); +} + +void ovrApp::HandleSessionStateChanges(XrSessionState state) { + if (state == XR_SESSION_STATE_READY) { +#if defined(XR_USE_PLATFORM_ANDROID) + assert(Resumed); +#endif // defined(XR_USE_PLATFORM_ANDROID) + assert(SessionActive == false); + + XrSessionBeginInfo sessionBeginInfo = {XR_TYPE_SESSION_BEGIN_INFO}; + sessionBeginInfo.primaryViewConfigurationType = ViewportConfig.viewConfigurationType; + + XrResult result; + OXR(result = xrBeginSession(Session, &sessionBeginInfo)); + + SessionActive = (result == XR_SUCCESS); + +#if defined(XR_USE_PLATFORM_ANDROID) + // Set session state once we have entered VR mode and have a valid session object. + if (SessionActive) { + XrPerfSettingsLevelEXT cpuPerfLevel = XR_PERF_SETTINGS_LEVEL_SUSTAINED_HIGH_EXT; + switch (CpuLevel) { + case 0: + cpuPerfLevel = XR_PERF_SETTINGS_LEVEL_POWER_SAVINGS_EXT; + break; + case 1: + cpuPerfLevel = XR_PERF_SETTINGS_LEVEL_SUSTAINED_LOW_EXT; + break; + case 2: + cpuPerfLevel = XR_PERF_SETTINGS_LEVEL_SUSTAINED_HIGH_EXT; + break; + case 3: + cpuPerfLevel = XR_PERF_SETTINGS_LEVEL_BOOST_EXT; + break; + default: + ALOGE("Invalid CPU level %d", CpuLevel); + break; + } + + XrPerfSettingsLevelEXT gpuPerfLevel = XR_PERF_SETTINGS_LEVEL_SUSTAINED_HIGH_EXT; + switch (GpuLevel) { + case 0: + gpuPerfLevel = XR_PERF_SETTINGS_LEVEL_POWER_SAVINGS_EXT; + break; + case 1: + gpuPerfLevel = XR_PERF_SETTINGS_LEVEL_SUSTAINED_LOW_EXT; + break; + case 2: + gpuPerfLevel = XR_PERF_SETTINGS_LEVEL_SUSTAINED_HIGH_EXT; + break; + case 3: + gpuPerfLevel = XR_PERF_SETTINGS_LEVEL_BOOST_EXT; + break; + default: + ALOGE("Invalid GPU level %d", GpuLevel); + break; + } + + PFN_xrPerfSettingsSetPerformanceLevelEXT pfnPerfSettingsSetPerformanceLevelEXT = NULL; + OXR(xrGetInstanceProcAddr( + instance, + "xrPerfSettingsSetPerformanceLevelEXT", + (PFN_xrVoidFunction*)(&pfnPerfSettingsSetPerformanceLevelEXT))); + + OXR(pfnPerfSettingsSetPerformanceLevelEXT( + Session, XR_PERF_SETTINGS_DOMAIN_CPU_EXT, cpuPerfLevel)); + OXR(pfnPerfSettingsSetPerformanceLevelEXT( + Session, XR_PERF_SETTINGS_DOMAIN_GPU_EXT, gpuPerfLevel)); + + PFN_xrSetAndroidApplicationThreadKHR pfnSetAndroidApplicationThreadKHR = NULL; + OXR(xrGetInstanceProcAddr( + instance, + "xrSetAndroidApplicationThreadKHR", + (PFN_xrVoidFunction*)(&pfnSetAndroidApplicationThreadKHR))); + + OXR(pfnSetAndroidApplicationThreadKHR( + Session, XR_ANDROID_THREAD_TYPE_APPLICATION_MAIN_KHR, MainThreadTid)); + OXR(pfnSetAndroidApplicationThreadKHR( + Session, XR_ANDROID_THREAD_TYPE_RENDERER_MAIN_KHR, RenderThreadTid)); + } +#endif // defined(XR_USE_PLATFORM_ANDROID) + } else if (state == XR_SESSION_STATE_STOPPING) { +#if defined(XR_USE_PLATFORM_ANDROID) + assert(Resumed == false); +#endif // defined(XR_USE_PLATFORM_ANDROID) + assert(SessionActive); + + OXR(xrEndSession(Session)); + SessionActive = false; + } +} + +bool ovrApp::IsComponentSupported(XrSpace space, XrSpaceComponentTypeFB type) { + uint32_t numComponents = 0; + OXR(FunPtrs.xrEnumerateSpaceSupportedComponentsFB(space, 0, &numComponents, nullptr)); + std::vector components(numComponents); + OXR(FunPtrs.xrEnumerateSpaceSupportedComponentsFB( + space, numComponents, &numComponents, components.data())); + + bool supported = false; + for (uint32_t c = 0; c < numComponents; ++c) { + if (components[c] == type) { + supported = true; + break; + } + } + return supported; +} + +bool ovrApp::IsComponentEnabled(XrSpace space, XrSpaceComponentTypeFB type) { + XrSpaceComponentStatusFB status = {XR_TYPE_SPACE_COMPONENT_STATUS_FB, nullptr}; + OXR(FunPtrs.xrGetSpaceComponentStatusFB(space, type, &status)); + return (status.enabled && !status.changePending); +} + +void ovrApp::CollectRoomLayoutUuids(XrSpace space, std::unordered_set& uuidSet) { + assert(FunPtrs.xrGetSpaceRoomLayoutFB != nullptr); + + XrRoomLayoutFB roomLayout = {XR_TYPE_ROOM_LAYOUT_FB}; + std::vector wallUuids; + + // First call + OXR(FunPtrs.xrGetSpaceRoomLayoutFB(Session, space, &roomLayout)); + // If wallUuidCountOutput == 0, no walls in the component. The UUIDs of the ceiling and floor + // has been returned if available + if (roomLayout.wallUuidCountOutput != 0) { + // Second call + wallUuids.resize(roomLayout.wallUuidCountOutput); + roomLayout.wallUuidCapacityInput = wallUuids.size(); + roomLayout.wallUuids = wallUuids.data(); + OXR(FunPtrs.xrGetSpaceRoomLayoutFB(Session, space, &roomLayout)); + } + if (isValid(roomLayout.floorUuid)) { + uuidSet.insert(uuidToHexString(roomLayout.floorUuid)); + } + if (isValid(roomLayout.ceilingUuid)) { + uuidSet.insert(uuidToHexString(roomLayout.ceilingUuid)); + } + for (uint32_t i = 0; i < roomLayout.wallUuidCountOutput; i++) { + uuidSet.insert(uuidToHexString(roomLayout.wallUuids[i])); + } +} + +void ovrApp::CollectSpaceContainerUuids(XrSpace space, std::unordered_set& uuidSet) { + assert(FunPtrs.xrGetSpaceContainerFB != nullptr); + + XrSpaceContainerFB spaceContainer = {XR_TYPE_SPACE_CONTAINER_FB}; + // First call + OXR(FunPtrs.xrGetSpaceContainerFB(Session, space, &spaceContainer)); + if (spaceContainer.uuidCountOutput == 0) { + // No UUIDs + return; + } + // Second call + std::vector uuids(spaceContainer.uuidCountOutput); + spaceContainer.uuidCapacityInput = uuids.size(); + spaceContainer.uuids = uuids.data(); + OXR(FunPtrs.xrGetSpaceContainerFB(Session, space, &spaceContainer)); + + for (uint32_t i = 0; i < spaceContainer.uuidCountOutput; i++) { + uuidSet.insert(uuidToHexString(spaceContainer.uuids[i])); + } +} + +std::string GetSemanticLabels(ovrApp& app, const XrSpace space) { + static const std::string recognizedLabels = + "TABLE,COUCH,FLOOR,CEILING,WALL_FACE,WINDOW_FRAME,DOOR_FRAME,STORAGE,BED,SCREEN,LAMP,PLANT,WALL_ART,INVISIBLE_WALL_FACE,OTHER"; + const XrSemanticLabelsSupportInfoFB semanticLabelsSupportInfo = { + XR_TYPE_SEMANTIC_LABELS_SUPPORT_INFO_FB, + nullptr, + XR_SEMANTIC_LABELS_SUPPORT_MULTIPLE_SEMANTIC_LABELS_BIT_FB | XR_SEMANTIC_LABELS_SUPPORT_ACCEPT_DESK_TO_TABLE_MIGRATION_BIT_FB | + XR_SEMANTIC_LABELS_SUPPORT_ACCEPT_INVISIBLE_WALL_FACE_BIT_FB, + recognizedLabels.c_str()}; + + XrSemanticLabelsFB labels = {XR_TYPE_SEMANTIC_LABELS_FB, &semanticLabelsSupportInfo, 0}; + + if (!app.IsComponentEnabled(space, XR_SPACE_COMPONENT_TYPE_SEMANTIC_LABELS_FB)) { + return std::string(); + } + + // First call. + OXR(app.FunPtrs.xrGetSpaceSemanticLabelsFB(app.Session, space, &labels)); + // Second call + std::vector labelData(labels.bufferCountOutput); + labels.bufferCapacityInput = labelData.size(); + labels.buffer = labelData.data(); + OXR(app.FunPtrs.xrGetSpaceSemanticLabelsFB(app.Session, space, &labels)); + + return std::string(labels.buffer, labels.bufferCountOutput); +} + +bool UpdateOvrPlane(ovrApp& app, ovrPlane& plane) { + const auto labels = GetSemanticLabels(app, plane.Space); + const auto color = GetColorForSemanticLabels(labels); + + // Move windows, doors, and wall arts so they appear in front of the walls + if (labels.find("WINDOW_FRAME") != std::string::npos || + labels.find("DOOR_FRAME") != std::string::npos || + labels.find("WALL_ART") != std::string::npos) { + plane.SetZOffset(0.01f); // move 1cm on Z+ + } + + if (app.CurrentPlaneVisualizationMode == ovrApp::PlaneVisualizationMode::BoundingBox) { + XrResult res; + XrRect2Df boundingBox2D; + + assert(app.FunPtrs.xrGetSpaceBoundingBox2DFB != nullptr); + OXR(res = app.FunPtrs.xrGetSpaceBoundingBox2DFB(app.Session, plane.Space, &boundingBox2D)); + if (XR_FAILED(res)) { + ALOGE("Failed getting bounding box 2D!"); + return false; + } + plane.Update(boundingBox2D, color); + return true; + } else if (app.CurrentPlaneVisualizationMode == ovrApp::PlaneVisualizationMode::Boundary) { + XrResult res; + XrBoundary2DFB boundary2D = {XR_TYPE_BOUNDARY_2D_FB, nullptr, 0}; + + assert(app.FunPtrs.xrGetSpaceBoundary2DFB != nullptr); + // First call + OXR(res = app.FunPtrs.xrGetSpaceBoundary2DFB(app.Session, plane.Space, &boundary2D)); + if (XR_FAILED(res)) { + ALOGE("Failed getting boundary 2D!"); + return false; + } + // Second call + std::vector vertices(boundary2D.vertexCountOutput); + boundary2D.vertexCapacityInput = vertices.size(); + boundary2D.vertices = vertices.data(); + OXR(res = app.FunPtrs.xrGetSpaceBoundary2DFB(app.Session, plane.Space, &boundary2D)); + if (XR_FAILED(res)) { + ALOGE("Failed getting boundary 2D!"); + return false; + } + plane.Update(boundary2D, color); + return true; + } + return false; +} + +bool UpdateOvrVolume(ovrApp& app, ovrVolume& volume) { + XrResult res; + XrRect3DfFB boundingBox3D; + assert(app.FunPtrs.xrGetSpaceBoundingBox3DFB != nullptr); + OXR(res = app.FunPtrs.xrGetSpaceBoundingBox3DFB(app.Session, volume.Space, &boundingBox3D)); + if (XR_FAILED(res)) { + ALOGE("Failed getting bounding box 3D!"); + return false; + } + const auto labels = GetSemanticLabels(app, volume.Space); + + volume.Update(boundingBox3D, GetColorForSemanticLabels(labels)); + return true; +} + +bool UpdateOvrMesh(ovrApp& app, ovrMesh& mesh) { + XrResult res; + const XrSpaceTriangleMeshGetInfoMETA getInfo = {XR_TYPE_SPACE_TRIANGLE_MESH_GET_INFO_META}; + XrSpaceTriangleMeshMETA triangleMesh = {XR_TYPE_SPACE_TRIANGLE_MESH_META}; + assert(app.FunPtrs.xrGetSpaceTriangleMeshMETA != nullptr); + // First call + OXR(res = app.FunPtrs.xrGetSpaceTriangleMeshMETA(mesh.Space, &getInfo, &triangleMesh)); + if (XR_FAILED(res)) { + ALOGE("Failed getting triangle mesh!"); + return false; + } + // Second call + std::vector vertices(triangleMesh.vertexCountOutput); + std::vector indices(triangleMesh.indexCountOutput); + triangleMesh.vertexCapacityInput = vertices.size(); + triangleMesh.vertices = vertices.data(); + triangleMesh.indexCapacityInput = indices.size(); + triangleMesh.indices = indices.data(); + OXR(res = app.FunPtrs.xrGetSpaceTriangleMeshMETA(mesh.Space, &getInfo, &triangleMesh)); + if (XR_FAILED(res)) { + ALOGE("Failed getting triangle mesh!"); + return false; + } + mesh.Update(triangleMesh); + return true; +} + +void ovrApp::HandleXrEvents() { + XrEventDataBuffer eventDataBuffer = {}; + + // Poll for events + for (;;) { + XrEventDataBaseHeader* baseEventHeader = (XrEventDataBaseHeader*)(&eventDataBuffer); + baseEventHeader->type = XR_TYPE_EVENT_DATA_BUFFER; + baseEventHeader->next = NULL; + XrResult r; + OXR(r = xrPollEvent(instance, &eventDataBuffer)); + if (r != XR_SUCCESS) { + break; + } + + switch (baseEventHeader->type) { + case XR_TYPE_EVENT_DATA_EVENTS_LOST: + ALOGV("xrPollEvent: received XR_TYPE_EVENT_DATA_EVENTS_LOST event"); + break; + case XR_TYPE_EVENT_DATA_INSTANCE_LOSS_PENDING: + ALOGV("xrPollEvent: received XR_TYPE_EVENT_DATA_INSTANCE_LOSS_PENDING event"); + break; + case XR_TYPE_EVENT_DATA_INTERACTION_PROFILE_CHANGED: + ALOGV("xrPollEvent: received XR_TYPE_EVENT_DATA_INTERACTION_PROFILE_CHANGED event"); + break; + case XR_TYPE_EVENT_DATA_PERF_SETTINGS_EXT: { + const XrEventDataPerfSettingsEXT* perf_settings_event = + (XrEventDataPerfSettingsEXT*)(baseEventHeader); + ALOGV( + "xrPollEvent: received XR_TYPE_EVENT_DATA_PERF_SETTINGS_EXT event: type %d subdomain %d : level %d -> level %d", + perf_settings_event->type, + perf_settings_event->subDomain, + perf_settings_event->fromLevel, + perf_settings_event->toLevel); + } break; + case XR_TYPE_EVENT_DATA_REFERENCE_SPACE_CHANGE_PENDING: + ALOGV( + "xrPollEvent: received XR_TYPE_EVENT_DATA_REFERENCE_SPACE_CHANGE_PENDING event"); + break; + case XR_TYPE_EVENT_DATA_SESSION_STATE_CHANGED: { + const XrEventDataSessionStateChanged* session_state_changed_event = + (XrEventDataSessionStateChanged*)(baseEventHeader); + ALOGV( + "xrPollEvent: received XR_TYPE_EVENT_DATA_SESSION_STATE_CHANGED: %d for session %p at time %f", + session_state_changed_event->state, + (void*)session_state_changed_event->session, + FromXrTime(session_state_changed_event->time)); + + switch (session_state_changed_event->state) { + case XR_SESSION_STATE_FOCUSED: + Focused = true; + break; + case XR_SESSION_STATE_VISIBLE: + Focused = false; + break; + case XR_SESSION_STATE_READY: + case XR_SESSION_STATE_STOPPING: + HandleSessionStateChanges(session_state_changed_event->state); + break; + case XR_SESSION_STATE_EXITING: + ShouldExit = true; + break; + default: + break; + } + } break; + case XR_TYPE_EVENT_DATA_SPACE_SET_STATUS_COMPLETE_FB: { + ALOGV("xrPollEvent: received XR_TYPE_EVENT_DATA_SPACE_SET_STATUS_COMPLETE_FB"); + const XrEventDataSpaceSetStatusCompleteFB* setStatusComplete = + (XrEventDataSpaceSetStatusCompleteFB*)(baseEventHeader); + if (setStatusComplete->result == XR_SUCCESS) { + if (setStatusComplete->componentType == XR_SPACE_COMPONENT_TYPE_LOCATABLE_FB) { + if (IsComponentEnabled( + setStatusComplete->space, XR_SPACE_COMPONENT_TYPE_BOUNDED_2D_FB)) { + ovrPlane plane(setStatusComplete->space); + if (UpdateOvrPlane(*this, plane)) { + AppRenderer.Scene.Planes.emplace_back(plane); + } + } + if (IsComponentEnabled( + setStatusComplete->space, XR_SPACE_COMPONENT_TYPE_BOUNDED_3D_FB)) { + ovrVolume volume(setStatusComplete->space); + if (UpdateOvrVolume(*this, volume)) { + AppRenderer.Scene.Volumes.emplace_back(volume); + } + } + if (IsComponentEnabled( + setStatusComplete->space, XR_SPACE_COMPONENT_TYPE_TRIANGLE_MESH_META)) { + ovrMesh mesh(setStatusComplete->space); + if (UpdateOvrMesh(*this, mesh)) { + AppRenderer.Scene.Meshes.emplace_back(mesh); + } + } + } + } + } break; + case XR_TYPE_EVENT_DATA_SPACE_QUERY_RESULTS_AVAILABLE_FB: { + ALOGV("xrPollEvent: received XR_TYPE_EVENT_DATA_SPACE_QUERY_RESULTS_AVAILABLE_FB"); + const XrEventDataSpaceQueryResultsAvailableFB* resultsAvailable = + (XrEventDataSpaceQueryResultsAvailableFB*)(baseEventHeader); + + XrResult res = XR_SUCCESS; + + XrSpaceQueryResultsFB queryResults{XR_TYPE_SPACE_QUERY_RESULTS_FB}; + queryResults.resultCapacityInput = 0; + queryResults.resultCountOutput = 0; + queryResults.results = nullptr; + + res = FunPtrs.xrRetrieveSpaceQueryResultsFB( + Session, resultsAvailable->requestId, &queryResults); + if (res != XR_SUCCESS) { + ALOGV("xrRetrieveSpaceQueryResultsFB: error %u", res); + break; + } + + std::vector results(queryResults.resultCountOutput); + queryResults.resultCapacityInput = results.size(); + queryResults.resultCountOutput = 0; + queryResults.results = results.data(); + + res = FunPtrs.xrRetrieveSpaceQueryResultsFB( + Session, resultsAvailable->requestId, &queryResults); + if (res != XR_SUCCESS) { + ALOGV("xrRetrieveSpaceQueryResultsFB: error %u", res); + break; + } + + ALOGV("xrPollEvent: num of results received: %d", queryResults.resultCountOutput); + for (uint32_t i = 0; i < queryResults.resultCountOutput; ++i) { + auto& result = results[i]; + + if (IsComponentSupported(result.space, XR_SPACE_COMPONENT_TYPE_LOCATABLE_FB)) { + XrSpaceComponentStatusSetInfoFB request = { + XR_TYPE_SPACE_COMPONENT_STATUS_SET_INFO_FB, + nullptr, + XR_SPACE_COMPONENT_TYPE_LOCATABLE_FB, + true, + 0}; + XrAsyncRequestIdFB requestId; + res = + FunPtrs.xrSetSpaceComponentStatusFB(result.space, &request, &requestId); + if (res == XR_ERROR_SPACE_COMPONENT_STATUS_ALREADY_SET_FB) { + if (IsComponentEnabled( + result.space, XR_SPACE_COMPONENT_TYPE_BOUNDED_2D_FB)) { + ovrPlane plane(result.space); + if (UpdateOvrPlane(*this, plane)) { + AppRenderer.Scene.Planes.emplace_back(plane); + } + } + if (IsComponentEnabled( + result.space, XR_SPACE_COMPONENT_TYPE_BOUNDED_3D_FB)) { + ovrVolume volume(result.space); + if (UpdateOvrVolume(*this, volume)) { + AppRenderer.Scene.Volumes.emplace_back(volume); + } + } + if (IsComponentEnabled( + result.space, XR_SPACE_COMPONENT_TYPE_TRIANGLE_MESH_META)) { + ovrMesh mesh(result.space); + if (UpdateOvrMesh(*this, mesh)) { + AppRenderer.Scene.Meshes.emplace_back(mesh); + } + } + } + } + + if (QueryAllAnchorsInRoom) { + if (IsComponentSupported( + result.space, XR_SPACE_COMPONENT_TYPE_SPACE_CONTAINER_FB) && + IsComponentEnabled( + result.space, XR_SPACE_COMPONENT_TYPE_SPACE_CONTAINER_FB)) { + CollectSpaceContainerUuids(result.space, UuidSet); + } + } else { + if (IsComponentSupported( + result.space, XR_SPACE_COMPONENT_TYPE_ROOM_LAYOUT_FB) && + IsComponentEnabled( + result.space, XR_SPACE_COMPONENT_TYPE_ROOM_LAYOUT_FB)) { + CollectRoomLayoutUuids(result.space, UuidSet); + } + } + + } + } break; + case XR_TYPE_EVENT_DATA_SPACE_QUERY_COMPLETE_FB: { + ALOGV("xrPollEvent: received XR_TYPE_EVENT_DATA_SPACE_QUERY_COMPLETE_FB"); + IsQueryComplete = true; + } break; + case XR_TYPE_EVENT_DATA_SCENE_CAPTURE_COMPLETE_FB: { + ALOGV("xrPollEvent: received XR_TYPE_EVENT_DATA_SCENE_CAPTURE_COMPLETE_FB"); + + const XrEventDataSceneCaptureCompleteFB* captureResult = + (XrEventDataSceneCaptureCompleteFB*)(baseEventHeader); + if (captureResult->result == XR_SUCCESS) { + ALOGV( + "xrPollEvent: Scene capture (ID = %" PRIu64 ") succeeded", + captureResult->requestId); + } else { + ALOGE( + "xrPollEvent: Scene capture (ID = %" PRIu64 ") failed with an error %d", + captureResult->requestId, + captureResult->result); + } + } break; + case XR_TYPE_EVENT_DATA_BOUNDARY_VISIBILITY_CHANGED_META: { + ALOGV("xrPollEvent: received XR_TYPE_EVENT_DATA_BOUNDARY_VISIBILITY_CHANGED_META"); + + const XrEventDataBoundaryVisibilityChangedMETA* boundaryVisibilityChanged = + (XrEventDataBoundaryVisibilityChangedMETA*)(baseEventHeader); + CurrentBoundaryVisibility = boundaryVisibilityChanged->boundaryVisibility; + ALOGV( + "xrPollEvent: Boundary visibility changed to %s", + BoundaryVisibilityToString(CurrentBoundaryVisibility).c_str()); + } break; + default: + ALOGV("xrPollEvent: Unknown event"); + break; + } + } +} + +/* +================================================================================ + +Native Activity + +================================================================================ +*/ + +#if defined(ANDROID) +/** + * Process the next main command. + */ +static void app_handle_cmd(struct android_app* androidApp, int32_t cmd) { + ovrApp& app = *(ovrApp*)androidApp->userData; + + switch (cmd) { + // There is no APP_CMD_CREATE. The ANativeActivity creates the + // application thread from onCreate(). The application thread + // then calls android_main(). + case APP_CMD_START: { + ALOGV("onStart()"); + ALOGV(" APP_CMD_START"); + break; + } + case APP_CMD_RESUME: { + ALOGV("onResume()"); + ALOGV(" APP_CMD_RESUME"); + app.Resumed = true; + break; + } + case APP_CMD_PAUSE: { + ALOGV("onPause()"); + ALOGV(" APP_CMD_PAUSE"); + app.Resumed = false; + break; + } + case APP_CMD_STOP: { + ALOGV("onStop()"); + ALOGV(" APP_CMD_STOP"); + break; + } + case APP_CMD_DESTROY: { + ALOGV("onDestroy()"); + ALOGV(" APP_CMD_DESTROY"); + app.Clear(); + break; + } + case APP_CMD_INIT_WINDOW: { + ALOGV("surfaceCreated()"); + ALOGV(" APP_CMD_INIT_WINDOW"); + break; + } + case APP_CMD_TERM_WINDOW: { + ALOGV("surfaceDestroyed()"); + ALOGV(" APP_CMD_TERM_WINDOW"); + break; + } + } +} +#endif + +static bool QueryAllAnchors(ovrApp& app) { + XrSpaceQueryInfoFB queryInfo = { + XR_TYPE_SPACE_QUERY_INFO_FB, + nullptr, + XR_SPACE_QUERY_ACTION_LOAD_FB, + MAX_PERSISTENT_SPACES, + 0, + nullptr, + nullptr}; + + XrAsyncRequestIdFB requestId; + OXR(app.FunPtrs.xrQuerySpacesFB( + app.Session, (XrSpaceQueryInfoBaseHeaderFB*)&queryInfo, &requestId)); + return true; +} + +static bool QueryAllAnchorsWithSpecificComponentEnabled( + ovrApp& app, + const XrSpaceComponentTypeFB componentType) { + XrSpaceStorageLocationFilterInfoFB storageLocationFilterInfo = { + XR_TYPE_SPACE_STORAGE_LOCATION_FILTER_INFO_FB, nullptr, XR_SPACE_STORAGE_LOCATION_LOCAL_FB}; + + XrSpaceComponentFilterInfoFB componentFilterInfo = { + XR_TYPE_SPACE_COMPONENT_FILTER_INFO_FB, &storageLocationFilterInfo, componentType}; + + XrSpaceQueryInfoFB queryInfo = { + XR_TYPE_SPACE_QUERY_INFO_FB, + nullptr, + XR_SPACE_QUERY_ACTION_LOAD_FB, + MAX_PERSISTENT_SPACES, + 0, + (XrSpaceFilterInfoBaseHeaderFB*)&componentFilterInfo, + nullptr}; + + XrAsyncRequestIdFB requestId; + OXR(app.FunPtrs.xrQuerySpacesFB( + app.Session, (XrSpaceQueryInfoBaseHeaderFB*)&queryInfo, &requestId)); + return true; +} + +static bool QueryAnchorsByUuids(ovrApp& app) { + if (app.UuidSet.empty()) { + ALOGV("No UUID to query"); + return false; + } + + std::vector uuidsToQuery(app.UuidSet.size()); + int i = 0; + for (const std::string& uuid : app.UuidSet) { + hexStringToUuid(uuid, uuidsToQuery[i++]); + ALOGV("Added UUID %s to the query", uuid.c_str()); + } + + XrSpaceStorageLocationFilterInfoFB storageLocationFilterInfo = { + XR_TYPE_SPACE_STORAGE_LOCATION_FILTER_INFO_FB, nullptr, XR_SPACE_STORAGE_LOCATION_LOCAL_FB}; + + XrSpaceUuidFilterInfoFB uuidFilterInfo = { + XR_TYPE_SPACE_UUID_FILTER_INFO_FB, + &storageLocationFilterInfo, + (uint32_t)uuidsToQuery.size(), + uuidsToQuery.data()}; + + XrSpaceQueryInfoFB queryInfo = { + XR_TYPE_SPACE_QUERY_INFO_FB, + nullptr, + XR_SPACE_QUERY_ACTION_LOAD_FB, + MAX_PERSISTENT_SPACES, + 0, + (XrSpaceFilterInfoBaseHeaderFB*)&uuidFilterInfo, + nullptr}; + + XrAsyncRequestIdFB requestId; + OXR(app.FunPtrs.xrQuerySpacesFB( + app.Session, (XrSpaceQueryInfoBaseHeaderFB*)&queryInfo, &requestId)); + return true; +} + +void UpdateStageBounds(ovrApp& app) { + XrExtent2Df stageBounds = {}; + + XrResult result; + OXR(result = xrGetReferenceSpaceBoundsRect( + app.Session, XR_REFERENCE_SPACE_TYPE_STAGE, &stageBounds)); + if (result != XR_SUCCESS) { + ALOGV("Stage bounds query failed: using small defaults"); + stageBounds.width = 1.0f; + stageBounds.height = 1.0f; + } + + app.StageBounds = OVR::Vector3f(stageBounds.width * 0.5f, 1.0f, stageBounds.height * 0.5f); +} + +void UpdateScenePlanes(ovrApp& app, const XrFrameState& frameState) { + auto& scene = app.AppRenderer.Scene; + for (auto& plane : scene.Planes) { + XrSpaceLocation spaceLocation = {XR_TYPE_SPACE_LOCATION}; + XrResult res = XR_SUCCESS; + OXR(res = xrLocateSpace( + plane.Space, app.LocalSpace, frameState.predictedDisplayTime, &spaceLocation)); + if (XR_FAILED(res)) { + ALOGE("Failed getting anchor pose!"); + continue; + } + plane.SetPose(spaceLocation.pose); + } +} + +void CycleScenePlaneVisualizationMode(ovrApp& app) { + app.CurrentPlaneVisualizationMode = static_cast( + (static_cast(app.CurrentPlaneVisualizationMode) + 1) % + (static_cast(ovrApp::PlaneVisualizationMode::Count))); + auto& scene = app.AppRenderer.Scene; + for (auto& plane : scene.Planes) { + UpdateOvrPlane(app, plane); + } +} + +void CycleSceneVisualizationMode(ovrApp& app) { + app.CurrentVisualizationMode = static_cast( + (static_cast(app.CurrentVisualizationMode) + 1) % + (static_cast(ovrApp::VisualizationMode::Count))); + + const bool isVisiblePlanesAndVolumes = + app.CurrentVisualizationMode == ovrApp::VisualizationMode::VisualizeAll || + app.CurrentVisualizationMode == ovrApp::VisualizationMode::VisualizePlanesAndVolumes; + const bool isVisibleMeshes = + app.CurrentVisualizationMode == ovrApp::VisualizationMode::VisualizeAll || + app.CurrentVisualizationMode == ovrApp::VisualizationMode::VisualizeMeshes; + + auto& scene = app.AppRenderer.Scene; + for (auto& plane : scene.Planes) { + plane.SetVisible(isVisiblePlanesAndVolumes); + } + for (auto& volume : scene.Volumes) { + volume.SetVisible(isVisiblePlanesAndVolumes); + } + for (auto& mesh : scene.Meshes) { + mesh.SetVisible(isVisibleMeshes); + } +} + +void UpdateSceneVolumes(ovrApp& app, const XrFrameState& frameState) { + auto& scene = app.AppRenderer.Scene; + + for (auto& volume : scene.Volumes) { + XrSpaceLocation spaceLocation = {XR_TYPE_SPACE_LOCATION}; + XrResult res = XR_SUCCESS; + OXR(res = xrLocateSpace( + volume.Space, app.LocalSpace, frameState.predictedDisplayTime, &spaceLocation)); + if (XR_FAILED(res)) { + ALOGE("Failed getting anchor pose!"); + continue; + } + volume.SetPose(spaceLocation.pose); + } +} + +void UpdateSceneMeshes(ovrApp& app, const XrFrameState& frameState) { + auto& scene = app.AppRenderer.Scene; + + for (auto& mesh : scene.Meshes) { + XrSpaceLocation spaceLocation = {XR_TYPE_SPACE_LOCATION}; + XrResult res = XR_SUCCESS; + OXR(res = xrLocateSpace( + mesh.Space, app.LocalSpace, frameState.predictedDisplayTime, &spaceLocation)); + if (XR_FAILED(res)) { + ALOGE("Failed getting anchor pose!"); + continue; + } + mesh.SetPose(spaceLocation.pose); + } +} + +void CreatePassthrough(ovrApp& app) { + XrPassthroughCreateInfoFB ptci = {XR_TYPE_PASSTHROUGH_CREATE_INFO_FB}; + XrResult result; + OXR(result = app.FunPtrs.xrCreatePassthroughFB(app.Session, &ptci, &app.Passthrough)); + + if (XR_SUCCEEDED(result)) { + XrPassthroughLayerCreateInfoFB plci = {XR_TYPE_PASSTHROUGH_LAYER_CREATE_INFO_FB}; + plci.passthrough = app.Passthrough; + plci.purpose = XR_PASSTHROUGH_LAYER_PURPOSE_RECONSTRUCTION_FB; + OXR(app.FunPtrs.xrCreatePassthroughLayerFB(app.Session, &plci, &app.PassthroughLayer)); + } + + if (XR_SUCCEEDED(result)) { + XrPassthroughStyleFB style = {XR_TYPE_PASSTHROUGH_STYLE_FB}; + OXR(app.FunPtrs.xrPassthroughLayerResumeFB(app.PassthroughLayer)); + style.textureOpacityFactor = 0.5f; + style.edgeColor = {0.0f, 0.0f, 0.0f, 0.0f}; + OXR(app.FunPtrs.xrPassthroughLayerSetStyleFB(app.PassthroughLayer, &style)); + } + + if (result != XR_ERROR_FEATURE_UNSUPPORTED) { + OXR(result = app.FunPtrs.xrPassthroughStartFB(app.Passthrough)); + } +} + +void DestroyPassthrough(ovrApp& app) { + if (app.PassthroughLayer) { + OXR(app.FunPtrs.xrDestroyPassthroughLayerFB(app.PassthroughLayer)); + } + if (app.Passthrough) { + OXR(app.FunPtrs.xrDestroyPassthroughFB(app.Passthrough)); + } +} + +#if defined(XR_USE_PLATFORM_ANDROID) +/** + * This is the main entry point of a native application that is using + * android_native_app_glue. It runs in its own thread, with its own + * event loop for receiving input events and doing other things. + */ +void android_main(struct android_app* androidApp) { +#else +int main() { +#endif + +#if defined(XR_USE_PLATFORM_ANDROID) + ALOGV("----------------------------------------------------------------"); + ALOGV("android_app_entry()"); + ALOGV(" android_main()"); + + JNIEnv* Env; + (*androidApp->activity->vm).AttachCurrentThread(&Env, nullptr); + + // Note that AttachCurrentThread will reset the thread name. + prctl(PR_SET_NAME, (long)"OVR::Main", 0, 0, 0); +#endif + + ovrApp app; + app.Clear(); + + // Start the app with a query. + app.NextQueryType = ovrApp::QueryType::QueryAll; + +#if defined(XR_USE_PLATFORM_ANDROID) + PFN_xrInitializeLoaderKHR xrInitializeLoaderKHR; + xrGetInstanceProcAddr( + XR_NULL_HANDLE, "xrInitializeLoaderKHR", (PFN_xrVoidFunction*)&xrInitializeLoaderKHR); + if (xrInitializeLoaderKHR != NULL) { + XrLoaderInitInfoAndroidKHR loaderInitializeInfoAndroid = { + XR_TYPE_LOADER_INIT_INFO_ANDROID_KHR}; + loaderInitializeInfoAndroid.applicationVM = androidApp->activity->vm; + loaderInitializeInfoAndroid.applicationContext = androidApp->activity->clazz; + xrInitializeLoaderKHR((XrLoaderInitInfoBaseHeaderKHR*)&loaderInitializeInfoAndroid); + } +#endif + + // Log available layers. + { + XrResult result; + + PFN_xrEnumerateApiLayerProperties xrEnumerateApiLayerProperties; + OXR(result = xrGetInstanceProcAddr( + XR_NULL_HANDLE, + "xrEnumerateApiLayerProperties", + (PFN_xrVoidFunction*)&xrEnumerateApiLayerProperties)); + if (result != XR_SUCCESS) { + ALOGE("Failed to get xrEnumerateApiLayerProperties function pointer."); + exit(1); + } + + uint32_t layerCount = 0; + OXR(xrEnumerateApiLayerProperties(0, &layerCount, NULL)); + std::vector layerProperties( + layerCount, {XR_TYPE_API_LAYER_PROPERTIES}); + OXR(xrEnumerateApiLayerProperties(layerCount, &layerCount, layerProperties.data())); + + for (const auto& layer : layerProperties) { + ALOGV("Found layer %s", layer.layerName); + } + } + + // Check that the extensions required are present. + const char* const requiredExtensionNames[] = { +#if defined(XR_USE_GRAPHICS_API_OPENGL_ES) + XR_KHR_OPENGL_ES_ENABLE_EXTENSION_NAME, +#elif defined(XR_USE_GRAPHICS_API_OPENGL) + XR_KHR_OPENGL_ENABLE_EXTENSION_NAME, +#endif +#if defined(XR_USE_PLATFORM_ANDROID) + XR_EXT_PERFORMANCE_SETTINGS_EXTENSION_NAME, + XR_KHR_ANDROID_THREAD_SETTINGS_EXTENSION_NAME, +#endif + XR_FB_PASSTHROUGH_EXTENSION_NAME, + XR_FB_SPATIAL_ENTITY_EXTENSION_NAME, + XR_FB_SPATIAL_ENTITY_QUERY_EXTENSION_NAME, + XR_FB_SPATIAL_ENTITY_STORAGE_EXTENSION_NAME, + XR_FB_SPATIAL_ENTITY_CONTAINER_EXTENSION_NAME, + XR_META_SPATIAL_ENTITY_MESH_EXTENSION_NAME, + XR_META_BOUNDARY_VISIBILITY_EXTENSION_NAME, + XR_FB_SCENE_EXTENSION_NAME, +#if defined(ANDROID) + XR_FB_SCENE_CAPTURE_EXTENSION_NAME, +#endif + }; + const uint32_t numRequiredExtensions = + sizeof(requiredExtensionNames) / sizeof(requiredExtensionNames[0]); + + // Check the list of required extensions against what is supported by the runtime. + { + uint32_t numOutputExtensions = 0; + OXR(xrEnumerateInstanceExtensionProperties(nullptr, 0, &numOutputExtensions, nullptr)); + ALOGV("xrEnumerateInstanceExtensionProperties found %u extension(s).", numOutputExtensions); + + auto extensionProperties = + std::vector(numOutputExtensions, {XR_TYPE_EXTENSION_PROPERTIES}); + + OXR(xrEnumerateInstanceExtensionProperties( + NULL, numOutputExtensions, &numOutputExtensions, extensionProperties.data())); + for (uint32_t i = 0; i < numOutputExtensions; i++) { + ALOGV("Extension #%d = '%s'.", i, extensionProperties[i].extensionName); + } + + for (uint32_t i = 0; i < numRequiredExtensions; i++) { + bool found = false; + for (uint32_t j = 0; j < numOutputExtensions; j++) { + if (!strcmp(requiredExtensionNames[i], extensionProperties[j].extensionName)) { + ALOGV("Found required extension %s", requiredExtensionNames[i]); + found = true; + break; + } + } + if (!found) { + ALOGE("Failed to find required extension %s", requiredExtensionNames[i]); + exit(1); + } + } + } + + // Create the OpenXR instance. + XrApplicationInfo appInfo = {}; + strcpy(appInfo.applicationName, "SceneModelXr"); + appInfo.applicationVersion = 0; + strcpy(appInfo.engineName, "Oculus Mobile Sample"); + appInfo.engineVersion = 0; + appInfo.apiVersion = XR_MAKE_VERSION(1, 0, 34); + + XrInstanceCreateInfo instanceCreateInfo = {XR_TYPE_INSTANCE_CREATE_INFO}; + instanceCreateInfo.createFlags = 0; + instanceCreateInfo.applicationInfo = appInfo; + instanceCreateInfo.enabledApiLayerCount = 0; + instanceCreateInfo.enabledApiLayerNames = NULL; + instanceCreateInfo.enabledExtensionCount = numRequiredExtensions; + instanceCreateInfo.enabledExtensionNames = requiredExtensionNames; + + XrResult initResult; + OXR(initResult = xrCreateInstance(&instanceCreateInfo, &instance)); + if (initResult != XR_SUCCESS) { + ALOGE("Failed to create XR instance: %d.", initResult); + exit(1); + } + + XrInstanceProperties instanceInfo = {XR_TYPE_INSTANCE_PROPERTIES}; + OXR(xrGetInstanceProperties(instance, &instanceInfo)); + ALOGV( + "Runtime %s: Version : %u.%u.%u", + instanceInfo.runtimeName, + XR_VERSION_MAJOR(instanceInfo.runtimeVersion), + XR_VERSION_MINOR(instanceInfo.runtimeVersion), + XR_VERSION_PATCH(instanceInfo.runtimeVersion)); + + XrSystemGetInfo systemGetInfo = {XR_TYPE_SYSTEM_GET_INFO}; + systemGetInfo.formFactor = XR_FORM_FACTOR_HEAD_MOUNTED_DISPLAY; + + XrSystemId systemId; + OXR(initResult = xrGetSystem(instance, &systemGetInfo, &systemId)); + if (initResult != XR_SUCCESS) { + if (initResult == XR_ERROR_FORM_FACTOR_UNAVAILABLE) { + ALOGE("Failed to get system; the specified form factor is not available. Is your headset connected?"); + } else { + ALOGE("xrGetSystem failed, error %d", initResult); + } + exit(1); + } + + XrSystemProperties systemProperties = {XR_TYPE_SYSTEM_PROPERTIES}; + OXR(xrGetSystemProperties(instance, systemId, &systemProperties)); + + ALOGV( + "System Properties: Name=%s VendorId=%x", + systemProperties.systemName, + systemProperties.vendorId); + ALOGV( + "System Graphics Properties: MaxWidth=%d MaxHeight=%d MaxLayers=%d", + systemProperties.graphicsProperties.maxSwapchainImageWidth, + systemProperties.graphicsProperties.maxSwapchainImageHeight, + systemProperties.graphicsProperties.maxLayerCount); + ALOGV( + "System Tracking Properties: OrientationTracking=%s PositionTracking=%s", + systemProperties.trackingProperties.orientationTracking ? "True" : "False", + systemProperties.trackingProperties.positionTracking ? "True" : "False"); + + assert(ovrMaxLayerCount <= systemProperties.graphicsProperties.maxLayerCount); + +#if defined(XR_USE_GRAPHICS_API_OPENGL_ES) + // Get the graphics requirements. + PFN_xrGetOpenGLESGraphicsRequirementsKHR pfnGetOpenGLESGraphicsRequirementsKHR = NULL; + OXR(xrGetInstanceProcAddr( + instance, + "xrGetOpenGLESGraphicsRequirementsKHR", + (PFN_xrVoidFunction*)(&pfnGetOpenGLESGraphicsRequirementsKHR))); + + XrGraphicsRequirementsOpenGLESKHR graphicsRequirements = { + XR_TYPE_GRAPHICS_REQUIREMENTS_OPENGL_ES_KHR}; + OXR(pfnGetOpenGLESGraphicsRequirementsKHR(instance, systemId, &graphicsRequirements)); +#elif defined(XR_USE_GRAPHICS_API_OPENGL) + // Get the graphics requirements. + PFN_xrGetOpenGLGraphicsRequirementsKHR pfnGetOpenGLGraphicsRequirementsKHR = NULL; + OXR(xrGetInstanceProcAddr( + instance, + "xrGetOpenGLGraphicsRequirementsKHR", + (PFN_xrVoidFunction*)(&pfnGetOpenGLGraphicsRequirementsKHR))); + + XrGraphicsRequirementsOpenGLKHR graphicsRequirements = {}; + graphicsRequirements.type = XR_TYPE_GRAPHICS_REQUIREMENTS_OPENGL_KHR; + OXR(pfnGetOpenGLGraphicsRequirementsKHR(instance, systemId, &graphicsRequirements)); +#endif + + // Create the EGL Context + app.Egl.CreateContext(nullptr); + + // Check the graphics requirements. + int eglMajor = 0; + int eglMinor = 0; + glGetIntegerv(GL_MAJOR_VERSION, &eglMajor); + glGetIntegerv(GL_MINOR_VERSION, &eglMinor); + const XrVersion eglVersion = XR_MAKE_VERSION(eglMajor, eglMinor, 0); + if (eglVersion < graphicsRequirements.minApiVersionSupported || + eglVersion > graphicsRequirements.maxApiVersionSupported) { + ALOGE("GLES version %d.%d not supported", eglMajor, eglMinor); + exit(0); + } + + app.CpuLevel = CPU_LEVEL; + app.GpuLevel = GPU_LEVEL; +#if defined(ANDROID) + app.MainThreadTid = gettid(); +#else + app.MainThreadTid = (int)std::hash{}(std::this_thread::get_id()); +#endif + + app.SystemId = systemId; + + // Create the OpenXR Session. +#if defined(XR_USE_GRAPHICS_API_OPENGL_ES) + XrGraphicsBindingOpenGLESAndroidKHR graphicsBinding = {}; + graphicsBinding.type = XR_TYPE_GRAPHICS_BINDING_OPENGL_ES_ANDROID_KHR; + graphicsBinding.next = NULL; + graphicsBinding.display = app.Egl.Display; + graphicsBinding.config = app.Egl.Config; + graphicsBinding.context = app.Egl.Context; +#elif defined(XR_USE_GRAPHICS_API_OPENGL) + XrGraphicsBindingOpenGLWin32KHR graphicsBinding = {}; + graphicsBinding.type = XR_TYPE_GRAPHICS_BINDING_OPENGL_WIN32_KHR; + graphicsBinding.next = NULL; + graphicsBinding.hDC = app.Egl.hDC; + graphicsBinding.hGLRC = app.Egl.hGLRC; +#endif + + XrSessionCreateInfo sessionCreateInfo = {XR_TYPE_SESSION_CREATE_INFO}; + sessionCreateInfo.next = &graphicsBinding; + sessionCreateInfo.createFlags = 0; + sessionCreateInfo.systemId = app.SystemId; + + OXR(initResult = xrCreateSession(instance, &sessionCreateInfo, &app.Session)); + if (initResult != XR_SUCCESS) { + ALOGE("Failed to create XR session: %d.", initResult); + exit(1); + } + + // App only supports the primary stereo view config. + const XrViewConfigurationType supportedViewConfigType = + XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO; + + // Enumerate the viewport configurations. + uint32_t viewportConfigTypeCount = 0; + OXR(xrEnumerateViewConfigurations(instance, app.SystemId, 0, &viewportConfigTypeCount, NULL)); + + auto viewportConfigurationTypes = new XrViewConfigurationType[viewportConfigTypeCount]; + + OXR(xrEnumerateViewConfigurations( + instance, + app.SystemId, + viewportConfigTypeCount, + &viewportConfigTypeCount, + viewportConfigurationTypes)); + + ALOGV("Available Viewport Configuration Types: %d", viewportConfigTypeCount); + + for (uint32_t i = 0; i < viewportConfigTypeCount; i++) { + const XrViewConfigurationType viewportConfigType = viewportConfigurationTypes[i]; + + ALOGV( + "Viewport configuration type %d : %s", + viewportConfigType, + viewportConfigType == supportedViewConfigType ? "Selected" : ""); + + XrViewConfigurationProperties viewportConfig = {XR_TYPE_VIEW_CONFIGURATION_PROPERTIES}; + OXR(xrGetViewConfigurationProperties( + instance, app.SystemId, viewportConfigType, &viewportConfig)); + ALOGV( + "FovMutable=%s ConfigurationType %d", + viewportConfig.fovMutable ? "true" : "false", + viewportConfig.viewConfigurationType); + + uint32_t viewCount; + OXR(xrEnumerateViewConfigurationViews( + instance, app.SystemId, viewportConfigType, 0, &viewCount, NULL)); + + if (viewCount > 0) { + auto elements = new XrViewConfigurationView[viewCount]; + + for (uint32_t e = 0; e < viewCount; e++) { + elements[e].type = XR_TYPE_VIEW_CONFIGURATION_VIEW; + elements[e].next = NULL; + } + + OXR(xrEnumerateViewConfigurationViews( + instance, app.SystemId, viewportConfigType, viewCount, &viewCount, elements)); + + // Log the view config info for each view type for debugging purposes. + for (uint32_t e = 0; e < viewCount; e++) { + const XrViewConfigurationView* element = &elements[e]; + + ALOGV( + "Viewport [%d]: Recommended Width=%d Height=%d SampleCount=%d", + e, + element->recommendedImageRectWidth, + element->recommendedImageRectHeight, + element->recommendedSwapchainSampleCount); + + ALOGV( + "Viewport [%d]: Max Width=%d Height=%d SampleCount=%d", + e, + element->maxImageRectWidth, + element->maxImageRectHeight, + element->maxSwapchainSampleCount); + } + + // Cache the view config properties for the selected config type. + if (viewportConfigType == supportedViewConfigType) { + assert(viewCount == NUM_EYES); + for (uint32_t e = 0; e < viewCount; e++) { + app.ViewConfigurationView[e] = elements[e]; + } + } + + delete[] elements; + } else { + ALOGE("Empty viewport configuration type: %d", viewCount); + } + } + + delete[] viewportConfigurationTypes; + + // Get the viewport configuration info for the chosen viewport configuration type. + app.ViewportConfig.type = XR_TYPE_VIEW_CONFIGURATION_PROPERTIES; + + OXR(xrGetViewConfigurationProperties( + instance, app.SystemId, supportedViewConfigType, &app.ViewportConfig)); + + bool stageSupported = false; + + uint32_t numOutputSpaces = 0; + OXR(xrEnumerateReferenceSpaces(app.Session, 0, &numOutputSpaces, NULL)); + + auto referenceSpaces = new XrReferenceSpaceType[numOutputSpaces]; + + OXR(xrEnumerateReferenceSpaces( + app.Session, numOutputSpaces, &numOutputSpaces, referenceSpaces)); + + for (uint32_t i = 0; i < numOutputSpaces; i++) { + if (referenceSpaces[i] == XR_REFERENCE_SPACE_TYPE_STAGE) { + stageSupported = true; + break; + } + } + + delete[] referenceSpaces; + + // Create a space to the first path + XrReferenceSpaceCreateInfo spaceCreateInfo = {XR_TYPE_REFERENCE_SPACE_CREATE_INFO}; + spaceCreateInfo.referenceSpaceType = XR_REFERENCE_SPACE_TYPE_VIEW; + spaceCreateInfo.poseInReferenceSpace.orientation.w = 1.0f; + OXR(xrCreateReferenceSpace(app.Session, &spaceCreateInfo, &app.HeadSpace)); + + spaceCreateInfo.referenceSpaceType = XR_REFERENCE_SPACE_TYPE_LOCAL; + OXR(xrCreateReferenceSpace(app.Session, &spaceCreateInfo, &app.LocalSpace)); + + if (stageSupported) { + spaceCreateInfo.referenceSpaceType = XR_REFERENCE_SPACE_TYPE_STAGE; + spaceCreateInfo.poseInReferenceSpace.position.y = 0.0f; + OXR(xrCreateReferenceSpace(app.Session, &spaceCreateInfo, &app.StageSpace)); + ALOGV("Created stage space"); + } + + XrView projections[NUM_EYES]; + for (int eye = 0; eye < NUM_EYES; eye++) { + projections[eye] = XrView{XR_TYPE_VIEW}; + } + + GLenum format = GL_SRGB8_ALPHA8; + int width = app.ViewConfigurationView[0].recommendedImageRectWidth; + int height = app.ViewConfigurationView[0].recommendedImageRectHeight; + + XrSwapchainCreateInfo swapChainCreateInfo = {XR_TYPE_SWAPCHAIN_CREATE_INFO}; + swapChainCreateInfo.usageFlags = + XR_SWAPCHAIN_USAGE_SAMPLED_BIT | XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT; + swapChainCreateInfo.format = format; + swapChainCreateInfo.sampleCount = 1; + swapChainCreateInfo.width = width; + swapChainCreateInfo.height = height; + swapChainCreateInfo.faceCount = 1; + swapChainCreateInfo.arraySize = 2; + swapChainCreateInfo.mipCount = 1; + + // Create the swapchain. + OXR(xrCreateSwapchain(app.Session, &swapChainCreateInfo, &app.ColorSwapChain)); + OXR(xrEnumerateSwapchainImages(app.ColorSwapChain, 0, &app.SwapChainLength, nullptr)); +#if defined(XR_USE_GRAPHICS_API_OPENGL_ES) + auto images = new XrSwapchainImageOpenGLESKHR[app.SwapChainLength]; +#elif defined(XR_USE_GRAPHICS_API_OPENGL) + auto images = new XrSwapchainImageOpenGLKHR[app.SwapChainLength]; +#endif + // Populate the swapchain image array. + for (uint32_t i = 0; i < app.SwapChainLength; i++) { +#if defined(XR_USE_GRAPHICS_API_OPENGL_ES) + images[i] = {XR_TYPE_SWAPCHAIN_IMAGE_OPENGL_ES_KHR}; +#elif defined(XR_USE_GRAPHICS_API_OPENGL) + images[i] = {XR_TYPE_SWAPCHAIN_IMAGE_OPENGL_KHR}; +#endif + } + + OXR(xrEnumerateSwapchainImages( + app.ColorSwapChain, + app.SwapChainLength, + &app.SwapChainLength, + (XrSwapchainImageBaseHeader*)images)); + + auto colorTextures = new GLuint[app.SwapChainLength]; + for (uint32_t i = 0; i < app.SwapChainLength; i++) { + colorTextures[i] = GLuint(images[i].image); + } + + app.AppRenderer.Create( + format, width, height, NUM_MULTI_SAMPLES, app.SwapChainLength, colorTextures); + + delete[] images; + delete[] colorTextures; + +#if defined(ANDROID) + androidApp->userData = &app; + androidApp->onAppCmd = app_handle_cmd; +#endif + + bool stageBoundsDirty = true; + + SimpleXrInput* input = CreateSimpleXrInput(instance); + input->BeginSession(app.Session); + + OXR(xrGetInstanceProcAddr( + instance, + "xrCreatePassthroughFB", + (PFN_xrVoidFunction*)(&app.FunPtrs.xrCreatePassthroughFB))); + OXR(xrGetInstanceProcAddr( + instance, + "xrDestroyPassthroughFB", + (PFN_xrVoidFunction*)(&app.FunPtrs.xrDestroyPassthroughFB))); + OXR(xrGetInstanceProcAddr( + instance, + "xrCreatePassthroughLayerFB", + (PFN_xrVoidFunction*)(&app.FunPtrs.xrCreatePassthroughLayerFB))); + OXR(xrGetInstanceProcAddr( + instance, + "xrDestroyPassthroughLayerFB", + (PFN_xrVoidFunction*)(&app.FunPtrs.xrDestroyPassthroughLayerFB))); + OXR(xrGetInstanceProcAddr( + instance, + "xrPassthroughLayerResumeFB", + (PFN_xrVoidFunction*)(&app.FunPtrs.xrPassthroughLayerResumeFB))); + OXR(xrGetInstanceProcAddr( + instance, + "xrPassthroughLayerPauseFB", + (PFN_xrVoidFunction*)(&app.FunPtrs.xrPassthroughLayerPauseFB))); + OXR(xrGetInstanceProcAddr( + instance, + "xrPassthroughLayerSetStyleFB", + (PFN_xrVoidFunction*)(&app.FunPtrs.xrPassthroughLayerSetStyleFB))); + OXR(xrGetInstanceProcAddr( + instance, + "xrPassthroughStartFB", + (PFN_xrVoidFunction*)(&app.FunPtrs.xrPassthroughStartFB))); + OXR(xrGetInstanceProcAddr( + instance, + "xrPassthroughPauseFB", + (PFN_xrVoidFunction*)(&app.FunPtrs.xrPassthroughPauseFB))); + OXR(xrGetInstanceProcAddr( + instance, + "xrEnumerateSpaceSupportedComponentsFB", + (PFN_xrVoidFunction*)(&app.FunPtrs.xrEnumerateSpaceSupportedComponentsFB))); + OXR(xrGetInstanceProcAddr( + instance, + "xrGetSpaceComponentStatusFB", + (PFN_xrVoidFunction*)(&app.FunPtrs.xrGetSpaceComponentStatusFB))); + OXR(xrGetInstanceProcAddr( + instance, + "xrSetSpaceComponentStatusFB", + (PFN_xrVoidFunction*)(&app.FunPtrs.xrSetSpaceComponentStatusFB))); + OXR(xrGetInstanceProcAddr( + instance, "xrQuerySpacesFB", (PFN_xrVoidFunction*)(&app.FunPtrs.xrQuerySpacesFB))); + OXR(xrGetInstanceProcAddr( + instance, + "xrRetrieveSpaceQueryResultsFB", + (PFN_xrVoidFunction*)(&app.FunPtrs.xrRetrieveSpaceQueryResultsFB))); + OXR(xrGetInstanceProcAddr( + instance, + "xrGetSpaceBoundingBox2DFB", + (PFN_xrVoidFunction*)(&app.FunPtrs.xrGetSpaceBoundingBox2DFB))); + OXR(xrGetInstanceProcAddr( + instance, + "xrGetSpaceBoundingBox3DFB", + (PFN_xrVoidFunction*)(&app.FunPtrs.xrGetSpaceBoundingBox3DFB))); + OXR(xrGetInstanceProcAddr( + instance, + "xrGetSpaceSemanticLabelsFB", + (PFN_xrVoidFunction*)(&app.FunPtrs.xrGetSpaceSemanticLabelsFB))); + OXR(xrGetInstanceProcAddr( + instance, + "xrGetSpaceBoundary2DFB", + (PFN_xrVoidFunction*)(&app.FunPtrs.xrGetSpaceBoundary2DFB))); + OXR(xrGetInstanceProcAddr( + instance, + "xrGetSpaceRoomLayoutFB", + (PFN_xrVoidFunction*)(&app.FunPtrs.xrGetSpaceRoomLayoutFB))); + OXR(xrGetInstanceProcAddr( + instance, + "xrGetSpaceContainerFB", + (PFN_xrVoidFunction*)(&app.FunPtrs.xrGetSpaceContainerFB))); + OXR(xrGetInstanceProcAddr( + instance, + "xrGetSpaceTriangleMeshMETA", + (PFN_xrVoidFunction*)(&app.FunPtrs.xrGetSpaceTriangleMeshMETA))); +#if defined(ANDROID) + OXR(xrGetInstanceProcAddr( + instance, + "xrRequestSceneCaptureFB", + (PFN_xrVoidFunction*)(&app.FunPtrs.xrRequestSceneCaptureFB))); +#endif + OXR(xrGetInstanceProcAddr( + instance, + "xrRequestBoundaryVisibilityMETA", + (PFN_xrVoidFunction*)(&app.FunPtrs.xrRequestBoundaryVisibilityMETA))); + + CreatePassthrough(app); + + // Two values for left and right controllers. + std::array lastInputTimes = {0, 0}; +#if defined(XR_USE_PLATFORM_ANDROID) + while (androidApp->destroyRequested == 0) +#else + while (true) +#endif + { +#if defined(XR_USE_PLATFORM_ANDROID) + // Read all pending events. + for (;;) { + int events; + struct android_poll_source* source; + // If the timeout is zero, returns immediately without blocking. + // If the timeout is negative, waits indefinitely until an event appears. + const int timeoutMilliseconds = (app.Resumed == false && app.SessionActive == false && + androidApp->destroyRequested == 0) + ? -1 + : 0; + if (ALooper_pollAll(timeoutMilliseconds, NULL, &events, (void**)&source) < 0) { + break; + } + + // Process this event. + if (source != NULL) { + source->process(androidApp, source); + } + } +#elif defined(XR_USE_PLATFORM_WIN32) + MSG msg; + while (::PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) > 0) { + if (msg.message == WM_QUIT) { + app.ShouldExit = true; + } else { + ::TranslateMessage(&msg); + ::DispatchMessage(&msg); + } + } +#endif + + app.HandleXrEvents(); + + if (app.ShouldExit) { + break; + } + + if (app.SessionActive == false) { + continue; + } + + input->SyncActions(); + + // Create the scene if not yet created. + // The scene is created here to be able to show a loading icon. + if (!app.AppRenderer.Scene.IsCreated()) { + // Create the scene. + app.AppRenderer.Scene.Create(); + } + + if (stageBoundsDirty) { + UpdateStageBounds(app); + stageBoundsDirty = false; + } + + if (app.ClearScene) { + // This is called after the app starts, or after Button X is pressed. + for (auto& plane : app.AppRenderer.Scene.Planes) { + plane.Geometry.DestroyVAO(); + plane.Geometry.Destroy(); + } + app.AppRenderer.Scene.Planes.clear(); + + for (auto& volume : app.AppRenderer.Scene.Volumes) { + volume.Geometry.DestroyVAO(); + volume.Geometry.Destroy(); + } + app.AppRenderer.Scene.Volumes.clear(); + + for (auto& mesh : app.AppRenderer.Scene.Meshes) { + mesh.Geometry.DestroyVAO(); + mesh.Geometry.Destroy(); + } + app.AppRenderer.Scene.Meshes.clear(); + + app.ClearScene = false; + + app.UuidSet.clear(); + + + app.IsQueryComplete = true; + } + + if (app.NextQueryType != ovrApp::QueryType::None && app.IsQueryComplete) { + // Start the next query if there is a new query and the current query has completed + bool result = false; + if (app.NextQueryType == ovrApp::QueryType::QueryAll) { + result = QueryAllAnchors(app); + app.NextQueryType = ovrApp::QueryType::None; + } else if (app.NextQueryType == ovrApp::QueryType::QueryAllBounded2DEnabled) { + result = QueryAllAnchorsWithSpecificComponentEnabled( + app, XR_SPACE_COMPONENT_TYPE_BOUNDED_2D_FB); + app.NextQueryType = ovrApp::QueryType::None; + } else if (app.NextQueryType == ovrApp::QueryType::QueryAllRoomLayoutEnabled) { + result = QueryAllAnchorsWithSpecificComponentEnabled( + app, XR_SPACE_COMPONENT_TYPE_ROOM_LAYOUT_FB); + app.NextQueryType = ovrApp::QueryType::QueryByUuids; + } else if (app.NextQueryType == ovrApp::QueryType::QueryByUuids) { + result = QueryAnchorsByUuids(app); + app.NextQueryType = ovrApp::QueryType::None; + } + app.IsQueryComplete = result ? false : true; + } + + // NOTE: OpenXR does not use the concept of frame indices. Instead, + // XrWaitFrame returns the predicted display time. + XrFrameWaitInfo waitFrameInfo = {XR_TYPE_FRAME_WAIT_INFO}; + + XrFrameState frameState = {XR_TYPE_FRAME_STATE}; + + OXR(xrWaitFrame(app.Session, &waitFrameInfo, &frameState)); + + // Get the HMD pose, predicted for the middle of the time period during which + // the new eye images will be displayed. The number of frames predicted ahead + // depends on the pipeline depth of the engine and the synthesis rate. + // The better the prediction, the less black will be pulled in at the edges. + XrFrameBeginInfo beginFrameDesc = {XR_TYPE_FRAME_BEGIN_INFO}; + OXR(xrBeginFrame(app.Session, &beginFrameDesc)); + + XrSpaceLocation loc = {XR_TYPE_SPACE_LOCATION}; + OXR(xrLocateSpace(app.HeadSpace, app.LocalSpace, frameState.predictedDisplayTime, &loc)); + XrPosef xfLocalFromHead = loc.pose; + + XrViewState viewState = {XR_TYPE_VIEW_STATE}; + + XrViewLocateInfo projectionInfo = {XR_TYPE_VIEW_LOCATE_INFO}; + projectionInfo.viewConfigurationType = app.ViewportConfig.viewConfigurationType; + projectionInfo.displayTime = frameState.predictedDisplayTime; + projectionInfo.space = app.HeadSpace; + + uint32_t projectionCapacityInput = NUM_EYES; + uint32_t projectionCountOutput = projectionCapacityInput; + + OXR(xrLocateViews( + app.Session, + &projectionInfo, + &viewState, + projectionCapacityInput, + &projectionCountOutput, + projections)); + + UpdateScenePlanes(app, frameState); + + UpdateSceneVolumes(app, frameState); + + UpdateSceneMeshes(app, frameState); + + assert(input != nullptr); + // A Button: Refresh all by querying room entity that has room layout component enabled. + if (input->IsButtonAPressed()) { + app.ClearScene = true; + app.NextQueryType = ovrApp::QueryType::QueryAllRoomLayoutEnabled; + app.QueryAllAnchorsInRoom = true; + lastInputTimes[1] = frameState.predictedDisplayTime; + } + + // B Button: Refresh anchors by querying all that have Bounded 2D component enabled. + if (input->IsButtonBPressed()) { + app.ClearScene = true; + app.NextQueryType = ovrApp::QueryType::QueryAllBounded2DEnabled; + lastInputTimes[1] = frameState.predictedDisplayTime; + } + + // X Button: Refresh anchors by querying all. + if (input->IsButtonXPressed()) { + app.ClearScene = true; + app.NextQueryType = ovrApp::QueryType::QueryAll; + lastInputTimes[0] = frameState.predictedDisplayTime; + } + + // Y Button: Refresh all by querying room entity for room layout anchors + // (ceiling/floor/walls) only. + if (input->IsButtonYPressed()) { + app.ClearScene = true; + app.NextQueryType = ovrApp::QueryType::QueryAllRoomLayoutEnabled; + app.QueryAllAnchorsInRoom = false; + lastInputTimes[0] = frameState.predictedDisplayTime; + } + + // Left Index Trigger: Toggle plane visualization mode. + if (input->IsTriggerPressed(SimpleXrInput::Side_Left)) { + CycleScenePlaneVisualizationMode(app); + } + + // Left Thumb Click: Toggle visualization mode. + if (input->IsThumbClickPressed(SimpleXrInput::Side_Left)) { + CycleSceneVisualizationMode(app); + } + + +#if defined(ANDROID) + // Right Index Trigger: Request scene capture. + if (input->IsTriggerPressed(SimpleXrInput::Side_Right)) { + XrAsyncRequestIdFB requestId; + XrSceneCaptureRequestInfoFB request = {XR_TYPE_SCENE_CAPTURE_REQUEST_INFO_FB}; + request.requestByteCount = 0; + request.request = nullptr; + OXR(app.FunPtrs.xrRequestSceneCaptureFB(app.Session, &request, &requestId)); + lastInputTimes[1] = frameState.predictedDisplayTime; + } +#endif + + // Right Thumb Click: Toggle Passthrough. + if (input->IsThumbClickPressed(SimpleXrInput::Side_Right)) { + app.DisplayPassthrough = !app.DisplayPassthrough; + if (app.DisplayPassthrough) { + OXR(app.FunPtrs.xrPassthroughStartFB(app.Passthrough)); + } else { + OXR(app.FunPtrs.xrPassthroughPauseFB(app.Passthrough)); + } + lastInputTimes[1] = frameState.predictedDisplayTime; + } + + // Right Grip: Toggle boundary visibility request. + if (input->IsGripPressed(SimpleXrInput::Side_Right)) { + const XrBoundaryVisibilityMETA boundaryVisibilityRequested = + (app.CurrentBoundaryVisibility == XR_BOUNDARY_VISIBILITY_NOT_SUPPRESSED_META) ? + XR_BOUNDARY_VISIBILITY_SUPPRESSED_META : XR_BOUNDARY_VISIBILITY_NOT_SUPPRESSED_META; + OXR(app.FunPtrs.xrRequestBoundaryVisibilityMETA(app.Session, boundaryVisibilityRequested)); + lastInputTimes[1] = frameState.predictedDisplayTime; + } + + ovrAppRenderer::FrameIn frameIn; + uint32_t chainIndex = 0; + XrSwapchainImageAcquireInfo acquireInfo = {XR_TYPE_SWAPCHAIN_IMAGE_ACQUIRE_INFO, NULL}; + OXR(xrAcquireSwapchainImage(app.ColorSwapChain, &acquireInfo, &chainIndex)); + frameIn.SwapChainIndex = int(chainIndex); + + XrPosef xfLocalFromEye[NUM_EYES]; + + for (int eye = 0; eye < NUM_EYES; eye++) { + // LOG_POSE( "viewTransform", &projectionInfo.projections[eye].viewTransform ); + XrPosef xfHeadFromEye = projections[eye].pose; + XrPosef_Multiply(&xfLocalFromEye[eye], &xfLocalFromHead, &xfHeadFromEye); + + XrPosef xfEyeFromLocal; + XrPosef_Invert(&xfEyeFromLocal, &xfLocalFromEye[eye]); + + XrMatrix4x4f viewMat{}; + XrMatrix4x4f_CreateFromRigidTransform(&viewMat, &xfEyeFromLocal); + + const XrFovf fov = projections[eye].fov; + XrMatrix4x4f projMat; + XrMatrix4x4f_CreateProjectionFov(&projMat, GRAPHICS_OPENGL_ES, fov, 0.1f, 0.0f); + + frameIn.View[eye] = OvrFromXr(viewMat); + frameIn.Proj[eye] = OvrFromXr(projMat); + } + + if (app.StageSpace != XR_NULL_HANDLE) { + loc = {XR_TYPE_SPACE_LOCATION}; + OXR(xrLocateSpace( + app.StageSpace, app.LocalSpace, frameState.predictedDisplayTime, &loc)); + XrPosef xfLocalFromStage = loc.pose; + + frameIn.HasStage = true; + frameIn.StagePose = OvrFromXr(xfLocalFromStage); + frameIn.StageScale = app.StageBounds; + } else { + frameIn.HasStage = false; + } + + for (int controllerIndex = 0; controllerIndex < 2; ++controllerIndex) { + // If there is an input signal from this controller, pause rendring this controller + // for 0.1 second to give the user a sense of feedback. + frameIn.RenderController[controllerIndex] = + frameState.predictedDisplayTime > lastInputTimes[controllerIndex] + 100000000; + if (frameIn.RenderController[controllerIndex]) { + frameIn.ControllerPoses[controllerIndex] = input->FromControllerSpace( + controllerIndex == 0 ? SimpleXrInput::Side_Left : SimpleXrInput::Side_Right, + SimpleXrInput::Controller_Aim, + app.LocalSpace, + frameState.predictedDisplayTime); + } + } + + XrSwapchainImageWaitInfo waitInfo = {XR_TYPE_SWAPCHAIN_IMAGE_WAIT_INFO}; + waitInfo.timeout = 1000000000; /* timeout in nanoseconds */ + XrResult res = xrWaitSwapchainImage(app.ColorSwapChain, &waitInfo); + int retry = 0; + while (res == XR_TIMEOUT_EXPIRED) { + res = xrWaitSwapchainImage(app.ColorSwapChain, &waitInfo); + retry++; + ALOGV( + " Retry xrWaitSwapchainImage %d times due to XR_TIMEOUT_EXPIRED (duration %f seconds)", + retry, + waitInfo.timeout * (1E-9)); + } + + app.AppRenderer.RenderFrame(frameIn); + + XrSwapchainImageReleaseInfo releaseInfo = {XR_TYPE_SWAPCHAIN_IMAGE_RELEASE_INFO, NULL}; + OXR(xrReleaseSwapchainImage(app.ColorSwapChain, &releaseInfo)); + + // Set-up the compositor layers for this frame. + // NOTE: Multiple independent layers are allowed, but they need to be added + // in a depth consistent order. + + XrCompositionLayerProjectionView proj_views[2] = {}; + + app.LayerCount = 0; + memset(app.Layers, 0, sizeof(ovrCompositorLayer_Union) * ovrMaxLayerCount); + + if (app.DisplayPassthrough && app.PassthroughLayer != XR_NULL_HANDLE) { + XrCompositionLayerPassthroughFB passthroughLayer = {XR_TYPE_COMPOSITION_LAYER_PASSTHROUGH_FB}; + passthroughLayer.layerHandle = app.PassthroughLayer; + passthroughLayer.flags = XR_COMPOSITION_LAYER_BLEND_TEXTURE_SOURCE_ALPHA_BIT; + passthroughLayer.space = XR_NULL_HANDLE; + app.Layers[app.LayerCount++].Passthrough = passthroughLayer; + } + + XrCompositionLayerProjection proj_layer = {XR_TYPE_COMPOSITION_LAYER_PROJECTION}; + proj_layer.layerFlags = XR_COMPOSITION_LAYER_BLEND_TEXTURE_SOURCE_ALPHA_BIT; + proj_layer.layerFlags |= XR_COMPOSITION_LAYER_CORRECT_CHROMATIC_ABERRATION_BIT; + proj_layer.space = app.LocalSpace; + proj_layer.viewCount = NUM_EYES; + proj_layer.views = proj_views; + + for (int eye = 0; eye < NUM_EYES; eye++) { + XrCompositionLayerProjectionView& proj_view = proj_views[eye]; + proj_view = {XR_TYPE_COMPOSITION_LAYER_PROJECTION_VIEW}; + proj_view.pose = xfLocalFromEye[eye]; + proj_view.fov = projections[eye].fov; + + proj_view.subImage.swapchain = app.ColorSwapChain; + proj_view.subImage.imageRect.offset.x = 0; + proj_view.subImage.imageRect.offset.y = 0; + proj_view.subImage.imageRect.extent.width = width; + proj_view.subImage.imageRect.extent.height = height; + proj_view.subImage.imageArrayIndex = eye; + } + + app.Layers[app.LayerCount++].Projection = proj_layer; + + // Compose the layers for this frame. + const XrCompositionLayerBaseHeader* layers[ovrMaxLayerCount] = {}; + for (int i = 0; i < app.LayerCount; i++) { + layers[i] = (const XrCompositionLayerBaseHeader*)&app.Layers[i]; + } + + XrFrameEndInfo endFrameInfo = {XR_TYPE_FRAME_END_INFO}; + endFrameInfo.displayTime = frameState.predictedDisplayTime; + endFrameInfo.environmentBlendMode = XR_ENVIRONMENT_BLEND_MODE_OPAQUE; + endFrameInfo.layerCount = app.LayerCount; + endFrameInfo.layers = layers; + + OXR(xrEndFrame(app.Session, &endFrameInfo)); + } + + input->EndSession(); + + delete input; + + app.AppRenderer.Destroy(); + + DestroyPassthrough(app); + + OXR(xrDestroySwapchain(app.ColorSwapChain)); + OXR(xrDestroySpace(app.HeadSpace)); + OXR(xrDestroySpace(app.LocalSpace)); + // StageSpace is optional. + if (app.StageSpace != XR_NULL_HANDLE) { + OXR(xrDestroySpace(app.StageSpace)); + } + OXR(xrDestroySession(app.Session)); + + app.Egl.DestroyContext(); + + OXR(xrDestroyInstance(instance)); + +#if defined(XR_USE_PLATFORM_ANDROID) + (*androidApp->activity->vm).DetachCurrentThread(); +#endif // defined(XR_USE_PLATFORM_ANDROID) +} diff --git a/Samples/XrSamples/XrSceneModel/Src/SceneModelXr.h b/Samples/XrSamples/XrSceneModel/Src/SceneModelXr.h new file mode 100755 index 0000000..39ca735 --- /dev/null +++ b/Samples/XrSamples/XrSceneModel/Src/SceneModelXr.h @@ -0,0 +1,21 @@ +#pragma once + +#if defined(ANDROID) +#include +#include +#include +#include + +#define XR_USE_GRAPHICS_API_OPENGL_ES 1 +#define XR_USE_PLATFORM_ANDROID 1 +#elif defined(WIN32) +#include "Render/GlWrapperWin32.h" + +#include +#define XR_USE_GRAPHICS_API_OPENGL 1 +#define XR_USE_PLATFORM_WIN32 1 +#endif + +#include +#include +#include diff --git a/Samples/XrSamples/XrSceneModel/Src/SimpleXrInput.cpp b/Samples/XrSamples/XrSceneModel/Src/SimpleXrInput.cpp new file mode 100755 index 0000000..567a9e8 --- /dev/null +++ b/Samples/XrSamples/XrSceneModel/Src/SimpleXrInput.cpp @@ -0,0 +1,397 @@ +// Simple Xr Input + +#if defined(ANDROID) +#include +#endif + +#include +#include + +#include "SimpleXrInput.h" + +#if defined(ANDROID) +#define DEBUG 1 +#define OVR_LOG_TAG "SimpleXrInput" + +#define ALOGE(...) __android_log_print(ANDROID_LOG_ERROR, OVR_LOG_TAG, __VA_ARGS__) +#define ALOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, OVR_LOG_TAG, __VA_ARGS__) +#else +#define DEBUG 1 +#define ALOGE(...) \ + printf("ERROR: "); \ + printf(__VA_ARGS__); \ + printf("\n") +#define ALOGV(...) \ + printf("VERBOSE: "); \ + printf(__VA_ARGS__); \ + printf("\n") +#endif + +/* +================================================================================ + +OpenXR Utility Functions + +================================================================================ +*/ + +#if defined(DEBUG) +static void +OXR_CheckErrors(XrInstance instance, XrResult result, const char* function, bool failOnError) { + if (XR_FAILED(result)) { + char errorBuffer[XR_MAX_RESULT_STRING_SIZE]; + xrResultToString(instance, result, errorBuffer); + if (failOnError) { + ALOGE("OpenXR error: %s: %s\n", function, errorBuffer); + } else { + ALOGV("OpenXR error: %s: %s\n", function, errorBuffer); + } + } +} +#endif + +#if defined(DEBUG) +#define OXR(func) OXR_CheckErrors(instance, func, #func, true); +#else +#define OXR(func) OXR_CheckErrors(instance, func, #func, false); +#endif + +struct SimpleXrInputImpl : public SimpleXrInput { + static constexpr float kThumbStickDirectionThreshold = 0.5f; + + XrInstance instance = XR_NULL_HANDLE; + + XrPath leftHandPath; + XrPath rightHandPath; + + XrActionSet actionSet = XR_NULL_HANDLE; + XrAction a = XR_NULL_HANDLE; + XrAction b = XR_NULL_HANDLE; + XrAction x = XR_NULL_HANDLE; + XrAction y = XR_NULL_HANDLE; + XrAction aim = XR_NULL_HANDLE; + XrAction grip = XR_NULL_HANDLE; + XrAction trigger = XR_NULL_HANDLE; + XrAction squeeze = XR_NULL_HANDLE; + XrAction thumbClick = XR_NULL_HANDLE; + XrAction thumbStick = XR_NULL_HANDLE; + + XrSession session = XR_NULL_HANDLE; + + bool currentThumbStickUp = false; + bool currentThumbStickDown = false; + bool currentThumbStickLeft = false; + bool currentThumbStickRight = false; + bool isThumbStickUp = false; + bool isThumbStickDown = false; + bool isThumbStickLeft = false; + bool isThumbStickRight = false; + + struct AimGrip { + XrSpace aim; + XrSpace grip; + }; + + struct LeftRight { + AimGrip left; + AimGrip right; + }; + + LeftRight spaces; + uint32_t syncCount; + + SimpleXrInputImpl(XrInstance instance_) : instance(instance_) { + InitializeInput(); + } + + ~SimpleXrInputImpl() override { + if (actionSet != XR_NULL_HANDLE) { + OXR(xrDestroyActionSet(actionSet)); + } + } + + void InitializeInput() { + XrPath interactionProfile; + OXR(xrStringToPath( + instance, "/interaction_profiles/oculus/touch_controller", &interactionProfile)); + + OXR(xrStringToPath(instance, "/user/hand/left", &leftHandPath)); + OXR(xrStringToPath(instance, "/user/hand/right", &rightHandPath)); + XrPath handSubactionPaths[2] = {leftHandPath, rightHandPath}; + + actionSet = CreateActionSet(1, "main_action_set", "main ActionSet"); + a = CreateAction(actionSet, XR_ACTION_TYPE_BOOLEAN_INPUT, "a", "A button"); + b = CreateAction(actionSet, XR_ACTION_TYPE_BOOLEAN_INPUT, "b", "B button"); + x = CreateAction(actionSet, XR_ACTION_TYPE_BOOLEAN_INPUT, "x", "X button"); + y = CreateAction(actionSet, XR_ACTION_TYPE_BOOLEAN_INPUT, "y", "Y button"); + trigger = CreateAction( + actionSet, XR_ACTION_TYPE_BOOLEAN_INPUT, "trigger", nullptr, 2, handSubactionPaths); + squeeze = CreateAction( + actionSet, XR_ACTION_TYPE_BOOLEAN_INPUT, "squeeze", nullptr, 2, handSubactionPaths); + thumbClick = CreateAction( + actionSet, XR_ACTION_TYPE_BOOLEAN_INPUT, "thumb_click", nullptr, 2, handSubactionPaths); + + thumbStick = CreateAction( + actionSet, + XR_ACTION_TYPE_VECTOR2F_INPUT, + "thumb_stick", + nullptr, + 2, + handSubactionPaths); + aim = CreateAction( + actionSet, XR_ACTION_TYPE_POSE_INPUT, "aim_pose", nullptr, 2, handSubactionPaths); + grip = CreateAction( + actionSet, XR_ACTION_TYPE_POSE_INPUT, "grip_pose", nullptr, 2, handSubactionPaths); + + std::vector bindings; + bindings.push_back(Suggest(a, "/user/hand/right/input/a/click")); + bindings.push_back(Suggest(b, "/user/hand/right/input/b/click")); + bindings.push_back(Suggest(x, "/user/hand/left/input/x/click")); + bindings.push_back(Suggest(y, "/user/hand/left/input/y/click")); + bindings.push_back(Suggest(aim, "/user/hand/left/input/aim/pose")); + bindings.push_back(Suggest(aim, "/user/hand/right/input/aim/pose")); + bindings.push_back(Suggest(grip, "/user/hand/left/input/grip/pose")); + bindings.push_back(Suggest(grip, "/user/hand/right/input/grip/pose")); + bindings.push_back(Suggest(trigger, "/user/hand/left/input/trigger")); + bindings.push_back(Suggest(trigger, "/user/hand/right/input/trigger")); + bindings.push_back(Suggest(squeeze, "/user/hand/left/input/squeeze")); + bindings.push_back(Suggest(squeeze, "/user/hand/right/input/squeeze")); + bindings.push_back(Suggest(thumbClick, "/user/hand/left/input/thumbstick/click")); + bindings.push_back(Suggest(thumbClick, "/user/hand/right/input/thumbstick/click")); + bindings.push_back(Suggest(thumbStick, "/user/hand/left/input/thumbstick")); + bindings.push_back(Suggest(thumbStick, "/user/hand/right/input/thumbstick")); + + XrInteractionProfileSuggestedBinding suggestions = { + XR_TYPE_INTERACTION_PROFILE_SUGGESTED_BINDING}; + suggestions.interactionProfile = interactionProfile; + suggestions.suggestedBindings = &bindings[0]; + suggestions.countSuggestedBindings = bindings.size(); + OXR(xrSuggestInteractionProfileBindings(instance, &suggestions)); + + syncCount = 0; + } + + void BeginSession(XrSession session_) override { + if (syncCount != 0) { + ALOGV("SimpleXrInput::%s call order invalid", __func__); + return; + } + syncCount++; + session = session_; + // Attach actionSet to session + XrSessionActionSetsAttachInfo attachInfo = {XR_TYPE_SESSION_ACTION_SETS_ATTACH_INFO}; + attachInfo.countActionSets = 1; + attachInfo.actionSets = &actionSet; + OXR(xrAttachSessionActionSets(session, &attachInfo)); + } + + void EndSession() override { + ALOGV("SimpleXrInput::%s", __func__); + syncCount = 0; + } + + // Detect whether joy stick moves in a direction (up/down/left/right). It returns true once when + // the stick moves and comes back by checking its current state and input state. + bool IsJoyStickMoved(bool& currentState, bool inputState) const { + bool isMoved = false; + if (currentState != inputState) { + isMoved = inputState; + } + // Update the current state for the next check + currentState = inputState; + return isMoved; + } + + void SyncActions() override { + if (syncCount == 1) { + spaces.left.aim = CreateActionSpace(aim, leftHandPath); + spaces.right.aim = CreateActionSpace(aim, rightHandPath); + spaces.left.grip = CreateActionSpace(grip, leftHandPath); + spaces.right.grip = CreateActionSpace(grip, rightHandPath); + } + // sync action data + XrActiveActionSet activeActionSet = {}; + activeActionSet.actionSet = actionSet; + activeActionSet.subactionPath = XR_NULL_PATH; + + XrActionsSyncInfo syncInfo = {XR_TYPE_ACTIONS_SYNC_INFO}; + syncInfo.countActiveActionSets = 1; + syncInfo.activeActionSets = &activeActionSet; + OXR(xrSyncActions(session, &syncInfo)); + syncCount++; + + auto ThumbStickState = GetActionStateVector2(thumbStick); + isThumbStickUp = IsJoyStickMoved( + currentThumbStickUp, (ThumbStickState.currentState.y > kThumbStickDirectionThreshold)); + isThumbStickDown = IsJoyStickMoved( + currentThumbStickDown, ThumbStickState.currentState.y < -kThumbStickDirectionThreshold); + isThumbStickRight = IsJoyStickMoved( + currentThumbStickRight, ThumbStickState.currentState.x > kThumbStickDirectionThreshold); + isThumbStickLeft = IsJoyStickMoved( + currentThumbStickLeft, ThumbStickState.currentState.x < -kThumbStickDirectionThreshold); + } + + OVR::Posef FromControllerSpace( + Side side, + ControllerSpace controllerSpace, + XrSpace baseSpace, + XrTime atTime) override { + AimGrip& ag = side == Side_Left ? spaces.left : spaces.right; + XrSpace& space = controllerSpace == Controller_Aim ? ag.aim : ag.grip; + XrSpaceLocation loc = {XR_TYPE_SPACE_LOCATION}; + OXR(xrLocateSpace(space, baseSpace, atTime, &loc)); + return *reinterpret_cast(&loc.pose); + } + + bool IsActionPressed(XrAction action) const { + const auto state = GetActionStateBoolean(action); + return state.changedSinceLastSync == XR_TRUE && state.currentState == XR_TRUE; + } + + bool IsActionPressed(XrAction action, Side side) const { + const XrPath path = side == Side::Side_Left ? leftHandPath : rightHandPath; + const auto state = GetActionStateBoolean(action, path); + return state.changedSinceLastSync == XR_TRUE && state.currentState == XR_TRUE; + } + + bool IsButtonAPressed() const override { + return IsActionPressed(a); + } + + bool IsButtonBPressed() const override { + return IsActionPressed(b); + } + + bool IsButtonXPressed() const override { + return IsActionPressed(x); + } + + bool IsButtonYPressed() const override { + return IsActionPressed(y); + } + + bool IsTriggerPressed(Side side) const override { + return IsActionPressed(trigger, side); + } + + bool IsGripPressed(Side side) const override { + return IsActionPressed(squeeze, side); + } + + bool IsThumbClickPressed(Side side) const override { + return IsActionPressed(thumbClick, side); + } + + bool IsThumbStickUp() const override { + return isThumbStickUp; + } + + bool IsThumbStickDown() const override { + return isThumbStickDown; + } + + bool IsThumbStickLeft() const override { + return isThumbStickLeft; + } + + bool IsThumbStickRight() const override { + return isThumbStickRight; + } + + XrActionSet CreateActionSet(int priority, const char* name, const char* localizedName) { + XrActionSetCreateInfo asci = {XR_TYPE_ACTION_SET_CREATE_INFO}; + asci.priority = priority; + strcpy(asci.actionSetName, name); + strcpy(asci.localizedActionSetName, localizedName); + XrActionSet as = XR_NULL_HANDLE; + OXR(xrCreateActionSet(instance, &asci, &as)); + return as; + } + + XrAction CreateAction( + XrActionSet as, + XrActionType type, + const char* actionName, + const char* localizedName, + int countSubactionPaths = 0, + XrPath* subactionPaths = nullptr) { + ALOGV("SimpleXrInput::%s %s, %" PRIi32, __func__, actionName, countSubactionPaths); + + XrActionCreateInfo aci = {XR_TYPE_ACTION_CREATE_INFO}; + aci.actionType = type; + if (countSubactionPaths > 0) { + aci.countSubactionPaths = countSubactionPaths; + aci.subactionPaths = subactionPaths; + } + strcpy(aci.actionName, actionName); + strcpy(aci.localizedActionName, localizedName ? localizedName : actionName); + XrAction action = XR_NULL_HANDLE; + OXR(xrCreateAction(as, &aci, &action)); + return action; + } + + XrActionSuggestedBinding Suggest(XrAction action, const char* bindingString) { + XrActionSuggestedBinding asb; + asb.action = action; + XrPath bindingPath; + OXR(xrStringToPath(instance, bindingString, &bindingPath)); + asb.binding = bindingPath; + return asb; + } + + XrSpace CreateActionSpace(XrAction poseAction, XrPath subactionPath) { + XrActionSpaceCreateInfo asci = {XR_TYPE_ACTION_SPACE_CREATE_INFO}; + asci.action = poseAction; + asci.poseInActionSpace.orientation.w = 1.0f; + asci.subactionPath = subactionPath; + XrSpace actionSpace = XR_NULL_HANDLE; + OXR(xrCreateActionSpace(session, &asci, &actionSpace)); + return actionSpace; + } + + XrActionStateBoolean GetActionStateBoolean(XrAction action, XrPath subactionPath = XR_NULL_PATH) + const { + XrActionStateGetInfo getInfo = {XR_TYPE_ACTION_STATE_GET_INFO}; + getInfo.action = action; + getInfo.subactionPath = subactionPath; + + XrActionStateBoolean state = {XR_TYPE_ACTION_STATE_BOOLEAN}; + + OXR(xrGetActionStateBoolean(session, &getInfo, &state)); + return state; + } + + XrActionStateFloat GetActionStateFloat(XrAction action) const { + XrActionStateGetInfo getInfo = {XR_TYPE_ACTION_STATE_GET_INFO}; + getInfo.action = action; + + XrActionStateFloat state = {XR_TYPE_ACTION_STATE_FLOAT}; + + OXR(xrGetActionStateFloat(session, &getInfo, &state)); + return state; + } + + XrActionStateVector2f GetActionStateVector2(XrAction action) const { + XrActionStateGetInfo getInfo = {XR_TYPE_ACTION_STATE_GET_INFO}; + getInfo.action = action; + + XrActionStateVector2f state = {XR_TYPE_ACTION_STATE_VECTOR2F}; + + OXR(xrGetActionStateVector2f(session, &getInfo, &state)); + return state; + } + + bool ActionPoseIsActive(XrAction action, XrPath subactionPath) const { + XrActionStateGetInfo getInfo = {XR_TYPE_ACTION_STATE_GET_INFO}; + getInfo.action = action; + getInfo.subactionPath = subactionPath; + + XrActionStatePose state = {XR_TYPE_ACTION_STATE_POSE}; + OXR(xrGetActionStatePose(session, &getInfo, &state)); + return state.isActive != XR_FALSE; + } +}; + +SimpleXrInput* CreateSimpleXrInput(XrInstance instance_) { + return new SimpleXrInputImpl(instance_); +} diff --git a/Samples/XrSamples/XrSceneModel/Src/SimpleXrInput.h b/Samples/XrSamples/XrSceneModel/Src/SimpleXrInput.h new file mode 100755 index 0000000..783dda6 --- /dev/null +++ b/Samples/XrSamples/XrSceneModel/Src/SimpleXrInput.h @@ -0,0 +1,85 @@ +// Simple Xr Input + +#if defined(ANDROID) +#include + +#include +#include +#include +#include + +#define XR_USE_GRAPHICS_API_OPENGL_ES 1 +#define XR_USE_PLATFORM_ANDROID 1 +#elif defined(WIN32) +#include "Render/GlWrapperWin32.h" + +#include +#define XR_USE_GRAPHICS_API_OPENGL 1 +#define XR_USE_PLATFORM_WIN32 1 +#endif + +#include +#include +#include + +#include "OVR_Math.h" + +class SimpleXrInput { + public: + enum Side { + Side_Left = 0, + Side_Right = 1, + }; + + enum ControllerSpace { + Controller_Aim = 0, + Controller_Grip = 1, + }; + + virtual ~SimpleXrInput() {} + virtual void BeginSession(XrSession session_) = 0; + virtual void EndSession() = 0; + virtual void SyncActions() = 0; + + // Returns the pose that transforms the controller space into baseSpace + virtual OVR::Posef FromControllerSpace( + Side side, + ControllerSpace controllerSpace, + XrSpace baseSpace, + XrTime atTime) = 0; + + // Whether the A button is pressed + virtual bool IsButtonAPressed() const = 0; + + // Whether the B button is pressed + virtual bool IsButtonBPressed() const = 0; + + // Whether the X button is pressed + virtual bool IsButtonXPressed() const = 0; + + // Whether the Y button is pressed + virtual bool IsButtonYPressed() const = 0; + + // Whether the left/right trigger is pressed + virtual bool IsTriggerPressed(Side side) const = 0; + + // Whether the left/right grip is pressed + virtual bool IsGripPressed(Side side) const = 0; + + // Whether the left/right thumb click is pressed + virtual bool IsThumbClickPressed(Side side) const = 0; + + // Whether the left/right thumb stick is moved up + virtual bool IsThumbStickUp() const = 0; + + // Whether the left/right thumb stick is moved down + virtual bool IsThumbStickDown() const = 0; + + // Whether the left/right thumb stick is moved left + virtual bool IsThumbStickLeft() const = 0; + + // Whether the left/right thumb stick is moved right + virtual bool IsThumbStickRight() const = 0; +}; + +SimpleXrInput* CreateSimpleXrInput(XrInstance instance_); diff --git a/Samples/XrSamples/XrSceneModel/assets/donotedelete.txt b/Samples/XrSamples/XrSceneModel/assets/donotedelete.txt new file mode 100644 index 0000000..e69de29 diff --git a/Samples/XrSamples/XrSceneModel/java/com/oculus/NativeActivity.java b/Samples/XrSamples/XrSceneModel/java/com/oculus/NativeActivity.java new file mode 100644 index 0000000..46f908d --- /dev/null +++ b/Samples/XrSamples/XrSceneModel/java/com/oculus/NativeActivity.java @@ -0,0 +1,51 @@ +// Copyright (c) Facebook Technologies, LLC and its affiliates. All Rights reserved. +package com.oculus; + +import android.content.pm.PackageManager; +import android.os.Bundle; +import android.util.Log; + +/** + * When using NativeActivity, we currently need to handle loading of dependent shared libraries + * manually before a shared library that depends on them is loaded, since there is not currently a + * way to specify a shared library dependency for NativeActivity via the manifest meta-data. + * + *

The simplest method for doing so is to subclass NativeActivity with an empty activity that + * calls System.loadLibrary on the dependent libraries, which is unfortunate when the goal is to + * write a pure native C/C++ only Android activity. + * + *

A native-code only solution is to load the dependent libraries dynamically using dlopen(). + * However, there are a few considerations, see: + * https://groups.google.com/forum/#!msg/android-ndk/l2E2qh17Q6I/wj6s_6HSjaYJ + * + *

1. Only call dlopen() if you're sure it will succeed as the bionic dynamic linker will + * remember if dlopen failed and will not re-try a dlopen on the same lib a second time. + * + *

2. Must remember what libraries have already been loaded to avoid infinitely looping when + * libraries have circular dependencies. + */ +public class NativeActivity extends android.app.NativeActivity { + private static final String PERMISSION_USE_SCENE = "com.oculus.permission.USE_SCENE"; + private static final int REQUEST_CODE_PERMISSION_USE_SCENE = 1; + private static final String TAG = "XrSceneModel"; + + static { + System.loadLibrary("openxr_loader"); + System.loadLibrary("scenemodel"); + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + requestScenePermissionIfNeeded(); + } + + private void requestScenePermissionIfNeeded() { + Log.d(TAG, "requestScenePermissionIfNeeded"); + if (checkSelfPermission(PERMISSION_USE_SCENE) != PackageManager.PERMISSION_GRANTED) { + Log.d(TAG, "Permission has not been granted, request " + PERMISSION_USE_SCENE); + requestPermissions( + new String[] {PERMISSION_USE_SCENE}, REQUEST_CODE_PERMISSION_USE_SCENE); + } + } +} diff --git a/Samples/XrSamples/XrSpaceWarp/CMakeLists.txt b/Samples/XrSamples/XrSpaceWarp/CMakeLists.txt new file mode 100755 index 0000000..e97518b --- /dev/null +++ b/Samples/XrSamples/XrSpaceWarp/CMakeLists.txt @@ -0,0 +1,21 @@ +# Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved. + +project(xrspacewarp) + +if(NOT TARGET OpenXR::openxr_loader) + find_package(OpenXR REQUIRED) +endif() + +if(ANDROID) + file(GLOB_RECURSE SRC_FILES + Src/*.c + Src/*.cpp + ) + + add_library(${PROJECT_NAME} MODULE ${SRC_FILES}) + target_link_libraries(${PROJECT_NAME} PRIVATE native_activity_framework) + set_target_properties(${PROJECT_NAME} PROPERTIES LINK_FLAGS "-u ANativeActivity_onCreate") + + # Common across platforms + target_include_directories(${PROJECT_NAME} PRIVATE Src) +endif() diff --git a/Samples/XrSamples/XrSpaceWarp/Projects/Android/AndroidManifest.xml b/Samples/XrSamples/XrSpaceWarp/Projects/Android/AndroidManifest.xml new file mode 100755 index 0000000..2523870 --- /dev/null +++ b/Samples/XrSamples/XrSpaceWarp/Projects/Android/AndroidManifest.xml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Samples/XrSamples/XrSpaceWarp/Projects/Android/build.bat b/Samples/XrSamples/XrSpaceWarp/Projects/Android/build.bat new file mode 100755 index 0000000..facf79f --- /dev/null +++ b/Samples/XrSamples/XrSpaceWarp/Projects/Android/build.bat @@ -0,0 +1,29 @@ +@rem Only edit the master copy of this file in SDK_ROOT/bin/scripts/build/perproject + +@setlocal enableextensions enabledelayedexpansion + +@if not exist "build.gradle" @echo Build script must be executed from project directory. & goto :Abort + +@set P=.. + +:TryAgain + +@rem @echo P = %P% + +@if exist "%P%\bin\scripts\build\build.py.bat" goto :Found + +@if exist "%P%\bin\scripts\build" @echo "Could not find build.py.bat" & goto :Abort + +@set P=%P%\.. + +@goto :TryAgain + +:Found + +@set P=%P%\bin\scripts\build +@call %P%\build.py.bat %1 %2 %3 %4 %5 +@goto :End + +:Abort + +:End diff --git a/Samples/XrSamples/XrSpaceWarp/Projects/Android/build.gradle b/Samples/XrSamples/XrSpaceWarp/Projects/Android/build.gradle new file mode 100755 index 0000000..c6bad4e --- /dev/null +++ b/Samples/XrSamples/XrSpaceWarp/Projects/Android/build.gradle @@ -0,0 +1,78 @@ +// Top-level build file where you can add configuration options common to all sub-projects/modules. +buildscript { + repositories { + google() + mavenCentral() + } + dependencies { + classpath "com.android.tools.build:gradle:7.0.3" + } +} + +repositories { + google() + mavenCentral() +} + +apply plugin: 'com.android.application' + +android { + compileSdk 32 + + defaultConfig { + applicationId "com.oculus.sdk.xrspacewarp" + minSdk 26 + targetSdk 32 + versionCode 1 + versionName "1.0" + + // override app plugin abiFilters for 64-bit support + externalNativeBuild { + ndk { + abiFilters 'arm64-v8a' + } + ndkBuild { + abiFilters 'arm64-v8a' + } + cmake { + targets "xrspacewarp" + } + } + } + + sourceSets { + main { + manifest.srcFile 'AndroidManifest.xml' + java.srcDirs = ['../../java'] + assets.srcDirs = ['../../assets'] + res.srcDirs = ['../../res'] + } + } + + buildTypes { + debug { + debuggable true + } + + release { + debuggable false + } + } + + externalNativeBuild { + cmake { + path file('../../../../CMakeLists.txt') + } + } + + // Enable prefab support for the OpenXR AAR + buildFeatures { + prefab true + } +} + +dependencies { + // Package/application AndroidManifest.xml properties, plus headers and libraries + // exposed to CMake + implementation 'org.khronos.openxr:openxr_loader_for_android:1.1.36' +} diff --git a/Samples/XrSamples/XrSpaceWarp/Projects/Android/build.py b/Samples/XrSamples/XrSpaceWarp/Projects/Android/build.py new file mode 100755 index 0000000..d4b6e58 --- /dev/null +++ b/Samples/XrSamples/XrSpaceWarp/Projects/Android/build.py @@ -0,0 +1,35 @@ +#!/usr/bin/python +# (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +# This first bit of code is common bootstrapping code +# to determine the SDK root, and to set up the import +# path for additional python code. + +# begin bootstrap +import os +import sys + + +def init(): + root = os.path.realpath(os.path.dirname(os.path.realpath(__file__))) + os.chdir(root) # make sure we are always executing from the project directory + while os.path.isdir(os.path.join(root, "bin/scripts/build")) == False: + root = os.path.realpath(os.path.join(root, "..")) + if ( + len(root) <= 5 + ): # Should catch both Posix and Windows root directories (e.g. '/' and 'C:\') + print("Unable to find SDK root. Exiting.") + sys.exit(1) + root = os.path.abspath(root) + os.environ["OCULUS_SDK_PATH"] = root + sys.path.append(root + "/bin/scripts/build") + + +init() +import ovrbuild + +ovrbuild.init() +# end bootstrap + + +ovrbuild.build() diff --git a/Samples/XrSamples/XrSpaceWarp/Projects/Android/gradle.properties b/Samples/XrSamples/XrSpaceWarp/Projects/Android/gradle.properties new file mode 100644 index 0000000..3e927b1 --- /dev/null +++ b/Samples/XrSamples/XrSpaceWarp/Projects/Android/gradle.properties @@ -0,0 +1,21 @@ +# Project-wide Gradle settings. +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. More details, visit +# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects +# org.gradle.parallel=true +# AndroidX package structure to make it clearer which packages are bundled with the +# Android operating system, and which are packaged with your app's APK +# https://developer.android.com/topic/libraries/support-library/androidx-rn +android.useAndroidX=true +# Enables namespacing of each library's R class so that its R class includes only the +# resources declared in the library itself and none from the library's dependencies, +# thereby reducing the size of the R class for that library +android.nonTransitiveRClass=true \ No newline at end of file diff --git a/Samples/XrSamples/XrSpaceWarp/Projects/Android/gradle/wrapper/gradle-wrapper.jar b/Samples/XrSamples/XrSpaceWarp/Projects/Android/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..7454180 Binary files /dev/null and b/Samples/XrSamples/XrSpaceWarp/Projects/Android/gradle/wrapper/gradle-wrapper.jar differ diff --git a/Samples/XrSamples/XrSpaceWarp/Projects/Android/gradle/wrapper/gradle-wrapper.properties b/Samples/XrSamples/XrSpaceWarp/Projects/Android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..ffed3a2 --- /dev/null +++ b/Samples/XrSamples/XrSpaceWarp/Projects/Android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/Samples/XrSamples/XrSpaceWarp/Projects/Android/gradlew b/Samples/XrSamples/XrSpaceWarp/Projects/Android/gradlew new file mode 100755 index 0000000..a69d9cb --- /dev/null +++ b/Samples/XrSamples/XrSpaceWarp/Projects/Android/gradlew @@ -0,0 +1,240 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# 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 +# +# https://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. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit + +APP_NAME="Gradle" +APP_BASE_NAME=${0##*/} + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/Samples/XrSamples/XrSpaceWarp/Projects/Android/gradlew.bat b/Samples/XrSamples/XrSpaceWarp/Projects/Android/gradlew.bat new file mode 100755 index 0000000..f127cfd --- /dev/null +++ b/Samples/XrSamples/XrSpaceWarp/Projects/Android/gradlew.bat @@ -0,0 +1,91 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/Samples/XrSamples/XrSpaceWarp/Projects/Android/settings.gradle b/Samples/XrSamples/XrSpaceWarp/Projects/Android/settings.gradle new file mode 100755 index 0000000..df1a880 --- /dev/null +++ b/Samples/XrSamples/XrSpaceWarp/Projects/Android/settings.gradle @@ -0,0 +1 @@ +rootProject.name = "XrSpaceWarp" diff --git a/Samples/XrSamples/XrSpaceWarp/README.md b/Samples/XrSamples/XrSpaceWarp/README.md new file mode 100644 index 0000000..06d506b --- /dev/null +++ b/Samples/XrSamples/XrSpaceWarp/README.md @@ -0,0 +1,7 @@ +# OpenXR Space Warp Sample + +## Overview +`XR_FB_space_warp`: By feeding application-generated motion vector and depth buffer images, the runtime can perform high-quality frame extrapolation and reprojection. This allows applications to run at half the fps while still providing a smooth experience to users. + +## The Sample +This sample shows the difference between enabling and disabling space warp. Users can pull the trigger to disable space warp and observe the difference. diff --git a/Samples/XrSamples/XrSpaceWarp/Src/XrSpaceWarp.c b/Samples/XrSamples/XrSpaceWarp/Src/XrSpaceWarp.c new file mode 100755 index 0000000..d11faf5 --- /dev/null +++ b/Samples/XrSamples/XrSpaceWarp/Src/XrSpaceWarp.c @@ -0,0 +1,3725 @@ +/************************************************************************************ + +Filename : XrSpaceWarp.c +Content : For demostrate Application space warp API usage +Created : +Authors : + +Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved. + +*************************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#if !defined(EGL_OPENGL_ES3_BIT_KHR) +#define EGL_OPENGL_ES3_BIT_KHR 0x0040 +#endif + +// EXT_texture_border_clamp +#ifndef GL_CLAMP_TO_BORDER +#define GL_CLAMP_TO_BORDER 0x812D +#endif + +#ifndef GL_TEXTURE_BORDER_COLOR +#define GL_TEXTURE_BORDER_COLOR 0x1004 +#endif + +#if !defined(GL_EXT_multisampled_render_to_texture) +typedef void(GL_APIENTRY* PFNGLRENDERBUFFERSTORAGEMULTISAMPLEEXTPROC)( + GLenum target, + GLsizei samples, + GLenum internalformat, + GLsizei width, + GLsizei height); +typedef void(GL_APIENTRY* PFNGLFRAMEBUFFERTEXTURE2DMULTISAMPLEEXTPROC)( + GLenum target, + GLenum attachment, + GLenum textarget, + GLuint texture, + GLint level, + GLsizei samples); +#endif + +// GL_EXT_texture_cube_map_array +#if !defined(GL_TEXTURE_CUBE_MAP_ARRAY) +#define GL_TEXTURE_CUBE_MAP_ARRAY 0x9009 +#endif + +#define XR_USE_GRAPHICS_API_OPENGL_ES 1 +#define XR_USE_PLATFORM_ANDROID 1 +#include +#include + +#include + +#define MATH_PI 3.14159265358979323846f + +#define DEBUG 1 +#define OVR_LOG_TAG "XrSpaceWarp" + +// AppSpaceWarp : Enable space warp by default +// You can disable space warp by holding controller. +// Holding trigger : normal 72 fps +// Releasing trigger: SpaceWarp 36 fps mode +static bool EnableSpaceWarp = true; +static bool ShowQuadLayer = true; +static bool QuadLayerFollowing = true; +static bool EnableDeterministicAnimation = false; + +// Store camera position and rotation +static XrVector3f cameraPos = {0.0f, 0.0f, 0.0f}; +static float rotationY = 0.0f; + +// Frame index for deterministic behavior +static long long frameIndex = 0; + +#define ALOGE(...) __android_log_print(ANDROID_LOG_ERROR, OVR_LOG_TAG, __VA_ARGS__) +#define ALOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, OVR_LOG_TAG, __VA_ARGS__) + +static const int CPU_LEVEL = 2; +static const int GPU_LEVEL = 3; +static const int NUM_MULTI_SAMPLES = 4; + +static bool GetSystemPropertyInt(const char* keyName, int* val) { + char value[128]; + if (__system_property_get(keyName, value) > 0) { + *val = atoi(value); + return true; + } + return false; +} + +typedef union { + XrCompositionLayerProjection Projection; + XrCompositionLayerQuad Quad; +} ovrCompositorLayer_Union; + +enum { ovrMaxLayerCount = 16 }; +enum { ovrMaxNumEyes = 2 }; + +enum { CUBE_COUNT_DIMENSION = 8 }; +enum { CUBE_SCALE = 10 }; +enum { CUBE_NUM_INSTANCES = CUBE_COUNT_DIMENSION * CUBE_COUNT_DIMENSION * CUBE_COUNT_DIMENSION }; + +static const float CUBE_ANIMATION_SPEED = 8.0f; +static const float CUBE_ANIMATION_DISTANCE = 3.0f; +static const float CUBE_ANIMATION_X_OFFSET = 0.7f; +static const float CUBE_ANIMATION_Y_OFFSET = 0.3f; +static const float CUBE_ANIMATION_Z_OFFSET = 0.17f; +static const float FRAME_BASED_CUBE_SPEED_FACTOR = 0.04f; // Factor to roughly match time based animation speed + +// Forward declarations +XrInstance ovrApp_GetInstance(); + +/* +================================================================================ + +OpenXR Utility Functions + +================================================================================ +*/ + +#if defined(DEBUG) +static void +OXR_CheckErrors(XrInstance instance, XrResult result, const char* function, bool failOnError) { + if (XR_FAILED(result)) { + char errorBuffer[XR_MAX_RESULT_STRING_SIZE]; + xrResultToString(instance, result, errorBuffer); + if (failOnError) { + ALOGE("OpenXR error: %s: %s\n", function, errorBuffer); + } else { + ALOGV("OpenXR error: %s: %s\n", function, errorBuffer); + } + } +} +#endif + +#if defined(DEBUG) +#define OXR(func) OXR_CheckErrors(ovrApp_GetInstance(), func, #func, true); +#else +#define OXR(func) OXR_CheckErrors(ovrApp_GetInstance(), func, #func, false); +#endif + +/* +================================================================================ + +OpenGL-ES Utility Functions + +================================================================================ +*/ + +static const char* EglErrorString(const EGLint error) { + switch (error) { + case EGL_SUCCESS: + return "EGL_SUCCESS"; + case EGL_NOT_INITIALIZED: + return "EGL_NOT_INITIALIZED"; + case EGL_BAD_ACCESS: + return "EGL_BAD_ACCESS"; + case EGL_BAD_ALLOC: + return "EGL_BAD_ALLOC"; + case EGL_BAD_ATTRIBUTE: + return "EGL_BAD_ATTRIBUTE"; + case EGL_BAD_CONTEXT: + return "EGL_BAD_CONTEXT"; + case EGL_BAD_CONFIG: + return "EGL_BAD_CONFIG"; + case EGL_BAD_CURRENT_SURFACE: + return "EGL_BAD_CURRENT_SURFACE"; + case EGL_BAD_DISPLAY: + return "EGL_BAD_DISPLAY"; + case EGL_BAD_SURFACE: + return "EGL_BAD_SURFACE"; + case EGL_BAD_MATCH: + return "EGL_BAD_MATCH"; + case EGL_BAD_PARAMETER: + return "EGL_BAD_PARAMETER"; + case EGL_BAD_NATIVE_PIXMAP: + return "EGL_BAD_NATIVE_PIXMAP"; + case EGL_BAD_NATIVE_WINDOW: + return "EGL_BAD_NATIVE_WINDOW"; + case EGL_CONTEXT_LOST: + return "EGL_CONTEXT_LOST"; + default: + return "unknown"; + } +} + +static const char* GlFrameBufferStatusString(GLenum status) { + switch (status) { + case GL_FRAMEBUFFER_UNDEFINED: + return "GL_FRAMEBUFFER_UNDEFINED"; + case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: + return "GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT"; + case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: + return "GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT"; + case GL_FRAMEBUFFER_UNSUPPORTED: + return "GL_FRAMEBUFFER_UNSUPPORTED"; + case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE: + return "GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE"; + default: + return "unknown"; + } +} + +#ifdef CHECK_GL_ERRORS + +static const char* GlErrorString(GLenum error) { + switch (error) { + case GL_NO_ERROR: + return "GL_NO_ERROR"; + case GL_INVALID_ENUM: + return "GL_INVALID_ENUM"; + case GL_INVALID_VALUE: + return "GL_INVALID_VALUE"; + case GL_INVALID_OPERATION: + return "GL_INVALID_OPERATION"; + case GL_INVALID_FRAMEBUFFER_OPERATION: + return "GL_INVALID_FRAMEBUFFER_OPERATION"; + case GL_OUT_OF_MEMORY: + return "GL_OUT_OF_MEMORY"; + default: + return "unknown"; + } +} + +static void GLCheckErrors(int line) { + for (int i = 0; i < 10; i++) { + const GLenum error = glGetError(); + if (error == GL_NO_ERROR) { + break; + } + ALOGE("GL error on line %d: %s", line, GlErrorString(error)); + } +} + +#define GL(func) \ + func; \ + GLCheckErrors(__LINE__); + +#else // CHECK_GL_ERRORS + +#define GL(func) func; + +#endif // CHECK_GL_ERRORS + +/* +================================================================================ + +ovrEgl + +================================================================================ +*/ + +typedef struct { + EGLint MajorVersion; + EGLint MinorVersion; + EGLDisplay Display; + EGLConfig Config; + EGLSurface TinySurface; + EGLSurface MainSurface; + EGLContext Context; +} ovrEgl; + +static void ovrEgl_Clear(ovrEgl* egl) { + egl->MajorVersion = 0; + egl->MinorVersion = 0; + egl->Display = 0; + egl->Config = 0; + egl->TinySurface = EGL_NO_SURFACE; + egl->MainSurface = EGL_NO_SURFACE; + egl->Context = EGL_NO_CONTEXT; +} + +static void ovrEgl_CreateContext(ovrEgl* egl, const ovrEgl* shareEgl) { + if (egl->Display != 0) { + return; + } + + egl->Display = eglGetDisplay(EGL_DEFAULT_DISPLAY); + ALOGV(" eglInitialize( Display, &MajorVersion, &MinorVersion )"); + eglInitialize(egl->Display, &egl->MajorVersion, &egl->MinorVersion); + // Do NOT use eglChooseConfig, because the Android EGL code pushes in multisample + // flags in eglChooseConfig if the user has selected the "force 4x MSAA" option in + // settings, and that is completely wasted for our warp target. + enum { MAX_CONFIGS = 1024 }; + EGLConfig configs[MAX_CONFIGS]; + EGLint numConfigs = 0; + if (eglGetConfigs(egl->Display, configs, MAX_CONFIGS, &numConfigs) == EGL_FALSE) { + ALOGE(" eglGetConfigs() failed: %s", EglErrorString(eglGetError())); + return; + } + const EGLint configAttribs[] = { + EGL_RED_SIZE, + 8, + EGL_GREEN_SIZE, + 8, + EGL_BLUE_SIZE, + 8, + EGL_ALPHA_SIZE, + 8, // need alpha for the multi-pass timewarp compositor + EGL_DEPTH_SIZE, + 0, + EGL_STENCIL_SIZE, + 0, + EGL_SAMPLES, + 0, + EGL_NONE}; + egl->Config = 0; + for (int i = 0; i < numConfigs; i++) { + EGLint value = 0; + + eglGetConfigAttrib(egl->Display, configs[i], EGL_RENDERABLE_TYPE, &value); + if ((value & EGL_OPENGL_ES3_BIT_KHR) != EGL_OPENGL_ES3_BIT_KHR) { + continue; + } + + // The pbuffer config also needs to be compatible with normal window rendering + // so it can share textures with the window context. + eglGetConfigAttrib(egl->Display, configs[i], EGL_SURFACE_TYPE, &value); + if ((value & (EGL_WINDOW_BIT | EGL_PBUFFER_BIT)) != (EGL_WINDOW_BIT | EGL_PBUFFER_BIT)) { + continue; + } + + int j = 0; + for (; configAttribs[j] != EGL_NONE; j += 2) { + eglGetConfigAttrib(egl->Display, configs[i], configAttribs[j], &value); + if (value != configAttribs[j + 1]) { + break; + } + } + if (configAttribs[j] == EGL_NONE) { + egl->Config = configs[i]; + break; + } + } + if (egl->Config == 0) { + ALOGE(" eglChooseConfig() failed: %s", EglErrorString(eglGetError())); + return; + } + EGLint contextAttribs[] = {EGL_CONTEXT_CLIENT_VERSION, 3, EGL_NONE}; + ALOGV(" Context = eglCreateContext( Display, Config, EGL_NO_CONTEXT, contextAttribs )"); + egl->Context = eglCreateContext( + egl->Display, + egl->Config, + (shareEgl != NULL) ? shareEgl->Context : EGL_NO_CONTEXT, + contextAttribs); + if (egl->Context == EGL_NO_CONTEXT) { + ALOGE(" eglCreateContext() failed: %s", EglErrorString(eglGetError())); + return; + } + const EGLint surfaceAttribs[] = {EGL_WIDTH, 16, EGL_HEIGHT, 16, EGL_NONE}; + ALOGV(" TinySurface = eglCreatePbufferSurface( Display, Config, surfaceAttribs )"); + egl->TinySurface = eglCreatePbufferSurface(egl->Display, egl->Config, surfaceAttribs); + if (egl->TinySurface == EGL_NO_SURFACE) { + ALOGE(" eglCreatePbufferSurface() failed: %s", EglErrorString(eglGetError())); + eglDestroyContext(egl->Display, egl->Context); + egl->Context = EGL_NO_CONTEXT; + return; + } + ALOGV(" eglMakeCurrent( Display, TinySurface, TinySurface, Context )"); + if (eglMakeCurrent(egl->Display, egl->TinySurface, egl->TinySurface, egl->Context) == + EGL_FALSE) { + ALOGE(" eglMakeCurrent() failed: %s", EglErrorString(eglGetError())); + eglDestroySurface(egl->Display, egl->TinySurface); + eglDestroyContext(egl->Display, egl->Context); + egl->Context = EGL_NO_CONTEXT; + return; + } +} + +static void ovrEgl_DestroyContext(ovrEgl* egl) { + if (egl->Display != 0) { + ALOGE(" eglMakeCurrent( Display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT )"); + if (eglMakeCurrent(egl->Display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT) == + EGL_FALSE) { + ALOGE(" eglMakeCurrent() failed: %s", EglErrorString(eglGetError())); + } + } + if (egl->Context != EGL_NO_CONTEXT) { + ALOGE(" eglDestroyContext( Display, Context )"); + if (eglDestroyContext(egl->Display, egl->Context) == EGL_FALSE) { + ALOGE(" eglDestroyContext() failed: %s", EglErrorString(eglGetError())); + } + egl->Context = EGL_NO_CONTEXT; + } + if (egl->TinySurface != EGL_NO_SURFACE) { + ALOGE(" eglDestroySurface( Display, TinySurface )"); + if (eglDestroySurface(egl->Display, egl->TinySurface) == EGL_FALSE) { + ALOGE(" eglDestroySurface() failed: %s", EglErrorString(eglGetError())); + } + egl->TinySurface = EGL_NO_SURFACE; + } + if (egl->Display != 0) { + ALOGE(" eglTerminate( Display )"); + if (eglTerminate(egl->Display) == EGL_FALSE) { + ALOGE(" eglTerminate() failed: %s", EglErrorString(eglGetError())); + } + egl->Display = 0; + } +} + +/* +================================================================================ + +ovrGeometry + +================================================================================ +*/ + +typedef struct { + GLint Index; + GLint Size; + GLenum Type; + GLboolean Normalized; + GLsizei Stride; + const GLvoid* Pointer; +} ovrVertexAttribPointer; + +#define MAX_VERTEX_ATTRIB_POINTERS 3 + +typedef struct { + GLuint VertexBuffer; + GLuint IndexBuffer; + GLuint VertexArrayObject; + int VertexCount; + int IndexCount; + ovrVertexAttribPointer VertexAttribs[MAX_VERTEX_ATTRIB_POINTERS]; +} ovrGeometry; + +enum VertexAttributeLocation { + VERTEX_ATTRIBUTE_LOCATION_POSITION, + VERTEX_ATTRIBUTE_LOCATION_COLOR, + VERTEX_ATTRIBUTE_LOCATION_UV, + VERTEX_ATTRIBUTE_LOCATION_TRANSFORM +}; + +typedef struct { + enum VertexAttributeLocation location; + const char* name; +} ovrVertexAttribute; + +static ovrVertexAttribute ProgramVertexAttributes[] = { + {VERTEX_ATTRIBUTE_LOCATION_POSITION, "vertexPosition"}, + {VERTEX_ATTRIBUTE_LOCATION_COLOR, "vertexColor"}, + {VERTEX_ATTRIBUTE_LOCATION_UV, "vertexUv"}, +}; + +static void ovrGeometry_Clear(ovrGeometry* geometry) { + geometry->VertexBuffer = 0; + geometry->IndexBuffer = 0; + geometry->VertexArrayObject = 0; + geometry->VertexCount = 0; + geometry->IndexCount = 0; + for (int i = 0; i < MAX_VERTEX_ATTRIB_POINTERS; i++) { + memset(&geometry->VertexAttribs[i], 0, sizeof(geometry->VertexAttribs[i])); + geometry->VertexAttribs[i].Index = -1; + } +} + +static void ovrGeometry_CreateGroundPlane(ovrGeometry* geometry) { + typedef struct { + float positions[12][4]; + unsigned char colors[12][4]; + } ovrCubeVertices; + + static const ovrCubeVertices cubeVertices = { + // positions + {{4.5f, 0.0f, 4.5f, 1.0f}, + {4.5f, 0.0f, -4.5f, 1.0f}, + {-4.5f, 0.0f, -4.5f, 1.0f}, + {-4.5f, 0.0f, 4.5f, 1.0f}, + + {4.5f, -10.0f, 4.5f, 1.0f}, + {4.5f, -10.0f, -4.5f, 1.0f}, + {-4.5f, -10.0f, -4.5f, 1.0f}, + {-4.5f, -10.0f, 4.5f, 1.0f}, + + {4.5f, 10.0f, 4.5f, 1.0f}, + {4.5f, 10.0f, -4.5f, 1.0f}, + {-4.5f, 10.0f, -4.5f, 1.0f}, + {-4.5f, 10.0f, 4.5f, 1.0f}}, + // colors + {{255, 0, 0, 255}, + {0, 255, 0, 255}, + {0, 0, 255, 255}, + {255, 255, 0, 255}, + + {255, 128, 0, 255}, + {0, 255, 255, 255}, + {0, 0, 255, 255}, + {255, 0, 255, 255}, + + {255, 128, 128, 255}, + {128, 255, 128, 255}, + {128, 128, 255, 255}, + {255, 255, 128, 255}}, + }; + + static const unsigned short cubeIndices[18] = { + 0, + 1, + 2, + 0, + 2, + 3, + + 4, + 5, + 6, + 4, + 6, + 7, + + 8, + 9, + 10, + 8, + 10, + 11, + }; + + geometry->VertexCount = 12; + geometry->IndexCount = 18; + + geometry->VertexAttribs[0].Index = VERTEX_ATTRIBUTE_LOCATION_POSITION; + geometry->VertexAttribs[0].Size = 4; + geometry->VertexAttribs[0].Type = GL_FLOAT; + geometry->VertexAttribs[0].Normalized = false; + geometry->VertexAttribs[0].Stride = sizeof(cubeVertices.positions[0]); + geometry->VertexAttribs[0].Pointer = (const GLvoid*)offsetof(ovrCubeVertices, positions); + + geometry->VertexAttribs[1].Index = VERTEX_ATTRIBUTE_LOCATION_COLOR; + geometry->VertexAttribs[1].Size = 4; + geometry->VertexAttribs[1].Type = GL_UNSIGNED_BYTE; + geometry->VertexAttribs[1].Normalized = true; + geometry->VertexAttribs[1].Stride = sizeof(cubeVertices.colors[0]); + geometry->VertexAttribs[1].Pointer = (const GLvoid*)offsetof(ovrCubeVertices, colors); + + GL(glGenBuffers(1, &geometry->VertexBuffer)); + GL(glBindBuffer(GL_ARRAY_BUFFER, geometry->VertexBuffer)); + GL(glBufferData(GL_ARRAY_BUFFER, sizeof(cubeVertices), &cubeVertices, GL_STATIC_DRAW)); + GL(glBindBuffer(GL_ARRAY_BUFFER, 0)); + + GL(glGenBuffers(1, &geometry->IndexBuffer)); + GL(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, geometry->IndexBuffer)); + GL(glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(cubeIndices), cubeIndices, GL_STATIC_DRAW)); + GL(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)); +} + +static void ovrGeometry_CreateStagePlane( + ovrGeometry* geometry, + float minx, + float minz, + float maxx, + float maxz) { + typedef struct { + float positions[12][4]; + unsigned char colors[12][4]; + } ovrCubeVertices; + + const ovrCubeVertices cubeVertices = { + // positions + {{maxx, 0.0f, maxz, 1.0f}, + {maxx, 0.0f, minz, 1.0f}, + {minx, 0.0f, minz, 1.0f}, + {minx, 0.0f, maxz, 1.0f}}, + // colors + { + {128, 0, 0, 255}, + {0, 128, 0, 255}, + {0, 0, 128, 255}, + {128, 128, 0, 255}, + }, + }; + + static const unsigned short cubeIndices[18] = { + 0, + 1, + 2, + 0, + 2, + 3, + }; + + geometry->VertexCount = 4; + geometry->IndexCount = 6; + + geometry->VertexAttribs[0].Index = VERTEX_ATTRIBUTE_LOCATION_POSITION; + geometry->VertexAttribs[0].Size = 4; + geometry->VertexAttribs[0].Type = GL_FLOAT; + geometry->VertexAttribs[0].Normalized = false; + geometry->VertexAttribs[0].Stride = sizeof(cubeVertices.positions[0]); + geometry->VertexAttribs[0].Pointer = (const GLvoid*)offsetof(ovrCubeVertices, positions); + + geometry->VertexAttribs[1].Index = VERTEX_ATTRIBUTE_LOCATION_COLOR; + geometry->VertexAttribs[1].Size = 4; + geometry->VertexAttribs[1].Type = GL_UNSIGNED_BYTE; + geometry->VertexAttribs[1].Normalized = true; + geometry->VertexAttribs[1].Stride = sizeof(cubeVertices.colors[0]); + geometry->VertexAttribs[1].Pointer = (const GLvoid*)offsetof(ovrCubeVertices, colors); + + GL(glGenBuffers(1, &geometry->VertexBuffer)); + GL(glBindBuffer(GL_ARRAY_BUFFER, geometry->VertexBuffer)); + GL(glBufferData(GL_ARRAY_BUFFER, sizeof(cubeVertices), &cubeVertices, GL_STATIC_DRAW)); + GL(glBindBuffer(GL_ARRAY_BUFFER, 0)); + + GL(glGenBuffers(1, &geometry->IndexBuffer)); + GL(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, geometry->IndexBuffer)); + GL(glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(cubeIndices), cubeIndices, GL_STATIC_DRAW)); + GL(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)); +} + +static void ovrGeometry_CreateBox(ovrGeometry* geometry) { + typedef struct { + float positions[8][4]; + unsigned char colors[8][4]; + } ovrCubeVertices; + + static const ovrCubeVertices cubeVertices = { + // positions + {{-1.0f, -1.0f, -1.0f, 1.0f}, + {1.0f, -1.0f, -1.0f, 1.0f}, + {-1.0f, 1.0f, -1.0f, 1.0f}, + {1.0f, 1.0f, -1.0f, 1.0f}, + + {-1.0f, -1.0f, 1.0f, 1.0f}, + {1.0f, -1.0f, 1.0f, 1.0f}, + {-1.0f, 1.0f, 1.0f, 1.0f}, + {1.0f, 1.0f, 1.0f, 1.0f}}, + // colors + { + {255, 0, 0, 255}, + {250, 255, 0, 255}, + {250, 0, 255, 255}, + {255, 255, 0, 255}, + {255, 0, 0, 255}, + {250, 255, 0, 255}, + {250, 0, 255, 255}, + {255, 255, 0, 255}, + }, + }; + + // 6------7 + // /| /| + // 2-+----3 | + // | | | | + // | 4----+-5 + // |/ |/ + // 0------1 + + static const unsigned short cubeIndices[36] = {0, 1, 3, 0, 3, 2, + + 5, 4, 6, 5, 6, 7, + + 4, 0, 2, 4, 2, 6, + + 1, 5, 7, 1, 7, 3, + + 4, 5, 1, 4, 1, 0, + + 2, 3, 7, 2, 7, 6}; + + geometry->VertexCount = 8; + geometry->IndexCount = 36; + + geometry->VertexAttribs[0].Index = VERTEX_ATTRIBUTE_LOCATION_POSITION; + geometry->VertexAttribs[0].Size = 4; + geometry->VertexAttribs[0].Type = GL_FLOAT; + geometry->VertexAttribs[0].Normalized = false; + geometry->VertexAttribs[0].Stride = sizeof(cubeVertices.positions[0]); + geometry->VertexAttribs[0].Pointer = (const GLvoid*)offsetof(ovrCubeVertices, positions); + + geometry->VertexAttribs[1].Index = VERTEX_ATTRIBUTE_LOCATION_COLOR; + geometry->VertexAttribs[1].Size = 4; + geometry->VertexAttribs[1].Type = GL_UNSIGNED_BYTE; + geometry->VertexAttribs[1].Normalized = true; + geometry->VertexAttribs[1].Stride = sizeof(cubeVertices.colors[0]); + geometry->VertexAttribs[1].Pointer = (const GLvoid*)offsetof(ovrCubeVertices, colors); + + GL(glGenBuffers(1, &geometry->VertexBuffer)); + GL(glBindBuffer(GL_ARRAY_BUFFER, geometry->VertexBuffer)); + GL(glBufferData(GL_ARRAY_BUFFER, sizeof(cubeVertices), &cubeVertices, GL_STATIC_DRAW)); + GL(glBindBuffer(GL_ARRAY_BUFFER, 0)); + + GL(glGenBuffers(1, &geometry->IndexBuffer)); + GL(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, geometry->IndexBuffer)); + GL(glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(cubeIndices), cubeIndices, GL_STATIC_DRAW)); + GL(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)); +} + +static void ovrGeometry_Destroy(ovrGeometry* geometry) { + GL(glDeleteBuffers(1, &geometry->IndexBuffer)); + GL(glDeleteBuffers(1, &geometry->VertexBuffer)); + + ovrGeometry_Clear(geometry); +} + +static void ovrGeometry_CreateVAO(ovrGeometry* geometry) { + GL(glGenVertexArrays(1, &geometry->VertexArrayObject)); + GL(glBindVertexArray(geometry->VertexArrayObject)); + + GL(glBindBuffer(GL_ARRAY_BUFFER, geometry->VertexBuffer)); + + for (int i = 0; i < MAX_VERTEX_ATTRIB_POINTERS; i++) { + if (geometry->VertexAttribs[i].Index != -1) { + GL(glEnableVertexAttribArray(geometry->VertexAttribs[i].Index)); + GL(glVertexAttribPointer( + geometry->VertexAttribs[i].Index, + geometry->VertexAttribs[i].Size, + geometry->VertexAttribs[i].Type, + geometry->VertexAttribs[i].Normalized, + geometry->VertexAttribs[i].Stride, + geometry->VertexAttribs[i].Pointer)); + } + } + + GL(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, geometry->IndexBuffer)); + + GL(glBindVertexArray(0)); +} + +static void ovrGeometry_DestroyVAO(ovrGeometry* geometry) { + GL(glDeleteVertexArrays(1, &geometry->VertexArrayObject)); +} + +/* +================================================================================ + +ovrProgram + +================================================================================ +*/ + +#define MAX_PROGRAM_UNIFORMS 8 +#define MAX_PROGRAM_TEXTURES 8 + +typedef struct { + GLuint Program; + GLuint VertexShader; + GLuint FragmentShader; + // These will be -1 if not used by the program. + GLint UniformLocation[MAX_PROGRAM_UNIFORMS]; // ProgramUniforms[].name + GLint UniformBinding[MAX_PROGRAM_UNIFORMS]; // ProgramUniforms[].name + GLint Textures[MAX_PROGRAM_TEXTURES]; // Texture%i +} ovrProgram; + +typedef enum ovrUniformIndex { + MODEL_MATRIX, + PREV_MODEL_MATRIX, + VIEW_PROJ_MATRIX, + PREV_VIEW_PROJ_MATRIX, + SCENE_MATRICES, +} ovrUniformIndex; + +typedef enum ovrUniformType { + VECTOR4, + MATRIX4X4, + INTEGER, + BUFFER, +} ovrUniformType; + +typedef struct { + ovrUniformIndex index; + ovrUniformType type; + const char* name; +} ovrUniform; + +static ovrUniform ProgramUniforms[] = { + {MODEL_MATRIX, MATRIX4X4, "modelMatrix"}, + {PREV_MODEL_MATRIX, MATRIX4X4, "prevModelMatrix"}, + {VIEW_PROJ_MATRIX, MATRIX4X4, "viewProjectionMatrix"}, + {PREV_VIEW_PROJ_MATRIX, MATRIX4X4, "prevViewProjectionMatrix"}, +}; + +static void ovrProgram_Clear(ovrProgram* program) { + program->Program = 0; + program->VertexShader = 0; + program->FragmentShader = 0; + memset(program->UniformLocation, 0, sizeof(program->UniformLocation)); + memset(program->UniformBinding, 0, sizeof(program->UniformBinding)); + memset(program->Textures, 0, sizeof(program->Textures)); +} + +static bool +ovrProgram_Create(ovrProgram* program, const char* vertexSource, const char* fragmentSource) { + GLint r; + + GL(program->VertexShader = glCreateShader(GL_VERTEX_SHADER)); + + GL(glShaderSource(program->VertexShader, 1, &vertexSource, 0)); + GL(glCompileShader(program->VertexShader)); + GL(glGetShaderiv(program->VertexShader, GL_COMPILE_STATUS, &r)); + if (r == GL_FALSE) { + GLchar msg[4096]; + GL(glGetShaderInfoLog(program->VertexShader, sizeof(msg), 0, msg)); + ALOGE("%s\n%s\n", vertexSource, msg); + return false; + } + + GL(program->FragmentShader = glCreateShader(GL_FRAGMENT_SHADER)); + GL(glShaderSource(program->FragmentShader, 1, &fragmentSource, 0)); + GL(glCompileShader(program->FragmentShader)); + GL(glGetShaderiv(program->FragmentShader, GL_COMPILE_STATUS, &r)); + if (r == GL_FALSE) { + GLchar msg[4096]; + GL(glGetShaderInfoLog(program->FragmentShader, sizeof(msg), 0, msg)); + ALOGE("%s\n%s\n", fragmentSource, msg); + return false; + } + + GL(program->Program = glCreateProgram()); + GL(glAttachShader(program->Program, program->VertexShader)); + GL(glAttachShader(program->Program, program->FragmentShader)); + + // Bind the vertex attribute locations. + for (size_t i = 0; i < sizeof(ProgramVertexAttributes) / sizeof(ProgramVertexAttributes[0]); + i++) { + GL(glBindAttribLocation( + program->Program, + ProgramVertexAttributes[i].location, + ProgramVertexAttributes[i].name)); + } + + GL(glLinkProgram(program->Program)); + GL(glGetProgramiv(program->Program, GL_LINK_STATUS, &r)); + if (r == GL_FALSE) { + GLchar msg[4096]; + GL(glGetProgramInfoLog(program->Program, sizeof(msg), 0, msg)); + ALOGE("Linking program failed: %s\n", msg); + return false; + } + + int numBufferBindings = 0; + + // Get the uniform locations. + memset(program->UniformLocation, -1, sizeof(program->UniformLocation)); + for (size_t i = 0; i < sizeof(ProgramUniforms) / sizeof(ProgramUniforms[0]); i++) { + const int uniformIndex = ProgramUniforms[i].index; + if (ProgramUniforms[i].type == BUFFER) { + GL(program->UniformLocation[uniformIndex] = + glGetUniformBlockIndex(program->Program, ProgramUniforms[i].name)); + program->UniformBinding[uniformIndex] = numBufferBindings++; + GL(glUniformBlockBinding( + program->Program, + program->UniformLocation[uniformIndex], + program->UniformBinding[uniformIndex])); + } else { + GL(program->UniformLocation[uniformIndex] = + glGetUniformLocation(program->Program, ProgramUniforms[i].name)); + program->UniformBinding[uniformIndex] = program->UniformLocation[uniformIndex]; + } + } + + GL(glUseProgram(program->Program)); + + // Get the texture locations. + for (int i = 0; i < MAX_PROGRAM_TEXTURES; i++) { + char name[32]; + sprintf(name, "Texture%i", i); + program->Textures[i] = glGetUniformLocation(program->Program, name); + if (program->Textures[i] != -1) { + GL(glUniform1i(program->Textures[i], i)); + } + } + + GL(glUseProgram(0)); + + return true; +} + +static void ovrProgram_Destroy(ovrProgram* program) { + if (program->Program != 0) { + GL(glDeleteProgram(program->Program)); + program->Program = 0; + } + if (program->VertexShader != 0) { + GL(glDeleteShader(program->VertexShader)); + program->VertexShader = 0; + } + if (program->FragmentShader != 0) { + GL(glDeleteShader(program->FragmentShader)); + program->FragmentShader = 0; + } +} + +// Main pass shader : VERTEX_SHADER / FRAGMENT_SHADER are for normal render pass +static const char VERTEX_SHADER[] = + "#version 300 es\n" + "in vec3 vertexPosition;\n" + "in vec4 vertexColor;\n" + "uniform mat4 viewProjectionMatrix;\n" + "uniform mat4 modelMatrix;\n" + "out vec4 fragmentColor;\n" + "out highp vec3 localPosition;\n" + "void main()\n" + "{\n" + " localPosition = vertexPosition;\n" + " gl_Position = viewProjectionMatrix * ( modelMatrix * vec4( vertexPosition, 1.0 ) );\n" + " fragmentColor = vertexColor;\n" + "}\n"; + +static const char FRAGMENT_SHADER[] = + "#version 300 es\n" + "in lowp vec4 fragmentColor;\n" + "in highp vec3 localPosition;\n" + "in highp vec4 clipPos;\n" + "out lowp vec4 outColor;\n" + "void main()\n" + "{\n" + " outColor = fragmentColor;\n" + " float offCenter = dot(localPosition, localPosition) / 5.0f;\n" + " outColor.rgb = mix(vec3(1.0f, 1.0f, 0.0f), vec3(0.6f, 0.1f, 0.0f), offCenter);\n" + " outColor.rgb = outColor.rgb * outColor.rgb;\n" + "}\n"; + +// AppSpaceWarp : MOTION_VECTOR_VERTEX_SHADER / MOTION_VECTOR_FRAGMENT_SHADER are for motion vector +// pass, which will calculate a vertex's previous and current clip space position, convert them +// into NDC in fragment shader, thus the difference of the 2 NDC value are the motion vector, In +// normal video game, usually motion vector pass shader is much simipler compared with the Main pass +// shader, eg. motion vector shader might not need any texture sampling in fragment shader unless it +// can trigger a pixel discard. + +static const char MOTION_VECTOR_VERTEX_SHADER[] = + "#version 300 es\n" + "in vec3 vertexPosition;\n" + "in vec4 vertexColor;\n" + "uniform mat4 viewProjectionMatrix;\n" + "uniform mat4 prevViewProjectionMatrix;\n" + "uniform mat4 modelMatrix;\n" + "uniform mat4 prevModelMatrix;\n" + "out highp vec4 clipPos;\n" + "out highp vec4 prevClipPos;\n" + "void main()\n" + "{\n" + " clipPos = viewProjectionMatrix * ( modelMatrix * vec4( vertexPosition, 1.0 ) );\n" + " prevClipPos = prevViewProjectionMatrix * ( prevModelMatrix * vec4( vertexPosition, 1.0 ) );\n" + " gl_Position = clipPos;\n" + "}\n"; + +static const char MOTION_VECTOR_FRAGMENT_SHADER[] = + "#version 300 es\n" + "in highp vec4 clipPos;\n" + "in highp vec4 prevClipPos;\n" + "in highp vec3 localPosition;\n" + "out highp vec4 outColor;\n" + "void main()\n" + "{\n" + " highp vec4 motionVector = ( clipPos / clipPos.w - prevClipPos / prevClipPos.w ); \n" + " outColor = motionVector;\n" + "}\n"; + +typedef struct { + XrSwapchain Handle; + uint32_t Width; + uint32_t Height; +} ovrSwapChain; + +/* +================================================================================ + +ovrFramebuffer + +================================================================================ +*/ + +typedef struct { + int Multisamples; + uint32_t TextureSwapChainLength; + uint32_t TextureSwapChainIndex; + + // Color Swap Chain + int Width; + int Height; + ovrSwapChain ColorSwapChain; + XrSwapchainImageOpenGLESKHR* ColorSwapChainImage; + GLuint* DepthBuffers; + GLuint* FrameBuffers; + + // AppSpaceWarp : Motion vector pass will render into both color attachmment + // (MotionVectorSwapChain / MotionVectorSwapChainImage) and depth attachment + // (MotionVectorDepthSwapChain / MotionVectorDepthSwapChainImage) + int MotionVectorWidth; + int MotionVectorHeight; + ovrSwapChain MotionVectorSwapChain; + XrSwapchainImageOpenGLESKHR* MotionVectorSwapChainImage; + ovrSwapChain MotionVectorDepthSwapChain; + XrSwapchainImageOpenGLESKHR* MotionVectorDepthSwapChainImage; + GLuint* MotionVectorFrameBuffers; + +} ovrFramebuffer; + +static void ovrFramebuffer_Clear(ovrFramebuffer* frameBuffer) { + frameBuffer->Width = 0; + frameBuffer->Height = 0; + frameBuffer->Multisamples = 0; + frameBuffer->TextureSwapChainLength = 0; + frameBuffer->TextureSwapChainIndex = 0; + frameBuffer->ColorSwapChain.Handle = XR_NULL_HANDLE; + frameBuffer->ColorSwapChain.Width = 0; + frameBuffer->ColorSwapChain.Height = 0; + frameBuffer->ColorSwapChainImage = NULL; + frameBuffer->DepthBuffers = NULL; + frameBuffer->FrameBuffers = NULL; + + // AppSpaceWarp + frameBuffer->MotionVectorSwapChain.Handle = XR_NULL_HANDLE; + frameBuffer->MotionVectorSwapChain.Width = 0; + frameBuffer->MotionVectorSwapChain.Height = 0; + frameBuffer->MotionVectorSwapChainImage = NULL; + frameBuffer->MotionVectorDepthSwapChain.Handle = XR_NULL_HANDLE; + frameBuffer->MotionVectorDepthSwapChain.Width = 0; + frameBuffer->MotionVectorDepthSwapChain.Height = 0; + frameBuffer->MotionVectorDepthSwapChainImage = NULL; + frameBuffer->MotionVectorFrameBuffers = NULL; +} + +static bool ovrFramebuffer_Create( + XrSession session, + ovrFramebuffer* frameBuffer, + const GLenum colorFormat, + const int width, + const int height, + const int motionVectorWidth, + const int motionVectorHeight, + const int multisamples) { + PFNGLRENDERBUFFERSTORAGEMULTISAMPLEEXTPROC glRenderbufferStorageMultisampleEXT = + (PFNGLRENDERBUFFERSTORAGEMULTISAMPLEEXTPROC)eglGetProcAddress( + "glRenderbufferStorageMultisampleEXT"); + PFNGLFRAMEBUFFERTEXTURE2DMULTISAMPLEEXTPROC glFramebufferTexture2DMultisampleEXT = + (PFNGLFRAMEBUFFERTEXTURE2DMULTISAMPLEEXTPROC)eglGetProcAddress( + "glFramebufferTexture2DMultisampleEXT"); + + frameBuffer->Width = width; + frameBuffer->Height = height; + frameBuffer->Multisamples = multisamples; + + GLenum requestedGLFormat = colorFormat; + + // Get the number of supported formats. + uint32_t numInputFormats = 0; + uint32_t numOutputFormats = 0; + OXR(xrEnumerateSwapchainFormats(session, numInputFormats, &numOutputFormats, NULL)); + + // Allocate an array large enough to contain the supported formats. + numInputFormats = numOutputFormats; + int64_t* supportedFormats = (int64_t*)malloc(numOutputFormats * sizeof(int64_t)); + if (supportedFormats != NULL) { + OXR(xrEnumerateSwapchainFormats( + session, numInputFormats, &numOutputFormats, supportedFormats)); + } + + // Verify the requested format is supported. + uint64_t selectedFormat = 0; + for (uint32_t i = 0; i < numOutputFormats; i++) { + if (supportedFormats[i] == requestedGLFormat) { + selectedFormat = supportedFormats[i]; + break; + } + } + + free(supportedFormats); + + if (selectedFormat == 0) { + ALOGE("Format not supported"); + } + + // Swapchain for main pass + { + XrSwapchainCreateInfo swapChainCreateInfo = {XR_TYPE_SWAPCHAIN_CREATE_INFO}; + swapChainCreateInfo.usageFlags = + XR_SWAPCHAIN_USAGE_SAMPLED_BIT | XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT; + swapChainCreateInfo.format = selectedFormat; + swapChainCreateInfo.sampleCount = 1; + swapChainCreateInfo.width = width; + swapChainCreateInfo.height = height; + swapChainCreateInfo.faceCount = 1; + swapChainCreateInfo.arraySize = 1; + swapChainCreateInfo.mipCount = 1; + + frameBuffer->ColorSwapChain.Width = swapChainCreateInfo.width; + frameBuffer->ColorSwapChain.Height = swapChainCreateInfo.height; + + // Create the swapchain. + OXR(xrCreateSwapchain(session, &swapChainCreateInfo, &frameBuffer->ColorSwapChain.Handle)); + // Get the number of swapchain images. + OXR(xrEnumerateSwapchainImages( + frameBuffer->ColorSwapChain.Handle, 0, &frameBuffer->TextureSwapChainLength, NULL)); + // Allocate the swapchain images array. + frameBuffer->ColorSwapChainImage = (XrSwapchainImageOpenGLESKHR*)malloc( + frameBuffer->TextureSwapChainLength * sizeof(XrSwapchainImageOpenGLESKHR)); + + // Populate the swapchain image array. + for (uint32_t i = 0; i < frameBuffer->TextureSwapChainLength; i++) { + frameBuffer->ColorSwapChainImage[i].type = XR_TYPE_SWAPCHAIN_IMAGE_OPENGL_ES_KHR; + frameBuffer->ColorSwapChainImage[i].next = NULL; + } + OXR(xrEnumerateSwapchainImages( + frameBuffer->ColorSwapChain.Handle, + frameBuffer->TextureSwapChainLength, + &frameBuffer->TextureSwapChainLength, + (XrSwapchainImageBaseHeader*)frameBuffer->ColorSwapChainImage)); + + frameBuffer->DepthBuffers = + (GLuint*)malloc(frameBuffer->TextureSwapChainLength * sizeof(GLuint)); + frameBuffer->FrameBuffers = + (GLuint*)malloc(frameBuffer->TextureSwapChainLength * sizeof(GLuint)); + + for (uint32_t i = 0; i < frameBuffer->TextureSwapChainLength; i++) { + // Create the color buffer texture. + const GLuint colorTexture = frameBuffer->ColorSwapChainImage[i].image; + + GLenum colorTextureTarget = GL_TEXTURE_2D; + GL(glBindTexture(colorTextureTarget, colorTexture)); + GL(glTexParameteri(colorTextureTarget, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)); + GL(glTexParameteri(colorTextureTarget, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)); + GL(glTexParameteri(colorTextureTarget, GL_TEXTURE_MIN_FILTER, GL_LINEAR)); + GL(glTexParameteri(colorTextureTarget, GL_TEXTURE_MAG_FILTER, GL_LINEAR)); + GL(glBindTexture(colorTextureTarget, 0)); + + if (multisamples > 1 && glRenderbufferStorageMultisampleEXT != NULL && + glFramebufferTexture2DMultisampleEXT != NULL) { + // Create multisampled depth buffer. + GL(glGenRenderbuffers(1, &frameBuffer->DepthBuffers[i])); + GL(glBindRenderbuffer(GL_RENDERBUFFER, frameBuffer->DepthBuffers[i])); + GL(glRenderbufferStorageMultisampleEXT( + GL_RENDERBUFFER, multisamples, GL_DEPTH_COMPONENT24, width, height)); + GL(glBindRenderbuffer(GL_RENDERBUFFER, 0)); + + // Create the frame buffer. + // NOTE: glFramebufferTexture2DMultisampleEXT only works with GL_FRAMEBUFFER. + GL(glGenFramebuffers(1, &frameBuffer->FrameBuffers[i])); + GL(glBindFramebuffer(GL_FRAMEBUFFER, frameBuffer->FrameBuffers[i])); + GL(glFramebufferTexture2DMultisampleEXT( + GL_FRAMEBUFFER, + GL_COLOR_ATTACHMENT0, + GL_TEXTURE_2D, + colorTexture, + 0, + multisamples)); + GL(glFramebufferRenderbuffer( + GL_FRAMEBUFFER, + GL_DEPTH_ATTACHMENT, + GL_RENDERBUFFER, + frameBuffer->DepthBuffers[i])); + GL(GLenum renderFramebufferStatus = glCheckFramebufferStatus(GL_FRAMEBUFFER)); + GL(glBindFramebuffer(GL_FRAMEBUFFER, 0)); + if (renderFramebufferStatus != GL_FRAMEBUFFER_COMPLETE) { + ALOGE( + "Incomplete frame buffer object: %s", + GlFrameBufferStatusString(renderFramebufferStatus)); + return false; + } + } else { + // Create depth buffer. + GL(glGenRenderbuffers(1, &frameBuffer->DepthBuffers[i])); + GL(glBindRenderbuffer(GL_RENDERBUFFER, frameBuffer->DepthBuffers[i])); + GL(glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, width, height)); + GL(glBindRenderbuffer(GL_RENDERBUFFER, 0)); + + // Create the frame buffer. + GL(glGenFramebuffers(1, &frameBuffer->FrameBuffers[i])); + GL(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, frameBuffer->FrameBuffers[i])); + GL(glFramebufferRenderbuffer( + GL_DRAW_FRAMEBUFFER, + GL_DEPTH_ATTACHMENT, + GL_RENDERBUFFER, + frameBuffer->DepthBuffers[i])); + GL(glFramebufferTexture2D( + GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, colorTexture, 0)); + GL(GLenum renderFramebufferStatus = glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER)); + GL(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0)); + if (renderFramebufferStatus != GL_FRAMEBUFFER_COMPLETE) { + ALOGE( + "Incomplete frame buffer object: %s", + GlFrameBufferStatusString(renderFramebufferStatus)); + return false; + } + } + } + } + + // AppSpaceWarp : initialize swapchains and frame buffers for motion vector pass + { + frameBuffer->MotionVectorWidth = motionVectorWidth; + frameBuffer->MotionVectorHeight = motionVectorHeight; + + GLenum mvFormat = GL_RGBA16F; + GLenum mvDepthFormat = GL_DEPTH_COMPONENT24; + XrSwapchainCreateInfo swapChainCreateInfo = {XR_TYPE_SWAPCHAIN_CREATE_INFO}; + swapChainCreateInfo.usageFlags = + XR_SWAPCHAIN_USAGE_SAMPLED_BIT | XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT; + swapChainCreateInfo.format = mvFormat; + swapChainCreateInfo.sampleCount = 1; + swapChainCreateInfo.width = frameBuffer->MotionVectorWidth; + swapChainCreateInfo.height = frameBuffer->MotionVectorHeight; + swapChainCreateInfo.faceCount = 1; + swapChainCreateInfo.arraySize = 1; + swapChainCreateInfo.mipCount = 1; + + frameBuffer->MotionVectorSwapChain.Width = swapChainCreateInfo.width; + frameBuffer->MotionVectorSwapChain.Height = swapChainCreateInfo.height; + + // Create the swapchain. + OXR(xrCreateSwapchain( + session, &swapChainCreateInfo, &frameBuffer->MotionVectorSwapChain.Handle)); + + // Get the number of swapchain images. + OXR(xrEnumerateSwapchainImages( + frameBuffer->MotionVectorSwapChain.Handle, + 0, + &frameBuffer->TextureSwapChainLength, + NULL)); + // Allocate the swapchain images array. + frameBuffer->MotionVectorSwapChainImage = (XrSwapchainImageOpenGLESKHR*)malloc( + frameBuffer->TextureSwapChainLength * sizeof(XrSwapchainImageOpenGLESKHR)); + + // Populate the swapchain image array. + for (uint32_t i = 0; i < frameBuffer->TextureSwapChainLength; i++) { + frameBuffer->MotionVectorSwapChainImage[i].type = XR_TYPE_SWAPCHAIN_IMAGE_OPENGL_ES_KHR; + frameBuffer->MotionVectorSwapChainImage[i].next = NULL; + } + OXR(xrEnumerateSwapchainImages( + frameBuffer->MotionVectorSwapChain.Handle, + frameBuffer->TextureSwapChainLength, + &frameBuffer->TextureSwapChainLength, + (XrSwapchainImageBaseHeader*)frameBuffer->MotionVectorSwapChainImage)); + + // Motion Vector depth construction + swapChainCreateInfo.usageFlags = + XR_SWAPCHAIN_USAGE_SAMPLED_BIT | XR_SWAPCHAIN_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; + + frameBuffer->MotionVectorDepthSwapChain.Width = swapChainCreateInfo.width; + frameBuffer->MotionVectorDepthSwapChain.Height = swapChainCreateInfo.height; + + swapChainCreateInfo.format = mvDepthFormat; + OXR(xrCreateSwapchain( + session, &swapChainCreateInfo, &frameBuffer->MotionVectorDepthSwapChain.Handle)); + + // Get the number of swapchain images. + OXR(xrEnumerateSwapchainImages( + frameBuffer->MotionVectorDepthSwapChain.Handle, + 0, + &frameBuffer->TextureSwapChainLength, + NULL)); + + // Allocate the swapchain images array. + frameBuffer->MotionVectorDepthSwapChainImage = (XrSwapchainImageOpenGLESKHR*)malloc( + frameBuffer->TextureSwapChainLength * sizeof(XrSwapchainImageOpenGLESKHR)); + + // Populate the swapchain image array. + for (uint32_t i = 0; i < frameBuffer->TextureSwapChainLength; i++) { + frameBuffer->MotionVectorDepthSwapChainImage[i].type = + XR_TYPE_SWAPCHAIN_IMAGE_OPENGL_ES_KHR; + frameBuffer->MotionVectorDepthSwapChainImage[i].next = NULL; + } + OXR(xrEnumerateSwapchainImages( + frameBuffer->MotionVectorDepthSwapChain.Handle, + frameBuffer->TextureSwapChainLength, + &frameBuffer->TextureSwapChainLength, + (XrSwapchainImageBaseHeader*)frameBuffer->MotionVectorDepthSwapChainImage)); + + frameBuffer->MotionVectorFrameBuffers = + (GLuint*)malloc(frameBuffer->TextureSwapChainLength * sizeof(GLuint)); + + for (uint32_t i = 0; i < frameBuffer->TextureSwapChainLength; i++) { + // Color buffer texture. + const GLuint motionVectorTexture = frameBuffer->MotionVectorSwapChainImage[i].image; + GLenum colorTextureTarget = GL_TEXTURE_2D; + GL(glBindTexture(colorTextureTarget, motionVectorTexture)); + GL(glTexParameteri(colorTextureTarget, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)); + GL(glTexParameteri(colorTextureTarget, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)); + GL(glTexParameteri(colorTextureTarget, GL_TEXTURE_MIN_FILTER, GL_LINEAR)); + GL(glTexParameteri(colorTextureTarget, GL_TEXTURE_MAG_FILTER, GL_LINEAR)); + GL(glBindTexture(colorTextureTarget, 0)); + + // depth buffer texture. + const GLuint motionVectorDepthTexture = + frameBuffer->MotionVectorDepthSwapChainImage[i].image; + GL(glBindTexture(colorTextureTarget, motionVectorDepthTexture)); + GL(glTexParameteri(colorTextureTarget, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)); + GL(glTexParameteri(colorTextureTarget, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)); + GL(glTexParameteri(colorTextureTarget, GL_TEXTURE_MIN_FILTER, GL_NEAREST)); + GL(glTexParameteri(colorTextureTarget, GL_TEXTURE_MAG_FILTER, GL_NEAREST)); + GL(glBindTexture(colorTextureTarget, 0)); + + // Create the frame buffer. + GL(glGenFramebuffers(1, &frameBuffer->MotionVectorFrameBuffers[i])); + GL(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, frameBuffer->MotionVectorFrameBuffers[i])); + GL(glFramebufferTexture2D( + GL_DRAW_FRAMEBUFFER, + GL_DEPTH_ATTACHMENT, + GL_TEXTURE_2D, + motionVectorDepthTexture, + 0)); + + GL(glFramebufferTexture2D( + GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, motionVectorTexture, 0)); + + GL(GLenum renderFramebufferStatus = glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER)); + GL(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0)); + if (renderFramebufferStatus != GL_FRAMEBUFFER_COMPLETE) { + ALOGE( + "Incomplete frame buffer object: %s", + GlFrameBufferStatusString(renderFramebufferStatus)); + return false; + } + } + } + + return true; +} + +static void ovrFramebuffer_Destroy(ovrFramebuffer* frameBuffer) { + GL(glDeleteFramebuffers(frameBuffer->TextureSwapChainLength, frameBuffer->FrameBuffers)); + GL(glDeleteRenderbuffers(frameBuffer->TextureSwapChainLength, frameBuffer->DepthBuffers)); + OXR(xrDestroySwapchain(frameBuffer->ColorSwapChain.Handle)); + free(frameBuffer->ColorSwapChainImage); + + free(frameBuffer->DepthBuffers); + free(frameBuffer->FrameBuffers); + + // AppSpaceWarp Clear render resources + GL(glDeleteFramebuffers( + frameBuffer->TextureSwapChainLength, frameBuffer->MotionVectorFrameBuffers)); + OXR(xrDestroySwapchain(frameBuffer->MotionVectorSwapChain.Handle)); + free(frameBuffer->MotionVectorSwapChainImage); + OXR(xrDestroySwapchain(frameBuffer->MotionVectorDepthSwapChain.Handle)); + free(frameBuffer->MotionVectorDepthSwapChainImage); + free(frameBuffer->MotionVectorFrameBuffers); + + ovrFramebuffer_Clear(frameBuffer); +} + +static void ovrFramebuffer_SetCurrent(ovrFramebuffer* frameBuffer, bool isMotionVectorPass) { + // AppSpaceWarp + if (isMotionVectorPass) { + GL(glBindFramebuffer( + GL_DRAW_FRAMEBUFFER, + frameBuffer->MotionVectorFrameBuffers[frameBuffer->TextureSwapChainIndex])); + } else { + GL(glBindFramebuffer( + GL_DRAW_FRAMEBUFFER, frameBuffer->FrameBuffers[frameBuffer->TextureSwapChainIndex])); + } +} + +static void ovrFramebuffer_SetNone() { + GL(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0)); +} + +static void ovrFramebuffer_Resolve(ovrFramebuffer* frameBuffer, bool isMotionVectorPass) { + if (isMotionVectorPass) { + // AppSpaceWarp Both depth and color buffer will be resolved for motion vector pass + } else { + // Discard the depth buffer, so the tiler won't need to write it back out to memory. + const GLenum depthAttachment[1] = {GL_DEPTH_ATTACHMENT}; + glInvalidateFramebuffer(GL_DRAW_FRAMEBUFFER, 1, depthAttachment); + // We now let the resolve happen implicitly. + } +} + +static void ovrFramebuffer_Acquire(ovrFramebuffer* frameBuffer, bool isMotionVectorPass) { + // Acquire the swapchain image + XrSwapchainImageAcquireInfo acquireInfo = {XR_TYPE_SWAPCHAIN_IMAGE_ACQUIRE_INFO, NULL}; + + if (isMotionVectorPass) { + // AppSpaceWarp Need wait both MotionVectorSwapChain and MotionVectorDepthSwapChain are + // ready + XrSwapchainImageWaitInfo waitInfo = {XR_TYPE_SWAPCHAIN_IMAGE_WAIT_INFO}; + waitInfo.timeout = 1000000000; /* timeout in nanoseconds */ + + OXR(xrAcquireSwapchainImage( + frameBuffer->MotionVectorSwapChain.Handle, + &acquireInfo, + &frameBuffer->TextureSwapChainIndex)); + + XrResult res = xrWaitSwapchainImage(frameBuffer->MotionVectorSwapChain.Handle, &waitInfo); + int i = 0; + while (res == XR_TIMEOUT_EXPIRED) { + res = xrWaitSwapchainImage(frameBuffer->MotionVectorSwapChain.Handle, &waitInfo); + i++; + ALOGV( + " Retry xrWaitSwapchainImage %d times due to XR_TIMEOUT_EXPIRED (duration %f seconds)", + i, + waitInfo.timeout * (1E-9)); + } + + OXR(xrAcquireSwapchainImage( + frameBuffer->MotionVectorDepthSwapChain.Handle, + &acquireInfo, + &frameBuffer->TextureSwapChainIndex)); + + res = xrWaitSwapchainImage(frameBuffer->MotionVectorDepthSwapChain.Handle, &waitInfo); + i = 0; + while (res == XR_TIMEOUT_EXPIRED) { + res = xrWaitSwapchainImage(frameBuffer->MotionVectorDepthSwapChain.Handle, &waitInfo); + i++; + ALOGV( + " Retry xrWaitSwapchainImage %d times due to XR_TIMEOUT_EXPIRED (duration %f seconds)", + i, + waitInfo.timeout * (1E-9)); + } + } else { + OXR(xrAcquireSwapchainImage( + frameBuffer->ColorSwapChain.Handle, &acquireInfo, &frameBuffer->TextureSwapChainIndex)); + + XrSwapchainImageWaitInfo waitInfo = {XR_TYPE_SWAPCHAIN_IMAGE_WAIT_INFO}; + waitInfo.timeout = 1000000000; /* timeout in nanoseconds */ + XrResult res = xrWaitSwapchainImage(frameBuffer->ColorSwapChain.Handle, &waitInfo); + int i = 0; + while (res == XR_TIMEOUT_EXPIRED) { + res = xrWaitSwapchainImage(frameBuffer->ColorSwapChain.Handle, &waitInfo); + i++; + ALOGV( + " Retry xrWaitSwapchainImage %d times due to XR_TIMEOUT_EXPIRED (duration %f seconds)", + i, + waitInfo.timeout * (1E-9)); + } + } +} + +static void ovrFramebuffer_Release(ovrFramebuffer* frameBuffer, bool isMotionVectorPass) { + XrSwapchainImageReleaseInfo releaseInfo = {XR_TYPE_SWAPCHAIN_IMAGE_RELEASE_INFO, NULL}; + + // AppSpaceWarp + if (isMotionVectorPass) { + OXR(xrReleaseSwapchainImage(frameBuffer->MotionVectorSwapChain.Handle, &releaseInfo)); + OXR(xrReleaseSwapchainImage(frameBuffer->MotionVectorDepthSwapChain.Handle, &releaseInfo)); + } else { + OXR(xrReleaseSwapchainImage(frameBuffer->ColorSwapChain.Handle, &releaseInfo)); + } +} + +/* +================================================================================ + +KTX Loading + +================================================================================ +*/ + +typedef struct { + int width; + int height; + int depth; + GLenum internalFormat; + GLenum target; + int numberOfArrayElements; + int numberOfFaces; + int numberOfMipmapLevels; + bool mipSizeStored; + + const void* data; // caller responsible for freeing memory + int dataOffset; + int dataSize; +} ktxImageInfo_t; + +bool LoadImageDataFromKTXFile(AAssetManager* amgr, const char* assetName, ktxImageInfo_t* outInfo) { +#pragma pack(1) + typedef struct { + unsigned char identifier[12]; + unsigned int endianness; + unsigned int glType; + unsigned int glTypeSize; + unsigned int glFormat; + unsigned int glInternalFormat; + unsigned int glBaseInternalFormat; + unsigned int pixelWidth; + unsigned int pixelHeight; + unsigned int pixelDepth; + unsigned int numberOfArrayElements; + unsigned int numberOfFaces; + unsigned int numberOfMipmapLevels; + unsigned int bytesOfKeyValueData; + } GlHeaderKTX_t; +#pragma pack() + + AAsset* asset = AAssetManager_open(amgr, assetName, AASSET_MODE_BUFFER); + if (asset == NULL) { + ALOGE("Failed to open %s", assetName); + return false; + } + const void* buffer = AAsset_getBuffer(asset); + if (buffer == NULL) { + ALOGE("Failed to read %s", assetName); + return false; + } + size_t bufferSize = AAsset_getLength(asset); + + if (bufferSize < sizeof(GlHeaderKTX_t)) { + ALOGE("%s: Invalid KTX file", assetName); + return false; + } + + const unsigned char fileIdentifier[12] = { + (unsigned char)'\xAB', + 'K', + 'T', + 'X', + ' ', + '1', + '1', + (unsigned char)'\xBB', + '\r', + '\n', + '\x1A', + '\n'}; + + const GlHeaderKTX_t* header = (GlHeaderKTX_t*)buffer; + if (memcmp(header->identifier, fileIdentifier, sizeof(fileIdentifier)) != 0) { + ALOGE("%s: Invalid KTX file", assetName); + return false; + } + // only support little endian + if (header->endianness != 0x04030201) { + ALOGE("%s: KTX file has wrong endianness", assetName); + return false; + } + // only support unsigned byte + if (header->glType != GL_UNSIGNED_BYTE) { + ALOGE("%s: KTX file has unsupported glType %d", assetName, header->glType); + return false; + } + // skip the key value data + const size_t startTex = sizeof(GlHeaderKTX_t) + header->bytesOfKeyValueData; + if ((startTex < sizeof(GlHeaderKTX_t)) || (startTex >= bufferSize)) { + ALOGE("%s: Invalid KTX header sizes", assetName); + return false; + } + + outInfo->width = header->pixelWidth; + outInfo->height = header->pixelHeight; + outInfo->depth = header->pixelDepth; + outInfo->internalFormat = header->glInternalFormat; + outInfo->numberOfArrayElements = + (header->numberOfArrayElements >= 1) ? header->numberOfArrayElements : 1; + outInfo->numberOfFaces = (header->numberOfFaces >= 1) ? header->numberOfFaces : 1; + outInfo->numberOfMipmapLevels = header->numberOfMipmapLevels; + outInfo->mipSizeStored = true; + outInfo->data = buffer; + outInfo->dataOffset = startTex; + outInfo->dataSize = bufferSize; + + outInfo->target = + ((outInfo->depth > 1) + ? GL_TEXTURE_3D + : ((outInfo->numberOfFaces > 1) + ? ((outInfo->numberOfArrayElements > 1) ? GL_TEXTURE_CUBE_MAP_ARRAY + : GL_TEXTURE_CUBE_MAP) + : ((outInfo->numberOfArrayElements > 1) ? GL_TEXTURE_2D_ARRAY + : GL_TEXTURE_2D))); + + return true; +} + +bool LoadTextureFromKTXImageMemory(const ktxImageInfo_t* info, const int textureId) { + if (info == NULL) { + return false; + } + + if (info->data != NULL) { + GL(glBindTexture(info->target, textureId)); + + const int numDataLevels = info->numberOfMipmapLevels; + const unsigned char* levelData = (const unsigned char*)(info->data) + info->dataOffset; + const unsigned char* endOfBuffer = levelData + (info->dataSize - info->dataOffset); + + for (int i = 0; i < numDataLevels; i++) { + const int w = (info->width >> i) >= 1 ? (info->width >> i) : 1; + const int h = (info->height >> i) >= 1 ? (info->height >> i) : 1; + const int d = (info->depth >> i) >= 1 ? (info->depth >> i) : 1; + + size_t mipSize = 0; + bool compressed = false; + GLenum glFormat = GL_RGBA; + GLenum glDataType = GL_UNSIGNED_BYTE; + switch (info->internalFormat) { + case GL_R8: { + mipSize = w * h * d * 1 * sizeof(unsigned char); + glFormat = GL_RED; + glDataType = GL_UNSIGNED_BYTE; + break; + } + case GL_RG8: { + mipSize = w * h * d * 2 * sizeof(unsigned char); + glFormat = GL_RG; + glDataType = GL_UNSIGNED_BYTE; + break; + } + case GL_RGB8: { + mipSize = w * h * d * 3 * sizeof(unsigned char); + glFormat = GL_RGB; + glDataType = GL_UNSIGNED_BYTE; + break; + } + case GL_RGBA8: { + mipSize = w * h * d * 4 * sizeof(unsigned char); + glFormat = GL_RGBA; + glDataType = GL_UNSIGNED_BYTE; + break; + } + } + if (mipSize == 0) { + ALOGE("Unsupported image format %d", info->internalFormat); + GL(glBindTexture(info->target, 0)); + return false; + } + + if (info->numberOfArrayElements > 1) { + mipSize = mipSize * info->numberOfArrayElements * info->numberOfFaces; + } + + // ALOGV( "mipSize%d = %zu (%d)", i, mipSize, info->numberOfMipmapLevels ); + + if (info->mipSizeStored) { + if (levelData + 4 > endOfBuffer) { + ALOGE("Image data exceeds buffer size"); + GL(glBindTexture(info->target, 0)); + return false; + } + const size_t storedMipSize = (size_t) * (const unsigned int*)levelData; + // ALOGV( "storedMipSize = %zu", storedMipSize ); + mipSize = storedMipSize; + levelData += 4; + } + + if (info->depth <= 1 && info->numberOfArrayElements <= 1) { + for (int face = 0; face < info->numberOfFaces; face++) { + if (mipSize <= 0 || mipSize > (size_t)(endOfBuffer - levelData)) { + ALOGE( + "Mip %d data exceeds buffer size (%zu > %zu)", + i, + mipSize, + (endOfBuffer - levelData)); + GL(glBindTexture(info->target, 0)); + return false; + } + + const GLenum uploadTarget = (info->target == GL_TEXTURE_CUBE_MAP) + ? GL_TEXTURE_CUBE_MAP_POSITIVE_X + : GL_TEXTURE_2D; + if (compressed) { + GL(glCompressedTexSubImage2D( + uploadTarget + face, + i, + 0, + 0, + w, + h, + info->internalFormat, + (GLsizei)mipSize, + levelData)); + } else { + GL(glTexSubImage2D( + uploadTarget + face, i, 0, 0, w, h, glFormat, glDataType, levelData)); + } + + levelData += mipSize; + + if (info->mipSizeStored) { + levelData += 3 - ((mipSize + 3) % 4); + if (levelData > endOfBuffer) { + ALOGE("Image data exceeds buffer size"); + GL(glBindTexture(info->target, 0)); + return false; + } + } + } + } else { + if (mipSize <= 0 || mipSize > (size_t)(endOfBuffer - levelData)) { + ALOGE( + "Mip %d data exceeds buffer size (%zu > %zu)", + i, + mipSize, + (endOfBuffer - levelData)); + GL(glBindTexture(info->target, 0)); + return false; + } + + if (compressed) { + GL(glCompressedTexSubImage3D( + info->target, + i, + 0, + 0, + 0, + w, + h, + d * info->numberOfArrayElements, + info->internalFormat, + (GLsizei)mipSize, + levelData)); + } else { + GL(glTexSubImage3D( + info->target, + i, + 0, + 0, + 0, + w, + h, + d * info->numberOfArrayElements, + glFormat, + glDataType, + levelData)); + } + + levelData += mipSize; + + if (info->mipSizeStored) { + levelData += 3 - ((mipSize + 3) % 4); + if (levelData > endOfBuffer) { + ALOGE("Image data exceeds buffer size"); + GL(glBindTexture(info->target, 0)); + return false; + } + } + } + } + + GL(glTexParameteri( + info->target, + GL_TEXTURE_MIN_FILTER, + (info->numberOfMipmapLevels > 1) ? GL_LINEAR_MIPMAP_LINEAR : GL_LINEAR)); + GL(glTexParameteri(info->target, GL_TEXTURE_MAG_FILTER, GL_LINEAR)); + + GL(glBindTexture(info->target, 0)); + } + + return true; +} + +/* +================================================================================ + +ovrTrackedController + +================================================================================ +*/ + +typedef struct { + bool Active; + XrPosef Pose; +} ovrTrackedController; + +static void ovrTrackedController_Clear(ovrTrackedController* controller) { + controller->Active = false; + XrPosef_CreateIdentity(&controller->Pose); +} + +/* +================================================================================ + +ovrScene + +================================================================================ +*/ + +typedef struct { + ovrTrackedController TrackedController[4]; // left aim, left grip, right aim, right grip + XrMatrix4x4f CubeTransforms[CUBE_NUM_INSTANCES]; +} ovrSceneTransform; + +typedef struct { + bool CreatedScene; + bool CreatedVAOs; + ovrProgram Program; + ovrGeometry GroundPlane; + ovrGeometry Box; + ovrSceneTransform SceneTransform; + + ovrSwapChain QuadSwapChain; + XrSwapchainImageOpenGLESKHR* QuadSwapChainImage; + + // AppSpaceWarp : Shader program for moiton vector pass + ovrProgram MotionVectorProgram; +} ovrScene; + +static void ovrScene_Clear(ovrScene* scene) { + scene->CreatedScene = false; + scene->CreatedVAOs = false; + ovrProgram_Clear(&scene->Program); + // AppSpaceWarp + ovrProgram_Clear(&scene->MotionVectorProgram); + ovrGeometry_Clear(&scene->GroundPlane); + ovrGeometry_Clear(&scene->Box); + for (int i = 0; i < 4; i++) { + ovrTrackedController_Clear(&scene->SceneTransform.TrackedController[i]); + } + + scene->QuadSwapChain.Handle = XR_NULL_HANDLE; + scene->QuadSwapChain.Width = 0; + scene->QuadSwapChain.Height = 0; + scene->QuadSwapChainImage = NULL; +} + +static bool ovrScene_IsCreated(ovrScene* scene) { + return scene->CreatedScene; +} + +static void ovrScene_CreateVAOs(ovrScene* scene) { + if (!scene->CreatedVAOs) { + ovrGeometry_CreateVAO(&scene->GroundPlane); + ovrGeometry_CreateVAO(&scene->Box); + scene->CreatedVAOs = true; + } +} + +static void ovrScene_DestroyVAOs(ovrScene* scene) { + if (scene->CreatedVAOs) { + ovrGeometry_DestroyVAO(&scene->GroundPlane); + ovrGeometry_DestroyVAO(&scene->Box); + scene->CreatedVAOs = false; + } +} + +static void +ovrScene_Create(AAssetManager* amgr, XrInstance instance, XrSession session, ovrScene* scene) { + // Simple ground plane and box geometry. + { + ovrProgram_Create(&scene->Program, VERTEX_SHADER, FRAGMENT_SHADER); + // AppSpaceWarp + ovrProgram_Create( + &scene->MotionVectorProgram, + MOTION_VECTOR_VERTEX_SHADER, + MOTION_VECTOR_FRAGMENT_SHADER); + + ovrGeometry_CreateGroundPlane(&scene->GroundPlane); + ovrGeometry_CreateBox(&scene->Box); + + ovrScene_CreateVAOs(scene); + } + + // Simple checkerboard pattern. + { + static const int QUAD_WIDTH = 256; + static const int QUAD_HEIGHT = 256; + + XrSwapchainCreateInfo swapChainCreateInfo = {XR_TYPE_SWAPCHAIN_CREATE_INFO}; + swapChainCreateInfo.createFlags = XR_SWAPCHAIN_CREATE_STATIC_IMAGE_BIT; + swapChainCreateInfo.usageFlags = + XR_SWAPCHAIN_USAGE_SAMPLED_BIT | XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT; + swapChainCreateInfo.format = GL_SRGB8_ALPHA8; + swapChainCreateInfo.sampleCount = 1; + swapChainCreateInfo.width = QUAD_WIDTH; + swapChainCreateInfo.height = QUAD_HEIGHT; + swapChainCreateInfo.faceCount = 1; + swapChainCreateInfo.arraySize = 1; + swapChainCreateInfo.mipCount = 1; + + scene->QuadSwapChain.Width = swapChainCreateInfo.width; + scene->QuadSwapChain.Height = swapChainCreateInfo.height; + + // Create the swapchain. + OXR(xrCreateSwapchain(session, &swapChainCreateInfo, &scene->QuadSwapChain.Handle)); + // Get the number of swapchain images. + uint32_t length; + OXR(xrEnumerateSwapchainImages(scene->QuadSwapChain.Handle, 0, &length, NULL)); + scene->QuadSwapChainImage = + (XrSwapchainImageOpenGLESKHR*)malloc(length * sizeof(XrSwapchainImageOpenGLESKHR)); + for (uint32_t i = 0; i < length; i++) { + scene->QuadSwapChainImage[i].type = XR_TYPE_SWAPCHAIN_IMAGE_OPENGL_ES_KHR; + scene->QuadSwapChainImage[i].next = NULL; + } + OXR(xrEnumerateSwapchainImages( + scene->QuadSwapChain.Handle, + length, + &length, + (XrSwapchainImageBaseHeader*)scene->QuadSwapChainImage)); + + uint32_t* texData = (uint32_t*)malloc(QUAD_WIDTH * QUAD_HEIGHT * sizeof(uint32_t)); + + for (int y = 0; y < QUAD_HEIGHT; y++) { + for (int x = 0; x < QUAD_WIDTH; x++) { + int gray = ((x ^ (x >> 1)) ^ (y ^ (y >> 1))) & 0xff; + int r = gray + 255.0f * ((1.0f - (gray / 255.0f)) * (x / (QUAD_WIDTH - 1.0f))); + int g = gray + 255.0f * ((1.0f - (gray / 255.0f)) * (y / (QUAD_HEIGHT - 1.0f))); + int rgba = (0x88 << 24) | (gray << 16) | (g << 8) | r; + texData[y * QUAD_WIDTH + x] = rgba; + } + } + + const int texId = scene->QuadSwapChainImage[0].image; + + glBindTexture(GL_TEXTURE_2D, texId); + glTexSubImage2D( + GL_TEXTURE_2D, 0, 0, 0, QUAD_WIDTH, QUAD_HEIGHT, GL_RGBA, GL_UNSIGNED_BYTE, texData); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); + GLfloat borderColor[] = {0.0f, 0.0f, 0.0f, 0.0f}; + glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor); + + glBindTexture(GL_TEXTURE_2D, 0); + + free(texData); + + uint32_t index = 0; + XrSwapchainImageAcquireInfo acquireInfo = {XR_TYPE_SWAPCHAIN_IMAGE_ACQUIRE_INFO, NULL}; + OXR(xrAcquireSwapchainImage(scene->QuadSwapChain.Handle, &acquireInfo, &index)); + + XrSwapchainImageWaitInfo waitInfo = {XR_TYPE_SWAPCHAIN_IMAGE_WAIT_INFO, NULL, 0}; + OXR(xrWaitSwapchainImage(scene->QuadSwapChain.Handle, &waitInfo)); + + XrSwapchainImageReleaseInfo releaseInfo = {XR_TYPE_SWAPCHAIN_IMAGE_RELEASE_INFO, NULL}; + OXR(xrReleaseSwapchainImage(scene->QuadSwapChain.Handle, &releaseInfo)); + } + + scene->CreatedScene = true; +} + +static void ovrScene_Destroy(ovrScene* scene) { + ovrScene_DestroyVAOs(scene); + + ovrProgram_Destroy(&scene->Program); + // AppSpaceWarp + ovrProgram_Destroy(&scene->MotionVectorProgram); + ovrGeometry_Destroy(&scene->GroundPlane); + ovrGeometry_Destroy(&scene->Box); + + OXR(xrDestroySwapchain(scene->QuadSwapChain.Handle)); + free(scene->QuadSwapChainImage); + + scene->CreatedScene = false; +} + +/* +================================================================================ + +ovrRenderer + +================================================================================ +*/ + +typedef struct { + ovrFramebuffer FrameBuffer[ovrMaxNumEyes]; +} ovrRenderer; + +static void ovrRenderer_Clear(ovrRenderer* renderer) { + for (int eye = 0; eye < ovrMaxNumEyes; eye++) { + ovrFramebuffer_Clear(&renderer->FrameBuffer[eye]); + } +} + +static void ovrRenderer_Create( + XrSession session, + ovrRenderer* renderer, + int suggestedEyeTextureWidth, + int suggestedEyeTextureHeight, + int recommendedMotionVectorWidth, + int recommendedMotionVectorHeight) { + // Create the frame buffers. + for (int eye = 0; eye < ovrMaxNumEyes; eye++) { + ovrFramebuffer_Create( + session, + &renderer->FrameBuffer[eye], + GL_SRGB8_ALPHA8, + suggestedEyeTextureWidth, + suggestedEyeTextureHeight, + recommendedMotionVectorWidth, + recommendedMotionVectorHeight, + NUM_MULTI_SAMPLES); + } +} + +static void ovrRenderer_Destroy(ovrRenderer* renderer) { + for (int eye = 0; eye < ovrMaxNumEyes; eye++) { + ovrFramebuffer_Destroy(&renderer->FrameBuffer[eye]); + } +} + +typedef struct { + // Matrix in CurrentSpace + XrMatrix4x4f ViewMatrix[ovrMaxNumEyes]; + XrMatrix4x4f ProjectionMatrix[ovrMaxNumEyes]; + + // CurrentSpace's pose in world space + XrPosef XrSpacePoseInWorld; +} ovrSceneMatrices; + +static void RenderScene( + ovrRenderer* renderer, + const ovrScene* scene, + const ovrSceneMatrices* sceneMatrices, + const ovrSceneMatrices* prevFrameSceneMatrices, + const ovrSceneTransform* prevFrameSceneTransform, + bool isMotionVectorPass) { + // AppSpaceWarp + const ovrProgram* selectProgram = + isMotionVectorPass ? &scene->MotionVectorProgram : &scene->Program; + + for (int eye = 0; eye < ovrMaxNumEyes; eye++) { + ovrFramebuffer* frameBuffer = &renderer->FrameBuffer[eye]; + + ovrFramebuffer_Acquire(frameBuffer, isMotionVectorPass); + + // Set the current framebuffer. + ovrFramebuffer_SetCurrent(frameBuffer, isMotionVectorPass); + GL(glUseProgram(selectProgram->Program)); + + // Static background has same MODEL_MATRIX & PREV_MODEL_MATRIX + XrMatrix4x4f modelMatrix; + XrMatrix4x4f_CreateIdentity(&modelMatrix); + glUniformMatrix4fv( + selectProgram->UniformLocation[MODEL_MATRIX], 1, GL_FALSE, &modelMatrix.m[0]); + + if (selectProgram->UniformLocation[PREV_MODEL_MATRIX] >= 0) { + glUniformMatrix4fv( + selectProgram->UniformLocation[PREV_MODEL_MATRIX], 1, GL_FALSE, &modelMatrix.m[0]); + } + + XrMatrix4x4f viewProjMatrix; + XrMatrix4x4f_Multiply( + &viewProjMatrix, + &sceneMatrices->ProjectionMatrix[eye], + &sceneMatrices->ViewMatrix[eye]); + glUniformMatrix4fv( + selectProgram->UniformLocation[VIEW_PROJ_MATRIX], 1, GL_FALSE, &viewProjMatrix.m[0]); + + if (selectProgram->UniformLocation[PREV_VIEW_PROJ_MATRIX] >= 0) { + XrMatrix4x4f prevViewProjMatrix; + XrMatrix4x4f_Multiply( + &prevViewProjMatrix, + &prevFrameSceneMatrices->ProjectionMatrix[eye], + &prevFrameSceneMatrices->ViewMatrix[eye]); + glUniformMatrix4fv( + selectProgram->UniformLocation[PREV_VIEW_PROJ_MATRIX], + 1, + GL_FALSE, + &prevViewProjMatrix.m[0]); + } + + GL(glEnable(GL_SCISSOR_TEST)); + GL(glDepthMask(GL_TRUE)); + GL(glEnable(GL_DEPTH_TEST)); + GL(glDepthFunc(GL_LEQUAL)); + GL(glDisable(GL_CULL_FACE)); + // GL( glCullFace( GL_BACK ) ); + if (isMotionVectorPass) { + GL(glViewport(0, 0, frameBuffer->MotionVectorWidth, frameBuffer->MotionVectorHeight)); + GL(glScissor(0, 0, frameBuffer->MotionVectorWidth, frameBuffer->MotionVectorHeight)); + } else { + GL(glViewport(0, 0, frameBuffer->Width, frameBuffer->Height)); + GL(glScissor(0, 0, frameBuffer->Width, frameBuffer->Height)); + } + + GL(glClearColor(0.02f, 0.02f, 0.1f, 1.0f)); + GL(glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)); + GL(glBindVertexArray(scene->GroundPlane.VertexArrayObject)); + GL(glDrawElements(GL_TRIANGLES, scene->GroundPlane.IndexCount, GL_UNSIGNED_SHORT, NULL)); + + for (int i = 0; i < 4; i++) { + if (scene->SceneTransform.TrackedController[i].Active == false) { + continue; + } + + // Current controller pose + XrPosef poseInWorld; + XrPosef_Multiply( + &poseInWorld, + &sceneMatrices->XrSpacePoseInWorld, + &scene->SceneTransform.TrackedController[i].Pose); + XrMatrix4x4f pose; + XrMatrix4x4f_CreateFromRigidTransform(&pose, &poseInWorld); + + XrMatrix4x4f scale; + if (i & 1) { + XrMatrix4x4f_CreateScale(&scale, 0.03f, 0.03f, 0.03f); + } else { + XrMatrix4x4f_CreateScale(&scale, 0.02f, 0.02f, 0.06f); + } + XrMatrix4x4f model; + XrMatrix4x4f_Multiply(&model, &pose, &scale); + glUniformMatrix4fv( + selectProgram->UniformLocation[MODEL_MATRIX], 1, GL_FALSE, &model.m[0]); + + // Previous controller pose + if (prevFrameSceneTransform->TrackedController[i].Active) { + XrPosef_Multiply( + &poseInWorld, + &prevFrameSceneMatrices->XrSpacePoseInWorld, + &prevFrameSceneTransform->TrackedController[i].Pose); + XrMatrix4x4f_CreateFromRigidTransform(&pose, &poseInWorld); + if (i & 1) { + XrMatrix4x4f_CreateScale(&scale, 0.03f, 0.03f, 0.03f); + } else { + XrMatrix4x4f_CreateScale(&scale, 0.02f, 0.02f, 0.06f); + } + XrMatrix4x4f_Multiply(&model, &pose, &scale); + } + + if (selectProgram->UniformLocation[PREV_MODEL_MATRIX] >= 0) { + glUniformMatrix4fv( + selectProgram->UniformLocation[PREV_MODEL_MATRIX], 1, GL_FALSE, &model.m[0]); + } + + GL(glBindVertexArray(scene->Box.VertexArrayObject)); + GL(glDrawElements(GL_TRIANGLES, scene->Box.IndexCount, GL_UNSIGNED_SHORT, NULL)); + } + + // Render cubes + for (int i = 0; i < CUBE_NUM_INSTANCES; i++) { + glUniformMatrix4fv( + selectProgram->UniformLocation[MODEL_MATRIX], + 1, + GL_FALSE, + &scene->SceneTransform.CubeTransforms[i].m[0]); + if (selectProgram->UniformLocation[PREV_MODEL_MATRIX] >= 0) { + glUniformMatrix4fv( + selectProgram->UniformLocation[PREV_MODEL_MATRIX], + 1, + GL_FALSE, + &prevFrameSceneTransform->CubeTransforms[i].m[0]); + } + + GL(glBindVertexArray(scene->Box.VertexArrayObject)); + GL(glDrawElements(GL_TRIANGLES, scene->Box.IndexCount, GL_UNSIGNED_SHORT, NULL)); + } + + GL(glBindVertexArray(0)); + GL(glUseProgram(0)); + + ovrFramebuffer_Resolve(frameBuffer, isMotionVectorPass); + ovrFramebuffer_Release(frameBuffer, isMotionVectorPass); + } +} + +static void ovrRenderer_RenderFrame( + ovrRenderer* renderer, + const ovrScene* scene, + const ovrSceneMatrices* sceneMatrices, + const ovrSceneMatrices* prevFrameSceneMatrices, + const ovrSceneTransform* prevFrameSceneTransform) { + // Render main pass + RenderScene( + renderer, scene, sceneMatrices, prevFrameSceneMatrices, prevFrameSceneTransform, false); + + // AppSpaceWarp Render Motion vector pass + RenderScene( + renderer, scene, sceneMatrices, prevFrameSceneMatrices, prevFrameSceneTransform, true); + ovrFramebuffer_SetNone(); +} + +/* +================================================================================ + +ovrApp + +================================================================================ +*/ + +typedef struct { + ovrEgl Egl; + bool Resumed; + bool Focused; + + XrInstance Instance; + XrSession Session; + XrViewConfigurationProperties ViewportConfig; + XrViewConfigurationView ViewConfigurationView[ovrMaxNumEyes]; + XrSystemId SystemId; + XrSpace HeadSpace; + XrSpace LocalSpace; + XrSpace StageSpace; + XrSpace FakeStageSpace; + XrSpace CurrentSpace; + bool SessionActive; + + ovrScene Scene; + + int SwapInterval; + int CpuLevel; + int GpuLevel; + // These threads will be marked as performance threads. + int MainThreadTid; + int RenderThreadTid; + ovrCompositorLayer_Union Layers[ovrMaxLayerCount]; + int LayerCount; + + bool TouchPadDownLastFrame; + ovrRenderer Renderer; +} ovrApp; + +static void ovrApp_Clear(ovrApp* app) { + app->Resumed = false; + app->Focused = false; + app->Instance = XR_NULL_HANDLE; + app->Session = XR_NULL_HANDLE; + memset(&app->ViewportConfig, 0, sizeof(XrViewConfigurationProperties)); + memset(&app->ViewConfigurationView, 0, ovrMaxNumEyes * sizeof(XrViewConfigurationView)); + app->SystemId = XR_NULL_SYSTEM_ID; + app->HeadSpace = XR_NULL_HANDLE; + app->LocalSpace = XR_NULL_HANDLE; + app->StageSpace = XR_NULL_HANDLE; + app->FakeStageSpace = XR_NULL_HANDLE; + app->CurrentSpace = XR_NULL_HANDLE; + app->SessionActive = false; + app->SwapInterval = 1; + memset(app->Layers, 0, sizeof(ovrCompositorLayer_Union) * ovrMaxLayerCount); + app->LayerCount = 0; + app->CpuLevel = 2; + app->GpuLevel = 2; + app->MainThreadTid = 0; + app->RenderThreadTid = 0; + app->TouchPadDownLastFrame = false; + + ovrEgl_Clear(&app->Egl); + ovrScene_Clear(&app->Scene); + ovrRenderer_Clear(&app->Renderer); +} + +static void ovrApp_Destroy(ovrApp* app) { + ovrApp_Clear(app); +} + +static void ovrApp_HandleSessionStateChanges(ovrApp* app, XrSessionState state) { + if (state == XR_SESSION_STATE_READY) { + assert(app->Resumed); + assert(app->SessionActive == false); + + XrSessionBeginInfo sessionBeginInfo = {XR_TYPE_SESSION_BEGIN_INFO}; + sessionBeginInfo.primaryViewConfigurationType = app->ViewportConfig.viewConfigurationType; + + XrResult result; + OXR(result = xrBeginSession(app->Session, &sessionBeginInfo)); + + app->SessionActive = (result == XR_SUCCESS); + + // Set session state once we have entered VR mode and have a valid session object. + if (app->SessionActive) { + XrPerfSettingsLevelEXT cpuPerfLevel = XR_PERF_SETTINGS_LEVEL_SUSTAINED_HIGH_EXT; + switch (app->CpuLevel) { + case 0: + cpuPerfLevel = XR_PERF_SETTINGS_LEVEL_POWER_SAVINGS_EXT; + break; + case 1: + cpuPerfLevel = XR_PERF_SETTINGS_LEVEL_SUSTAINED_LOW_EXT; + break; + case 2: + cpuPerfLevel = XR_PERF_SETTINGS_LEVEL_SUSTAINED_HIGH_EXT; + break; + case 3: + cpuPerfLevel = XR_PERF_SETTINGS_LEVEL_BOOST_EXT; + break; + default: + ALOGE("Invalid CPU level %d", app->CpuLevel); + break; + } + + XrPerfSettingsLevelEXT gpuPerfLevel = XR_PERF_SETTINGS_LEVEL_SUSTAINED_HIGH_EXT; + switch (app->GpuLevel) { + case 0: + gpuPerfLevel = XR_PERF_SETTINGS_LEVEL_POWER_SAVINGS_EXT; + break; + case 1: + gpuPerfLevel = XR_PERF_SETTINGS_LEVEL_SUSTAINED_LOW_EXT; + break; + case 2: + gpuPerfLevel = XR_PERF_SETTINGS_LEVEL_SUSTAINED_HIGH_EXT; + break; + case 3: + gpuPerfLevel = XR_PERF_SETTINGS_LEVEL_BOOST_EXT; + break; + default: + ALOGE("Invalid GPU level %d", app->GpuLevel); + break; + } + + PFN_xrPerfSettingsSetPerformanceLevelEXT pfnPerfSettingsSetPerformanceLevelEXT = NULL; + OXR(xrGetInstanceProcAddr( + app->Instance, + "xrPerfSettingsSetPerformanceLevelEXT", + (PFN_xrVoidFunction*)(&pfnPerfSettingsSetPerformanceLevelEXT))); + + OXR(pfnPerfSettingsSetPerformanceLevelEXT( + app->Session, XR_PERF_SETTINGS_DOMAIN_CPU_EXT, cpuPerfLevel)); + OXR(pfnPerfSettingsSetPerformanceLevelEXT( + app->Session, XR_PERF_SETTINGS_DOMAIN_GPU_EXT, gpuPerfLevel)); + + PFN_xrSetAndroidApplicationThreadKHR pfnSetAndroidApplicationThreadKHR = NULL; + OXR(xrGetInstanceProcAddr( + app->Instance, + "xrSetAndroidApplicationThreadKHR", + (PFN_xrVoidFunction*)(&pfnSetAndroidApplicationThreadKHR))); + + OXR(pfnSetAndroidApplicationThreadKHR( + app->Session, XR_ANDROID_THREAD_TYPE_APPLICATION_MAIN_KHR, app->MainThreadTid)); + OXR(pfnSetAndroidApplicationThreadKHR( + app->Session, XR_ANDROID_THREAD_TYPE_RENDERER_MAIN_KHR, app->RenderThreadTid)); + } + } else if (state == XR_SESSION_STATE_STOPPING) { + assert(app->Resumed == false); + assert(app->SessionActive); + + OXR(xrEndSession(app->Session)); + app->SessionActive = false; + } +} + +static void ovrApp_HandleXrEvents(ovrApp* app) { + XrEventDataBuffer eventDataBuffer = {XR_TYPE_EVENT_DATA_BUFFER}; + + // Poll for events + for (;;) { + XrEventDataBaseHeader* baseEventHeader = (XrEventDataBaseHeader*)(&eventDataBuffer); + baseEventHeader->type = XR_TYPE_EVENT_DATA_BUFFER; + baseEventHeader->next = NULL; + XrResult r; + OXR(r = xrPollEvent(app->Instance, &eventDataBuffer)); + if (r != XR_SUCCESS) { + break; + } + + switch (baseEventHeader->type) { + case XR_TYPE_EVENT_DATA_EVENTS_LOST: + ALOGV("xrPollEvent: received XR_TYPE_EVENT_DATA_EVENTS_LOST event"); + break; + case XR_TYPE_EVENT_DATA_INSTANCE_LOSS_PENDING: { + const XrEventDataInstanceLossPending* instance_loss_pending_event = + (XrEventDataInstanceLossPending*)(baseEventHeader); + ALOGV( + "xrPollEvent: received XR_TYPE_EVENT_DATA_INSTANCE_LOSS_PENDING event: time %f", + FromXrTime(instance_loss_pending_event->lossTime)); + } break; + case XR_TYPE_EVENT_DATA_INTERACTION_PROFILE_CHANGED: + ALOGV("xrPollEvent: received XR_TYPE_EVENT_DATA_INTERACTION_PROFILE_CHANGED event"); + break; + case XR_TYPE_EVENT_DATA_PERF_SETTINGS_EXT: { + const XrEventDataPerfSettingsEXT* perf_settings_event = + (XrEventDataPerfSettingsEXT*)(baseEventHeader); + ALOGV( + "xrPollEvent: received XR_TYPE_EVENT_DATA_PERF_SETTINGS_EXT event: type %d subdomain %d : level %d -> level %d", + perf_settings_event->type, + perf_settings_event->subDomain, + perf_settings_event->fromLevel, + perf_settings_event->toLevel); + } break; + case XR_TYPE_EVENT_DATA_REFERENCE_SPACE_CHANGE_PENDING: { + XrEventDataReferenceSpaceChangePending* ref_space_change_event = + (XrEventDataReferenceSpaceChangePending*)(baseEventHeader); + ALOGV( + "xrPollEvent: received XR_TYPE_EVENT_DATA_REFERENCE_SPACE_CHANGE_PENDING event: changed space: %d for session %p at time %f", + ref_space_change_event->referenceSpaceType, + (void*)ref_space_change_event->session, + FromXrTime(ref_space_change_event->changeTime)); + } break; + case XR_TYPE_EVENT_DATA_SESSION_STATE_CHANGED: { + const XrEventDataSessionStateChanged* session_state_changed_event = + (XrEventDataSessionStateChanged*)(baseEventHeader); + ALOGV( + "xrPollEvent: received XR_TYPE_EVENT_DATA_SESSION_STATE_CHANGED: %d for session %p at time %f", + session_state_changed_event->state, + (void*)session_state_changed_event->session, + FromXrTime(session_state_changed_event->time)); + + switch (session_state_changed_event->state) { + case XR_SESSION_STATE_FOCUSED: + app->Focused = true; + break; + case XR_SESSION_STATE_VISIBLE: + app->Focused = false; + break; + case XR_SESSION_STATE_READY: + case XR_SESSION_STATE_STOPPING: + ovrApp_HandleSessionStateChanges(app, session_state_changed_event->state); + break; + default: + break; + } + } break; + default: + ALOGV("xrPollEvent: Unknown event"); + break; + } + } +} + +/* +================================================================================ + +Native Activity + +================================================================================ +*/ + +/** + * Process the next main command. + */ +static void app_handle_cmd(struct android_app* app, int32_t cmd) { + ovrApp* appState = (ovrApp*)app->userData; + + switch (cmd) { + // There is no APP_CMD_CREATE. The ANativeActivity creates the + // application thread from onCreate(). The application thread + // then calls android_main(). + case APP_CMD_START: { + ALOGV("onStart()"); + ALOGV(" APP_CMD_START"); + break; + } + case APP_CMD_RESUME: { + ALOGV("onResume()"); + ALOGV(" APP_CMD_RESUME"); + appState->Resumed = true; + break; + } + case APP_CMD_PAUSE: { + ALOGV("onPause()"); + ALOGV(" APP_CMD_PAUSE"); + appState->Resumed = false; + break; + } + case APP_CMD_STOP: { + ALOGV("onStop()"); + ALOGV(" APP_CMD_STOP"); + break; + } + case APP_CMD_DESTROY: { + ALOGV("onDestroy()"); + ALOGV(" APP_CMD_DESTROY"); + ovrApp_Clear(appState); + break; + } + case APP_CMD_INIT_WINDOW: { + ALOGV("surfaceCreated()"); + ALOGV(" APP_CMD_INIT_WINDOW"); + break; + } + case APP_CMD_TERM_WINDOW: { + ALOGV("surfaceDestroyed()"); + ALOGV(" APP_CMD_TERM_WINDOW"); + break; + } + } +} + +void UpdateStageBounds(ovrApp* pappState) { + XrExtent2Df stageBounds = {0}; + + XrResult result; + OXR(result = xrGetReferenceSpaceBoundsRect( + pappState->Session, XR_REFERENCE_SPACE_TYPE_STAGE, &stageBounds)); + if (result != XR_SUCCESS) { + ALOGV("Stage bounds query failed: using small defaults"); + stageBounds.width = 1.0f; + stageBounds.height = 1.0f; + + pappState->CurrentSpace = pappState->FakeStageSpace; + } + + ALOGV("Stage bounds: width = %f, depth %f", stageBounds.width, stageBounds.height); + + // using fixed ground plan for bound visual + const float halfWidth = 1.5f; + const float halfDepth = 1.5f; + + ovrGeometry_Destroy(&pappState->Scene.GroundPlane); + ovrGeometry_DestroyVAO(&pappState->Scene.GroundPlane); + ovrGeometry_CreateStagePlane( + &pappState->Scene.GroundPlane, -halfWidth, -halfDepth, halfWidth, halfDepth); + ovrGeometry_CreateVAO(&pappState->Scene.GroundPlane); +} + +ovrApp appState; + +XrInstance ovrApp_GetInstance() { + return appState.Instance; +} + +static XrActionSet CreateActionSet(int priority, const char* name, const char* localizedName) { + XrActionSetCreateInfo asci = {XR_TYPE_ACTION_SET_CREATE_INFO}; + asci.priority = priority; + strncpy(asci.actionSetName, name, sizeof(asci.actionSetName)); + strncpy(asci.localizedActionSetName, localizedName, sizeof(asci.localizedActionSetName)); + XrActionSet actionSet = XR_NULL_HANDLE; + OXR(xrCreateActionSet(appState.Instance, &asci, &actionSet)); + return actionSet; +} + +static XrAction CreateAction( + XrActionSet actionSet, + XrActionType type, + const char* actionName, + const char* localizedName, + int countSubactionPaths, + XrPath* subactionPaths) { + ALOGV("CreateAction %s, %" PRIi32, actionName, countSubactionPaths); + + XrActionCreateInfo aci = {XR_TYPE_ACTION_CREATE_INFO}; + aci.actionType = type; + if (countSubactionPaths > 0) { + aci.countSubactionPaths = countSubactionPaths; + aci.subactionPaths = subactionPaths; + } + strncpy(aci.actionName, actionName, sizeof(aci.actionName)); + strncpy( + aci.localizedActionName, + localizedName ? localizedName : actionName, + sizeof(aci.localizedActionName)); + XrAction action = XR_NULL_HANDLE; + OXR(xrCreateAction(actionSet, &aci, &action)); + return action; +} + +static XrActionSuggestedBinding ActionSuggestedBinding(XrAction action, const char* bindingString) { + XrActionSuggestedBinding asb; + asb.action = action; + XrPath bindingPath; + OXR(xrStringToPath(appState.Instance, bindingString, &bindingPath)); + asb.binding = bindingPath; + return asb; +} + +static XrSpace CreateActionSpace(XrAction poseAction, XrPath subactionPath) { + XrActionSpaceCreateInfo asci = {XR_TYPE_ACTION_SPACE_CREATE_INFO}; + asci.action = poseAction; + asci.poseInActionSpace.orientation.w = 1.0f; + asci.subactionPath = subactionPath; + XrSpace actionSpace = XR_NULL_HANDLE; + OXR(xrCreateActionSpace(appState.Session, &asci, &actionSpace)); + return actionSpace; +} + +static XrActionStateBoolean GetActionStateBoolean(XrAction action) { + XrActionStateGetInfo getInfo = {XR_TYPE_ACTION_STATE_GET_INFO}; + getInfo.action = action; + + XrActionStateBoolean state = {XR_TYPE_ACTION_STATE_BOOLEAN}; + + OXR(xrGetActionStateBoolean(appState.Session, &getInfo, &state)); + return state; +} + +static XrActionStateFloat GetActionStateFloat(XrAction action) { + XrActionStateGetInfo getInfo = {XR_TYPE_ACTION_STATE_GET_INFO}; + getInfo.action = action; + + XrActionStateFloat state = {XR_TYPE_ACTION_STATE_FLOAT}; + + OXR(xrGetActionStateFloat(appState.Session, &getInfo, &state)); + return state; +} + +static XrActionStateVector2f GetActionStateVector2(XrAction action) { + XrActionStateGetInfo getInfo = {XR_TYPE_ACTION_STATE_GET_INFO}; + getInfo.action = action; + + XrActionStateVector2f state = {XR_TYPE_ACTION_STATE_VECTOR2F}; + + OXR(xrGetActionStateVector2f(appState.Session, &getInfo, &state)); + return state; +} + +static bool ActionPoseIsActive(XrAction action, XrPath subactionPath) { + XrActionStateGetInfo getInfo = {XR_TYPE_ACTION_STATE_GET_INFO}; + getInfo.action = action; + getInfo.subactionPath = subactionPath; + + XrActionStatePose state = {XR_TYPE_ACTION_STATE_POSE}; + OXR(xrGetActionStatePose(appState.Session, &getInfo, &state)); + return state.isActive != XR_FALSE; +} + +typedef struct { + XrSpaceLocation loc; + XrSpaceVelocity vel; +} LocVel; + +static LocVel GetSpaceLocVel(XrSpace space, XrTime time) { + LocVel lv = {{XR_TYPE_SPACE_LOCATION}, {XR_TYPE_SPACE_VELOCITY}}; + lv.loc.next = &lv.vel; + OXR(xrLocateSpace(space, appState.CurrentSpace, time, &lv.loc)); + lv.loc.next = NULL; // pointer no longer valid or necessary + return lv; +} + +/** + * This is the main entry point of a native application that is using + * android_native_app_glue. It runs in its own thread, with its own + * event loop for receiving input events and doing other things. + */ +void android_main(struct android_app* app) { + ALOGV("----------------------------------------------------------------"); + ALOGV("android_app_entry()"); + ALOGV(" android_main()"); + + cameraPos.x = 0.0f; + cameraPos.y = 0.0f; + cameraPos.z = 0.0f; + rotationY = 0.0f; + + JNIEnv* Env; +#if defined(__cplusplus) + app->activity->vm->AttachCurrentThread(&Env, NULL); +#else + (*app->activity->vm)->AttachCurrentThread(app->activity->vm, &Env, NULL); +#endif + + // Note that AttachCurrentThread will reset the thread name. + prctl(PR_SET_NAME, (long)"OVR::Main", 0, 0, 0); + + ovrApp_Clear(&appState); + + PFN_xrInitializeLoaderKHR xrInitializeLoaderKHR; + xrGetInstanceProcAddr( + XR_NULL_HANDLE, "xrInitializeLoaderKHR", (PFN_xrVoidFunction*)&xrInitializeLoaderKHR); + if (xrInitializeLoaderKHR != NULL) { + XrLoaderInitInfoAndroidKHR loaderInitializeInfoAndroid = { + XR_TYPE_LOADER_INIT_INFO_ANDROID_KHR}; + loaderInitializeInfoAndroid.applicationVM = app->activity->vm; + loaderInitializeInfoAndroid.applicationContext = app->activity->clazz; + xrInitializeLoaderKHR((XrLoaderInitInfoBaseHeaderKHR*)&loaderInitializeInfoAndroid); + } + + // Log available layers. + { + uint32_t numLayers = 0; + OXR(xrEnumerateApiLayerProperties(0, &numLayers, NULL)); + + XrApiLayerProperties* layerProperties = + (XrApiLayerProperties*)malloc(numLayers * sizeof(XrApiLayerProperties)); + + for (uint32_t i = 0; i < numLayers; i++) { + layerProperties[i].type = XR_TYPE_API_LAYER_PROPERTIES; + layerProperties[i].next = NULL; + } + + OXR(xrEnumerateApiLayerProperties(numLayers, &numLayers, layerProperties)); + + for (uint32_t i = 0; i < numLayers; i++) { + ALOGV("Found layer %s", layerProperties[i].layerName); + } + + free(layerProperties); + } + + // Check that the extensions required are present. + const char* const requiredExtensionNames[] = { + XR_KHR_OPENGL_ES_ENABLE_EXTENSION_NAME, + XR_EXT_PERFORMANCE_SETTINGS_EXTENSION_NAME, + XR_KHR_ANDROID_THREAD_SETTINGS_EXTENSION_NAME, + XR_FB_SWAPCHAIN_UPDATE_STATE_EXTENSION_NAME, + XR_FB_SWAPCHAIN_UPDATE_STATE_OPENGL_ES_EXTENSION_NAME, + // AppSpaceWarp Enable the extension + XR_FB_SPACE_WARP_EXTENSION_NAME}; + const uint32_t numRequiredExtensions = + sizeof(requiredExtensionNames) / sizeof(requiredExtensionNames[0]); + + // Check the list of required extensions against what is supported by the runtime. + { + uint32_t numExtensions = 0; + OXR(xrEnumerateInstanceExtensionProperties( + NULL, 0, &numExtensions, NULL)); + ALOGV("xrEnumerateInstanceExtensionProperties found %u extension(s).", numExtensions); + + XrExtensionProperties* extensionProperties = + (XrExtensionProperties*)malloc(numExtensions * sizeof(XrExtensionProperties)); + + for (uint32_t i = 0; i < numExtensions; i++) { + extensionProperties[i].type = XR_TYPE_EXTENSION_PROPERTIES; + extensionProperties[i].next = NULL; + } + + OXR(xrEnumerateInstanceExtensionProperties( + NULL, numExtensions, &numExtensions, extensionProperties)); + for (uint32_t i = 0; i < numExtensions; i++) { + ALOGV("Extension #%d = '%s'.", i, extensionProperties[i].extensionName); + } + + for (uint32_t i = 0; i < numRequiredExtensions; i++) { + bool found = false; + for (uint32_t j = 0; j < numExtensions; j++) { + if (!strcmp(requiredExtensionNames[i], extensionProperties[j].extensionName)) { + ALOGV("Found required extension %s", requiredExtensionNames[i]); + found = true; + break; + } + } + if (!found) { + ALOGE("Failed to find required extension %s", requiredExtensionNames[i]); + exit(1); + } + } + + free(extensionProperties); + } + + // Create the OpenXR instance. + XrApplicationInfo appInfo; + memset(&appInfo, 0, sizeof(appInfo)); + strncpy(appInfo.applicationName, "OpenXR_SpaceWarp", sizeof(appInfo.applicationName)); + appInfo.applicationVersion = 0; + strncpy(appInfo.engineName, "Oculus Mobile Sample", sizeof(appInfo.engineName)); + appInfo.engineVersion = 0; + appInfo.apiVersion = XR_MAKE_VERSION(1, 0, 34); + + XrInstanceCreateInfo instanceCreateInfo = {XR_TYPE_INSTANCE_CREATE_INFO}; + instanceCreateInfo.createFlags = 0; + instanceCreateInfo.applicationInfo = appInfo; + instanceCreateInfo.enabledApiLayerCount = 0; + instanceCreateInfo.enabledApiLayerNames = NULL; + instanceCreateInfo.enabledExtensionCount = numRequiredExtensions; + instanceCreateInfo.enabledExtensionNames = requiredExtensionNames; + + XrResult initResult; + OXR(initResult = xrCreateInstance(&instanceCreateInfo, &appState.Instance)); + if (initResult != XR_SUCCESS) { + ALOGE("Failed to create XR instance: %d.", initResult); + exit(1); + } + + XrInstanceProperties instanceInfo = {XR_TYPE_INSTANCE_PROPERTIES}; + OXR(xrGetInstanceProperties(appState.Instance, &instanceInfo)); + ALOGV( + "Runtime %s: Version : %u.%u.%u", + instanceInfo.runtimeName, + XR_VERSION_MAJOR(instanceInfo.runtimeVersion), + XR_VERSION_MINOR(instanceInfo.runtimeVersion), + XR_VERSION_PATCH(instanceInfo.runtimeVersion)); + + XrSystemGetInfo systemGetInfo = {XR_TYPE_SYSTEM_GET_INFO}; + systemGetInfo.formFactor = XR_FORM_FACTOR_HEAD_MOUNTED_DISPLAY; + + XrSystemId systemId; + OXR(initResult = xrGetSystem(appState.Instance, &systemGetInfo, &systemId)); + if (initResult != XR_SUCCESS) { + if (initResult == XR_ERROR_FORM_FACTOR_UNAVAILABLE) { + ALOGE("Failed to get system; the specified form factor is not available. Is your headset connected?"); + } else { + ALOGE("xrGetSystem failed, error %d", initResult); + } + exit(1); + } + + // AppSpaceWarp Get recommended motion vector resolution, we don't recommend to use higher + // motion vector resolution than recommended It won't give you extra quality improvement even it + // make your motion vector pass more expensive. + XrSystemSpaceWarpPropertiesFB spaceWarpProperties = {XR_TYPE_SYSTEM_SPACE_WARP_PROPERTIES_FB}; + + XrSystemProperties systemProperties = {XR_TYPE_SYSTEM_PROPERTIES}; + systemProperties.next = &spaceWarpProperties; + + OXR(xrGetSystemProperties(appState.Instance, systemId, &systemProperties)); + + ALOGV( + "System Properties: Name=%s VendorId=%x", + systemProperties.systemName, + systemProperties.vendorId); + ALOGV( + "System Graphics Properties: MaxWidth=%d MaxHeight=%d MaxLayers=%d", + systemProperties.graphicsProperties.maxSwapchainImageWidth, + systemProperties.graphicsProperties.maxSwapchainImageHeight, + systemProperties.graphicsProperties.maxLayerCount); + ALOGV( + "System Tracking Properties: OrientationTracking=%s PositionTracking=%s", + systemProperties.trackingProperties.orientationTracking ? "True" : "False", + systemProperties.trackingProperties.positionTracking ? "True" : "False"); + + ALOGV( + "SpaceWarp Properties: recommendedMotionVectorImageRectWidth=%d recommendedMotionVectorImageRectHeight=%d", + spaceWarpProperties.recommendedMotionVectorImageRectWidth, + spaceWarpProperties.recommendedMotionVectorImageRectHeight); + + assert(ovrMaxLayerCount <= systemProperties.graphicsProperties.maxLayerCount); + + // Get the graphics requirements. + PFN_xrGetOpenGLESGraphicsRequirementsKHR pfnGetOpenGLESGraphicsRequirementsKHR = NULL; + OXR(xrGetInstanceProcAddr( + appState.Instance, + "xrGetOpenGLESGraphicsRequirementsKHR", + (PFN_xrVoidFunction*)(&pfnGetOpenGLESGraphicsRequirementsKHR))); + + XrGraphicsRequirementsOpenGLESKHR graphicsRequirements = { + XR_TYPE_GRAPHICS_REQUIREMENTS_OPENGL_ES_KHR}; + OXR(pfnGetOpenGLESGraphicsRequirementsKHR(appState.Instance, systemId, &graphicsRequirements)); + + // Create the EGL Context + ovrEgl_CreateContext(&appState.Egl, NULL); + + // Check the graphics requirements. + int eglMajor = 0; + int eglMinor = 0; + glGetIntegerv(GL_MAJOR_VERSION, &eglMajor); + glGetIntegerv(GL_MINOR_VERSION, &eglMinor); + const XrVersion eglVersion = XR_MAKE_VERSION(eglMajor, eglMinor, 0); + if (eglVersion < graphicsRequirements.minApiVersionSupported || + eglVersion > graphicsRequirements.maxApiVersionSupported) { + ALOGE("GLES version %d.%d not supported", eglMajor, eglMinor); + exit(0); + } + + appState.CpuLevel = CPU_LEVEL; + appState.GpuLevel = GPU_LEVEL; + appState.MainThreadTid = gettid(); + + appState.SystemId = systemId; + + // Create the OpenXR Session. + XrGraphicsBindingOpenGLESAndroidKHR graphicsBindingAndroidGLES = { + XR_TYPE_GRAPHICS_BINDING_OPENGL_ES_ANDROID_KHR}; + graphicsBindingAndroidGLES.display = appState.Egl.Display; + graphicsBindingAndroidGLES.config = appState.Egl.Config; + graphicsBindingAndroidGLES.context = appState.Egl.Context; + + XrSessionCreateInfo sessionCreateInfo = {XR_TYPE_SESSION_CREATE_INFO}; + sessionCreateInfo.next = &graphicsBindingAndroidGLES; + sessionCreateInfo.createFlags = 0; + sessionCreateInfo.systemId = appState.SystemId; + + OXR(initResult = xrCreateSession(appState.Instance, &sessionCreateInfo, &appState.Session)); + if (initResult != XR_SUCCESS) { + ALOGE("Failed to create XR session: %d.", initResult); + exit(1); + } + + // App only supports the primary stereo view config. + const XrViewConfigurationType supportedViewConfigType = + XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO; + + // Enumerate the viewport configurations. + uint32_t viewportConfigTypeCount = 0; + OXR(xrEnumerateViewConfigurations( + appState.Instance, appState.SystemId, 0, &viewportConfigTypeCount, NULL)); + + XrViewConfigurationType* viewportConfigurationTypes = + (XrViewConfigurationType*)malloc(viewportConfigTypeCount * sizeof(XrViewConfigurationType)); + + OXR(xrEnumerateViewConfigurations( + appState.Instance, + appState.SystemId, + viewportConfigTypeCount, + &viewportConfigTypeCount, + viewportConfigurationTypes)); + + ALOGV("Available Viewport Configuration Types: %d", viewportConfigTypeCount); + + for (uint32_t i = 0; i < viewportConfigTypeCount; i++) { + const XrViewConfigurationType viewportConfigType = viewportConfigurationTypes[i]; + + ALOGV( + "Viewport configuration type %d : %s", + viewportConfigType, + viewportConfigType == supportedViewConfigType ? "Selected" : ""); + + XrViewConfigurationProperties viewportConfig = {XR_TYPE_VIEW_CONFIGURATION_PROPERTIES}; + OXR(xrGetViewConfigurationProperties( + appState.Instance, appState.SystemId, viewportConfigType, &viewportConfig)); + ALOGV( + "FovMutable=%s ConfigurationType %d", + viewportConfig.fovMutable ? "true" : "false", + viewportConfig.viewConfigurationType); + + uint32_t viewCount; + OXR(xrEnumerateViewConfigurationViews( + appState.Instance, appState.SystemId, viewportConfigType, 0, &viewCount, NULL)); + + if (viewCount > 0) { + XrViewConfigurationView* elements = + (XrViewConfigurationView*)malloc(viewCount * sizeof(XrViewConfigurationView)); + + for (uint32_t e = 0; e < viewCount; e++) { + elements[e].type = XR_TYPE_VIEW_CONFIGURATION_VIEW; + elements[e].next = NULL; + } + + OXR(xrEnumerateViewConfigurationViews( + appState.Instance, + appState.SystemId, + viewportConfigType, + viewCount, + &viewCount, + elements)); + + // Log the view config info for each view type for debugging purposes. + for (uint32_t e = 0; e < viewCount; e++) { + const XrViewConfigurationView* element = &elements[e]; + + ALOGV( + "Viewport [%d]: Recommended Width=%d Height=%d SampleCount=%d", + e, + element->recommendedImageRectWidth, + element->recommendedImageRectHeight, + element->recommendedSwapchainSampleCount); + + ALOGV( + "Viewport [%d]: Max Width=%d Height=%d SampleCount=%d", + e, + element->maxImageRectWidth, + element->maxImageRectHeight, + element->maxSwapchainSampleCount); + } + + // Cache the view config properties for the selected config type. + if (viewportConfigType == supportedViewConfigType) { + assert(viewCount == ovrMaxNumEyes); + for (uint32_t e = 0; e < viewCount; e++) { + appState.ViewConfigurationView[e] = elements[e]; + } + } + + free(elements); + } else { + ALOGE("Empty viewport configuration type: %d", viewCount); + } + } + + free(viewportConfigurationTypes); + + // Get the viewport configuration info for the chosen viewport configuration type. + appState.ViewportConfig.type = XR_TYPE_VIEW_CONFIGURATION_PROPERTIES; + + OXR(xrGetViewConfigurationProperties( + appState.Instance, appState.SystemId, supportedViewConfigType, &appState.ViewportConfig)); + + bool stageSupported = false; + + uint32_t numOutputSpaces = 0; + OXR(xrEnumerateReferenceSpaces(appState.Session, 0, &numOutputSpaces, NULL)); + + XrReferenceSpaceType* referenceSpaces = + (XrReferenceSpaceType*)malloc(numOutputSpaces * sizeof(XrReferenceSpaceType)); + + OXR(xrEnumerateReferenceSpaces( + appState.Session, numOutputSpaces, &numOutputSpaces, referenceSpaces)); + + for (uint32_t i = 0; i < numOutputSpaces; i++) { + if (referenceSpaces[i] == XR_REFERENCE_SPACE_TYPE_STAGE) { + stageSupported = true; + break; + } + } + + free(referenceSpaces); + + // Create a space to the first path + XrReferenceSpaceCreateInfo spaceCreateInfo = {XR_TYPE_REFERENCE_SPACE_CREATE_INFO}; + spaceCreateInfo.referenceSpaceType = XR_REFERENCE_SPACE_TYPE_VIEW; + spaceCreateInfo.poseInReferenceSpace.orientation.w = 1.0f; + OXR(xrCreateReferenceSpace(appState.Session, &spaceCreateInfo, &appState.HeadSpace)); + + spaceCreateInfo.referenceSpaceType = XR_REFERENCE_SPACE_TYPE_LOCAL; + OXR(xrCreateReferenceSpace(appState.Session, &spaceCreateInfo, &appState.LocalSpace)); + + // Create a default stage space to use if SPACE_TYPE_STAGE is not + // supported, or calls to xrGetReferenceSpaceBoundsRect fail. + { + spaceCreateInfo.referenceSpaceType = XR_REFERENCE_SPACE_TYPE_LOCAL; + spaceCreateInfo.poseInReferenceSpace.position.y = -1.6750f; + OXR(xrCreateReferenceSpace(appState.Session, &spaceCreateInfo, &appState.FakeStageSpace)); + ALOGV("Created fake stage space from local space with offset"); + appState.CurrentSpace = appState.FakeStageSpace; + } + + if (stageSupported) { + spaceCreateInfo.referenceSpaceType = XR_REFERENCE_SPACE_TYPE_STAGE; + spaceCreateInfo.poseInReferenceSpace.position.y = 0.0f; + OXR(xrCreateReferenceSpace(appState.Session, &spaceCreateInfo, &appState.StageSpace)); + ALOGV("Created stage space"); + appState.CurrentSpace = appState.StageSpace; + } + + XrView* projections = (XrView*)(malloc(ovrMaxNumEyes * sizeof(XrView))); + for (int eye = 0; eye < ovrMaxNumEyes; eye++) { + memset(&projections[eye], 0, sizeof(XrView)); + projections[eye].type = XR_TYPE_VIEW; + } + + // Actions + XrActionSet runningActionSet = + CreateActionSet(1, "running_action_set", "Action Set used on main loop"); + XrAction toggleAction = + CreateAction(runningActionSet, XR_ACTION_TYPE_BOOLEAN_INPUT, "toggle", "Toggle", 0, NULL); + XrAction moveOnXAction = CreateAction( + runningActionSet, XR_ACTION_TYPE_FLOAT_INPUT, "move_on_x", "Move on X", 0, NULL); + XrAction moveOnYAction = CreateAction( + runningActionSet, XR_ACTION_TYPE_FLOAT_INPUT, "move_on_y", "Move on Y", 0, NULL); + XrAction moveOnJoystickAction = CreateAction( + runningActionSet, XR_ACTION_TYPE_VECTOR2F_INPUT, "move_on_joy", "Move on Joy", 0, NULL); + XrAction thumbstickClickAction = CreateAction( + runningActionSet, + XR_ACTION_TYPE_BOOLEAN_INPUT, + "thumbstick_click", + "Thumbstick Click", + 0, + NULL); + + XrPath leftHandPath; + OXR(xrStringToPath(appState.Instance, "/user/hand/left", &leftHandPath)); + XrPath rightHandPath; + OXR(xrStringToPath(appState.Instance, "/user/hand/right", &rightHandPath)); + XrPath handSubactionPaths[2] = {leftHandPath, rightHandPath}; + + XrAction aimPoseAction = CreateAction( + runningActionSet, XR_ACTION_TYPE_POSE_INPUT, "aim_pose", NULL, 2, handSubactionPaths); + XrAction gripPoseAction = CreateAction( + runningActionSet, XR_ACTION_TYPE_POSE_INPUT, "grip_pose", NULL, 2, handSubactionPaths); + + XrPath interactionProfilePath = XR_NULL_PATH; + XrPath interactionProfilePathTouch = XR_NULL_PATH; + + OXR(xrStringToPath( + appState.Instance, + "/interaction_profiles/oculus/touch_controller", + &interactionProfilePathTouch)); + + { + // Query Set + XrActionSet queryActionSet = + CreateActionSet(1, "query_action_set", "Action Set used to query device caps"); + XrAction dummyAction = CreateAction( + queryActionSet, XR_ACTION_TYPE_BOOLEAN_INPUT, "dummy_action", "Dummy Action", 0, NULL); + + // Map bindings + XrActionSuggestedBinding bindings[1]; + int currBinding = 0; + bindings[currBinding++] = + ActionSuggestedBinding(dummyAction, "/user/hand/right/input/system/click"); + + XrInteractionProfileSuggestedBinding suggestedBindings = { + XR_TYPE_INTERACTION_PROFILE_SUGGESTED_BINDING}; + suggestedBindings.suggestedBindings = bindings; + suggestedBindings.countSuggestedBindings = currBinding; + + // Try all + suggestedBindings.interactionProfile = interactionProfilePathTouch; + XrResult suggestTouchResult = + xrSuggestInteractionProfileBindings(appState.Instance, &suggestedBindings); + OXR(suggestTouchResult); + + if (XR_SUCCESS == suggestTouchResult) { + ALOGV("xrSuggestInteractionProfileBindings found bindings for QUEST controller"); + interactionProfilePath = interactionProfilePathTouch; + } + } + + // Action creation + { + // Map bindings + + XrActionSuggestedBinding bindings[22]; // large enough for all profiles + int currBinding = 0; + + { + if (interactionProfilePath == interactionProfilePathTouch) { + bindings[currBinding++] = + ActionSuggestedBinding(toggleAction, "/user/hand/left/input/trigger"); + bindings[currBinding++] = + ActionSuggestedBinding(toggleAction, "/user/hand/right/input/trigger"); + bindings[currBinding++] = + ActionSuggestedBinding(toggleAction, "/user/hand/left/input/x/click"); + bindings[currBinding++] = + ActionSuggestedBinding(toggleAction, "/user/hand/right/input/a/click"); + bindings[currBinding++] = + ActionSuggestedBinding(moveOnXAction, "/user/hand/left/input/squeeze/value"); + bindings[currBinding++] = + ActionSuggestedBinding(moveOnXAction, "/user/hand/right/input/squeeze/value"); + bindings[currBinding++] = + ActionSuggestedBinding(moveOnYAction, "/user/hand/left/input/trigger/value"); + bindings[currBinding++] = + ActionSuggestedBinding(moveOnYAction, "/user/hand/right/input/trigger/value"); + bindings[currBinding++] = ActionSuggestedBinding( + moveOnJoystickAction, "/user/hand/left/input/thumbstick"); + bindings[currBinding++] = ActionSuggestedBinding( + thumbstickClickAction, "/user/hand/left/input/thumbstick/click"); + bindings[currBinding++] = ActionSuggestedBinding( + thumbstickClickAction, "/user/hand/right/input/thumbstick/click"); + bindings[currBinding++] = + ActionSuggestedBinding(aimPoseAction, "/user/hand/left/input/aim/pose"); + bindings[currBinding++] = + ActionSuggestedBinding(aimPoseAction, "/user/hand/right/input/aim/pose"); + bindings[currBinding++] = + ActionSuggestedBinding(gripPoseAction, "/user/hand/left/input/grip/pose"); + bindings[currBinding++] = + ActionSuggestedBinding(gripPoseAction, "/user/hand/right/input/grip/pose"); + } + } + + XrInteractionProfileSuggestedBinding suggestedBindings = { + XR_TYPE_INTERACTION_PROFILE_SUGGESTED_BINDING}; + suggestedBindings.interactionProfile = interactionProfilePath; + suggestedBindings.suggestedBindings = bindings; + suggestedBindings.countSuggestedBindings = currBinding; + OXR(xrSuggestInteractionProfileBindings(appState.Instance, &suggestedBindings)); + + // Attach to session + XrSessionActionSetsAttachInfo attachInfo = {XR_TYPE_SESSION_ACTION_SETS_ATTACH_INFO}; + attachInfo.countActionSets = 1; + attachInfo.actionSets = &runningActionSet; + OXR(xrAttachSessionActionSets(appState.Session, &attachInfo)); + + // Enumerate actions + XrPath actionPathsBuffer[16]; + char stringBuffer[256]; + XrAction actionsToEnumerate[] = { + toggleAction, + moveOnXAction, + moveOnYAction, + moveOnJoystickAction, + thumbstickClickAction, + aimPoseAction, + gripPoseAction, + }; + for (size_t i = 0; i < sizeof(actionsToEnumerate) / sizeof(actionsToEnumerate[0]); ++i) { + XrBoundSourcesForActionEnumerateInfo enumerateInfo = { + XR_TYPE_BOUND_SOURCES_FOR_ACTION_ENUMERATE_INFO}; + enumerateInfo.action = actionsToEnumerate[i]; + + // Get Count + uint32_t countOutput = 0; + OXR(xrEnumerateBoundSourcesForAction( + appState.Session, &enumerateInfo, 0 /* request size */, &countOutput, NULL)); + ALOGV( + "xrEnumerateBoundSourcesForAction action=%lld count=%u", + (long long)enumerateInfo.action, + countOutput); + + if (countOutput < 16) { + OXR(xrEnumerateBoundSourcesForAction( + appState.Session, &enumerateInfo, 16, &countOutput, actionPathsBuffer)); + for (uint32_t a = 0; a < countOutput; ++a) { + XrInputSourceLocalizedNameGetInfo nameGetInfo = { + XR_TYPE_INPUT_SOURCE_LOCALIZED_NAME_GET_INFO}; + nameGetInfo.sourcePath = actionPathsBuffer[a]; + nameGetInfo.whichComponents = XR_INPUT_SOURCE_LOCALIZED_NAME_USER_PATH_BIT | + XR_INPUT_SOURCE_LOCALIZED_NAME_INTERACTION_PROFILE_BIT | + XR_INPUT_SOURCE_LOCALIZED_NAME_COMPONENT_BIT; + + uint32_t stringCount = 0u; + OXR(xrGetInputSourceLocalizedName( + appState.Session, &nameGetInfo, 0, &stringCount, NULL)); + if (stringCount < 256) { + OXR(xrGetInputSourceLocalizedName( + appState.Session, &nameGetInfo, 256, &stringCount, stringBuffer)); + char pathStr[256]; + uint32_t strLen = 0; + OXR(xrPathToString( + appState.Instance, + actionPathsBuffer[a], + (uint32_t)sizeof(pathStr), + &strLen, + pathStr)); + ALOGV( + " -> path = %lld `%s` -> `%s`", + (long long)actionPathsBuffer[a], + pathStr, + stringBuffer); + } + } + } + } + } + + XrSpace leftControllerAimSpace = XR_NULL_HANDLE; + XrSpace rightControllerAimSpace = XR_NULL_HANDLE; + XrSpace leftControllerGripSpace = XR_NULL_HANDLE; + XrSpace rightControllerGripSpace = XR_NULL_HANDLE; + + ovrRenderer_Create( + appState.Session, + &appState.Renderer, + appState.ViewConfigurationView[0].recommendedImageRectWidth, + appState.ViewConfigurationView[0].recommendedImageRectHeight, + spaceWarpProperties.recommendedMotionVectorImageRectWidth, + spaceWarpProperties.recommendedMotionVectorImageRectHeight); + + app->userData = &appState; + app->onAppCmd = app_handle_cmd; + + bool stageBoundsDirty = true; + + double startTime = -1.0; + + // App-specific input + float appJoystickX = 0.0f; + float appJoystickY = 0.0f; + + while (app->destroyRequested == 0) { + // Read all pending events. + for (;;) { + int events; + struct android_poll_source* source; + // If the timeout is zero, returns immediately without blocking. + // If the timeout is negative, waits indefinitely until an event appears. + const int timeoutMilliseconds = + (appState.Resumed == false && appState.SessionActive == false && + app->destroyRequested == 0) + ? -1 + : 0; + if (ALooper_pollAll(timeoutMilliseconds, NULL, &events, (void**)&source) < 0) { + break; + } + + // Process this event. + if (source != NULL) { + source->process(app, source); + } + } + + ovrApp_HandleXrEvents(&appState); + + if (appState.SessionActive == false) { + continue; + } + + if (leftControllerAimSpace == XR_NULL_HANDLE) { + leftControllerAimSpace = CreateActionSpace(aimPoseAction, leftHandPath); + } + if (rightControllerAimSpace == XR_NULL_HANDLE) { + rightControllerAimSpace = CreateActionSpace(aimPoseAction, rightHandPath); + } + if (leftControllerGripSpace == XR_NULL_HANDLE) { + leftControllerGripSpace = CreateActionSpace(gripPoseAction, leftHandPath); + } + if (rightControllerGripSpace == XR_NULL_HANDLE) { + rightControllerGripSpace = CreateActionSpace(gripPoseAction, rightHandPath); + } + + // Create the scene if not yet created. + // The scene is created here to be able to show a loading icon. + if (!ovrScene_IsCreated(&appState.Scene)) { + ovrScene_Create( + app->activity->assetManager, appState.Instance, appState.Session, &appState.Scene); + } + + if (stageBoundsDirty) { + UpdateStageBounds(&appState); + stageBoundsDirty = false; + } + + // NOTE: OpenXR does not use the concept of frame indices. Instead, + // XrWaitFrame returns the predicted display time. + XrFrameWaitInfo waitFrameInfo = {XR_TYPE_FRAME_WAIT_INFO}; + XrFrameState frameState = {XR_TYPE_FRAME_STATE}; + + OXR(xrWaitFrame(appState.Session, &waitFrameInfo, &frameState)); + + // Get the HMD pose, predicted for the middle of the time period during which + // the new eye images will be displayed. The number of frames predicted ahead + // depends on the pipeline depth of the engine and the synthesis rate. + // The better the prediction, the less black will be pulled in at the edges. + XrFrameBeginInfo beginFrameDesc = {XR_TYPE_FRAME_BEGIN_INFO}; + OXR(xrBeginFrame(appState.Session, &beginFrameDesc)); + + XrSpaceLocation loc = {XR_TYPE_SPACE_LOCATION}; + OXR(xrLocateSpace( + appState.HeadSpace, appState.CurrentSpace, frameState.predictedDisplayTime, &loc)); + XrPosef xfStageFromHead = loc.pose; + OXR(xrLocateSpace( + appState.HeadSpace, appState.LocalSpace, frameState.predictedDisplayTime, &loc)); + + XrViewLocateInfo projectionInfo = {XR_TYPE_VIEW_LOCATE_INFO}; + projectionInfo.viewConfigurationType = appState.ViewportConfig.viewConfigurationType; + projectionInfo.displayTime = frameState.predictedDisplayTime; + projectionInfo.space = appState.HeadSpace; + + XrViewState viewState = {XR_TYPE_VIEW_STATE, NULL}; + + uint32_t projectionCapacityInput = ovrMaxNumEyes; + uint32_t projectionCountOutput = projectionCapacityInput; + + OXR(xrLocateViews( + appState.Session, + &projectionInfo, + &viewState, + projectionCapacityInput, + &projectionCountOutput, + projections)); + + // Simple animation + double timeInSeconds = FromXrTime(frameState.predictedDisplayTime); + if (startTime < 0.0) { + startTime = timeInSeconds; + } + timeInSeconds -= startTime; + + ovrSceneMatrices sceneMatrices; + XrPosef viewTransform[2]; + + // Rotation around Y when moving thumbstick left <-> right + if (appState.Focused) { + rotationY -= appJoystickX * 0.04f; + } + + const XrVector3f axisY = {0.0f, 1.0f, 0.0f}; + XrQuaternionf_CreateFromAxisAngle(&sceneMatrices.XrSpacePoseInWorld.orientation, &axisY, rotationY); + + // Moving forward + XrVector3f forwardDir = {0.0f, 0.0f, -1.0f}; + XrPosef xfWorldFromHead; + XrPosef_Multiply(&xfWorldFromHead, &sceneMatrices.XrSpacePoseInWorld, &xfStageFromHead); + + XrQuaternionf_RotateVector3f(&forwardDir, &xfWorldFromHead.orientation, &forwardDir); + + if (appState.Focused) { + XrVector3f scaled; + XrVector3f_Scale(&scaled, &forwardDir, 0.1f * appJoystickY); + XrVector3f newCameraPos; + XrVector3f_Add(&newCameraPos, &cameraPos, &scaled); + cameraPos = newCameraPos; + } + + cameraPos.y = 0.0f; + sceneMatrices.XrSpacePoseInWorld.position = cameraPos; + + XrPosef_Multiply(&xfWorldFromHead, &sceneMatrices.XrSpacePoseInWorld, &xfStageFromHead); + + for (int eye = 0; eye < ovrMaxNumEyes; eye++) { + XrPosef xfHeadFromEye = projections[eye].pose; + XrPosef xfStageFromEye; + XrPosef_Multiply(&xfStageFromEye, &xfStageFromHead, &xfHeadFromEye); + XrPosef_Invert(&viewTransform[eye], &xfStageFromEye); + + // ViewMatrix is in world space, so we can do camera locomotion + XrPosef xfWorldFromEye; + XrPosef_Multiply(&xfWorldFromEye, &xfWorldFromHead, &xfHeadFromEye); + XrPosef renderViewTransform; + XrPosef_Invert(&renderViewTransform, &xfWorldFromEye); + XrMatrix4x4f_CreateFromRigidTransform( + &sceneMatrices.ViewMatrix[eye], &renderViewTransform); + + const XrFovf fov = projections[eye].fov; + XrMatrix4x4f_CreateProjectionFov( + &sceneMatrices.ProjectionMatrix[eye], GRAPHICS_OPENGL_ES, fov, 0.1f, 0.0f); + } + + // update input information + XrAction controller[] = {aimPoseAction, gripPoseAction, aimPoseAction, gripPoseAction}; + XrPath subactionPath[] = {leftHandPath, leftHandPath, rightHandPath, rightHandPath}; + XrSpace controllerSpace[] = { + leftControllerAimSpace, + leftControllerGripSpace, + rightControllerAimSpace, + rightControllerGripSpace, + }; + for (int i = 0; i < 4; i++) { + if (ActionPoseIsActive(controller[i], subactionPath[i])) { + LocVel lv = GetSpaceLocVel(controllerSpace[i], frameState.predictedDisplayTime); + appState.Scene.SceneTransform.TrackedController[i].Active = + (lv.loc.locationFlags & XR_SPACE_LOCATION_POSITION_VALID_BIT) != 0; + appState.Scene.SceneTransform.TrackedController[i].Pose = lv.loc.pose; + for (int j = 0; j < 3; j++) { + float dt = 0.01f; // use 0.2f for for testing velocity vectors + (&appState.Scene.SceneTransform.TrackedController[i].Pose.position.x)[j] += + (&lv.vel.linearVelocity.x)[j] * dt; + } + } else { + ovrTrackedController_Clear(&appState.Scene.SceneTransform.TrackedController[i]); + } + } + + // OpenXR input + // sync action data + XrActiveActionSet activeActionSet = {0}; + activeActionSet.actionSet = runningActionSet; + activeActionSet.subactionPath = XR_NULL_PATH; + + XrActionsSyncInfo syncInfo = {XR_TYPE_ACTIONS_SYNC_INFO}; + syncInfo.countActiveActionSets = 1; + syncInfo.activeActionSets = &activeActionSet; + OXR(xrSyncActions(appState.Session, &syncInfo)); + + // query input action states + XrActionStateGetInfo getInfo = {XR_TYPE_ACTION_STATE_GET_INFO}; + getInfo.subactionPath = XR_NULL_PATH; + + XrActionStateBoolean toggleState = GetActionStateBoolean(toggleAction); + + { + (void)GetActionStateFloat(moveOnXAction); + (void)GetActionStateFloat(moveOnYAction); + + XrActionStateVector2f moveJoystickState = GetActionStateVector2(moveOnJoystickAction); + if (moveJoystickState.changedSinceLastSync) { + appJoystickX = moveJoystickState.currentState.x; + appJoystickY = moveJoystickState.currentState.y; + } + } + + // AppSpaceWarp : Hold controller to disable spacewarp for the frame + // Holding trigger : normal 72 fps + // Releasing trigger: SpaceWarp 36 fps mode + + bool enableSpaceWarpForTheFrame = EnableSpaceWarp && (!toggleState.currentState); + + // Make it toggleable by setprop + int enableSpaceWarpDebugProp = -1; + GetSystemPropertyInt("debug.oculus.enableSpaceWarp", &enableSpaceWarpDebugProp); + if (enableSpaceWarpDebugProp >= 0) { + EnableSpaceWarp = enableSpaceWarpDebugProp > 0; + } + + int enableDeterministicAnimationProp = -1; + GetSystemPropertyInt("debug.oculus.enableDeterministicAnimation", &enableDeterministicAnimationProp); + if (enableDeterministicAnimationProp >= 0) { + EnableDeterministicAnimation = enableDeterministicAnimationProp > 0; + } + + // Determine cube animation tic based on what animation mode is being used + float cubeTic = 0.0f; + if (EnableDeterministicAnimation) { + cubeTic = CUBE_ANIMATION_SPEED * FRAME_BASED_CUBE_SPEED_FACTOR * frameIndex; + } else { + // Use Original GL logic + cubeTic = CUBE_ANIMATION_SPEED * timeInSeconds; + } + + // CubeAnimation + { + for (int z = 0; z < CUBE_COUNT_DIMENSION; z++) { + for (int y = 0; y < CUBE_COUNT_DIMENSION; y++) { + for (int x = 0; x < CUBE_COUNT_DIMENSION; x++) { + const int instance = + (z * CUBE_COUNT_DIMENSION + y) * CUBE_COUNT_DIMENSION + x; + + appState.Scene.SceneTransform.CubeTransforms[instance].m[0 * 4 + 0] = 1; + appState.Scene.SceneTransform.CubeTransforms[instance].m[0 * 4 + 1] = 0; + appState.Scene.SceneTransform.CubeTransforms[instance].m[0 * 4 + 2] = 0; + appState.Scene.SceneTransform.CubeTransforms[instance].m[0 * 4 + 3] = 0; + + appState.Scene.SceneTransform.CubeTransforms[instance].m[1 * 4 + 0] = 0; + appState.Scene.SceneTransform.CubeTransforms[instance].m[1 * 4 + 1] = 1; + appState.Scene.SceneTransform.CubeTransforms[instance].m[1 * 4 + 2] = 0; + appState.Scene.SceneTransform.CubeTransforms[instance].m[1 * 4 + 3] = 0; + + appState.Scene.SceneTransform.CubeTransforms[instance].m[2 * 4 + 0] = 0; + appState.Scene.SceneTransform.CubeTransforms[instance].m[2 * 4 + 1] = 0; + appState.Scene.SceneTransform.CubeTransforms[instance].m[2 * 4 + 2] = 1; + appState.Scene.SceneTransform.CubeTransforms[instance].m[2 * 4 + 3] = 0; + + // Only translate the cube on the Y axis follwing a sin curve + // a * sin(b * time + offset). + // a for distance the cube travels + // b for speed the cube travels + // offset is set to y * 0.3 + z * 0.17 + x * 0.7 to make cube move with a small delay + // based on the location to produce the waving motion + const float cubePositionY = CUBE_ANIMATION_DISTANCE * + sin(cubeTic + y * CUBE_ANIMATION_Y_OFFSET + + z * CUBE_ANIMATION_Z_OFFSET + x * CUBE_ANIMATION_X_OFFSET); + + appState.Scene.SceneTransform.CubeTransforms[instance].m[3 * 4 + 0] = + (x - 0.5f * CUBE_COUNT_DIMENSION) * CUBE_SCALE - CUBE_SCALE / 2; + + appState.Scene.SceneTransform.CubeTransforms[instance].m[3 * 4 + 1] = + (y - 0.5f * CUBE_COUNT_DIMENSION) * CUBE_SCALE - CUBE_SCALE / 2 + + cubePositionY; + + appState.Scene.SceneTransform.CubeTransforms[instance].m[3 * 4 + 2] = + (z - 0.5f * CUBE_COUNT_DIMENSION) * CUBE_SCALE - CUBE_SCALE / 2; + appState.Scene.SceneTransform.CubeTransforms[instance].m[3 * 4 + 3] = 1.0f; + } + } + } + } + + // Set-up the compositor layers for this frame. + // NOTE: Multiple independent layers are allowed, but they need to be added + // in a depth consistent order. + + XrCompositionLayerProjectionView projection_layer_elements[2] = { + {XR_TYPE_COMPOSITION_LAYER_PROJECTION_VIEW}, + {XR_TYPE_COMPOSITION_LAYER_PROJECTION_VIEW}}; + XrCompositionLayerSpaceWarpInfoFB proj_spacewarp_views[2] = { + {XR_TYPE_COMPOSITION_LAYER_SPACE_WARP_INFO_FB}, + {XR_TYPE_COMPOSITION_LAYER_SPACE_WARP_INFO_FB}}; + + appState.LayerCount = 0; + memset(appState.Layers, 0, sizeof(ovrCompositorLayer_Union) * ovrMaxLayerCount); + bool shouldRenderWorldLayer = true; + + // AppSpaceWarp Cache previous frame transform for motion vector generation. + static bool IsFirstFrame = true; + static ovrSceneMatrices prevFrameSceneMatrices; + static ovrSceneTransform prevFrameSceneTransform; + + if (IsFirstFrame) { + prevFrameSceneMatrices = sceneMatrices; + prevFrameSceneTransform = appState.Scene.SceneTransform; + } + + // Render the world-view layer (simple ground plane) + if (shouldRenderWorldLayer) { + ovrRenderer_RenderFrame( + &appState.Renderer, + &appState.Scene, + &sceneMatrices, + &prevFrameSceneMatrices, + &prevFrameSceneTransform); + + XrCompositionLayerProjection projection_layer = {XR_TYPE_COMPOSITION_LAYER_PROJECTION}; + projection_layer.layerFlags = XR_COMPOSITION_LAYER_BLEND_TEXTURE_SOURCE_ALPHA_BIT; + projection_layer.layerFlags |= XR_COMPOSITION_LAYER_CORRECT_CHROMATIC_ABERRATION_BIT; + projection_layer.space = appState.CurrentSpace; + projection_layer.viewCount = ovrMaxNumEyes; + projection_layer.views = projection_layer_elements; + + for (int eye = 0; eye < ovrMaxNumEyes; eye++) { + ovrFramebuffer* frameBuffer = &appState.Renderer.FrameBuffer[eye]; + + memset( + &projection_layer_elements[eye], 0, sizeof(XrCompositionLayerProjectionView)); + projection_layer_elements[eye].type = XR_TYPE_COMPOSITION_LAYER_PROJECTION_VIEW; + + XrPosef_Invert(&projection_layer_elements[eye].pose, &viewTransform[eye]); + projection_layer_elements[eye].fov = projections[eye].fov; + + memset(&projection_layer_elements[eye].subImage, 0, sizeof(XrSwapchainSubImage)); + projection_layer_elements[eye].subImage.swapchain = + frameBuffer->ColorSwapChain.Handle; + projection_layer_elements[eye].subImage.imageRect.offset.x = 0; + projection_layer_elements[eye].subImage.imageRect.offset.y = 0; + projection_layer_elements[eye].subImage.imageRect.extent.width = + frameBuffer->ColorSwapChain.Width; + projection_layer_elements[eye].subImage.imageRect.extent.height = + frameBuffer->ColorSwapChain.Height; + projection_layer_elements[eye].subImage.imageArrayIndex = 0; + + // AppSpaceWarp : submit motion vector information XR runtime + if (enableSpaceWarpForTheFrame) { + projection_layer_elements[eye].next = &proj_spacewarp_views[eye]; + proj_spacewarp_views[eye].type = XR_TYPE_COMPOSITION_LAYER_SPACE_WARP_INFO_FB; + proj_spacewarp_views[eye].next = NULL; + proj_spacewarp_views[eye].layerFlags = 0; + + proj_spacewarp_views[eye].motionVectorSubImage.swapchain = + frameBuffer->MotionVectorSwapChain.Handle; + proj_spacewarp_views[eye].motionVectorSubImage.imageRect.offset.x = 0; + proj_spacewarp_views[eye].motionVectorSubImage.imageRect.offset.y = 0; + proj_spacewarp_views[eye].motionVectorSubImage.imageRect.extent.width = + frameBuffer->MotionVectorSwapChain.Width; + proj_spacewarp_views[eye].motionVectorSubImage.imageRect.extent.height = + frameBuffer->MotionVectorSwapChain.Height; + proj_spacewarp_views[eye].motionVectorSubImage.imageArrayIndex = 0; + proj_spacewarp_views[eye].depthSubImage.swapchain = + frameBuffer->MotionVectorDepthSwapChain.Handle; + proj_spacewarp_views[eye].nearZ = 0.1f; + proj_spacewarp_views[eye].farZ = INFINITY; + proj_spacewarp_views[eye].depthSubImage.imageRect.offset.x = 0; + proj_spacewarp_views[eye].depthSubImage.imageRect.offset.y = 0; + proj_spacewarp_views[eye].depthSubImage.imageRect.extent.width = + frameBuffer->MotionVectorDepthSwapChain.Width; + proj_spacewarp_views[eye].depthSubImage.imageRect.extent.height = + frameBuffer->MotionVectorDepthSwapChain.Height; + proj_spacewarp_views[eye].depthSubImage.imageArrayIndex = 0; + + // AppSpaceWarp: appSpaceDeltaPose is used to capture appState.CurrentSpace's + // movement between previous frame and current frame. + // + // For example: + // * In previous frame, appState.CurrentSpace's application Space (or world + // space) pose is currentPose. + // * In current frame, appState.CurrentSpace's application Space pose is + // prevPose. + // + // Then appSpaceDeltaPose should be the different of the 2 poses. + // appSpaceDeltaPose = Inv(prevPose) * currentPose + // + // The information will be used in XrRuntime for a couple purposes: + // 1. Fill in background motion vector: if a pixel on the screen isn't touched + // by any drawcalls, the pixel will be kept as clear color, which can't be used + // as correct motion vector. XrRuntime will try to generate it automaticlly, but + // it need to know if there is any motion driven by the application artificial + // locomotion, appSpaceDeltaPose provides the information for that. + // 2. Turn off extrapolation for extreme case: for example if the app had + // triggered a huge camera movement, we might want to disable frame + // extrapolation for the frame appSpaceDeltaPose can be used to detect cases + // like that (eg. teleportation) + // It is important to make this pose correct by testing camera artificial + // locomotion rotation with a scene which has foreground rendered object and + // background only has clear color. + + XrPosef PrevFrameXrSpacePoseInWorld = prevFrameSceneMatrices.XrSpacePoseInWorld; + XrPosef InvPrevFrameXrSpacePoseInWorld; + XrPosef_Invert(&InvPrevFrameXrSpacePoseInWorld, &PrevFrameXrSpacePoseInWorld); + XrPosef XrSpacePoseInWorld = sceneMatrices.XrSpacePoseInWorld; + + XrPosef_Multiply( + &proj_spacewarp_views[eye].appSpaceDeltaPose, + &InvPrevFrameXrSpacePoseInWorld, + &XrSpacePoseInWorld); + + // Make debugDeltaPose =1 and rotating the camera with thumbstick, + // if you looked carefully on the pixels between cube and background color, + // you can see the artifact of using wrong appSpaceDeltaPose + int debugDeltaPose = 0; + if (!GetSystemPropertyInt("debug.oculus.debugDeltaPose", &debugDeltaPose)) { + debugDeltaPose = 0; // default value + } + if (debugDeltaPose > 0) { + XrPosef_CreateIdentity(&proj_spacewarp_views[eye].appSpaceDeltaPose); + ALOGV("Bad appSpaceDeltaPose: watch carefully on background artifacts"); + } + + proj_spacewarp_views[eye].minDepth = 0.0f; + proj_spacewarp_views[eye].maxDepth = 1.0f; + } + } + + appState.Layers[appState.LayerCount++].Projection = projection_layer; + } + + // Backup for next frame + prevFrameSceneMatrices = sceneMatrices; + prevFrameSceneTransform = appState.Scene.SceneTransform; + IsFirstFrame = false; + + // Build the quad layer + if (ShowQuadLayer) { + const XrVector3f axis = {0.0f, 1.0f, 0.0f}; + XrVector3f pos = {-2.0f, 2.0f, -2.0f}; + XrExtent2Df size = {1.0f, 1.0f}; + + XrPosef InvXrSpacePoseInWorld; + XrPosef_Invert(&InvXrSpacePoseInWorld, &sceneMatrices.XrSpacePoseInWorld); + + XrCompositionLayerQuad quad_layer_left = {XR_TYPE_COMPOSITION_LAYER_QUAD}; + quad_layer_left.layerFlags = XR_COMPOSITION_LAYER_BLEND_TEXTURE_SOURCE_ALPHA_BIT; + quad_layer_left.space = appState.CurrentSpace; + quad_layer_left.eyeVisibility = XR_EYE_VISIBILITY_LEFT; + memset(&quad_layer_left.subImage, 0, sizeof(XrSwapchainSubImage)); + quad_layer_left.subImage.swapchain = appState.Scene.QuadSwapChain.Handle; + quad_layer_left.subImage.imageRect.offset.x = 0; + quad_layer_left.subImage.imageRect.offset.y = 0; + quad_layer_left.subImage.imageRect.extent.width = appState.Scene.QuadSwapChain.Width; + quad_layer_left.subImage.imageRect.extent.height = appState.Scene.QuadSwapChain.Height; + quad_layer_left.subImage.imageArrayIndex = 0; + XrPosef_CreateIdentity(&quad_layer_left.pose); + XrQuaternionf_CreateFromAxisAngle(&quad_layer_left.pose.orientation, &axis, 45.0f * MATH_PI / 180.0f); + quad_layer_left.pose.position = pos; + + if (!QuadLayerFollowing) { + // Transform layer pose back to appState.CurrentSpace + XrPosef p; + XrPosef_Multiply(&p, &InvXrSpacePoseInWorld, &quad_layer_left.pose); + quad_layer_left.pose = p; + } + quad_layer_left.size = size; + appState.Layers[appState.LayerCount++].Quad = quad_layer_left; + + XrCompositionLayerQuad quad_layer_right = {XR_TYPE_COMPOSITION_LAYER_QUAD}; + quad_layer_right.layerFlags = XR_COMPOSITION_LAYER_BLEND_TEXTURE_SOURCE_ALPHA_BIT; + quad_layer_right.space = appState.CurrentSpace; + quad_layer_right.eyeVisibility = XR_EYE_VISIBILITY_RIGHT; + memset(&quad_layer_right.subImage, 0, sizeof(XrSwapchainSubImage)); + quad_layer_right.subImage.swapchain = appState.Scene.QuadSwapChain.Handle; + quad_layer_right.subImage.imageRect.offset.x = 0; + quad_layer_right.subImage.imageRect.offset.y = 0; + quad_layer_right.subImage.imageRect.extent.width = appState.Scene.QuadSwapChain.Width; + quad_layer_right.subImage.imageRect.extent.height = appState.Scene.QuadSwapChain.Height; + quad_layer_right.subImage.imageArrayIndex = 0; + XrPosef_CreateIdentity(&quad_layer_right.pose); + XrQuaternionf_CreateFromAxisAngle(&quad_layer_right.pose.orientation, &axis, 45.0f * MATH_PI / 180.0f); + quad_layer_right.pose.position = pos; + + if (!QuadLayerFollowing) { + // Transform layer pose back to appState.CurrentSpace + XrPosef p; + XrPosef_Multiply(&p, &InvXrSpacePoseInWorld, &quad_layer_right.pose); + quad_layer_right.pose = p; + } + + quad_layer_right.size = size; + appState.Layers[appState.LayerCount++].Quad = quad_layer_right; + } + + // Compose the layers for this frame. + const XrCompositionLayerBaseHeader* layers[ovrMaxLayerCount] = {0}; + for (int i = 0; i < appState.LayerCount; i++) { + layers[i] = (const XrCompositionLayerBaseHeader*)&appState.Layers[i]; + } + + XrFrameEndInfo endFrameInfo = {XR_TYPE_FRAME_END_INFO}; + endFrameInfo.displayTime = frameState.predictedDisplayTime; + endFrameInfo.environmentBlendMode = XR_ENVIRONMENT_BLEND_MODE_OPAQUE; + endFrameInfo.layerCount = appState.LayerCount; + endFrameInfo.layers = layers; + + OXR(xrEndFrame(appState.Session, &endFrameInfo)); + frameIndex++; + } + + ovrRenderer_Destroy(&appState.Renderer); + + free(projections); + + ovrScene_Destroy(&appState.Scene); + ovrEgl_DestroyContext(&appState.Egl); + + OXR(xrDestroySpace(appState.HeadSpace)); + OXR(xrDestroySpace(appState.LocalSpace)); + // StageSpace is optional. + if (appState.StageSpace != XR_NULL_HANDLE) { + OXR(xrDestroySpace(appState.StageSpace)); + } + OXR(xrDestroySpace(appState.FakeStageSpace)); + appState.CurrentSpace = XR_NULL_HANDLE; + OXR(xrDestroySession(appState.Session)); + OXR(xrDestroyInstance(appState.Instance)); + + ovrApp_Destroy(&appState); + +#if defined(__cplusplus) + app->activity->vm->DetachCurrentThread(); +#else + (*app->activity->vm)->DetachCurrentThread(app->activity->vm); +#endif +} diff --git a/Samples/XrSamples/XrSpaceWarp/assets/donotdelete.txt b/Samples/XrSamples/XrSpaceWarp/assets/donotdelete.txt new file mode 100644 index 0000000..e69de29 diff --git a/Samples/XrSamples/XrSpatialAnchor/CMakeLists.txt b/Samples/XrSamples/XrSpatialAnchor/CMakeLists.txt new file mode 100755 index 0000000..d45fa7b --- /dev/null +++ b/Samples/XrSamples/XrSpatialAnchor/CMakeLists.txt @@ -0,0 +1,35 @@ +# Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved. + +project(spatialanchor) + +if(NOT TARGET OpenXR::openxr_loader) + find_package(OpenXR REQUIRED) +endif() + +file(GLOB_RECURSE SRC_FILES + Src/*.c + Src/*.cpp +) + +if(ANDROID) + add_library(${PROJECT_NAME} MODULE ${SRC_FILES}) + target_link_libraries(${PROJECT_NAME} PRIVATE samplecommon_gl) + set_target_properties(${PROJECT_NAME} PROPERTIES LINK_FLAGS "-u ANativeActivity_onCreate") +elseif(WIN32) + add_executable(${PROJECT_NAME} ${SRC_FILES}) + add_custom_command(TARGET ${PROJECT_NAME} PRE_BUILD + COMMAND "${CMAKE_COMMAND}" -E copy_directory + "${CMAKE_CURRENT_LIST_DIR}/assets" + "$/assets" + VERBATIM) + + add_custom_command(TARGET ${PROJECT_NAME} PRE_BUILD + COMMAND "${CMAKE_COMMAND}" -E copy_directory + "${CMAKE_SOURCE_DIR}/SampleXrFramework/res/raw" + "$/font/res/raw" + VERBATIM) + target_link_libraries(${PROJECT_NAME} PRIVATE samplecommon_gl) +endif() + +# Common across platforms +target_include_directories(${PROJECT_NAME} PRIVATE Src) diff --git a/Samples/XrSamples/XrSpatialAnchor/Projects/Android/AndroidManifest.xml b/Samples/XrSamples/XrSpatialAnchor/Projects/Android/AndroidManifest.xml new file mode 100755 index 0000000..5dd345a --- /dev/null +++ b/Samples/XrSamples/XrSpatialAnchor/Projects/Android/AndroidManifest.xml @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Samples/XrSamples/XrSpatialAnchor/Projects/Android/build.bat b/Samples/XrSamples/XrSpatialAnchor/Projects/Android/build.bat new file mode 100755 index 0000000..facf79f --- /dev/null +++ b/Samples/XrSamples/XrSpatialAnchor/Projects/Android/build.bat @@ -0,0 +1,29 @@ +@rem Only edit the master copy of this file in SDK_ROOT/bin/scripts/build/perproject + +@setlocal enableextensions enabledelayedexpansion + +@if not exist "build.gradle" @echo Build script must be executed from project directory. & goto :Abort + +@set P=.. + +:TryAgain + +@rem @echo P = %P% + +@if exist "%P%\bin\scripts\build\build.py.bat" goto :Found + +@if exist "%P%\bin\scripts\build" @echo "Could not find build.py.bat" & goto :Abort + +@set P=%P%\.. + +@goto :TryAgain + +:Found + +@set P=%P%\bin\scripts\build +@call %P%\build.py.bat %1 %2 %3 %4 %5 +@goto :End + +:Abort + +:End diff --git a/Samples/XrSamples/XrSpatialAnchor/Projects/Android/build.gradle b/Samples/XrSamples/XrSpatialAnchor/Projects/Android/build.gradle new file mode 100755 index 0000000..7dab4a0 --- /dev/null +++ b/Samples/XrSamples/XrSpatialAnchor/Projects/Android/build.gradle @@ -0,0 +1,78 @@ +// Top-level build file where you can add configuration options common to all sub-projects/modules. +buildscript { + repositories { + google() + mavenCentral() + } + dependencies { + classpath "com.android.tools.build:gradle:7.0.3" + } +} + +repositories { + google() + mavenCentral() +} + +apply plugin: 'com.android.application' + +android { + compileSdk 32 + + defaultConfig { + applicationId "com.oculus.sdk.spatialanchor" + minSdk 26 + targetSdk 32 + versionCode 1 + versionName "1.0" + + // override app plugin abiFilters for 64-bit support + externalNativeBuild { + ndk { + abiFilters 'arm64-v8a' + } + ndkBuild { + abiFilters 'arm64-v8a' + } + cmake { + targets "spatialanchor" + } + } + } + + sourceSets { + main { + manifest.srcFile 'AndroidManifest.xml' + java.srcDirs = ['../../java'] + assets.srcDirs = ['../../assets'] + res.srcDirs = ['../../res'] + } + } + + buildTypes { + debug { + debuggable true + } + + release { + debuggable false + } + } + + externalNativeBuild { + cmake { + path file('../../../../CMakeLists.txt') + } + } + + // Enable prefab support for the OpenXR AAR + buildFeatures { + prefab true + } +} + +dependencies { + // Package/application AndroidManifest.xml properties, plus headers and libraries + // exposed to CMake + implementation 'org.khronos.openxr:openxr_loader_for_android:1.1.36' +} diff --git a/Samples/XrSamples/XrSpatialAnchor/Projects/Android/build.py b/Samples/XrSamples/XrSpatialAnchor/Projects/Android/build.py new file mode 100755 index 0000000..d4b6e58 --- /dev/null +++ b/Samples/XrSamples/XrSpatialAnchor/Projects/Android/build.py @@ -0,0 +1,35 @@ +#!/usr/bin/python +# (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +# This first bit of code is common bootstrapping code +# to determine the SDK root, and to set up the import +# path for additional python code. + +# begin bootstrap +import os +import sys + + +def init(): + root = os.path.realpath(os.path.dirname(os.path.realpath(__file__))) + os.chdir(root) # make sure we are always executing from the project directory + while os.path.isdir(os.path.join(root, "bin/scripts/build")) == False: + root = os.path.realpath(os.path.join(root, "..")) + if ( + len(root) <= 5 + ): # Should catch both Posix and Windows root directories (e.g. '/' and 'C:\') + print("Unable to find SDK root. Exiting.") + sys.exit(1) + root = os.path.abspath(root) + os.environ["OCULUS_SDK_PATH"] = root + sys.path.append(root + "/bin/scripts/build") + + +init() +import ovrbuild + +ovrbuild.init() +# end bootstrap + + +ovrbuild.build() diff --git a/Samples/XrSamples/XrSpatialAnchor/Projects/Android/gradle.properties b/Samples/XrSamples/XrSpatialAnchor/Projects/Android/gradle.properties new file mode 100644 index 0000000..3e927b1 --- /dev/null +++ b/Samples/XrSamples/XrSpatialAnchor/Projects/Android/gradle.properties @@ -0,0 +1,21 @@ +# Project-wide Gradle settings. +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. More details, visit +# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects +# org.gradle.parallel=true +# AndroidX package structure to make it clearer which packages are bundled with the +# Android operating system, and which are packaged with your app's APK +# https://developer.android.com/topic/libraries/support-library/androidx-rn +android.useAndroidX=true +# Enables namespacing of each library's R class so that its R class includes only the +# resources declared in the library itself and none from the library's dependencies, +# thereby reducing the size of the R class for that library +android.nonTransitiveRClass=true \ No newline at end of file diff --git a/Samples/XrSamples/XrSpatialAnchor/Projects/Android/gradle/wrapper/gradle-wrapper.jar b/Samples/XrSamples/XrSpatialAnchor/Projects/Android/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..7454180 Binary files /dev/null and b/Samples/XrSamples/XrSpatialAnchor/Projects/Android/gradle/wrapper/gradle-wrapper.jar differ diff --git a/Samples/XrSamples/XrSpatialAnchor/Projects/Android/gradle/wrapper/gradle-wrapper.properties b/Samples/XrSamples/XrSpatialAnchor/Projects/Android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..ffed3a2 --- /dev/null +++ b/Samples/XrSamples/XrSpatialAnchor/Projects/Android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/Samples/XrSamples/XrSpatialAnchor/Projects/Android/gradlew b/Samples/XrSamples/XrSpatialAnchor/Projects/Android/gradlew new file mode 100755 index 0000000..a69d9cb --- /dev/null +++ b/Samples/XrSamples/XrSpatialAnchor/Projects/Android/gradlew @@ -0,0 +1,240 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# 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 +# +# https://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. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit + +APP_NAME="Gradle" +APP_BASE_NAME=${0##*/} + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/Samples/XrSamples/XrSpatialAnchor/Projects/Android/gradlew.bat b/Samples/XrSamples/XrSpatialAnchor/Projects/Android/gradlew.bat new file mode 100755 index 0000000..f127cfd --- /dev/null +++ b/Samples/XrSamples/XrSpatialAnchor/Projects/Android/gradlew.bat @@ -0,0 +1,91 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/Samples/XrSamples/XrSpatialAnchor/Projects/Android/settings.gradle b/Samples/XrSamples/XrSpatialAnchor/Projects/Android/settings.gradle new file mode 100755 index 0000000..0bb6eef --- /dev/null +++ b/Samples/XrSamples/XrSpatialAnchor/Projects/Android/settings.gradle @@ -0,0 +1 @@ +rootProject.name = "XrSpatialAnchor" diff --git a/Samples/XrSamples/XrSpatialAnchor/README.md b/Samples/XrSamples/XrSpatialAnchor/README.md new file mode 100644 index 0000000..c5a6f7d --- /dev/null +++ b/Samples/XrSamples/XrSpatialAnchor/README.md @@ -0,0 +1,14 @@ +# OpenXR Spatial Anchors Sample + +## Overview +Spatial entities are application-defined content that persist in real-world locations. + +* `XR_FB_spatial_entity`: This extension enables applications to persist the real-world location of content over time and contains definitions for the Entity-Component System. All Facebook spatial entities and scene extensions are dependent on this one. +* `XR_FB_spatial_entity_query`: This extension enables an application to query for shared spatial entities in the area. +* `XR_META_spatial_entity_discovery`: This extension enables an application to discover persistent spatial entities in the area and restore them. +* `XR_META_spatial_entity_persistence`: This extension enables spatial entities to be persisted such that they're retrievable across sessions and sharable. +* `XR_FB_spatial_entity_sharing`: This extension enables spatial entities to be shared between users. +* `XR_FB_spatial_entity_user`: This extension enables the creation and management of user objects which can be used by the application to reference a user other than the current user. + +## The Sample +In this sample, the orange cuboid is an object rendered with respect to Spatial Anchors that are tracked in the scene. The user is able to place an anchor using the 'A' button at the controller's position, after successfully completing an orange cuboid should appear at the position. The user can erase and destroy previously created/queried/discovered anchors by using the 'B' button. The user can retrieve shared anchors and persisted anchors by querying and discovering all anchors using the 'X' button. The user can share all tracked anchors using the 'Y' button. diff --git a/Samples/XrSamples/XrSpatialAnchor/Src/SimpleXrInput.cpp b/Samples/XrSamples/XrSpatialAnchor/Src/SimpleXrInput.cpp new file mode 100755 index 0000000..ec4db8c --- /dev/null +++ b/Samples/XrSamples/XrSpatialAnchor/Src/SimpleXrInput.cpp @@ -0,0 +1,298 @@ +// Simple Xr Input + +#if defined(ANDROID) +#include +#endif + +#include +#include + +#include "SimpleXrInput.h" + +#if defined(ANDROID) +#define DEBUG 1 +#define OVR_LOG_TAG "SimpleXrInput" + +#define ALOGE(...) __android_log_print(ANDROID_LOG_ERROR, OVR_LOG_TAG, __VA_ARGS__) +#define ALOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, OVR_LOG_TAG, __VA_ARGS__) +#else +#define DEBUG 1 +#define ALOGE(...) \ + printf("ERROR: "); \ + printf(__VA_ARGS__); \ + printf("\n") +#define ALOGV(...) \ + printf("VERBOSE: "); \ + printf(__VA_ARGS__); \ + printf("\n") +#endif + +/* +================================================================================ + +OpenXR Utility Functions + +================================================================================ +*/ + +#if defined(DEBUG) +static void +OXR_CheckErrors(XrInstance instance, XrResult result, const char* function, bool failOnError) { + if (XR_FAILED(result)) { + char errorBuffer[XR_MAX_RESULT_STRING_SIZE]; + xrResultToString(instance, result, errorBuffer); + if (failOnError) { + ALOGE("OpenXR error: %s: %s\n", function, errorBuffer); + } else { + ALOGV("OpenXR error: %s: %s\n", function, errorBuffer); + } + } +} +#endif + +#if defined(DEBUG) +#define OXR(func) OXR_CheckErrors(instance, func, #func, true); +#else +#define OXR(func) OXR_CheckErrors(instance, func, #func, false); +#endif + +struct SimpleXrInputImpl : public SimpleXrInput { + XrInstance instance = XR_NULL_HANDLE; + + XrPath leftHandPath; + XrPath rightHandPath; + + XrActionSet actionSet = XR_NULL_HANDLE; + XrAction a = XR_NULL_HANDLE; + XrAction b = XR_NULL_HANDLE; + XrAction x = XR_NULL_HANDLE; + XrAction y = XR_NULL_HANDLE; + XrAction aim = XR_NULL_HANDLE; + XrAction grip = XR_NULL_HANDLE; + + XrSession session = XR_NULL_HANDLE; + + struct AimGrip { + XrSpace aim; + XrSpace grip; + }; + + struct LeftRight { + AimGrip left; + AimGrip right; + }; + + LeftRight spaces; + uint32_t syncCount; + + SimpleXrInputImpl(XrInstance instance_) : instance(instance_) { + InitializeInput(); + } + + ~SimpleXrInputImpl() override { + if (actionSet != XR_NULL_HANDLE) { + OXR(xrDestroyActionSet(actionSet)); + } + } + + void InitializeInput() { + XrPath interactionProfile; + OXR(xrStringToPath( + instance, "/interaction_profiles/oculus/touch_controller", &interactionProfile)); + + OXR(xrStringToPath(instance, "/user/hand/left", &leftHandPath)); + OXR(xrStringToPath(instance, "/user/hand/right", &rightHandPath)); + XrPath handSubactionPaths[2] = {leftHandPath, rightHandPath}; + + actionSet = CreateActionSet(1, "main_action_set", "main ActionSet"); + a = CreateAction(actionSet, XR_ACTION_TYPE_BOOLEAN_INPUT, "a", "A button"); + b = CreateAction(actionSet, XR_ACTION_TYPE_BOOLEAN_INPUT, "b", "B button"); + x = CreateAction(actionSet, XR_ACTION_TYPE_BOOLEAN_INPUT, "x", "X button"); + y = CreateAction(actionSet, XR_ACTION_TYPE_BOOLEAN_INPUT, "y", "Y button"); + + aim = CreateAction( + actionSet, XR_ACTION_TYPE_POSE_INPUT, "aim_pose", nullptr, 2, handSubactionPaths); + grip = CreateAction( + actionSet, XR_ACTION_TYPE_POSE_INPUT, "grip_pose", nullptr, 2, handSubactionPaths); + + std::vector bindings; + bindings.push_back(Suggest(a, "/user/hand/right/input/a/click")); + bindings.push_back(Suggest(b, "/user/hand/right/input/b/click")); + bindings.push_back(Suggest(x, "/user/hand/left/input/x/click")); + bindings.push_back(Suggest(y, "/user/hand/left/input/y/click")); + bindings.push_back(Suggest(aim, "/user/hand/left/input/aim/pose")); + bindings.push_back(Suggest(aim, "/user/hand/right/input/aim/pose")); + bindings.push_back(Suggest(grip, "/user/hand/left/input/grip/pose")); + bindings.push_back(Suggest(grip, "/user/hand/right/input/grip/pose")); + + XrInteractionProfileSuggestedBinding suggestions = { + XR_TYPE_INTERACTION_PROFILE_SUGGESTED_BINDING}; + suggestions.interactionProfile = interactionProfile; + suggestions.suggestedBindings = &bindings[0]; + suggestions.countSuggestedBindings = bindings.size(); + OXR(xrSuggestInteractionProfileBindings(instance, &suggestions)); + + syncCount = 0; + } + + void BeginSession(XrSession session_) override { + if (syncCount != 0) { + ALOGV("SimpleXrInput::BeginSession call order invalid"); + return; + } + syncCount++; + session = session_; + // Attach actionSet to session + XrSessionActionSetsAttachInfo attachInfo = {XR_TYPE_SESSION_ACTION_SETS_ATTACH_INFO}; + attachInfo.countActionSets = 1; + attachInfo.actionSets = &actionSet; + OXR(xrAttachSessionActionSets(session, &attachInfo)); + } + + void EndSession() override { + ALOGV("SimpleXrInput::EndSession"); + syncCount = 0; + } + + void SyncActions() override { + if (syncCount == 1) { + spaces.left.aim = CreateActionSpace(aim, leftHandPath); + spaces.right.aim = CreateActionSpace(aim, rightHandPath); + spaces.left.grip = CreateActionSpace(grip, leftHandPath); + spaces.right.grip = CreateActionSpace(grip, rightHandPath); + } + // sync action data + XrActiveActionSet activeActionSet = {}; + activeActionSet.actionSet = actionSet; + activeActionSet.subactionPath = XR_NULL_PATH; + + XrActionsSyncInfo syncInfo = {XR_TYPE_ACTIONS_SYNC_INFO}; + syncInfo.countActiveActionSets = 1; + syncInfo.activeActionSets = &activeActionSet; + OXR(xrSyncActions(session, &syncInfo)); + syncCount++; + } + + OVR::Posef FromControllerSpace( + Side side, + ControllerSpace controllerSpace, + XrSpace baseSpace, + XrTime atTime) override { + AimGrip& ag = side == Side_Left ? spaces.left : spaces.right; + XrSpace& space = controllerSpace == Controller_Aim ? ag.aim : ag.grip; + XrSpaceLocation loc = {XR_TYPE_SPACE_LOCATION}; + OXR(xrLocateSpace(space, baseSpace, atTime, &loc)); + return *reinterpret_cast(&loc.pose); + } + + bool A() override { + return GetActionStateBoolean(a).currentState == XR_TRUE; + } + + bool B() override { + return GetActionStateBoolean(b).currentState == XR_TRUE; + } + + bool X() override { + return GetActionStateBoolean(x).currentState == XR_TRUE; + } + + bool Y() override { + return GetActionStateBoolean(y).currentState == XR_TRUE; + } + + XrActionSet CreateActionSet(int priority, const char* name, const char* localizedName) { + XrActionSetCreateInfo asci = {XR_TYPE_ACTION_SET_CREATE_INFO}; + asci.priority = priority; + strcpy(asci.actionSetName, name); + strcpy(asci.localizedActionSetName, localizedName); + XrActionSet as = XR_NULL_HANDLE; + OXR(xrCreateActionSet(instance, &asci, &as)); + return as; + } + + XrAction CreateAction( + XrActionSet as, + XrActionType type, + const char* actionName, + const char* localizedName, + int countSubactionPaths = 0, + XrPath* subactionPaths = nullptr) { + ALOGV("CreateAction %s, %" PRIi32, actionName, countSubactionPaths); + + XrActionCreateInfo aci = {XR_TYPE_ACTION_CREATE_INFO}; + aci.actionType = type; + if (countSubactionPaths > 0) { + aci.countSubactionPaths = countSubactionPaths; + aci.subactionPaths = subactionPaths; + } + strcpy(aci.actionName, actionName); + strcpy(aci.localizedActionName, localizedName ? localizedName : actionName); + XrAction action = XR_NULL_HANDLE; + OXR(xrCreateAction(as, &aci, &action)); + return action; + } + + XrActionSuggestedBinding Suggest(XrAction action, const char* bindingString) { + XrActionSuggestedBinding asb; + asb.action = action; + XrPath bindingPath; + OXR(xrStringToPath(instance, bindingString, &bindingPath)); + asb.binding = bindingPath; + return asb; + } + + XrSpace CreateActionSpace(XrAction poseAction, XrPath subactionPath) { + XrActionSpaceCreateInfo asci = {XR_TYPE_ACTION_SPACE_CREATE_INFO}; + asci.action = poseAction; + asci.poseInActionSpace.orientation.w = 1.0f; + asci.subactionPath = subactionPath; + XrSpace actionSpace = XR_NULL_HANDLE; + OXR(xrCreateActionSpace(session, &asci, &actionSpace)); + return actionSpace; + } + + XrActionStateBoolean GetActionStateBoolean(XrAction action) { + XrActionStateGetInfo getInfo = {XR_TYPE_ACTION_STATE_GET_INFO}; + getInfo.action = action; + + XrActionStateBoolean state = {XR_TYPE_ACTION_STATE_BOOLEAN}; + + OXR(xrGetActionStateBoolean(session, &getInfo, &state)); + return state; + } + + XrActionStateFloat GetActionStateFloat(XrAction action) { + XrActionStateGetInfo getInfo = {XR_TYPE_ACTION_STATE_GET_INFO}; + getInfo.action = action; + + XrActionStateFloat state = {XR_TYPE_ACTION_STATE_FLOAT}; + + OXR(xrGetActionStateFloat(session, &getInfo, &state)); + return state; + } + + XrActionStateVector2f GetActionStateVector2(XrAction action) { + XrActionStateGetInfo getInfo = {XR_TYPE_ACTION_STATE_GET_INFO}; + getInfo.action = action; + + XrActionStateVector2f state = {XR_TYPE_ACTION_STATE_VECTOR2F}; + + OXR(xrGetActionStateVector2f(session, &getInfo, &state)); + return state; + } + + bool ActionPoseIsActive(XrAction action, XrPath subactionPath) { + XrActionStateGetInfo getInfo = {XR_TYPE_ACTION_STATE_GET_INFO}; + getInfo.action = action; + getInfo.subactionPath = subactionPath; + + XrActionStatePose state = {XR_TYPE_ACTION_STATE_POSE}; + OXR(xrGetActionStatePose(session, &getInfo, &state)); + return state.isActive != XR_FALSE; + } +}; + +SimpleXrInput* CreateSimpleXrInput(XrInstance instance_) { + return new SimpleXrInputImpl(instance_); +} diff --git a/Samples/XrSamples/XrSpatialAnchor/Src/SimpleXrInput.h b/Samples/XrSamples/XrSpatialAnchor/Src/SimpleXrInput.h new file mode 100755 index 0000000..275f152 --- /dev/null +++ b/Samples/XrSamples/XrSpatialAnchor/Src/SimpleXrInput.h @@ -0,0 +1,57 @@ +// Simple Xr Input + +#if defined(ANDROID) +#include + +#include +#include +#include +#include + +#define XR_USE_GRAPHICS_API_OPENGL_ES 1 +#define XR_USE_PLATFORM_ANDROID 1 +#elif defined(WIN32) +#include "Render/GlWrapperWin32.h" + +#include +#define XR_USE_GRAPHICS_API_OPENGL 1 +#define XR_USE_PLATFORM_WIN32 1 +#endif + +#include +#include +#include + +#include "OVR_Math.h" + +class SimpleXrInput { + public: + enum Side { + Side_Left = 0, + Side_Right = 1, + }; + + enum ControllerSpace { + Controller_Aim = 0, + Controller_Grip = 1, + }; + + virtual ~SimpleXrInput() {} + virtual void BeginSession(XrSession session_) = 0; + virtual void EndSession() = 0; + virtual void SyncActions() = 0; + + // Returns the pose that transforms the controller space into baseSpace + virtual OVR::Posef FromControllerSpace( + Side side, + ControllerSpace controllerSpace, + XrSpace baseSpace, + XrTime atTime) = 0; + + virtual bool A() = 0; + virtual bool B() = 0; + virtual bool X() = 0; + virtual bool Y() = 0; +}; + +SimpleXrInput* CreateSimpleXrInput(XrInstance instance_); diff --git a/Samples/XrSamples/XrSpatialAnchor/Src/SpatialAnchorExternalDataHandler.h b/Samples/XrSamples/XrSpatialAnchor/Src/SpatialAnchorExternalDataHandler.h new file mode 100755 index 0000000..3994446 --- /dev/null +++ b/Samples/XrSamples/XrSpatialAnchor/Src/SpatialAnchorExternalDataHandler.h @@ -0,0 +1,20 @@ +#pragma once + +#include +#include + +class SpatialAnchorExternalDataHandler { + public: + virtual ~SpatialAnchorExternalDataHandler() {} + + // LoadShareUserList loads the list of FBIDs of users with whom to share Spatial Anchors. + virtual bool LoadShareUserList(std::vector& userIdList) = 0; + + // LoadInboundSpatialAnchorList loads the list of Spatial Anchors that have + // been shared with the current user. + virtual bool LoadInboundSpatialAnchorList(std::vector& spatialAnchorList) = 0; + + // WriteSharedSpatialAnchorList emits the list of Spatial Anchors shared by the current user + // to the specified list of users. + virtual bool WriteSharedSpatialAnchorList(const std::vector& spatialAnchorList, const std::vector& userIdList) = 0; +}; diff --git a/Samples/XrSamples/XrSpatialAnchor/Src/SpatialAnchorFileHandler.cpp b/Samples/XrSamples/XrSpatialAnchor/Src/SpatialAnchorFileHandler.cpp new file mode 100755 index 0000000..35aa9fd --- /dev/null +++ b/Samples/XrSamples/XrSpatialAnchor/Src/SpatialAnchorFileHandler.cpp @@ -0,0 +1,141 @@ +/************************************************************************************ + +Filename : SpatialAnchorFileHandler.cpp +Content : Handler for text file-based communication of Spatial Anchors and User IDs. +Created : +Authors : + +Copyright : Copyright (c) Meta Platforms, Inc. and its affiliates. All rights reserved. + +*************************************************************************************/ + +#include +#include +#include +#include + +#include "SpatialAnchorFileHandler.h" +#include "SpatialAnchorUtilities.h" + +#if defined(ANDROID) +#include +#endif + +#define DEBUG 1 + +#if defined(ANDROID) +#define OVR_LOG_TAG "SpatialAnchorFileHandler" + +#define ALOGE(...) __android_log_print(ANDROID_LOG_ERROR, OVR_LOG_TAG, __VA_ARGS__) +#define ALOGW(...) __android_log_print(ANDROID_LOG_WARN, OVR_LOG_TAG, __VA_ARGS__) +#define ALOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, OVR_LOG_TAG, __VA_ARGS__) +#else +#define ALOGE(...) \ + printf("ERROR: "); \ + printf(__VA_ARGS__); \ + printf("\n") +#define ALOGW(...) \ + printf("WARN: "); \ + printf(__VA_ARGS__); \ + printf("\n") +#define ALOGV(...) \ + printf("VERBOSE: "); \ + printf(__VA_ARGS__); \ + printf("\n") +#endif + +SpatialAnchorFileHandler::SpatialAnchorFileHandler() { + dataDir = kDefaultDataPath; + ALOGV("Using data path %s", dataDir.c_str()); + assert(dataDir.back() == '/' || dataDir.back() == '\\'); +} + +bool SpatialAnchorFileHandler::LoadShareUserList(std::vector& userIdList) { + ALOGV("LoadShareUserList"); + + std::string filePath = dataDir + kShareUserListFilename; + + ::FILE* file = ::fopen(filePath.c_str(), "r"); + if (!file) { + ALOGE("LoadShareUserList: Failed to open file: %s", filePath.c_str()); + return false; + } + if (::feof(file)) { + ALOGE("LoadShareUserList: File is empty"); + return false; + } + const int maxUserIdCstrLength = 21; + char line[maxUserIdCstrLength]; + while (!::feof(file)) { + ::fscanf(file, "%20s", line); + if (::ferror(file)) { + ALOGE("LoadShareUserList: Failed to read from file: %s", filePath.c_str()); + fclose(file); + return false; + } + XrSpaceUserIdFB userId = (XrSpaceUserIdFB)strtoull(line, nullptr, 10); + userIdList.emplace_back(userId); + } + fclose(file); + return true; +} + +bool SpatialAnchorFileHandler::LoadInboundSpatialAnchorList(std::vector& spatialAnchorList) { + ALOGV("LoadInboundSpatialAnchorList"); + + std::string filePath = dataDir + kInboundSpatialAnchorListFilename; + + ::FILE* file = ::fopen(filePath.c_str(), "r"); + if (!file) { + ALOGE("LoadInboundSpatialAnchorList: Failed to open file: %s", filePath.c_str()); + return false; + } + if (::feof(file)) { + ALOGE("LoadInboundSpatialAnchorList: File is empty"); + return false; + } + const int uuidCstrLength = XR_UUID_SIZE_EXT * 2 + 1; + char line[uuidCstrLength]; + while (!::feof(file)) { + ::fscanf(file, "%32s\n", line); + if (::ferror(file)) { + ALOGE("LoadInboundSpatialAnchorList: Failed to read from file: %s", filePath.c_str()); + fclose(file); + return false; + } + XrUuidEXT uuid; + if (!hexStringToUuid(line, uuid)) { + ALOGE("LoadInboundSpatialAnchorList: Failed to parse UUID string: %s", line); + } + spatialAnchorList.emplace_back(uuid); + } + fclose(file); + return true; +} + +bool SpatialAnchorFileHandler::WriteSharedSpatialAnchorList(const std::vector& spatialAnchorList, const std::vector& userIdList) { + ALOGV("WriteSharedSpatialAnchorList"); + if (spatialAnchorList.size() == 0) { + ALOGE("WriteSharedSpatialAnchorList: spatialAnchorList cannot be empty"); + return false; + } + + std::string filePath = dataDir + kSharedSpatialAnchorListFilename; + ::FILE* file = ::fopen(filePath.c_str(), "w"); + if (!file) { + ALOGE("Failed to create file: %s", filePath.c_str()); + return false; + } + + int res; + for (uint32_t i = 0; i < spatialAnchorList.size(); i++) { + // We'll use a human-readable format for easier debugging. + res = ::fprintf(file, "%s\n", uuidToHexString(spatialAnchorList[i]).c_str()); + if (res <= 0) { + ALOGE("Failed to write data to file: %s", filePath.c_str()); + break; + } + } + fclose(file); + return (res > 0); +} diff --git a/Samples/XrSamples/XrSpatialAnchor/Src/SpatialAnchorFileHandler.h b/Samples/XrSamples/XrSpatialAnchor/Src/SpatialAnchorFileHandler.h new file mode 100755 index 0000000..6a4a289 --- /dev/null +++ b/Samples/XrSamples/XrSpatialAnchor/Src/SpatialAnchorFileHandler.h @@ -0,0 +1,30 @@ +#pragma once + +#include "SpatialAnchorExternalDataHandler.h" +#include +#include +#include + +class SpatialAnchorFileHandler : public SpatialAnchorExternalDataHandler { + public: + SpatialAnchorFileHandler(); + + // LoadShareUserList loads the list of FBIDs of users with whom to share Spatial Anchors. + bool LoadShareUserList(std::vector& userIdList) override; + bool LoadInboundSpatialAnchorList(std::vector& spatialAnchorList) override; + bool WriteSharedSpatialAnchorList(const std::vector& spatialAnchorList, const std::vector& userIdList) override; + + private: + std::string dataDir; + const char* kShareUserListFilename = "shareUserList.txt"; + const char* kInboundSpatialAnchorListFilename = "inboundSpatialAnchorList.txt"; + const char* kSharedSpatialAnchorListFilename = "sharedSpatialAnchorList.txt"; + +// Replace this value with the path you want the named files above to be. +// Make sure to include the trailing slash (backslash for Windows). +#ifdef WIN32 + const char* kDefaultDataPath = "C:\\temp_SpatialAnchorXr\\"; +#else + const char* kDefaultDataPath = "/sdcard/Android/data/com.oculus.sdk.spatialanchor/files/"; +#endif +}; diff --git a/Samples/XrSamples/XrSpatialAnchor/Src/SpatialAnchorGl.cpp b/Samples/XrSamples/XrSpatialAnchor/Src/SpatialAnchorGl.cpp new file mode 100755 index 0000000..79bc58f --- /dev/null +++ b/Samples/XrSamples/XrSpatialAnchor/Src/SpatialAnchorGl.cpp @@ -0,0 +1,1091 @@ +/************************************************************************************ + +Filename : SpatialAnchor.cpp +Content : This sample is derived from VrCubeWorld_SurfaceView. + When used in room scale mode, it draws a "carpet" under the + user to indicate where it is safe to walk around. +Created : July, 2020 +Authors : Cass Everitt + +Copyright : Copyright (c) Meta Platforms, Inc. and its affiliates. All rights reserved. + +*************************************************************************************/ + +#include +#include +#include +#include + +#if defined(ANDROID) +#include +#include +#include // for prctl( PR_SET_NAME ) +#include +#include // for native window JNI +#include +#endif + +#include +#include + +#if defined(ANDROID) +#include + +#include +#include +#include +#endif + +#include "SpatialAnchorGl.h" + +using namespace OVR; + +// EXT_texture_border_clamp +#ifndef GL_CLAMP_TO_BORDER +#define GL_CLAMP_TO_BORDER 0x812D +#endif + +#ifndef GL_TEXTURE_BORDER_COLOR +#define GL_TEXTURE_BORDER_COLOR 0x1004 +#endif + +#ifndef GL_FRAMEBUFFER_SRGB_EXT +#define GL_FRAMEBUFFER_SRGB_EXT 0x8DB9 +#endif + +#if !defined(GL_EXT_multisampled_render_to_texture) +typedef void(GL_APIENTRY* PFNGLRENDERBUFFERSTORAGEMULTISAMPLEEXTPROC)( + GLenum target, + GLsizei samples, + GLenum internalformat, + GLsizei width, + GLsizei height); +typedef void(GL_APIENTRY* PFNGLFRAMEBUFFERTEXTURE2DMULTISAMPLEEXTPROC)( + GLenum target, + GLenum attachment, + GLenum textarget, + GLuint texture, + GLint level, + GLsizei samples); +#endif + +#if !defined(GL_OVR_multiview) +typedef void(GL_APIENTRY* PFNGLFRAMEBUFFERTEXTUREMULTIVIEWOVRPROC)( + GLenum target, + GLenum attachment, + GLuint texture, + GLint level, + GLint baseViewIndex, + GLsizei numViews); +#endif + +#if !defined(GL_OVR_multiview_multisampled_render_to_texture) +typedef void(GL_APIENTRY* PFNGLFRAMEBUFFERTEXTUREMULTISAMPLEMULTIVIEWOVRPROC)( + GLenum target, + GLenum attachment, + GLuint texture, + GLint level, + GLsizei samples, + GLint baseViewIndex, + GLsizei numViews); +#endif + +#define DEBUG 1 + +#if defined(ANDROID) +#define OVR_LOG_TAG "SpatialAnchorGl" + +#define ALOGE(...) __android_log_print(ANDROID_LOG_ERROR, OVR_LOG_TAG, __VA_ARGS__) +#if DEBUG +#define ALOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, OVR_LOG_TAG, __VA_ARGS__) +#else +#define ALOGV(...) +#endif + +#else +#define ALOGE(...) \ + printf("ERROR: "); \ + printf(__VA_ARGS__); \ + printf("\n") +#define ALOGV(...) \ + printf("VERBOSE: "); \ + printf(__VA_ARGS__); \ + printf("\n") +#endif // defined(ANDROID) + +/* +================================================================================ + +OpenGL-ES Utility Functions + +================================================================================ +*/ + +namespace { +struct OpenGLExtensions_t { + bool multi_view; // GL_OVR_multiview, GL_OVR_multiview2 + bool EXT_texture_border_clamp; // GL_EXT_texture_border_clamp, GL_OES_texture_border_clamp + bool EXT_sRGB_write_control; +}; + +OpenGLExtensions_t glExtensions; +} // namespace + +static void EglInitExtensions() { + glExtensions = {}; + const char* allExtensions = (const char*)glGetString(GL_EXTENSIONS); + if (allExtensions != nullptr) { + glExtensions.multi_view = strstr(allExtensions, "GL_OVR_multiview2") && + strstr(allExtensions, "GL_OVR_multiview_multisampled_render_to_texture"); + + glExtensions.EXT_texture_border_clamp = + strstr(allExtensions, "GL_EXT_texture_border_clamp") || + strstr(allExtensions, "GL_OES_texture_border_clamp"); + glExtensions.EXT_sRGB_write_control = strstr(allExtensions, "GL_EXT_sRGB_write_control"); + } +} + +static const char* GlFrameBufferStatusString(GLenum status) { + switch (status) { + case GL_FRAMEBUFFER_UNDEFINED: + return "GL_FRAMEBUFFER_UNDEFINED"; + case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: + return "GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT"; + case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: + return "GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT"; + case GL_FRAMEBUFFER_UNSUPPORTED: + return "GL_FRAMEBUFFER_UNSUPPORTED"; + case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE: + return "GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE"; + default: + return "unknown"; + } +} + +#ifdef CHECK_GL_ERRORS + +static const char* GlErrorString(GLenum error) { + switch (error) { + case GL_NO_ERROR: + return "GL_NO_ERROR"; + case GL_INVALID_ENUM: + return "GL_INVALID_ENUM"; + case GL_INVALID_VALUE: + return "GL_INVALID_VALUE"; + case GL_INVALID_OPERATION: + return "GL_INVALID_OPERATION"; + case GL_INVALID_FRAMEBUFFER_OPERATION: + return "GL_INVALID_FRAMEBUFFER_OPERATION"; + case GL_OUT_OF_MEMORY: + return "GL_OUT_OF_MEMORY"; + default: + return "unknown"; + } +} + +static void GLCheckErrors(int line) { + for (int i = 0; i < 10; i++) { + const GLenum error = glGetError(); + if (error == GL_NO_ERROR) { + break; + } + ALOGE("GL error on line %d: %s", line, GlErrorString(error)); + } +} + +#define GL(func) \ + func; \ + GLCheckErrors(__LINE__); + +#else // CHECK_GL_ERRORS + +#define GL(func) func; + +#endif // CHECK_GL_ERRORS + +/* +================================================================================ + +ovrGeometry + +================================================================================ +*/ + +enum VertexAttributeLocation { + VERTEX_ATTRIBUTE_LOCATION_POSITION, + VERTEX_ATTRIBUTE_LOCATION_COLOR, + VERTEX_ATTRIBUTE_LOCATION_UV +}; + +struct ovrVertexAttribute { + enum VertexAttributeLocation location; + const char* name; +}; + +static ovrVertexAttribute ProgramVertexAttributes[] = { + {VERTEX_ATTRIBUTE_LOCATION_POSITION, "vertexPosition"}, + {VERTEX_ATTRIBUTE_LOCATION_COLOR, "vertexColor"}, + {VERTEX_ATTRIBUTE_LOCATION_UV, "vertexUv"}}; + +void ovrGeometry::Clear() { + VertexBuffer = 0; + IndexBuffer = 0; + VertexArrayObject = 0; + VertexCount = 0; + IndexCount = 0; + for (int i = 0; i < MAX_VERTEX_ATTRIB_POINTERS; i++) { + memset(&VertexAttribs[i], 0, sizeof(VertexAttribs[i])); + VertexAttribs[i].Index = -1; + } +} + +void ovrGeometry::CreateCube() { + struct ovrCubeVertices { + signed char positions[8][4]; + unsigned char colors[8][4]; + }; + + static const ovrCubeVertices cubeVertices = {// positions + {{-127, -127, -127, +127}, + {+127, -127, -127, +127}, + {-127, +127, -127, +127}, + {+127, +127, -127, +127}, + {-127, -127, +127, +127}, + {+127, -127, +127, +127}, + {-127, +127, +127, +127}, + {+127, +127, +127, +127}}, + // colors + {{0x00, 0x00, 0x00, 0xff}, + {0xff, 0x00, 0x00, 0xff}, + {0x00, 0xff, 0x00, 0xff}, + {0xff, 0xff, 0x00, 0xff}, + {0x00, 0x00, 0xff, 0xff}, + {0xff, 0x00, 0xff, 0xff}, + {0x00, 0xff, 0xff, 0xff}, + {0xff, 0xff, 0xff, 0xff}}}; + + static const unsigned short cubeIndices[36] = { + 0, 2, 1, 2, 3, 1, // back + 4, 5, 6, 6, 5, 7, // front + 6, 7, 2, 2, 7, 3, // top + 4, 0, 5, 5, 0, 1, // bottom + 0, 4, 2, 2, 4, 6, // left + 5, 1, 7, 7, 1, 3 // right + }; + + VertexCount = 8; + IndexCount = 36; + + VertexAttribs[0].Index = VERTEX_ATTRIBUTE_LOCATION_POSITION; + VertexAttribs[0].Size = 4; + VertexAttribs[0].Type = GL_BYTE; + VertexAttribs[0].Normalized = true; + VertexAttribs[0].Stride = sizeof(cubeVertices.positions[0]); + VertexAttribs[0].Pointer = (const GLvoid*)offsetof(ovrCubeVertices, positions); + + VertexAttribs[1].Index = VERTEX_ATTRIBUTE_LOCATION_COLOR; + VertexAttribs[1].Size = 4; + VertexAttribs[1].Type = GL_UNSIGNED_BYTE; + VertexAttribs[1].Normalized = true; + VertexAttribs[1].Stride = sizeof(cubeVertices.colors[0]); + VertexAttribs[1].Pointer = (const GLvoid*)offsetof(ovrCubeVertices, colors); + + GL(glGenBuffers(1, &VertexBuffer)); + GL(glBindBuffer(GL_ARRAY_BUFFER, VertexBuffer)); + GL(glBufferData(GL_ARRAY_BUFFER, sizeof(cubeVertices), &cubeVertices, GL_STATIC_DRAW)); + GL(glBindBuffer(GL_ARRAY_BUFFER, 0)); + + GL(glGenBuffers(1, &IndexBuffer)); + GL(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IndexBuffer)); + GL(glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(cubeIndices), cubeIndices, GL_STATIC_DRAW)); + GL(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)); +} + +void ovrGeometry::CreateAxes() { + struct ovrAxesVertices { + float positions[6][3]; + unsigned char colors[6][4]; + }; + + static const ovrAxesVertices axesVertices = { + // positions + {{0, 0, 0}, {1, 0, 0}, {0, 0, 0}, {0, 1, 0}, {0, 0, 0}, {0, 0, 1}}, + // colors + {{255, 0, 0, 255}, + {255, 0, 0, 255}, + {0, 255, 0, 255}, + {0, 255, 0, 255}, + {0, 0, 255, 255}, + {0, 0, 255, 255}}, + }; + + static const unsigned short axesIndices[6] = { + 0, + 1, // x axis - red + 2, + 3, // y axis - green + 4, + 5 // z axis - blue + }; + + VertexCount = 6; + IndexCount = 6; + + VertexAttribs[0].Index = VERTEX_ATTRIBUTE_LOCATION_POSITION; + VertexAttribs[0].Size = 3; + VertexAttribs[0].Type = GL_FLOAT; + VertexAttribs[0].Normalized = false; + VertexAttribs[0].Stride = sizeof(axesVertices.positions[0]); + VertexAttribs[0].Pointer = (const GLvoid*)offsetof(ovrAxesVertices, positions); + + VertexAttribs[1].Index = VERTEX_ATTRIBUTE_LOCATION_COLOR; + VertexAttribs[1].Size = 4; + VertexAttribs[1].Type = GL_UNSIGNED_BYTE; + VertexAttribs[1].Normalized = true; + VertexAttribs[1].Stride = sizeof(axesVertices.colors[0]); + VertexAttribs[1].Pointer = (const GLvoid*)offsetof(ovrAxesVertices, colors); + + GL(glGenBuffers(1, &VertexBuffer)); + GL(glBindBuffer(GL_ARRAY_BUFFER, VertexBuffer)); + GL(glBufferData(GL_ARRAY_BUFFER, sizeof(axesVertices), &axesVertices, GL_STATIC_DRAW)); + GL(glBindBuffer(GL_ARRAY_BUFFER, 0)); + + GL(glGenBuffers(1, &IndexBuffer)); + GL(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IndexBuffer)); + GL(glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(axesIndices), axesIndices, GL_STATIC_DRAW)); + GL(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)); +} + +void ovrGeometry::CreateStage() { + static const float stageVertices[12] = { + -1.0f, -1.0f, 0.0f, 1.0f, -1.0f, 0.0f, -1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f}; + + static const unsigned short stageIndices[6] = {0, 1, 2, 2, 1, 3}; + + VertexCount = 4; + IndexCount = 6; + + VertexAttribs[0].Index = VERTEX_ATTRIBUTE_LOCATION_POSITION; + VertexAttribs[0].Size = 3; + VertexAttribs[0].Type = GL_FLOAT; + VertexAttribs[0].Normalized = false; + VertexAttribs[0].Stride = 3 * sizeof(float); + VertexAttribs[0].Pointer = (const GLvoid*)0; + + GL(glGenBuffers(1, &VertexBuffer)); + GL(glBindBuffer(GL_ARRAY_BUFFER, VertexBuffer)); + GL(glBufferData(GL_ARRAY_BUFFER, sizeof(stageVertices), stageVertices, GL_STATIC_DRAW)); + GL(glBindBuffer(GL_ARRAY_BUFFER, 0)); + + GL(glGenBuffers(1, &IndexBuffer)); + GL(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IndexBuffer)); + GL(glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(stageIndices), stageIndices, GL_STATIC_DRAW)); + GL(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)); +} + +void ovrGeometry::Destroy() { + GL(glDeleteBuffers(1, &IndexBuffer)); + GL(glDeleteBuffers(1, &VertexBuffer)); + + Clear(); +} + +void ovrGeometry::CreateVAO() { + GL(glGenVertexArrays(1, &VertexArrayObject)); + GL(glBindVertexArray(VertexArrayObject)); + + GL(glBindBuffer(GL_ARRAY_BUFFER, VertexBuffer)); + + for (int i = 0; i < MAX_VERTEX_ATTRIB_POINTERS; i++) { + if (VertexAttribs[i].Index != -1) { + GL(glEnableVertexAttribArray(VertexAttribs[i].Index)); + GL(glVertexAttribPointer( + VertexAttribs[i].Index, + VertexAttribs[i].Size, + VertexAttribs[i].Type, + VertexAttribs[i].Normalized, + VertexAttribs[i].Stride, + VertexAttribs[i].Pointer)); + } + } + + GL(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IndexBuffer)); + + GL(glBindVertexArray(0)); +} + +void ovrGeometry::DestroyVAO() { + GL(glDeleteVertexArrays(1, &VertexArrayObject)); +} + +/* +================================================================================ + +ovrProgram + +================================================================================ +*/ + +struct ovrUniform { + enum Index { + MODEL_MATRIX, + VIEW_ID, + SCENE_MATRICES, + COLOR_SCALE, + COLOR_BIAS, + }; + enum Type { + VECTOR4, + MATRIX4X4, + INTEGER, + BUFFER, + }; + + Index index; + Type type; + const char* name; +}; + +static ovrUniform ProgramUniforms[] = { + {ovrUniform::Index::MODEL_MATRIX, ovrUniform::Type::MATRIX4X4, "ModelMatrix"}, + {ovrUniform::Index::VIEW_ID, ovrUniform::Type::INTEGER, "ViewID"}, + {ovrUniform::Index::SCENE_MATRICES, ovrUniform::Type::BUFFER, "SceneMatrices"}, + {ovrUniform::Index::COLOR_SCALE, ovrUniform::Type::VECTOR4, "ColorScale"}, + {ovrUniform::Index::COLOR_BIAS, ovrUniform::Type::VECTOR4, "ColorBias"}, +}; + +void ovrProgram::Clear() { + Program = 0; + VertexShader = 0; + FragmentShader = 0; + memset(UniformLocation, 0, sizeof(UniformLocation)); + memset(UniformBinding, 0, sizeof(UniformBinding)); + memset(Textures, 0, sizeof(Textures)); +} + +static const char* programVersion = "#version 300 es\n"; + +bool ovrProgram::Create(const char* vertexSource, const char* fragmentSource) { + GLint r; + + GL(VertexShader = glCreateShader(GL_VERTEX_SHADER)); + + const char* vertexSources[3] = {programVersion, "", vertexSource}; + GL(glShaderSource(VertexShader, 3, vertexSources, 0)); + GL(glCompileShader(VertexShader)); + GL(glGetShaderiv(VertexShader, GL_COMPILE_STATUS, &r)); + if (r == GL_FALSE) { + GLchar msg[4096]; + GL(glGetShaderInfoLog(VertexShader, sizeof(msg), 0, msg)); + ALOGE("vertex shader compile failed"); + ALOGE("%s\n%s\n", vertexSource, msg); + return false; + } + + const char* fragmentSources[2] = {programVersion, fragmentSource}; + GL(FragmentShader = glCreateShader(GL_FRAGMENT_SHADER)); + GL(glShaderSource(FragmentShader, 2, fragmentSources, 0)); + GL(glCompileShader(FragmentShader)); + GL(glGetShaderiv(FragmentShader, GL_COMPILE_STATUS, &r)); + if (r == GL_FALSE) { + GLchar msg[4096]; + GL(glGetShaderInfoLog(FragmentShader, sizeof(msg), 0, msg)); + ALOGE("fragment shader compile failed"); + ALOGE("%s\n%s\n", fragmentSource, msg); + return false; + } + + GL(Program = glCreateProgram()); + GL(glAttachShader(Program, VertexShader)); + GL(glAttachShader(Program, FragmentShader)); + + // Bind the vertex attribute locations. + for (size_t i = 0; i < sizeof(ProgramVertexAttributes) / sizeof(ProgramVertexAttributes[0]); + i++) { + GL(glBindAttribLocation( + Program, ProgramVertexAttributes[i].location, ProgramVertexAttributes[i].name)); + } + + GL(glLinkProgram(Program)); + GL(glGetProgramiv(Program, GL_LINK_STATUS, &r)); + if (r == GL_FALSE) { + GLchar msg[4096]; + GL(glGetProgramInfoLog(Program, sizeof(msg), 0, msg)); + ALOGE("Linking program failed: %s\n", msg); + return false; + } + + int numBufferBindings = 0; + + memset(UniformLocation, -1, sizeof(UniformLocation)); + for (size_t i = 0; i < sizeof(ProgramUniforms) / sizeof(ProgramUniforms[0]); i++) { + const int uniformIndex = ProgramUniforms[i].index; + if (ProgramUniforms[i].type == ovrUniform::Type::BUFFER) { + GL(UniformLocation[uniformIndex] = + glGetUniformBlockIndex(Program, ProgramUniforms[i].name)); + UniformBinding[uniformIndex] = numBufferBindings++; + GL(glUniformBlockBinding( + Program, UniformLocation[uniformIndex], UniformBinding[uniformIndex])); + } else { + GL(UniformLocation[uniformIndex] = + glGetUniformLocation(Program, ProgramUniforms[i].name)); + UniformBinding[uniformIndex] = UniformLocation[uniformIndex]; + } + } + + GL(glUseProgram(Program)); + + // Get the texture locations. + for (int i = 0; i < MAX_PROGRAM_TEXTURES; i++) { + char name[32]; + sprintf(name, "Texture%i", i); + Textures[i] = glGetUniformLocation(Program, name); + if (Textures[i] != -1) { + GL(glUniform1i(Textures[i], i)); + } + } + + GL(glUseProgram(0)); + + return true; +} + +void ovrProgram::Destroy() { + if (Program != 0) { + GL(glDeleteProgram(Program)); + Program = 0; + } + if (VertexShader != 0) { + GL(glDeleteShader(VertexShader)); + VertexShader = 0; + } + if (FragmentShader != 0) { + GL(glDeleteShader(FragmentShader)); + FragmentShader = 0; + } +} + +static const char CUBE_VERTEX_SHADER[] = + "#define NUM_VIEWS 2\n" + "#define VIEW_ID gl_ViewID_OVR\n" + "#extension GL_OVR_multiview2 : require\n" + "layout(num_views=NUM_VIEWS) in;\n" + "in vec3 vertexPosition;\n" + "in vec4 vertexColor;\n" + "uniform mat4 ModelMatrix;\n" + "uniform vec4 ColorScale;\n" + "uniform vec4 ColorBias;\n" + "uniform SceneMatrices\n" + "{\n" + " uniform mat4 ViewMatrix[NUM_VIEWS];\n" + " uniform mat4 ProjectionMatrix[NUM_VIEWS];\n" + "} sm;\n" + "out vec4 fragmentColor;\n" + "void main()\n" + "{\n" + " gl_Position = sm.ProjectionMatrix[VIEW_ID] * ( sm.ViewMatrix[VIEW_ID] * ( ModelMatrix * vec4( vertexPosition, 1.0 ) ) );\n" + " fragmentColor = vertexColor * ColorScale + ColorBias;\n" + "}\n"; + +static const char CUBE_FRAGMENT_SHADER[] = + "in lowp vec4 fragmentColor;\n" + "out lowp vec4 outColor;\n" + "void main()\n" + "{\n" + " outColor = fragmentColor;\n" + "}\n"; + +static const char STAGE_VERTEX_SHADER[] = + "#define NUM_VIEWS 2\n" + "#define VIEW_ID gl_ViewID_OVR\n" + "#extension GL_OVR_multiview2 : require\n" + "layout(num_views=NUM_VIEWS) in;\n" + "in vec3 vertexPosition;\n" + "uniform mat4 ModelMatrix;\n" + "uniform SceneMatrices\n" + "{\n" + " uniform mat4 ViewMatrix[NUM_VIEWS];\n" + " uniform mat4 ProjectionMatrix[NUM_VIEWS];\n" + "} sm;\n" + "void main()\n" + "{\n" + " gl_Position = sm.ProjectionMatrix[VIEW_ID] * ( sm.ViewMatrix[VIEW_ID] * ( ModelMatrix * ( vec4( vertexPosition, 1.0 ) ) ) );\n" + "}\n"; + +static const char STAGE_FRAGMENT_SHADER[] = + "out lowp vec4 outColor;\n" + "void main()\n" + "{\n" + " outColor = vec4( 0.5, 0.5, 1.0, 0.5 );\n" + "}\n"; + +static const char AXES_VERTEX_SHADER[] = + "#define NUM_VIEWS 2\n" + "#define VIEW_ID gl_ViewID_OVR\n" + "#extension GL_OVR_multiview2 : require\n" + "layout(num_views=NUM_VIEWS) in;\n" + "in vec3 vertexPosition;\n" + "in vec4 vertexColor;\n" + "uniform mat4 ModelMatrix;\n" + "uniform SceneMatrices\n" + "{\n" + " uniform mat4 ViewMatrix[NUM_VIEWS];\n" + " uniform mat4 ProjectionMatrix[NUM_VIEWS];\n" + "} sm;\n" + "out vec4 fragmentColor;\n" + "void main()\n" + "{\n" + " gl_Position = sm.ProjectionMatrix[VIEW_ID] * ( sm.ViewMatrix[VIEW_ID] * ( ModelMatrix * ( vec4( vertexPosition, 1.0 ) ) ) );\n" + " fragmentColor = vertexColor;\n" + "}\n"; + +static const char AXES_FRAGMENT_SHADER[] = + "in lowp vec4 fragmentColor;\n" + "out lowp vec4 outColor;\n" + "void main()\n" + "{\n" + " outColor = fragmentColor;\n" + "}\n"; + +/* +================================================================================ + +ovrFramebuffer + +================================================================================ +*/ + +void ovrFramebuffer::Clear() { + Width = 0; + Height = 0; + Multisamples = 0; + SwapChainLength = 0; + Elements = nullptr; +} + +static void* GlGetExtensionProc(const char* functionName) { +#if defined(ANDROID) + return (void*)eglGetProcAddress(functionName); +#elif defined(WIN32) + return (void*)wglGetProcAddress(functionName); +#else + static_assert(false); +#endif +} + +bool ovrFramebuffer::Create( + const GLenum colorFormat, + const int width, + const int height, + const int multisamples, + const int swapChainLength, + GLuint* colorTextures) { + PFNGLFRAMEBUFFERTEXTUREMULTIVIEWOVRPROC glFramebufferTextureMultiviewOVR = + (PFNGLFRAMEBUFFERTEXTUREMULTIVIEWOVRPROC)GlGetExtensionProc( + "glFramebufferTextureMultiviewOVR"); + PFNGLFRAMEBUFFERTEXTUREMULTISAMPLEMULTIVIEWOVRPROC glFramebufferTextureMultisampleMultiviewOVR = + (PFNGLFRAMEBUFFERTEXTUREMULTISAMPLEMULTIVIEWOVRPROC)GlGetExtensionProc( + "glFramebufferTextureMultisampleMultiviewOVR"); + + Width = width; + Height = height; + Multisamples = multisamples; + SwapChainLength = swapChainLength; + + Elements = new Element[SwapChainLength]; + + for (int i = 0; i < SwapChainLength; i++) { + Element& el = Elements[i]; + // Create the color buffer texture. + el.ColorTexture = colorTextures[i]; + GLenum colorTextureTarget = GL_TEXTURE_2D_ARRAY; + GL(glBindTexture(colorTextureTarget, el.ColorTexture)); + GL(glTexParameteri(colorTextureTarget, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER)); + GL(glTexParameteri(colorTextureTarget, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER)); + GLfloat borderColor[] = {0.0f, 0.0f, 0.0f, 0.0f}; + GL(glTexParameterfv(colorTextureTarget, GL_TEXTURE_BORDER_COLOR, borderColor)); + GL(glTexParameteri(colorTextureTarget, GL_TEXTURE_MIN_FILTER, GL_LINEAR)); + GL(glTexParameteri(colorTextureTarget, GL_TEXTURE_MAG_FILTER, GL_LINEAR)); + GL(glBindTexture(colorTextureTarget, 0)); + + // Create the depth buffer texture. + GL(glGenTextures(1, &el.DepthTexture)); + GL(glBindTexture(GL_TEXTURE_2D_ARRAY, el.DepthTexture)); + GL(glTexStorage3D(GL_TEXTURE_2D_ARRAY, 1, GL_DEPTH_COMPONENT24, width, height, 2)); + GL(glBindTexture(GL_TEXTURE_2D_ARRAY, 0)); + + // Create the frame buffer. + GL(glGenFramebuffers(1, &el.FrameBufferObject)); + GL(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, el.FrameBufferObject)); + if (multisamples > 1 && (glFramebufferTextureMultisampleMultiviewOVR != nullptr)) { + GL(glFramebufferTextureMultisampleMultiviewOVR( + GL_DRAW_FRAMEBUFFER, + GL_DEPTH_ATTACHMENT, + el.DepthTexture, + 0 /* level */, + multisamples /* samples */, + 0 /* baseViewIndex */, + 2 /* numViews */)); + GL(glFramebufferTextureMultisampleMultiviewOVR( + GL_DRAW_FRAMEBUFFER, + GL_COLOR_ATTACHMENT0, + el.ColorTexture, + 0 /* level */, + multisamples /* samples */, + 0 /* baseViewIndex */, + 2 /* numViews */)); + } else if (glFramebufferTextureMultiviewOVR) { + GL(glFramebufferTextureMultiviewOVR( + GL_DRAW_FRAMEBUFFER, + GL_DEPTH_ATTACHMENT, + el.DepthTexture, + 0 /* level */, + 0 /* baseViewIndex */, + 2 /* numViews */)); + GL(glFramebufferTextureMultiviewOVR( + GL_DRAW_FRAMEBUFFER, + GL_COLOR_ATTACHMENT0, + el.ColorTexture, + 0 /* level */, + 0 /* baseViewIndex */, + 2 /* numViews */)); + } + + GL(GLenum renderFramebufferStatus = glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER)); + GL(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0)); + if (renderFramebufferStatus != GL_FRAMEBUFFER_COMPLETE) { + ALOGE( + "Incomplete frame buffer object: %s", + GlFrameBufferStatusString(renderFramebufferStatus)); + return false; + } + } + + return true; +} + +void ovrFramebuffer::Destroy() { + for (int i = 0; i < SwapChainLength; i++) { + Element& el = Elements[i]; + GL(glDeleteFramebuffers(1, &el.FrameBufferObject)); + GL(glDeleteTextures(1, &el.DepthTexture)); + } + delete[] Elements; + Clear(); +} + +void ovrFramebuffer::Bind(int element) { + GL(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, Elements[element].FrameBufferObject)); +} + +void ovrFramebuffer::Unbind() { + GL(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0)); +} + +void ovrFramebuffer::Resolve() { +#if defined(ANDROID) + // Discard the depth buffer, so the tiler won't need to write it back out to memory. + const GLenum depthAttachment[1] = {GL_DEPTH_ATTACHMENT}; + glInvalidateFramebuffer(GL_DRAW_FRAMEBUFFER, 1, depthAttachment); +#endif // defined(ANDROID) + + // We now let the resolve happen implicitly. +} + +/* +================================================================================ + +ovrScene + +================================================================================ +*/ + +void ovrScene::SetClearColor(const float* c) { + for (int i = 0; i < 4; i++) { + ClearColor[i] = c[i]; + } +} + +void ovrScene::Clear() { + CreatedScene = false; + CreatedVAOs = false; + SceneMatrices = 0; + + CubeProgram.Clear(); + Cube.Clear(); + StageProgram.Clear(); + Stage.Clear(); + AxesProgram.Clear(); + Axes.Clear(); +} + +bool ovrScene::IsCreated() { + return CreatedScene; +} + +void ovrScene::CreateVAOs() { + if (!CreatedVAOs) { + // Cube + Cube.CreateVAO(); + + // Stage + Stage.CreateVAO(); + + // Axes + Axes.CreateVAO(); + + CreatedVAOs = true; + } +} + +void ovrScene::DestroyVAOs() { + if (CreatedVAOs) { + Cube.DestroyVAO(); + Stage.DestroyVAO(); + Axes.DestroyVAO(); + + CreatedVAOs = false; + } +} + +void ovrScene::Create() { + // Cube + CubeProgram.Create(CUBE_VERTEX_SHADER, CUBE_FRAGMENT_SHADER); + Cube.CreateCube(); + + // Setup the scene matrices. + GL(glGenBuffers(1, &SceneMatrices)); + GL(glBindBuffer(GL_UNIFORM_BUFFER, SceneMatrices)); + GL(glBufferData( + GL_UNIFORM_BUFFER, + 2 * sizeof(Matrix4f) /* 2 view matrices */ + + 2 * sizeof(Matrix4f) /* 2 projection matrices */, + nullptr, + GL_STATIC_DRAW)); + GL(glBindBuffer(GL_UNIFORM_BUFFER, 0)); + + // Stage + if (!StageProgram.Create(STAGE_VERTEX_SHADER, STAGE_FRAGMENT_SHADER)) { + ALOGE("Failed to compile stage program"); + } + Stage.CreateStage(); + + // Axes + if (!AxesProgram.Create(AXES_VERTEX_SHADER, AXES_FRAGMENT_SHADER)) { + ALOGE("Failed to compile axes program"); + } + Axes.CreateAxes(); + + CreatedScene = true; + + CreateVAOs(); + float c[] = {0.0, 0.0, 0.0, 0.0}; + SetClearColor(c); +} + +void ovrScene::Destroy() { + DestroyVAOs(); + + CubeProgram.Destroy(); + Cube.Destroy(); + GL(glDeleteBuffers(1, &SceneMatrices)); + StageProgram.Destroy(); + Stage.Destroy(); + AxesProgram.Destroy(); + Axes.Destroy(); + CreatedScene = false; +} + +/* +================================================================================ + +ovrAppRenderer + +================================================================================ +*/ + +void ovrAppRenderer::Clear() { + Framebuffer.Clear(); + Scene.Clear(); +} + +void ovrAppRenderer::Create( + GLenum format, + int width, + int height, + int numMultiSamples, + int swapChainLength, + GLuint* colorTextures) { + EglInitExtensions(); + Framebuffer.Create(format, width, height, numMultiSamples, swapChainLength, colorTextures); + if (glExtensions.EXT_sRGB_write_control) { + // This app was originally written with the presumption that + // its swapchains and compositor front buffer were RGB. + // In order to have the colors the same now that its compositing + // to an sRGB front buffer, we have to write to an sRGB swapchain + // but with the linear->sRGB conversion disabled on write. + GL(glDisable(GL_FRAMEBUFFER_SRGB_EXT)); + } +} + +void ovrAppRenderer::Destroy() { + Framebuffer.Destroy(); +} + +void ovrAppRenderer::RenderFrame(ovrAppRenderer::FrameIn frameIn) { + // Update the scene matrices. + GL(glBindBuffer(GL_UNIFORM_BUFFER, Scene.SceneMatrices)); + GL(Matrix4f* sceneMatrices = (Matrix4f*)glMapBufferRange( + GL_UNIFORM_BUFFER, + 0, + 4 * sizeof(Matrix4f) /* 2 view + 2 proj matrices */, + GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT)); + + if (sceneMatrices != nullptr) { + memcpy((char*)sceneMatrices, &frameIn.View, 4 * sizeof(Matrix4f)); + } + + GL(glUnmapBuffer(GL_UNIFORM_BUFFER)); + GL(glBindBuffer(GL_UNIFORM_BUFFER, 0)); + + // Render the eye images. + Framebuffer.Bind(frameIn.SwapChainIndex); + + GL(glEnable(GL_SCISSOR_TEST)); + GL(glDepthMask(GL_TRUE)); + GL(glEnable(GL_DEPTH_TEST)); + GL(glDepthFunc(GL_LEQUAL)); + GL(glEnable(GL_CULL_FACE)); + GL(glCullFace(GL_BACK)); + GL(glDisable(GL_BLEND)); + GL(glViewport(0, 0, Framebuffer.Width, Framebuffer.Height)); + GL(glScissor(0, 0, Framebuffer.Width, Framebuffer.Height)); + GL(glClearColor( + Scene.ClearColor[0], Scene.ClearColor[1], Scene.ClearColor[2], Scene.ClearColor[3])); + GL(glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)); + + // Cubes + GL(glUseProgram(Scene.CubeProgram.Program)); + GL(glBindBufferBase( + GL_UNIFORM_BUFFER, + Scene.CubeProgram.UniformBinding[ovrUniform::Index::SCENE_MATRICES], + Scene.SceneMatrices)); + if (Scene.CubeProgram.UniformLocation[ovrUniform::Index::VIEW_ID] >= + 0) // NOTE: will not be present when multiview path is enabled. + { + GL(glUniform1i(Scene.CubeProgram.UniformLocation[ovrUniform::Index::VIEW_ID], 0)); + } + for (auto c : Scene.CubeData) { + GLint loc = Scene.CubeProgram.UniformLocation[ovrUniform::Index::MODEL_MATRIX]; + if (loc >= 0) { + GL(glUniformMatrix4fv(loc, 1, GL_TRUE, &c.Model.M[0][0])); + } + loc = Scene.CubeProgram.UniformLocation[ovrUniform::Index::COLOR_SCALE]; + if (loc >= 0) { + GL(glUniform4fv(loc, 1, &c.ColorScale.x)); + } + loc = Scene.CubeProgram.UniformLocation[ovrUniform::Index::COLOR_BIAS]; + if (loc >= 0) { + GL(glUniform4fv(loc, 1, &c.ColorBias.x)); + } + GL(glBindVertexArray(Scene.Cube.VertexArrayObject)); + GL(glDrawElements(GL_TRIANGLES, Scene.Cube.IndexCount, GL_UNSIGNED_SHORT, nullptr)); + } + + GL(glBindVertexArray(0)); + GL(glUseProgram(0)); + + GL(glLineWidth(3.0)); + // "tracking space" axes (could be LOCAL or LOCAL_FLOOR) + GL(glUseProgram(Scene.AxesProgram.Program)); + GL(glBindBufferBase( + GL_UNIFORM_BUFFER, + Scene.AxesProgram.UniformBinding[ovrUniform::Index::SCENE_MATRICES], + Scene.SceneMatrices)); + if (Scene.AxesProgram.UniformLocation[ovrUniform::Index::VIEW_ID] >= + 0) // NOTE: will not be present when multiview path is enabled. + { + GL(glUniform1i(Scene.AxesProgram.UniformLocation[ovrUniform::Index::VIEW_ID], 0)); + } + if (Scene.AxesProgram.UniformLocation[ovrUniform::Index::MODEL_MATRIX] >= 0) { + const Matrix4f scale = Matrix4f::Scaling(0.1, 0.1, 0.1); + GL(glUniformMatrix4fv( + Scene.AxesProgram.UniformLocation[ovrUniform::Index::MODEL_MATRIX], + 1, + GL_TRUE, + &scale.M[0][0])); + } + GL(glBindVertexArray(Scene.Axes.VertexArrayObject)); + GL(glDrawElements(GL_LINES, Scene.Axes.IndexCount, GL_UNSIGNED_SHORT, nullptr)); + GL(glBindVertexArray(0)); + GL(glUseProgram(0)); + + if (frameIn.HasStage) { + // stage axes + GL(glUseProgram(Scene.AxesProgram.Program)); + GL(glBindBufferBase( + GL_UNIFORM_BUFFER, + Scene.AxesProgram.UniformBinding[ovrUniform::Index::SCENE_MATRICES], + Scene.SceneMatrices)); + if (Scene.AxesProgram.UniformLocation[ovrUniform::Index::VIEW_ID] >= + 0) // NOTE: will not be present when multiview path is enabled. + { + GL(glUniform1i(Scene.AxesProgram.UniformLocation[ovrUniform::Index::VIEW_ID], 0)); + } + if (Scene.AxesProgram.UniformLocation[ovrUniform::Index::MODEL_MATRIX] >= 0) { + const Matrix4f scale = Matrix4f::Scaling(0.5, 0.5, 0.5); + const Matrix4f stagePoseMat = Matrix4f(frameIn.StagePose); + const Matrix4f m1 = stagePoseMat * scale; + GL(glUniformMatrix4fv( + Scene.AxesProgram.UniformLocation[ovrUniform::Index::MODEL_MATRIX], + 1, + GL_TRUE, + &m1.M[0][0])); + } + GL(glBindVertexArray(Scene.Axes.VertexArrayObject)); + GL(glDrawElements(GL_LINES, Scene.Axes.IndexCount, GL_UNSIGNED_SHORT, nullptr)); + GL(glBindVertexArray(0)); + GL(glUseProgram(0)); + } + + if (frameIn.HasStage) { + // Stage + GL(glUseProgram(Scene.StageProgram.Program)); + GL(glBindBufferBase( + GL_UNIFORM_BUFFER, + Scene.StageProgram.UniformBinding[ovrUniform::Index::SCENE_MATRICES], + Scene.SceneMatrices)); + if (Scene.StageProgram.UniformLocation[ovrUniform::Index::VIEW_ID] >= + 0) // NOTE: will not be present when multiview path is enabled. + { + GL(glUniform1i(Scene.StageProgram.UniformLocation[ovrUniform::Index::VIEW_ID], 0)); + } + if (Scene.StageProgram.UniformLocation[ovrUniform::Index::MODEL_MATRIX] >= 0) { + const Matrix4f rotateVtoH = Matrix4f::RotationX(-M_PI / 2.0f); + const Matrix4f stageScaleMat = Matrix4f::Scaling(frameIn.StageScale); + const Matrix4f stagePoseMat = Matrix4f(frameIn.StagePose); + const Matrix4f m2 = stagePoseMat * stageScaleMat * rotateVtoH; + GL(glUniformMatrix4fv( + Scene.StageProgram.UniformLocation[ovrUniform::Index::MODEL_MATRIX], + 1, + GL_TRUE, + &m2.M[0][0])); + } + GL(glDepthMask(GL_FALSE)); + GL(glEnable(GL_DEPTH_TEST)); + GL(glDepthFunc(GL_LEQUAL)); + GL(glDisable(GL_CULL_FACE)); + GL(glEnable(GL_BLEND)); + GL(glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); + GL(glBindVertexArray(Scene.Stage.VertexArrayObject)); + GL(glDrawElements(GL_TRIANGLES, Scene.Stage.IndexCount, GL_UNSIGNED_SHORT, nullptr)); + GL(glDepthMask(GL_TRUE)); + GL(glDisable(GL_BLEND)); + GL(glBindVertexArray(0)); + GL(glUseProgram(0)); + + Framebuffer.Resolve(); + } + + Framebuffer.Unbind(); +} diff --git a/Samples/XrSamples/XrSpatialAnchor/Src/SpatialAnchorGl.h b/Samples/XrSamples/XrSpatialAnchor/Src/SpatialAnchorGl.h new file mode 100755 index 0000000..7c68419 --- /dev/null +++ b/Samples/XrSamples/XrSpatialAnchor/Src/SpatialAnchorGl.h @@ -0,0 +1,146 @@ +#pragma once + +#include + +#if defined(ANDROID) +#include + +#define XR_USE_GRAPHICS_API_OPENGL_ES 1 +#define XR_USE_PLATFORM_ANDROID 1 +#elif defined(WIN32) +#include "Render/GlWrapperWin32.h" + +#include +#define XR_USE_GRAPHICS_API_OPENGL 1 +#define XR_USE_PLATFORM_WIN32 1 +#endif + +#include +#include +#include + +#include "OVR_Math.h" + +#define NUM_EYES 2 + +struct ovrGeometry { + void Clear(); + void CreateCube(); + void CreateAxes(); + void CreateStage(); + void Destroy(); + void CreateVAO(); + void DestroyVAO(); + static constexpr int MAX_VERTEX_ATTRIB_POINTERS = 3; + struct VertexAttribPointer { + GLint Index; + GLint Size; + GLenum Type; + GLboolean Normalized; + GLsizei Stride; + const GLvoid* Pointer; + }; + GLuint VertexBuffer; + GLuint IndexBuffer; + GLuint VertexArrayObject; + int VertexCount; + int IndexCount; + VertexAttribPointer VertexAttribs[MAX_VERTEX_ATTRIB_POINTERS]; +}; + +struct ovrProgram { + static constexpr int MAX_PROGRAM_UNIFORMS = 8; + static constexpr int MAX_PROGRAM_TEXTURES = 8; + + void Clear(); + bool Create(const char* vertexSource, const char* fragmentSource); + void Destroy(); + GLuint Program; + GLuint VertexShader; + GLuint FragmentShader; + // These will be -1 if not used by the program. + GLint UniformLocation[MAX_PROGRAM_UNIFORMS]; // ProgramUniforms[].name + GLint UniformBinding[MAX_PROGRAM_UNIFORMS]; // ProgramUniforms[].name + GLint Textures[MAX_PROGRAM_TEXTURES]; // Texture%i +}; + +struct ovrFramebuffer { + void Clear(); + bool Create( + const GLenum colorFormat, + const int width, + const int height, + const int multisamples, + const int swapChainLength, + GLuint* colorTextures); + void Destroy(); + void Bind(int element); + void Unbind(); + void Resolve(); + int Width; + int Height; + int Multisamples; + int SwapChainLength; + struct Element { + GLuint ColorTexture; + GLuint DepthTexture; + GLuint FrameBufferObject; + }; + Element* Elements; +}; + +struct ovrCubeData { + ovrCubeData() : ColorScale(1.0f, 1.0f, 1.0f, 1.0f), ColorBias(0.0f, 0.0f, 0.0f, 0.0f) {} + OVR::Vector4f ColorScale; + OVR::Vector4f ColorBias; + OVR::Matrix4f Model; +}; + +struct ovrScene { + void Clear(); + void Create(); + void Destroy(); + bool IsCreated(); + void SetClearColor(const float* c); + void CreateVAOs(); + void DestroyVAOs(); + bool CreatedScene; + bool CreatedVAOs; + ovrProgram CubeProgram; + ovrGeometry Cube; + GLuint SceneMatrices; + ovrProgram StageProgram; + ovrGeometry Stage; + ovrProgram AxesProgram; + ovrGeometry Axes; + float ClearColor[4]; + + std::vector CubeData; + std::vector SpaceList; +}; + +struct ovrAppRenderer { + void Clear(); + void Create( + GLenum format, + int width, + int height, + int numMultiSamples, + int swapChainLength, + GLuint* colorTextures); + void Destroy(); + + struct FrameIn { + int SwapChainIndex; + OVR::Matrix4f View[NUM_EYES]; + OVR::Matrix4f Proj[NUM_EYES]; + bool HasStage; + OVR::Posef StagePose; + OVR::Vector3f StageScale; + }; + + void RenderFrame(FrameIn frameIn); + + ovrFramebuffer Framebuffer; + ovrScene Scene; +}; diff --git a/Samples/XrSamples/XrSpatialAnchor/Src/SpatialAnchorUtilities.cpp b/Samples/XrSamples/XrSpatialAnchor/Src/SpatialAnchorUtilities.cpp new file mode 100755 index 0000000..e00e04e --- /dev/null +++ b/Samples/XrSamples/XrSpatialAnchor/Src/SpatialAnchorUtilities.cpp @@ -0,0 +1,51 @@ +/************************************************************************************ + +Filename : SpatialAnchorUtilities.cpp +Content : Utility functions for XrSpatialAnchor +Created : +Authors : + +Copyright : Copyright (c) Meta Platforms, Inc. and its affiliates. All rights reserved. + +*************************************************************************************/ + +#include "SpatialAnchorUtilities.h" + +std::string bin2hex(const uint8_t* src, uint32_t size) { + std::string res; + res.reserve(size * 2); + const char hex[] = "0123456789ABCDEF"; + for (uint32_t i = 0; i < size; ++i) { + uint8_t c = src[i]; + res += hex[c >> 4]; + res += hex[c & 0xf]; + } + return res; +} + +std::string uuidToHexString(const XrUuidEXT& uuid) { + return bin2hex(reinterpret_cast(uuid.data), XR_UUID_SIZE_EXT); +} + +bool hexStringToUuid(const std::string& hex, XrUuidEXT& uuid) { + if (hex.length() != XR_UUID_SIZE_EXT * 2) { + return false; + } + for (uint32_t i = 0, k = 0; i < XR_UUID_SIZE_EXT; i++, k+=2) { + std::string byteStr = hex.substr(k, 2); + uuid.data[i] = (uint8_t)stol(byteStr, nullptr, 16); + } + return true; +} + +bool isExtensionEnumerated( + const char* extensionName, + XrExtensionProperties enumeratedExtensions[], + uint32_t enumeratedExtensionCount) { + for (uint32_t i = 0; i < enumeratedExtensionCount; i++) { + if (strcmp(extensionName, enumeratedExtensions[i].extensionName) == 0) { + return true; + } + } + return false; +} diff --git a/Samples/XrSamples/XrSpatialAnchor/Src/SpatialAnchorUtilities.h b/Samples/XrSamples/XrSpatialAnchor/Src/SpatialAnchorUtilities.h new file mode 100755 index 0000000..991024e --- /dev/null +++ b/Samples/XrSamples/XrSpatialAnchor/Src/SpatialAnchorUtilities.h @@ -0,0 +1,12 @@ +#pragma once + +#include +#include + +std::string bin2hex(const uint8_t* src, uint32_t size); +std::string uuidToHexString(const XrUuidEXT& uuid); +bool hexStringToUuid(const std::string& hex, XrUuidEXT& uuid); +bool isExtensionEnumerated( + const char* extensionName, + XrExtensionProperties enumeratedExtensions[], + uint32_t enumeratedExtensionCount); diff --git a/Samples/XrSamples/XrSpatialAnchor/Src/SpatialAnchorXr.cpp b/Samples/XrSamples/XrSpatialAnchor/Src/SpatialAnchorXr.cpp new file mode 100755 index 0000000..7f164b7 --- /dev/null +++ b/Samples/XrSamples/XrSpatialAnchor/Src/SpatialAnchorXr.cpp @@ -0,0 +1,2240 @@ +/************************************************************************************ + +Filename : SpatialAnchorXr.cpp +Content : This sample uses the Android NativeActivity class. +Created : +Authors : + +Copyright : Copyright (c) Meta Platforms, Inc. and its affiliates. All rights reserved. + +*************************************************************************************/ + +#include +#include +#include +#include +#include +#include // for memset +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#if defined(ANDROID) +#include +#include +#include // for prctl( PR_SET_NAME ) +#include +#include // for native window JNI +#include +#else +#include +#endif + +#include + +#include "SpatialAnchorXr.h" +#include "SpatialAnchorGl.h" +#include "SimpleXrInput.h" +#include "SpatialAnchorUtilities.h" +#include "SpatialAnchorFileHandler.h" + +#if defined(_WIN32) +// Favor the high performance NVIDIA or AMD GPUs +extern "C" { +// http://developer.download.nvidia.com/devzone/devcenter/gamegraphics/files/OptimusRenderingPolicies.pdf +__declspec(dllexport) DWORD NvOptimusEnablement = 0x00000001; +// https://gpuopen.com/learn/amdpowerxpressrequesthighperformance/ +__declspec(dllexport) DWORD AmdPowerXpressRequestHighPerformance = 0x00000001; +} +#endif // defined(_WIN32) + +using namespace OVR; + +#if !defined(EGL_OPENGL_ES3_BIT_KHR) +#define EGL_OPENGL_ES3_BIT_KHR 0x0040 +#endif + +#if defined(ANDROID) +#define DEBUG 1 +#define OVR_LOG_TAG "SpatialAnchorXr" + +#define ALOGE(...) __android_log_print(ANDROID_LOG_ERROR, OVR_LOG_TAG, __VA_ARGS__) +#define ALOGW(...) __android_log_print(ANDROID_LOG_WARN, OVR_LOG_TAG, __VA_ARGS__) +#define ALOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, OVR_LOG_TAG, __VA_ARGS__) +#else +#include +#define ALOGE(...) \ + printf("ERROR: "); \ + printf(__VA_ARGS__); \ + printf("\n") +#define ALOGW(...) \ + printf("WARN: "); \ + printf(__VA_ARGS__); \ + printf("\n") +#define ALOGV(...) \ + printf("VERBOSE: "); \ + printf(__VA_ARGS__); \ + printf("\n") +#endif + +static const int CPU_LEVEL = 2; +static const int GPU_LEVEL = 3; +static const int NUM_MULTI_SAMPLES = 4; + +static const uint32_t MAX_PERSISTENT_SPACES = 20; +static const uint32_t kMaxPersistenceSpacesPerApiCall = 32; + +union ovrCompositorLayer_Union { + XrCompositionLayerProjection Projection; + XrCompositionLayerQuad Quad; + XrCompositionLayerCylinderKHR Cylinder; + XrCompositionLayerCubeKHR Cube; + XrCompositionLayerEquirectKHR Equirect; + XrCompositionLayerPassthroughFB Passthrough; +}; + +enum { ovrMaxLayerCount = 16 }; + +// Forward declarations +XrInstance instance; + +/* +================================================================================ + +OpenXR Utility Functions + +================================================================================ +*/ + +static void OXR_CheckErrors(XrResult result, const char* function, bool failOnError) { + if (XR_FAILED(result)) { + char errorBuffer[XR_MAX_RESULT_STRING_SIZE]; + xrResultToString(instance, result, errorBuffer); + if (failOnError) { + ALOGE("OpenXR error: %s: %s\n", function, errorBuffer); + } else { + ALOGV("OpenXR error: %s: %s\n", function, errorBuffer); + } + } +} + +#if defined(DEBUG) +#define OXR(func) OXR_CheckErrors(func, #func, true); +#else +#define OXR(func) OXR_CheckErrors(func, #func, false); +#endif + +/* +================================================================================ + +OpenXR_Helpers.h Utility Functions + +================================================================================ +*/ + +static inline XrVector3f ToXrVector3f(const OVR::Vector3f& s) { + XrVector3f r; + r.x = s.x; + r.y = s.y; + r.z = s.z; + return r; +} + +static inline OVR::Vector3f FromXrVector3f(const XrVector3f& s) { + OVR::Vector3f r; + r.x = s.x; + r.y = s.y; + r.z = s.z; + return r; +} + +static inline XrQuaternionf ToXrQuaternionf(const OVR::Quatf& s) { + XrQuaternionf r; + r.x = s.x; + r.y = s.y; + r.z = s.z; + r.w = s.w; + return r; +} + +static inline OVR::Quatf FromXrQuaternionf(const XrQuaternionf& s) { + OVR::Quatf r; + r.x = s.x; + r.y = s.y; + r.z = s.z; + r.w = s.w; + return r; +} + +static inline XrPosef ToXrPosef(const OVR::Posef& s) { + XrPosef r; + r.orientation = ToXrQuaternionf(s.Rotation); + r.position = ToXrVector3f(s.Translation); + return r; +} + +static inline OVR::Posef FromXrPosef(const XrPosef& s) { + OVR::Posef r; + r.Rotation = FromXrQuaternionf(s.orientation); + r.Translation = FromXrVector3f(s.position); + return r; +} + +/* +================================================================================ + +Egl Utility Functions + +================================================================================ +*/ + +#if defined(XR_USE_GRAPHICS_API_OPENGL_ES) +static const char* EglErrorString(const EGLint error) { + switch (error) { + case EGL_SUCCESS: + return "EGL_SUCCESS"; + case EGL_NOT_INITIALIZED: + return "EGL_NOT_INITIALIZED"; + case EGL_BAD_ACCESS: + return "EGL_BAD_ACCESS"; + case EGL_BAD_ALLOC: + return "EGL_BAD_ALLOC"; + case EGL_BAD_ATTRIBUTE: + return "EGL_BAD_ATTRIBUTE"; + case EGL_BAD_CONTEXT: + return "EGL_BAD_CONTEXT"; + case EGL_BAD_CONFIG: + return "EGL_BAD_CONFIG"; + case EGL_BAD_CURRENT_SURFACE: + return "EGL_BAD_CURRENT_SURFACE"; + case EGL_BAD_DISPLAY: + return "EGL_BAD_DISPLAY"; + case EGL_BAD_SURFACE: + return "EGL_BAD_SURFACE"; + case EGL_BAD_MATCH: + return "EGL_BAD_MATCH"; + case EGL_BAD_PARAMETER: + return "EGL_BAD_PARAMETER"; + case EGL_BAD_NATIVE_PIXMAP: + return "EGL_BAD_NATIVE_PIXMAP"; + case EGL_BAD_NATIVE_WINDOW: + return "EGL_BAD_NATIVE_WINDOW"; + case EGL_CONTEXT_LOST: + return "EGL_CONTEXT_LOST"; + default: + return "unknown"; + } +} +#endif // defined(XR_USE_GRAPHICS_API_OPENGL_ES) + +/* +================================================================================ + +ovrEgl + +================================================================================ +*/ + +struct ovrEgl { + void Clear(); + void CreateContext(const ovrEgl* shareEgl); + void DestroyContext(); +#if defined(XR_USE_GRAPHICS_API_OPENGL_ES) + EGLint MajorVersion; + EGLint MinorVersion; + EGLDisplay Display; + EGLConfig Config; + EGLSurface TinySurface; + EGLSurface MainSurface; + EGLContext Context; +#elif defined(XR_USE_GRAPHICS_API_OPENGL) + HDC hDC; + HGLRC hGLRC; +#endif // +}; + +void ovrEgl::Clear() { +#if defined(XR_USE_GRAPHICS_API_OPENGL_ES) + MajorVersion = 0; + MinorVersion = 0; + Display = 0; + Config = 0; + TinySurface = EGL_NO_SURFACE; + MainSurface = EGL_NO_SURFACE; + Context = EGL_NO_CONTEXT; +#elif defined(XR_USE_GRAPHICS_API_OPENGL) + hDC = 0; + hGLRC = 0; +#endif +} + +#if defined(XR_USE_GRAPHICS_API_OPENGL_ES) +void ovrEgl::CreateContext(const ovrEgl* shareEgl) { + if (Display != 0) { + return; + } + + Display = eglGetDisplay(EGL_DEFAULT_DISPLAY); + ALOGV(" eglInitialize( Display, &MajorVersion, &MinorVersion )"); + eglInitialize(Display, &MajorVersion, &MinorVersion); + // Do NOT use eglChooseConfig, because the Android EGL code pushes in multisample + // flags in eglChooseConfig if the user has selected the "force 4x MSAA" option in + // settings, and that is completely wasted for our warp target. + const int MAX_CONFIGS = 1024; + EGLConfig configs[MAX_CONFIGS]; + EGLint numConfigs = 0; + if (eglGetConfigs(Display, configs, MAX_CONFIGS, &numConfigs) == EGL_FALSE) { + ALOGE(" eglGetConfigs() failed: %s", EglErrorString(eglGetError())); + return; + } + const EGLint configAttribs[] = { + EGL_RED_SIZE, + 8, + EGL_GREEN_SIZE, + 8, + EGL_BLUE_SIZE, + 8, + EGL_ALPHA_SIZE, + 8, // need alpha for the multi-pass timewarp compositor + EGL_DEPTH_SIZE, + 0, + EGL_STENCIL_SIZE, + 0, + EGL_SAMPLES, + 0, + EGL_NONE}; + Config = 0; + for (int i = 0; i < numConfigs; i++) { + EGLint value = 0; + + eglGetConfigAttrib(Display, configs[i], EGL_RENDERABLE_TYPE, &value); + if ((value & EGL_OPENGL_ES3_BIT_KHR) != EGL_OPENGL_ES3_BIT_KHR) { + continue; + } + + // The pbuffer config also needs to be compatible with normal window rendering + // so it can share textures with the window context. + eglGetConfigAttrib(Display, configs[i], EGL_SURFACE_TYPE, &value); + if ((value & (EGL_WINDOW_BIT | EGL_PBUFFER_BIT)) != (EGL_WINDOW_BIT | EGL_PBUFFER_BIT)) { + continue; + } + + int j = 0; + for (; configAttribs[j] != EGL_NONE; j += 2) { + eglGetConfigAttrib(Display, configs[i], configAttribs[j], &value); + if (value != configAttribs[j + 1]) { + break; + } + } + if (configAttribs[j] == EGL_NONE) { + Config = configs[i]; + break; + } + } + if (Config == 0) { + ALOGE(" eglChooseConfig() failed: %s", EglErrorString(eglGetError())); + return; + } + EGLint contextAttribs[] = {EGL_CONTEXT_CLIENT_VERSION, 3, EGL_NONE}; + ALOGV(" Context = eglCreateContext( Display, Config, EGL_NO_CONTEXT, contextAttribs )"); + Context = eglCreateContext( + Display, + Config, + (shareEgl != nullptr) ? shareEgl->Context : EGL_NO_CONTEXT, + contextAttribs); + if (Context == EGL_NO_CONTEXT) { + ALOGE(" eglCreateContext() failed: %s", EglErrorString(eglGetError())); + return; + } + const EGLint surfaceAttribs[] = {EGL_WIDTH, 16, EGL_HEIGHT, 16, EGL_NONE}; + ALOGV(" TinySurface = eglCreatePbufferSurface( Display, Config, surfaceAttribs )"); + TinySurface = eglCreatePbufferSurface(Display, Config, surfaceAttribs); + if (TinySurface == EGL_NO_SURFACE) { + ALOGE(" eglCreatePbufferSurface() failed: %s", EglErrorString(eglGetError())); + eglDestroyContext(Display, Context); + Context = EGL_NO_CONTEXT; + return; + } + ALOGV(" eglMakeCurrent( Display, TinySurface, TinySurface, Context )"); + if (eglMakeCurrent(Display, TinySurface, TinySurface, Context) == EGL_FALSE) { + ALOGE(" eglMakeCurrent() failed: %s", EglErrorString(eglGetError())); + eglDestroySurface(Display, TinySurface); + eglDestroyContext(Display, Context); + Context = EGL_NO_CONTEXT; + return; + } +} + +void ovrEgl::DestroyContext() { + if (Display != 0) { + ALOGE(" eglMakeCurrent( Display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT )"); + if (eglMakeCurrent(Display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT) == EGL_FALSE) { + ALOGE(" eglMakeCurrent() failed: %s", EglErrorString(eglGetError())); + } + } + if (Context != EGL_NO_CONTEXT) { + ALOGE(" eglDestroyContext( Display, Context )"); + if (eglDestroyContext(Display, Context) == EGL_FALSE) { + ALOGE(" eglDestroyContext() failed: %s", EglErrorString(eglGetError())); + } + Context = EGL_NO_CONTEXT; + } + if (TinySurface != EGL_NO_SURFACE) { + ALOGE(" eglDestroySurface( Display, TinySurface )"); + if (eglDestroySurface(Display, TinySurface) == EGL_FALSE) { + ALOGE(" eglDestroySurface() failed: %s", EglErrorString(eglGetError())); + } + TinySurface = EGL_NO_SURFACE; + } + if (Display != 0) { + ALOGE(" eglTerminate( Display )"); + if (eglTerminate(Display) == EGL_FALSE) { + ALOGE(" eglTerminate() failed: %s", EglErrorString(eglGetError())); + } + Display = 0; + } +} + +#elif defined(XR_USE_GRAPHICS_API_OPENGL) + +void ovrEgl::CreateContext(const ovrEgl*) { + ovrGl_CreateContext_Windows(&hDC, &hGLRC); +} + +void ovrEgl::DestroyContext() { + ovrGl_DestroyContext_Windows(); +} + +#endif + +/* +================================================================================ + +ovrApp + +================================================================================ +*/ + +struct ovrExtensionFunctionPointers { + PFN_xrCreatePassthroughFB xrCreatePassthroughFB = nullptr; + PFN_xrDestroyPassthroughFB xrDestroyPassthroughFB = nullptr; + PFN_xrCreatePassthroughLayerFB xrCreatePassthroughLayerFB = nullptr; + PFN_xrDestroyPassthroughLayerFB xrDestroyPassthroughLayerFB = nullptr; + PFN_xrPassthroughLayerResumeFB xrPassthroughLayerResumeFB = nullptr; + PFN_xrPassthroughLayerPauseFB xrPassthroughLayerPauseFB = nullptr; + PFN_xrPassthroughLayerSetStyleFB xrPassthroughLayerSetStyleFB = nullptr; + PFN_xrPassthroughStartFB xrPassthroughStartFB = nullptr; + PFN_xrPassthroughPauseFB xrPassthroughPauseFB = nullptr; + PFN_xrEnumerateSpaceSupportedComponentsFB xrEnumerateSpaceSupportedComponentsFB = nullptr; + PFN_xrSetSpaceComponentStatusFB xrSetSpaceComponentStatusFB = nullptr; + PFN_xrGetSpaceComponentStatusFB xrGetSpaceComponentStatusFB = nullptr; + PFN_xrCreateSpatialAnchorFB xrCreateSpatialAnchorFB = nullptr; + PFN_xrSaveSpacesMETA xrSaveSpacesMETA = nullptr; + PFN_xrEraseSpacesMETA xrEraseSpacesMETA = nullptr; + PFN_xrQuerySpacesFB xrQuerySpacesFB = nullptr; + PFN_xrRetrieveSpaceQueryResultsFB xrRetrieveSpaceQueryResultsFB = nullptr; + PFN_xrDiscoverSpacesMETA xrDiscoverSpacesMETA = nullptr; + PFN_xrRetrieveSpaceDiscoveryResultsMETA xrRetrieveSpaceDiscoveryResultsMETA = nullptr; + PFN_xrGetSpaceUuidFB xrGetSpaceUuidFB = nullptr; + PFN_xrCreateSpaceUserFB xrCreateSpaceUserFB = nullptr; + PFN_xrGetSpaceUserIdFB xrGetSpaceUserIdFB = nullptr; + PFN_xrDestroySpaceUserFB xrDestroySpaceUserFB = nullptr; + PFN_xrShareSpacesFB xrShareSpacesFB = nullptr; +}; + +struct ovrEnableComponentEvent { + XrSpaceComponentTypeFB componentType; + XrSpace space; +}; + +struct ovrApp { + void Clear(); + void HandleSessionStateChanges(XrSessionState state); + void HandleXrEvents(); + bool IsComponentSupported(XrSpace space, XrSpaceComponentTypeFB type); + + ovrEgl Egl; +#if defined(XR_USE_PLATFORM_ANDROID) + bool Resumed; +#endif // defined(XR_USE_PLATFORM_ANDROID) + bool ShouldExit; + bool Focused; + + XrSession Session; + XrViewConfigurationProperties ViewportConfig; + XrViewConfigurationView ViewConfigurationView[NUM_EYES]; + XrSystemId SystemId; + XrSpace HeadSpace; + XrSpace LocalSpace; + XrSpace StageSpace; + bool SessionActive; + + bool IsLocalMultiplayerSupported; + + ovrExtensionFunctionPointers FunPtrs; + ovrScene Scene; + + int SwapInterval; + int CpuLevel; + int GpuLevel; + // These threads will be marked as performance threads. + int MainThreadTid; + int RenderThreadTid; + ovrCompositorLayer_Union Layers[ovrMaxLayerCount]; + int LayerCount; + + bool TouchPadDownLastFrame; + + bool ShouldDiscoverAnchors; + bool ShouldShareAnchors; + + std::unique_ptr ExternalDataHandler = + std::make_unique(); + + // List of users to share Spatial Anchors with, if populated. + std::vector ShareUserList; + // List of inbound Shared Spatial Anchors, which could be empty. + std::vector InboundSpatialAnchorList; + + std::unordered_map> SaveForSharingEventMap; + std::unordered_map< + XrAsyncRequestIdFB, + std::pair, std::vector>> + ShareSpacesEventMap; + + XrSwapchain ColorSwapChain; + uint32_t SwapChainLength; + Vector3f StageBounds; + // Provided by SpatialAnchorGl, which is not aware of VrApi or OpenXR + ovrAppRenderer AppRenderer; + + std::unordered_map> DestroySpaceEventMap; +}; + +void ovrApp::Clear() { +#if defined(XR_USE_PLATFORM_ANDROID) + Resumed = false; +#endif // defined(XR_USE_PLATFORM_ANDROID) + ShouldExit = false; + Focused = false; + instance = XR_NULL_HANDLE; + Session = XR_NULL_HANDLE; + ViewportConfig = {}; + for (int i = 0; i < NUM_EYES; i++) { + ViewConfigurationView[i] = {}; + } + SystemId = XR_NULL_SYSTEM_ID; + HeadSpace = XR_NULL_HANDLE; + LocalSpace = XR_NULL_HANDLE; + StageSpace = XR_NULL_HANDLE; + SessionActive = false; + SwapInterval = 1; + for (int i = 0; i < ovrMaxLayerCount; i++) { + Layers[i] = {}; + } + LayerCount = 0; + CpuLevel = 2; + GpuLevel = 2; + MainThreadTid = 0; + RenderThreadTid = 0; + TouchPadDownLastFrame = false; + ShouldDiscoverAnchors = true; + + Egl.Clear(); + AppRenderer.Clear(); +} + +void ovrApp::HandleSessionStateChanges(XrSessionState state) { + if (state == XR_SESSION_STATE_READY) { +#if defined(XR_USE_PLATFORM_ANDROID) + assert(Resumed); +#endif // defined(XR_USE_PLATFORM_ANDROID) + assert(SessionActive == false); + + XrSessionBeginInfo sessionBeginInfo = {XR_TYPE_SESSION_BEGIN_INFO}; + sessionBeginInfo.primaryViewConfigurationType = ViewportConfig.viewConfigurationType; + + XrResult result; + OXR(result = xrBeginSession(Session, &sessionBeginInfo)); + + SessionActive = (result == XR_SUCCESS); + +#if defined(XR_USE_PLATFORM_ANDROID) + // Set session state once we have entered VR mode and have a valid session object. + if (SessionActive) { + XrPerfSettingsLevelEXT cpuPerfLevel = XR_PERF_SETTINGS_LEVEL_SUSTAINED_HIGH_EXT; + switch (CpuLevel) { + case 0: + cpuPerfLevel = XR_PERF_SETTINGS_LEVEL_POWER_SAVINGS_EXT; + break; + case 1: + cpuPerfLevel = XR_PERF_SETTINGS_LEVEL_SUSTAINED_LOW_EXT; + break; + case 2: + cpuPerfLevel = XR_PERF_SETTINGS_LEVEL_SUSTAINED_HIGH_EXT; + break; + case 3: + cpuPerfLevel = XR_PERF_SETTINGS_LEVEL_BOOST_EXT; + break; + default: + ALOGE("Invalid CPU level %d", CpuLevel); + break; + } + + XrPerfSettingsLevelEXT gpuPerfLevel = XR_PERF_SETTINGS_LEVEL_SUSTAINED_HIGH_EXT; + switch (GpuLevel) { + case 0: + gpuPerfLevel = XR_PERF_SETTINGS_LEVEL_POWER_SAVINGS_EXT; + break; + case 1: + gpuPerfLevel = XR_PERF_SETTINGS_LEVEL_SUSTAINED_LOW_EXT; + break; + case 2: + gpuPerfLevel = XR_PERF_SETTINGS_LEVEL_SUSTAINED_HIGH_EXT; + break; + case 3: + gpuPerfLevel = XR_PERF_SETTINGS_LEVEL_BOOST_EXT; + break; + default: + ALOGE("Invalid GPU level %d", GpuLevel); + break; + } + + PFN_xrPerfSettingsSetPerformanceLevelEXT pfnPerfSettingsSetPerformanceLevelEXT = NULL; + OXR(xrGetInstanceProcAddr( + instance, + "xrPerfSettingsSetPerformanceLevelEXT", + (PFN_xrVoidFunction*)(&pfnPerfSettingsSetPerformanceLevelEXT))); + + OXR(pfnPerfSettingsSetPerformanceLevelEXT( + Session, XR_PERF_SETTINGS_DOMAIN_CPU_EXT, cpuPerfLevel)); + OXR(pfnPerfSettingsSetPerformanceLevelEXT( + Session, XR_PERF_SETTINGS_DOMAIN_GPU_EXT, gpuPerfLevel)); + + PFN_xrSetAndroidApplicationThreadKHR pfnSetAndroidApplicationThreadKHR = NULL; + OXR(xrGetInstanceProcAddr( + instance, + "xrSetAndroidApplicationThreadKHR", + (PFN_xrVoidFunction*)(&pfnSetAndroidApplicationThreadKHR))); + + OXR(pfnSetAndroidApplicationThreadKHR( + Session, XR_ANDROID_THREAD_TYPE_APPLICATION_MAIN_KHR, MainThreadTid)); + OXR(pfnSetAndroidApplicationThreadKHR( + Session, XR_ANDROID_THREAD_TYPE_RENDERER_MAIN_KHR, RenderThreadTid)); + } +#endif // defined(XR_USE_PLATFORM_ANDROID) + } else if (state == XR_SESSION_STATE_STOPPING) { +#if defined(XR_USE_PLATFORM_ANDROID) + assert(Resumed == false); +#endif // defined(XR_USE_PLATFORM_ANDROID) + assert(SessionActive); + + OXR(xrEndSession(Session)); + SessionActive = false; + } +} + +bool ovrApp::IsComponentSupported(XrSpace space, XrSpaceComponentTypeFB type) { + uint32_t numComponents = 0; + OXR(FunPtrs.xrEnumerateSpaceSupportedComponentsFB(space, 0, &numComponents, nullptr)); + std::vector components(numComponents); + OXR(FunPtrs.xrEnumerateSpaceSupportedComponentsFB( + space, numComponents, &numComponents, components.data())); + + bool supported = false; + for (uint32_t c = 0; c < numComponents; ++c) { + if (components[c] == type) { + supported = true; + break; + } + } + return supported; +} + +void ovrApp::HandleXrEvents() { + XrEventDataBuffer eventDataBuffer = {}; + + // Poll for events + for (;;) { + XrEventDataBaseHeader* baseEventHeader = (XrEventDataBaseHeader*)(&eventDataBuffer); + baseEventHeader->type = XR_TYPE_EVENT_DATA_BUFFER; + baseEventHeader->next = NULL; + XrResult r; + OXR(r = xrPollEvent(instance, &eventDataBuffer)); + if (r != XR_SUCCESS) { + break; + } + + switch (baseEventHeader->type) { + case XR_TYPE_EVENT_DATA_EVENTS_LOST: + ALOGV("xrPollEvent: received XR_TYPE_EVENT_DATA_EVENTS_LOST event"); + break; + case XR_TYPE_EVENT_DATA_INSTANCE_LOSS_PENDING: + ALOGV("xrPollEvent: received XR_TYPE_EVENT_DATA_INSTANCE_LOSS_PENDING event"); + break; + case XR_TYPE_EVENT_DATA_INTERACTION_PROFILE_CHANGED: + ALOGV("xrPollEvent: received XR_TYPE_EVENT_DATA_INTERACTION_PROFILE_CHANGED event"); + break; + case XR_TYPE_EVENT_DATA_PERF_SETTINGS_EXT: { + const XrEventDataPerfSettingsEXT* perf_settings_event = + (XrEventDataPerfSettingsEXT*)(baseEventHeader); + ALOGV( + "xrPollEvent: received XR_TYPE_EVENT_DATA_PERF_SETTINGS_EXT event: type %d subdomain %d : level %d -> level %d", + perf_settings_event->type, + perf_settings_event->subDomain, + perf_settings_event->fromLevel, + perf_settings_event->toLevel); + } break; + case XR_TYPE_EVENT_DATA_REFERENCE_SPACE_CHANGE_PENDING: + ALOGV( + "xrPollEvent: received XR_TYPE_EVENT_DATA_REFERENCE_SPACE_CHANGE_PENDING event"); + break; + case XR_TYPE_EVENT_DATA_SESSION_STATE_CHANGED: { + const XrEventDataSessionStateChanged* session_state_changed_event = + (XrEventDataSessionStateChanged*)(baseEventHeader); + ALOGV( + "xrPollEvent: received XR_TYPE_EVENT_DATA_SESSION_STATE_CHANGED: %d for session %p at time %f", + session_state_changed_event->state, + (void*)session_state_changed_event->session, + FromXrTime(session_state_changed_event->time)); + + switch (session_state_changed_event->state) { + case XR_SESSION_STATE_FOCUSED: + Focused = true; + break; + case XR_SESSION_STATE_VISIBLE: + Focused = false; + break; + case XR_SESSION_STATE_READY: + case XR_SESSION_STATE_STOPPING: + HandleSessionStateChanges(session_state_changed_event->state); + break; + case XR_SESSION_STATE_EXITING: + ShouldExit = true; + break; + default: + break; + } + } break; + case XR_TYPE_EVENT_DATA_SPACE_SET_STATUS_COMPLETE_FB: { + ALOGV("xrPollEvent: received XR_TYPE_EVENT_DATA_SPACE_SET_STATUS_COMPLETE_FB"); + const XrEventDataSpaceSetStatusCompleteFB* enableResult = + (XrEventDataSpaceSetStatusCompleteFB*)(baseEventHeader); + if (enableResult->result == XR_SUCCESS && + enableResult->componentType == XR_SPACE_COMPONENT_TYPE_LOCATABLE_FB) { + if (AppRenderer.Scene.SpaceList.size() < MAX_PERSISTENT_SPACES) { + AppRenderer.Scene.SpaceList.push_back(enableResult->space); + } + } + } break; + case XR_TYPE_EVENT_DATA_SPATIAL_ANCHOR_CREATE_COMPLETE_FB: { + ALOGV("xrPollEvent: received XR_TYPE_EVENT_DATA_SPATIAL_ANCHOR_CREATE_COMPLETE_FB"); + const XrEventDataSpatialAnchorCreateCompleteFB* createAnchorResult = + (XrEventDataSpatialAnchorCreateCompleteFB*)(baseEventHeader); + XrSpace space = createAnchorResult->space; + AppRenderer.Scene.SpaceList.push_back(space); + + if (IsComponentSupported(space, XR_SPACE_COMPONENT_TYPE_STORABLE_FB)) { + XrSpaceComponentStatusSetInfoFB request = { + XR_TYPE_SPACE_COMPONENT_STATUS_SET_INFO_FB}; + request.componentType = XR_SPACE_COMPONENT_TYPE_STORABLE_FB; + request.enabled = XR_TRUE; + request.timeout = 0; + + XrAsyncRequestIdFB requestId; + FunPtrs.xrSetSpaceComponentStatusFB(space, &request, &requestId); + } + + if (IsComponentSupported(space, XR_SPACE_COMPONENT_TYPE_SHARABLE_FB)) { + XrSpaceComponentStatusSetInfoFB request = { + XR_TYPE_SPACE_COMPONENT_STATUS_SET_INFO_FB}; + request.componentType = XR_SPACE_COMPONENT_TYPE_SHARABLE_FB; + request.enabled = XR_TRUE; + request.timeout = 0; + + XrAsyncRequestIdFB requestId; + FunPtrs.xrSetSpaceComponentStatusFB(space, &request, &requestId); + } + + ALOGV( + "Number of anchors after calling PlaceAnchor: %zu", + AppRenderer.Scene.SpaceList.size()); + } break; + case XR_TYPE_EVENT_DATA_SPACE_QUERY_RESULTS_AVAILABLE_FB: { + ALOGV("xrPollEvent: received XR_TYPE_EVENT_DATA_SPACE_QUERY_RESULTS_AVAILABLE_FB"); + const auto resultsAvailable = + (XrEventDataSpaceQueryResultsAvailableFB*)baseEventHeader; + + XrResult res = XR_SUCCESS; + + XrSpaceQueryResultsFB queryResults{XR_TYPE_SPACE_QUERY_RESULTS_FB}; + queryResults.resultCapacityInput = 0; + queryResults.resultCountOutput = 0; + queryResults.results = nullptr; + + res = FunPtrs.xrRetrieveSpaceQueryResultsFB( + Session, resultsAvailable->requestId, &queryResults); + if (res != XR_SUCCESS) { + ALOGV("xrRetrieveSpaceQueryResultsFB: error %u", res); + break; + } + + std::vector results(queryResults.resultCountOutput); + queryResults.resultCapacityInput = results.size(); + queryResults.results = results.data(); + + res = FunPtrs.xrRetrieveSpaceQueryResultsFB( + Session, resultsAvailable->requestId, &queryResults); + if (res != XR_SUCCESS) { + ALOGV("xrRetrieveSpaceQueryResultsFB: error %u", res); + break; + } + + for (uint32_t i = 0; i < queryResults.resultCountOutput; ++i) { + auto& result = results[i]; + + if (IsComponentSupported(result.space, XR_SPACE_COMPONENT_TYPE_LOCATABLE_FB)) { + XrSpaceComponentStatusSetInfoFB request = { + XR_TYPE_SPACE_COMPONENT_STATUS_SET_INFO_FB}; + request.componentType = XR_SPACE_COMPONENT_TYPE_LOCATABLE_FB; + request.enabled = XR_TRUE; + request.timeout = 0; + XrAsyncRequestIdFB requestId; + res = + FunPtrs.xrSetSpaceComponentStatusFB(result.space, &request, &requestId); + if (res == XR_ERROR_SPACE_COMPONENT_STATUS_ALREADY_SET_FB) { + if (AppRenderer.Scene.SpaceList.size() < MAX_PERSISTENT_SPACES) { + AppRenderer.Scene.SpaceList.push_back(result.space); + } + } + } + + if (IsComponentSupported(result.space, XR_SPACE_COMPONENT_TYPE_STORABLE_FB)) { + XrSpaceComponentStatusSetInfoFB request = { + XR_TYPE_SPACE_COMPONENT_STATUS_SET_INFO_FB}; + request.componentType = XR_SPACE_COMPONENT_TYPE_STORABLE_FB; + request.enabled = XR_TRUE; + request.timeout = 0; + XrAsyncRequestIdFB requestId; + res = + FunPtrs.xrSetSpaceComponentStatusFB(result.space, &request, &requestId); + if (res == XR_ERROR_SPACE_COMPONENT_STATUS_ALREADY_SET_FB) { + ALOGV( + "xrPollEvent: Storable component was already enabled for Space uuid: %s", + uuidToHexString(result.uuid).c_str()); + } + } + + if (IsComponentSupported(result.space, XR_SPACE_COMPONENT_TYPE_SHARABLE_FB)) { + XrSpaceComponentStatusSetInfoFB request = { + XR_TYPE_SPACE_COMPONENT_STATUS_SET_INFO_FB}; + request.componentType = XR_SPACE_COMPONENT_TYPE_SHARABLE_FB; + request.enabled = XR_TRUE; + request.timeout = 0; + XrAsyncRequestIdFB requestId; + res = + FunPtrs.xrSetSpaceComponentStatusFB(result.space, &request, &requestId); + if (res == XR_ERROR_SPACE_COMPONENT_STATUS_ALREADY_SET_FB) { + ALOGV( + "xrPollEvent: Sharable component was already enabled for Space uuid: %s", + uuidToHexString(result.uuid).c_str()); + } + } + } + + ALOGV( + "Number of anchors after receiving XR_TYPE_EVENT_DATA_SPACE_QUERY_RESULTS_AVAILABLE_FB: %zu", + AppRenderer.Scene.SpaceList.size()); + + } break; + case XR_TYPE_EVENT_DATA_SPACE_QUERY_COMPLETE_FB: { + ALOGV("xrPollEvent: received XR_TYPE_EVENT_DATA_SPACE_QUERY_COMPLETE_FB"); + } break; + case XR_TYPE_EVENT_DATA_SPACE_DISCOVERY_RESULTS_AVAILABLE_META: { + ALOGV( + "xrPollEvent: received XR_TYPE_EVENT_DATA_SPACE_DISCOVERY_RESULTS_AVAILABLE_META"); + const auto resultsAvailable = + (XrEventDataSpaceDiscoveryResultsAvailableMETA*)baseEventHeader; + + XrResult res = XR_SUCCESS; + XrSpaceDiscoveryResultsMETA discoveryResults{XR_TYPE_SPACE_DISCOVERY_RESULTS_META}; + discoveryResults.resultCapacityInput = 0; + discoveryResults.resultCountOutput = 0; + discoveryResults.results = nullptr; + res = FunPtrs.xrRetrieveSpaceDiscoveryResultsMETA( + Session, resultsAvailable->requestId, &discoveryResults); + if (res != XR_SUCCESS) { + ALOGV("xrRetrieveSpaceDiscoveryResultsMETA: error %u", res); + break; + } + + std::vector results(discoveryResults.resultCountOutput); + discoveryResults.resultCapacityInput = results.size(); + discoveryResults.results = results.data(); + + res = FunPtrs.xrRetrieveSpaceDiscoveryResultsMETA( + Session, resultsAvailable->requestId, &discoveryResults); + if (res != XR_SUCCESS) { + ALOGV("xrRetrieveSpaceDiscoveryResultsMETA: error %u", res); + break; + } + + for (uint32_t i = 0; i < discoveryResults.resultCountOutput; ++i) { + auto& result = results[i]; + + if (IsComponentSupported(result.space, XR_SPACE_COMPONENT_TYPE_LOCATABLE_FB)) { + XrSpaceComponentStatusSetInfoFB request = { + XR_TYPE_SPACE_COMPONENT_STATUS_SET_INFO_FB}; + request.componentType = XR_SPACE_COMPONENT_TYPE_LOCATABLE_FB; + request.enabled = XR_TRUE; + request.timeout = 0; + XrAsyncRequestIdFB requestId; + res = + FunPtrs.xrSetSpaceComponentStatusFB(result.space, &request, &requestId); + if (res == XR_ERROR_SPACE_COMPONENT_STATUS_ALREADY_SET_FB) { + if (AppRenderer.Scene.SpaceList.size() < MAX_PERSISTENT_SPACES) { + AppRenderer.Scene.SpaceList.push_back(result.space); + } + } + } + + if (IsComponentSupported(result.space, XR_SPACE_COMPONENT_TYPE_STORABLE_FB)) { + XrSpaceComponentStatusSetInfoFB request = { + XR_TYPE_SPACE_COMPONENT_STATUS_SET_INFO_FB}; + request.componentType = XR_SPACE_COMPONENT_TYPE_STORABLE_FB; + request.enabled = XR_TRUE; + request.timeout = 0; + XrAsyncRequestIdFB requestId; + res = + FunPtrs.xrSetSpaceComponentStatusFB(result.space, &request, &requestId); + if (res == XR_ERROR_SPACE_COMPONENT_STATUS_ALREADY_SET_FB) { + ALOGV( + "xrPollEvent: Storable component was already enabled for Space uuid: %s", + uuidToHexString(result.uuid).c_str()); + } + } + + if (IsComponentSupported(result.space, XR_SPACE_COMPONENT_TYPE_SHARABLE_FB)) { + XrSpaceComponentStatusSetInfoFB request = { + XR_TYPE_SPACE_COMPONENT_STATUS_SET_INFO_FB}; + request.componentType = XR_SPACE_COMPONENT_TYPE_SHARABLE_FB; + request.enabled = XR_TRUE; + request.timeout = 0; + XrAsyncRequestIdFB requestId; + res = + FunPtrs.xrSetSpaceComponentStatusFB(result.space, &request, &requestId); + if (res == XR_ERROR_SPACE_COMPONENT_STATUS_ALREADY_SET_FB) { + ALOGV( + "xrPollEvent: Sharable component was already enabled for Space uuid: %s", + uuidToHexString(result.uuid).c_str()); + } + } + } + + ALOGV( + "Number of anchors after receiving XR_TYPE_EVENT_DATA_SPACE_DISCOVERY_RESULTS_AVAILABLE_META: %zu", + AppRenderer.Scene.SpaceList.size()); + } break; + case XR_TYPE_EVENT_DATA_SPACE_DISCOVERY_COMPLETE_META: { + ALOGV("xrPollEvent: received XR_TYPE_EVENT_DATA_SPACE_DISCOVERY_COMPLETE_META"); + const XrEventDataSpaceDiscoveryCompleteMETA* discoveryCompleteResult = + (XrEventDataSpaceDiscoveryCompleteMETA*)(baseEventHeader); + ALOGV( + "XR_TYPE_EVENT_DATA_SPACE_DISCOVERY_COMPLETE_META result: %d", + discoveryCompleteResult->result); + } break; + case XR_TYPE_EVENT_DATA_SPACES_SAVE_RESULT_META: { + ALOGV("xrPollEvent: received XR_TYPE_EVENT_DATA_SPACES_SAVE_RESULT_META"); + const XrEventDataSpacesSaveResultMETA* saveResult = + (XrEventDataSpacesSaveResultMETA*)(baseEventHeader); + auto requestId = saveResult->requestId; + if (saveResult->result < XR_SUCCESS) { + ALOGE( + "xrPollEvent: XR_TYPE_EVENT_DATA_SPACES_SAVE_RESULT_META came with error result: %" PRIu64, + requestId); + } + auto itr = SaveForSharingEventMap.find(requestId); + if (itr == SaveForSharingEventMap.end()) { + ALOGE( + "xrPollEvent: no requestId found in map for XR_TYPE_EVENT_DATA_SPACES_SAVE_RESULT_META"); + break; + } + if (saveResult->result < XR_SUCCESS) { + SaveForSharingEventMap.erase(itr); + break; + } + + if (IsLocalMultiplayerSupported && ShouldShareAnchors) { + auto spaceList = std::move(itr->second); + SaveForSharingEventMap.erase(itr); + + // Skip the share step if there are no users to share with. + if (ShareUserList.empty()) { + ALOGW( + "xrPollEvent: no users specified for share operation. Skipping share."); + } else { + // Set up the share audience + std::vector users; + for (std::size_t k = 0; k < ShareUserList.size(); k++) { + XrSpaceUserFB user; + XrSpaceUserCreateInfoFB createInfo = { + XR_TYPE_SPACE_USER_CREATE_INFO_FB}; + createInfo.userId = ShareUserList[k]; + OXR(FunPtrs.xrCreateSpaceUserFB(Session, &createInfo, &user)); + users.push_back(user); + } + + XrSpaceShareInfoFB shareInfo = {XR_TYPE_SPACE_SHARE_INFO_FB}; + shareInfo.spaceCount = static_cast(spaceList.size()); + shareInfo.spaces = spaceList.data(); + shareInfo.userCount = static_cast(users.size()); + shareInfo.users = users.data(); + XrAsyncRequestIdFB shareRequestId; + OXR(FunPtrs.xrShareSpacesFB(Session, &shareInfo, &shareRequestId)); + ShareSpacesEventMap[shareRequestId] = std::make_pair(spaceList, users); + } + } else { + ALOGW( + "xrPollEvent: Didn't share anchors. Local multiplayer supported: %s. Should Share Anchors: %s", + IsLocalMultiplayerSupported ? "true" : "false", + ShouldShareAnchors ? "true" : "false"); + } + ShouldShareAnchors = false; + } break; + case XR_TYPE_EVENT_DATA_SPACES_ERASE_RESULT_META: { + ALOGV("xrPollEvent: received XR_TYPE_EVENT_DATA_SPACES_ERASE_RESULT_META"); + const XrEventDataSpacesEraseResultMETA* eraseResult = + (XrEventDataSpacesEraseResultMETA*)(baseEventHeader); + + if (eraseResult->result == XR_SUCCESS) { + ALOGV( + "xrPollEvent: Erase Space successful! Async request: %" PRIu64, + eraseResult->requestId); + auto iter = DestroySpaceEventMap.find(eraseResult->requestId); + if (iter != DestroySpaceEventMap.end()) { + XrSpace* spaceList = iter->second.second; + for (size_t i = 0; i < iter->second.first; i++) { + xrDestroySpace(spaceList[i]); + } + AppRenderer.Scene.SpaceList.clear(); + DestroySpaceEventMap.erase(iter); + } + } else { + ALOGV( + "xrPollEvent: Erase Space failed! Async request: %" PRIu64, + eraseResult->requestId); + } + } break; + case XR_TYPE_EVENT_DATA_SPACE_SHARE_COMPLETE_FB: { + ALOGV("xrPollEvent: received XR_TYPE_EVENT_DATA_SPACE_SHARE_COMPLETE_FB"); + const XrEventDataSpaceShareCompleteFB* shareResult = + (XrEventDataSpaceShareCompleteFB*)(baseEventHeader); + + ALOGV( + "xrPollEvent: xrShareSpacesFB request %" PRIu64 " completed with result (%d)", + shareResult->requestId, + shareResult->result); + auto itr = ShareSpacesEventMap.find(shareResult->requestId); + if (itr != ShareSpacesEventMap.end()) { + auto spaces = std::move(itr->second.first); + auto users = std::move(itr->second.second); + ShareSpacesEventMap.erase(itr); + if (shareResult->result == XR_SUCCESS) { + std::vector sharedSpatialAnchors; + for (std::size_t k = 0; k < spaces.size(); k++) { + XrUuidEXT uuid; + OXR(FunPtrs.xrGetSpaceUuidFB(spaces[k], &uuid)); + ALOGV("xrPollEvent: Shared space %s", uuidToHexString(uuid).c_str()); + sharedSpatialAnchors.emplace_back(uuid); + } + + std::vector sharedUserIds; + for (std::size_t k = 0; k < users.size(); k++) { + XrSpaceUserIdFB userId; + OXR(FunPtrs.xrGetSpaceUserIdFB(users[k], &userId)); + ALOGV("xrPollEvent: Shared spaces with user %" PRIu64, userId); + sharedUserIds.emplace_back(userId); + } + + ExternalDataHandler->WriteSharedSpatialAnchorList( + sharedSpatialAnchors, sharedUserIds); + } + // We're done with the user handles at this point, so destroy them. + for (std::size_t k = 0; k < users.size(); k++) { + OXR(FunPtrs.xrDestroySpaceUserFB(users[k])); + ALOGV("xrPollEvent: Destroyed user handle %" PRIu64, (uint64_t)users[k]); + } + } else { + ALOGE( + "Failed to match xrShareSpacesFB request data to requestId %" PRIu64, + shareResult->requestId); + } + } break; + default: + ALOGV("xrPollEvent: Unknown event"); + break; + } + } +} + +/* +================================================================================ + +Native Activity + +================================================================================ +*/ + +#if defined(ANDROID) +/** + * Process the next main command. + */ +static void app_handle_cmd(struct android_app* androidApp, int32_t cmd) { + ovrApp& app = *(ovrApp*)androidApp->userData; + + switch (cmd) { + // There is no APP_CMD_CREATE. The ANativeActivity creates the + // application thread from onCreate(). The application thread + // then calls android_main(). + case APP_CMD_START: { + ALOGV("onStart()"); + ALOGV(" APP_CMD_START"); + break; + } + case APP_CMD_RESUME: { + ALOGV("onResume()"); + ALOGV(" APP_CMD_RESUME"); + app.Resumed = true; + break; + } + case APP_CMD_PAUSE: { + ALOGV("onPause()"); + ALOGV(" APP_CMD_PAUSE"); + app.Resumed = false; + break; + } + case APP_CMD_STOP: { + ALOGV("onStop()"); + ALOGV(" APP_CMD_STOP"); + break; + } + case APP_CMD_DESTROY: { + ALOGV("onDestroy()"); + ALOGV(" APP_CMD_DESTROY"); + app.Clear(); + break; + } + case APP_CMD_INIT_WINDOW: { + ALOGV("surfaceCreated()"); + ALOGV(" APP_CMD_INIT_WINDOW"); + break; + } + case APP_CMD_TERM_WINDOW: { + ALOGV("surfaceDestroyed()"); + ALOGV(" APP_CMD_TERM_WINDOW"); + break; + } + } +} +#endif // defined(ANDROID) + +static Matrix4f OvrFromXr(const XrMatrix4x4f& x) { + return Matrix4f( + x.m[0x0], + x.m[0x1], + x.m[0x2], + x.m[0x3], + x.m[0x4], + x.m[0x5], + x.m[0x6], + x.m[0x7], + x.m[0x8], + x.m[0x9], + x.m[0xa], + x.m[0xb], + x.m[0xc], + x.m[0xd], + x.m[0xe], + x.m[0xf]); +} + +static Quatf OvrFromXr(const XrQuaternionf& q) { + return Quatf(q.x, q.y, q.z, q.w); +} + +static Vector3f OvrFromXr(const XrVector3f& v) { + return Vector3f(v.x, v.y, v.z); +} + +static Posef OvrFromXr(const XrPosef& p) { + return Posef(OvrFromXr(p.orientation), OvrFromXr(p.position)); +} + +static void DiscoverAnchors(ovrApp& app) { + ALOGV("DiscoverAnchors"); + XrSpaceDiscoveryInfoMETA info = {XR_TYPE_SPACE_DISCOVERY_INFO_META}; + info.filters = nullptr; + info.filters = 0; + XrAsyncRequestIdFB requestId; + OXR(app.FunPtrs.xrDiscoverSpacesMETA(app.Session, &info, &requestId)); +} + +static void DownloadAnchors(ovrApp& app) { + ALOGV("DownloadAnchors"); + if (!app.IsLocalMultiplayerSupported) { + ALOGW("Local multiplayer is not supported. Skipping download."); + return; + } + + if (app.InboundSpatialAnchorList.empty()) { + ALOGW("DownloadAnchors: No Spatial Anchors to download"); + return; + } + + XrSpaceStorageLocationFilterInfoFB locationFilterInfo = { + XR_TYPE_SPACE_STORAGE_LOCATION_FILTER_INFO_FB}; + locationFilterInfo.location = XR_SPACE_STORAGE_LOCATION_CLOUD_FB; + + XrSpaceUuidFilterInfoFB uuidFilterInfo = {XR_TYPE_SPACE_UUID_FILTER_INFO_FB}; + uuidFilterInfo.next = &locationFilterInfo; + uuidFilterInfo.uuidCount = (uint32_t)app.InboundSpatialAnchorList.size(); + uuidFilterInfo.uuids = app.InboundSpatialAnchorList.data(); + + XrSpaceQueryInfoFB queryInfo = {XR_TYPE_SPACE_QUERY_INFO_FB}; + queryInfo.queryAction = XR_SPACE_QUERY_ACTION_LOAD_FB; + queryInfo.maxResultCount = MAX_PERSISTENT_SPACES; + queryInfo.timeout = 0; + queryInfo.filter = (XrSpaceFilterInfoBaseHeaderFB*)&uuidFilterInfo; + XrAsyncRequestIdFB requestId; + OXR(app.FunPtrs.xrQuerySpacesFB( + app.Session, (XrSpaceQueryInfoBaseHeaderFB*)&queryInfo, &requestId)); +} + +static void SaveAnchorsAndTrySharing(ovrApp& app) { + ALOGV("SaveAnchorsAndTrySharing"); + + if (!app.IsLocalMultiplayerSupported) { + ALOGW( + "SaveAnchorsAndTrySharing: Local multiplayer is not supported, will only save not share."); + } else { + app.ShouldShareAnchors = true; + } + + std::vector saveList; + for (XrSpace space : app.AppRenderer.Scene.SpaceList) { + // We will only upload Anchors that are Storable and Sharable. + XrSpaceComponentStatusFB storableStatus; + XrSpaceComponentStatusFB sharableStatus; + OXR(app.FunPtrs.xrGetSpaceComponentStatusFB( + space, XR_SPACE_COMPONENT_TYPE_STORABLE_FB, &storableStatus)); + OXR(app.FunPtrs.xrGetSpaceComponentStatusFB( + space, XR_SPACE_COMPONENT_TYPE_SHARABLE_FB, &sharableStatus)); + if (storableStatus.enabled && sharableStatus.enabled) { + saveList.push_back(space); + } + } + + XrSpacesSaveInfoMETA saveInfo = {XR_TYPE_SPACES_SAVE_INFO_META}; + saveInfo.spaceCount = static_cast(saveList.size()); + saveInfo.spaces = saveList.data(); + XrAsyncRequestIdFB requestId; + XrResult res = XR_SUCCESS; + OXR(res = app.FunPtrs.xrSaveSpacesMETA(app.Session, &saveInfo, &requestId)); + if (res == XR_SUCCESS) { + ALOGV("SaveAnchors call successfully kicked off!"); + } else { + ALOGV("SaveAnchors call failed to kick off!"); + } + + // The share operation will continue on the completion event of xrSaveSpacesMETA. + // Add the current spaces to the map. This step needs to be a copy here because we're taking a + // snapshot of the Spatial Anchors in the app state. + app.SaveForSharingEventMap[requestId] = saveList; +} + +void UpdateStageBounds(ovrApp& app) { + XrExtent2Df stageBounds = {}; + + XrResult result; + OXR(result = xrGetReferenceSpaceBoundsRect( + app.Session, XR_REFERENCE_SPACE_TYPE_STAGE, &stageBounds)); + if (result != XR_SUCCESS) { + ALOGV("Stage bounds query failed: using small defaults"); + stageBounds.width = 1.0f; + stageBounds.height = 1.0f; + } + + app.StageBounds = Vector3f(stageBounds.width * 0.5f, 1.0f, stageBounds.height * 0.5f); +} + +void EraseAndDestroyAnchors(ovrApp& app) { + if (app.AppRenderer.Scene.SpaceList.empty()) { + return; + } + + if (app.Session == XR_NULL_HANDLE) { + return; + } + + size_t numSpacesToEraseAndDestroy = + std::min((size_t)kMaxPersistenceSpacesPerApiCall, app.AppRenderer.Scene.SpaceList.size()); + ALOGV("About to call erase with %d handles", (int)numSpacesToEraseAndDestroy); + std::vector spacesToErase(numSpacesToEraseAndDestroy); + std::copy( + app.AppRenderer.Scene.SpaceList.begin(), + app.AppRenderer.Scene.SpaceList.begin() + numSpacesToEraseAndDestroy, + spacesToErase.begin()); + + XrSpacesEraseInfoMETA eraseInfo = {XR_TYPE_SPACES_ERASE_INFO_META}; + eraseInfo.spaceCount = static_cast(spacesToErase.size()); + eraseInfo.spaces = spacesToErase.data(); + eraseInfo.uuidCount = 0; + + XrAsyncRequestIdFB requestId; + XrResult res = XR_SUCCESS; + OXR(res = app.FunPtrs.xrEraseSpacesMETA(app.Session, &eraseInfo, &requestId)); + if (res == XR_SUCCESS) { + ALOGV("EraseAndDestroyAnchors erase call successfully kicked off!"); + app.DestroySpaceEventMap[requestId] = + std::pair(spacesToErase.size(), spacesToErase.data()); + } else { + ALOGE("EraseAndDestroyAnchors erase call failed to kick off!"); + } + // Call destroy in the event handling of erase API +} + +void PlaceAnchor(ovrApp& app, SimpleXrInput* input, const XrFrameState& frameState) { + // Handle Right Controller Events + if (input == nullptr) { + return; + } + + if (app.Session == XR_NULL_HANDLE) { + return; + } + + if (app.AppRenderer.Scene.SpaceList.size() >= MAX_PERSISTENT_SPACES) { + return; + } + + OVR::Posef localFromRightAim = input->FromControllerSpace( + SimpleXrInput::Side_Right, + SimpleXrInput::Controller_Aim, + app.LocalSpace, + frameState.predictedDisplayTime); + + XrSpatialAnchorCreateInfoFB anchorCreateInfo = {XR_TYPE_SPATIAL_ANCHOR_CREATE_INFO_FB}; + anchorCreateInfo.space = app.LocalSpace; + anchorCreateInfo.poseInSpace = ToXrPosef(localFromRightAim); + anchorCreateInfo.time = frameState.predictedDisplayTime; + XrAsyncRequestIdFB createRequest; + OXR(app.FunPtrs.xrCreateSpatialAnchorFB(app.Session, &anchorCreateInfo, &createRequest)); + ALOGV("Place Spatial Anchor initiated."); +} + +#if defined(XR_USE_PLATFORM_ANDROID) +/** + * This is the main entry point of a native application that is using + * android_native_app_glue. It runs in its own thread, with its own + * event loop for receiving input events and doing other things. + */ +void android_main(struct android_app* androidApp) { +#else +int main() { +#endif // defined(XR_USE_PLATFORM_ANDROID) + +#if defined(XR_USE_PLATFORM_ANDROID) + ALOGV("----------------------------------------------------------------"); + ALOGV("android_app_entry()"); + ALOGV(" android_main()"); + + JNIEnv* Env; + (*androidApp->activity->vm).AttachCurrentThread(&Env, nullptr); + + // Note that AttachCurrentThread will reset the thread name. + prctl(PR_SET_NAME, (long)"OVR::Main", 0, 0, 0); +#endif // defined(XR_USE_PLATFORM_ANDROID) + + ovrApp app; + app.Clear(); + +#if defined(XR_USE_PLATFORM_ANDROID) + PFN_xrInitializeLoaderKHR xrInitializeLoaderKHR; + xrGetInstanceProcAddr( + XR_NULL_HANDLE, "xrInitializeLoaderKHR", (PFN_xrVoidFunction*)&xrInitializeLoaderKHR); + if (xrInitializeLoaderKHR != NULL) { + XrLoaderInitInfoAndroidKHR loaderInitializeInfoAndroid = { + XR_TYPE_LOADER_INIT_INFO_ANDROID_KHR}; + loaderInitializeInfoAndroid.applicationVM = androidApp->activity->vm; + loaderInitializeInfoAndroid.applicationContext = androidApp->activity->clazz; + xrInitializeLoaderKHR((XrLoaderInitInfoBaseHeaderKHR*)&loaderInitializeInfoAndroid); + } +#endif // defined(XR_USE_PLATFORM_ANDROID) + + // Log available layers. + { + XrResult result; + + PFN_xrEnumerateApiLayerProperties xrEnumerateApiLayerProperties; + OXR(result = xrGetInstanceProcAddr( + XR_NULL_HANDLE, + "xrEnumerateApiLayerProperties", + (PFN_xrVoidFunction*)&xrEnumerateApiLayerProperties)); + if (result != XR_SUCCESS) { + ALOGE("Failed to get xrEnumerateApiLayerProperties function pointer."); + exit(1); + } + + uint32_t layerCount = 0; + OXR(xrEnumerateApiLayerProperties(0, &layerCount, NULL)); + std::vector layerProperties( + layerCount, {XR_TYPE_API_LAYER_PROPERTIES}); + OXR(xrEnumerateApiLayerProperties(layerCount, &layerCount, layerProperties.data())); + + for (const auto& layer : layerProperties) { + ALOGV("Found layer %s", layer.layerName); + } + } + + // Check that the extensions required are present. + std::vector requiredExtensionNames { +#if defined(XR_USE_GRAPHICS_API_OPENGL_ES) + XR_KHR_OPENGL_ES_ENABLE_EXTENSION_NAME, +#elif defined(XR_USE_GRAPHICS_API_OPENGL) + XR_KHR_OPENGL_ENABLE_EXTENSION_NAME, +#endif +#if defined(XR_USE_PLATFORM_ANDROID) + XR_EXT_PERFORMANCE_SETTINGS_EXTENSION_NAME, + XR_KHR_ANDROID_THREAD_SETTINGS_EXTENSION_NAME, +#endif // defined(XR_USE_PLATFORM_ANDROID) + XR_FB_PASSTHROUGH_EXTENSION_NAME, XR_FB_SPATIAL_ENTITY_EXTENSION_NAME, + XR_FB_SPATIAL_ENTITY_QUERY_EXTENSION_NAME, + XR_META_SPATIAL_ENTITY_DISCOVERY_EXTENSION_NAME, + XR_META_SPATIAL_ENTITY_PERSISTENCE_EXTENSION_NAME + }; + const uint32_t numRequiredExtensions = requiredExtensionNames.size(); + + std::vector localMultiplayerExtensionNames{ + XR_FB_SPATIAL_ENTITY_SHARING_EXTENSION_NAME, XR_FB_SPATIAL_ENTITY_USER_EXTENSION_NAME}; + const uint32_t numLocalMultiplayerExtensions = localMultiplayerExtensionNames.size(); + + std::vector requestedExtensionNames( + requiredExtensionNames.begin(), requiredExtensionNames.end()); + + // Check the list of required extensions against what is supported by the runtime. + { + uint32_t numOutputExtensions = 0; + OXR(xrEnumerateInstanceExtensionProperties(nullptr, 0, &numOutputExtensions, nullptr)); + ALOGV("xrEnumerateInstanceExtensionProperties found %u extension(s).", numOutputExtensions); + + auto extensionProperties = + std::vector(numOutputExtensions, {XR_TYPE_EXTENSION_PROPERTIES}); + + OXR(xrEnumerateInstanceExtensionProperties( + nullptr, numOutputExtensions, &numOutputExtensions, extensionProperties.data())); + for (uint32_t i = 0; i < numOutputExtensions; i++) { + ALOGV("Extension #%d = '%s'.", i, extensionProperties[i].extensionName); + } + + for (uint32_t i = 0; i < numRequiredExtensions; i++) { + if (!isExtensionEnumerated( + requiredExtensionNames[i], extensionProperties.data(), numOutputExtensions)) { + ALOGE("Failed to find required extension %s", requiredExtensionNames[i]); + exit(1); + } else { + ALOGV("Found required extension %s", requiredExtensionNames[i]); + } + } + + app.IsLocalMultiplayerSupported = true; + for (uint32_t i = 0; i < numLocalMultiplayerExtensions; i++) { + if (!isExtensionEnumerated( + localMultiplayerExtensionNames[i], + extensionProperties.data(), + numOutputExtensions)) { + ALOGW( + "Failed to find local multiplayer extension %s - feature is not supported on this device", + localMultiplayerExtensionNames[i]); + app.IsLocalMultiplayerSupported = false; + } else { + ALOGV("Found local multiplayer extension %s", localMultiplayerExtensionNames[i]); + requestedExtensionNames.push_back(localMultiplayerExtensionNames[i]); + } + } + } + + // Create the OpenXR instance. + XrApplicationInfo appInfo = {}; + strcpy(appInfo.applicationName, "SpatialAnchorXr"); + appInfo.applicationVersion = 0; + strcpy(appInfo.engineName, "Oculus Mobile Sample"); + appInfo.engineVersion = 0; + appInfo.apiVersion = XR_MAKE_VERSION(1, 0, 34); + + XrInstanceCreateInfo instanceCreateInfo = {XR_TYPE_INSTANCE_CREATE_INFO}; + instanceCreateInfo.createFlags = 0; + instanceCreateInfo.applicationInfo = appInfo; + instanceCreateInfo.enabledApiLayerCount = 0; + instanceCreateInfo.enabledApiLayerNames = NULL; + instanceCreateInfo.enabledExtensionCount = requestedExtensionNames.size(); + instanceCreateInfo.enabledExtensionNames = requestedExtensionNames.data(); + + XrResult initResult; + OXR(initResult = xrCreateInstance(&instanceCreateInfo, &instance)); + if (initResult != XR_SUCCESS) { + ALOGE("Failed to create XR instance: %d.", initResult); + exit(1); + } + + XrInstanceProperties instanceInfo = {XR_TYPE_INSTANCE_PROPERTIES}; + OXR(xrGetInstanceProperties(instance, &instanceInfo)); + ALOGV( + "Runtime %s: Version : %u.%u.%u", + instanceInfo.runtimeName, + XR_VERSION_MAJOR(instanceInfo.runtimeVersion), + XR_VERSION_MINOR(instanceInfo.runtimeVersion), + XR_VERSION_PATCH(instanceInfo.runtimeVersion)); + + XrSystemGetInfo systemGetInfo = {XR_TYPE_SYSTEM_GET_INFO}; + systemGetInfo.formFactor = XR_FORM_FACTOR_HEAD_MOUNTED_DISPLAY; + + XrSystemId systemId; + OXR(initResult = xrGetSystem(instance, &systemGetInfo, &systemId)); + if (initResult != XR_SUCCESS) { + if (initResult == XR_ERROR_FORM_FACTOR_UNAVAILABLE) { + ALOGE( + "Failed to get system; the specified form factor is not available. Is your headset connected?"); + } else { + ALOGE("xrGetSystem failed, error %d", initResult); + } + exit(1); + } + + XrSystemProperties systemProperties = {XR_TYPE_SYSTEM_PROPERTIES}; + OXR(xrGetSystemProperties(instance, systemId, &systemProperties)); + + ALOGV( + "System Properties: Name=%s VendorId=%x", + systemProperties.systemName, + systemProperties.vendorId); + ALOGV( + "System Graphics Properties: MaxWidth=%d MaxHeight=%d MaxLayers=%d", + systemProperties.graphicsProperties.maxSwapchainImageWidth, + systemProperties.graphicsProperties.maxSwapchainImageHeight, + systemProperties.graphicsProperties.maxLayerCount); + ALOGV( + "System Tracking Properties: OrientationTracking=%s PositionTracking=%s", + systemProperties.trackingProperties.orientationTracking ? "True" : "False", + systemProperties.trackingProperties.positionTracking ? "True" : "False"); + + assert(ovrMaxLayerCount <= systemProperties.graphicsProperties.maxLayerCount); + +#if defined(XR_USE_GRAPHICS_API_OPENGL_ES) + // Get the graphics requirements. + PFN_xrGetOpenGLESGraphicsRequirementsKHR pfnGetOpenGLESGraphicsRequirementsKHR = NULL; + OXR(xrGetInstanceProcAddr( + instance, + "xrGetOpenGLESGraphicsRequirementsKHR", + (PFN_xrVoidFunction*)(&pfnGetOpenGLESGraphicsRequirementsKHR))); + + XrGraphicsRequirementsOpenGLESKHR graphicsRequirements = { + XR_TYPE_GRAPHICS_REQUIREMENTS_OPENGL_ES_KHR}; + OXR(pfnGetOpenGLESGraphicsRequirementsKHR(instance, systemId, &graphicsRequirements)); +#elif defined(XR_USE_GRAPHICS_API_OPENGL) + // Get the graphics requirements. + PFN_xrGetOpenGLGraphicsRequirementsKHR pfnGetOpenGLGraphicsRequirementsKHR = NULL; + OXR(xrGetInstanceProcAddr( + instance, + "xrGetOpenGLGraphicsRequirementsKHR", + (PFN_xrVoidFunction*)(&pfnGetOpenGLGraphicsRequirementsKHR))); + + XrGraphicsRequirementsOpenGLKHR graphicsRequirements = { + XR_TYPE_GRAPHICS_REQUIREMENTS_OPENGL_KHR}; + OXR(pfnGetOpenGLGraphicsRequirementsKHR(instance, systemId, &graphicsRequirements)); +#endif + + // Create the EGL Context + app.Egl.CreateContext(nullptr); + + // Check the graphics requirements. + int eglMajor = 0; + int eglMinor = 0; + glGetIntegerv(GL_MAJOR_VERSION, &eglMajor); + glGetIntegerv(GL_MINOR_VERSION, &eglMinor); + const XrVersion eglVersion = XR_MAKE_VERSION(eglMajor, eglMinor, 0); + if (eglVersion < graphicsRequirements.minApiVersionSupported || + eglVersion > graphicsRequirements.maxApiVersionSupported) { + ALOGE("GLES version %d.%d not supported", eglMajor, eglMinor); + exit(0); + } + + app.CpuLevel = CPU_LEVEL; + app.GpuLevel = GPU_LEVEL; +#if defined(ANDROID) + app.MainThreadTid = gettid(); +#else + app.MainThreadTid = (int)std::hash{}(std::this_thread::get_id()); +#endif // defined(ANDROID) + + app.SystemId = systemId; + + // Create the OpenXR Session. +#if defined(XR_USE_GRAPHICS_API_OPENGL_ES) + XrGraphicsBindingOpenGLESAndroidKHR graphicsBinding = { + XR_TYPE_GRAPHICS_BINDING_OPENGL_ES_ANDROID_KHR}; + graphicsBinding.display = app.Egl.Display; + graphicsBinding.config = app.Egl.Config; + graphicsBinding.context = app.Egl.Context; +#elif defined(XR_USE_GRAPHICS_API_OPENGL) + XrGraphicsBindingOpenGLWin32KHR graphicsBinding = {XR_TYPE_GRAPHICS_BINDING_OPENGL_WIN32_KHR}; + graphicsBinding.hDC = app.Egl.hDC; + graphicsBinding.hGLRC = app.Egl.hGLRC; +#endif // + + XrSessionCreateInfo sessionCreateInfo = {XR_TYPE_SESSION_CREATE_INFO}; + sessionCreateInfo.next = &graphicsBinding; + sessionCreateInfo.createFlags = 0; + sessionCreateInfo.systemId = app.SystemId; + + OXR(initResult = xrCreateSession(instance, &sessionCreateInfo, &app.Session)); + if (initResult != XR_SUCCESS) { + ALOGE("Failed to create XR session: %d.", initResult); + exit(1); + } + + // App only supports the primary stereo view config. + const XrViewConfigurationType supportedViewConfigType = + XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO; + + // Enumerate the viewport configurations. + uint32_t viewportConfigTypeCount = 0; + OXR(xrEnumerateViewConfigurations(instance, app.SystemId, 0, &viewportConfigTypeCount, NULL)); + + auto viewportConfigurationTypes = new XrViewConfigurationType[viewportConfigTypeCount]; + + OXR(xrEnumerateViewConfigurations( + instance, + app.SystemId, + viewportConfigTypeCount, + &viewportConfigTypeCount, + viewportConfigurationTypes)); + + ALOGV("Available Viewport Configuration Types: %d", viewportConfigTypeCount); + + for (uint32_t i = 0; i < viewportConfigTypeCount; i++) { + const XrViewConfigurationType viewportConfigType = viewportConfigurationTypes[i]; + + ALOGV( + "Viewport configuration type %d : %s", + viewportConfigType, + viewportConfigType == supportedViewConfigType ? "Selected" : ""); + + XrViewConfigurationProperties viewportConfig = {XR_TYPE_VIEW_CONFIGURATION_PROPERTIES}; + OXR(xrGetViewConfigurationProperties( + instance, app.SystemId, viewportConfigType, &viewportConfig)); + ALOGV( + "FovMutable=%s ConfigurationType %d", + viewportConfig.fovMutable ? "true" : "false", + viewportConfig.viewConfigurationType); + + uint32_t viewCount; + OXR(xrEnumerateViewConfigurationViews( + instance, app.SystemId, viewportConfigType, 0, &viewCount, NULL)); + + if (viewCount > 0) { + auto elements = new XrViewConfigurationView[viewCount]; + + for (uint32_t e = 0; e < viewCount; e++) { + elements[e].type = XR_TYPE_VIEW_CONFIGURATION_VIEW; + elements[e].next = NULL; + } + + OXR(xrEnumerateViewConfigurationViews( + instance, app.SystemId, viewportConfigType, viewCount, &viewCount, elements)); + + // Log the view config info for each view type for debugging purposes. + for (uint32_t e = 0; e < viewCount; e++) { + const XrViewConfigurationView* element = &elements[e]; + + ALOGV( + "Viewport [%d]: Recommended Width=%d Height=%d SampleCount=%d", + e, + element->recommendedImageRectWidth, + element->recommendedImageRectHeight, + element->recommendedSwapchainSampleCount); + + ALOGV( + "Viewport [%d]: Max Width=%d Height=%d SampleCount=%d", + e, + element->maxImageRectWidth, + element->maxImageRectHeight, + element->maxSwapchainSampleCount); + } + + // Cache the view config properties for the selected config type. + if (viewportConfigType == supportedViewConfigType) { + assert(viewCount == NUM_EYES); + for (uint32_t e = 0; e < viewCount; e++) { + app.ViewConfigurationView[e] = elements[e]; + } + } + + delete[] elements; + } else { + ALOGE("Empty viewport configuration type: %d", viewCount); + } + } + + delete[] viewportConfigurationTypes; + + // Get the viewport configuration info for the chosen viewport configuration type. + app.ViewportConfig.type = XR_TYPE_VIEW_CONFIGURATION_PROPERTIES; + + OXR(xrGetViewConfigurationProperties( + instance, app.SystemId, supportedViewConfigType, &app.ViewportConfig)); + + bool stageSupported = false; + + uint32_t numOutputSpaces = 0; + OXR(xrEnumerateReferenceSpaces(app.Session, 0, &numOutputSpaces, NULL)); + + auto referenceSpaces = new XrReferenceSpaceType[numOutputSpaces]; + + OXR(xrEnumerateReferenceSpaces( + app.Session, numOutputSpaces, &numOutputSpaces, referenceSpaces)); + + for (uint32_t i = 0; i < numOutputSpaces; i++) { + if (referenceSpaces[i] == XR_REFERENCE_SPACE_TYPE_STAGE) { + stageSupported = true; + break; + } + } + + delete[] referenceSpaces; + + // Create a space to the first path + XrReferenceSpaceCreateInfo spaceCreateInfo = {XR_TYPE_REFERENCE_SPACE_CREATE_INFO}; + spaceCreateInfo.referenceSpaceType = XR_REFERENCE_SPACE_TYPE_VIEW; + spaceCreateInfo.poseInReferenceSpace.orientation.w = 1.0f; + OXR(xrCreateReferenceSpace(app.Session, &spaceCreateInfo, &app.HeadSpace)); + + spaceCreateInfo.referenceSpaceType = XR_REFERENCE_SPACE_TYPE_LOCAL; + OXR(xrCreateReferenceSpace(app.Session, &spaceCreateInfo, &app.LocalSpace)); + + if (stageSupported) { + spaceCreateInfo.referenceSpaceType = XR_REFERENCE_SPACE_TYPE_STAGE; + spaceCreateInfo.poseInReferenceSpace.position.y = 0.0f; + OXR(xrCreateReferenceSpace(app.Session, &spaceCreateInfo, &app.StageSpace)); + ALOGV("Created stage space"); + } + + XrView projections[NUM_EYES]; + for (int eye = 0; eye < NUM_EYES; eye++) { + projections[eye] = XrView{XR_TYPE_VIEW}; + } + + GLenum format = GL_SRGB8_ALPHA8; + int width = app.ViewConfigurationView[0].recommendedImageRectWidth; + int height = app.ViewConfigurationView[0].recommendedImageRectHeight; + + XrSwapchainCreateInfo swapChainCreateInfo = {XR_TYPE_SWAPCHAIN_CREATE_INFO}; + swapChainCreateInfo.usageFlags = + XR_SWAPCHAIN_USAGE_SAMPLED_BIT | XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT; + swapChainCreateInfo.format = format; + swapChainCreateInfo.sampleCount = 1; + swapChainCreateInfo.width = width; + swapChainCreateInfo.height = height; + swapChainCreateInfo.faceCount = 1; + swapChainCreateInfo.arraySize = 2; + swapChainCreateInfo.mipCount = 1; + + // Create the swapchain. + OXR(xrCreateSwapchain(app.Session, &swapChainCreateInfo, &app.ColorSwapChain)); + OXR(xrEnumerateSwapchainImages(app.ColorSwapChain, 0, &app.SwapChainLength, nullptr)); +#if defined(XR_USE_GRAPHICS_API_OPENGL_ES) + auto images = new XrSwapchainImageOpenGLESKHR[app.SwapChainLength]; +#elif defined(XR_USE_GRAPHICS_API_OPENGL) + auto images = new XrSwapchainImageOpenGLKHR[app.SwapChainLength]; +#endif + // Populate the swapchain image array. + for (uint32_t i = 0; i < app.SwapChainLength; i++) { +#if defined(XR_USE_GRAPHICS_API_OPENGL_ES) + images[i] = {XR_TYPE_SWAPCHAIN_IMAGE_OPENGL_ES_KHR}; +#elif defined(XR_USE_GRAPHICS_API_OPENGL) + images[i] = {XR_TYPE_SWAPCHAIN_IMAGE_OPENGL_KHR}; +#endif + } + + OXR(xrEnumerateSwapchainImages( + app.ColorSwapChain, + app.SwapChainLength, + &app.SwapChainLength, + (XrSwapchainImageBaseHeader*)images)); + + auto colorTextures = new GLuint[app.SwapChainLength]; + for (uint32_t i = 0; i < app.SwapChainLength; i++) { + colorTextures[i] = GLuint(images[i].image); + } + + app.AppRenderer.Create( + format, width, height, NUM_MULTI_SAMPLES, app.SwapChainLength, colorTextures); + + delete[] images; + delete[] colorTextures; + +#if defined(ANDROID) + androidApp->userData = &app; + androidApp->onAppCmd = app_handle_cmd; +#endif // defined(ANDROID) + + bool stageBoundsDirty = true; + + SimpleXrInput* input = CreateSimpleXrInput(instance); + input->BeginSession(app.Session); + + /// Hook up extensions for passthrough + OXR(xrGetInstanceProcAddr( + instance, + "xrCreatePassthroughFB", + (PFN_xrVoidFunction*)(&app.FunPtrs.xrCreatePassthroughFB))); + OXR(xrGetInstanceProcAddr( + instance, + "xrDestroyPassthroughFB", + (PFN_xrVoidFunction*)(&app.FunPtrs.xrDestroyPassthroughFB))); + OXR(xrGetInstanceProcAddr( + instance, + "xrCreatePassthroughLayerFB", + (PFN_xrVoidFunction*)(&app.FunPtrs.xrCreatePassthroughLayerFB))); + OXR(xrGetInstanceProcAddr( + instance, + "xrDestroyPassthroughLayerFB", + (PFN_xrVoidFunction*)(&app.FunPtrs.xrDestroyPassthroughLayerFB))); + OXR(xrGetInstanceProcAddr( + instance, + "xrPassthroughLayerResumeFB", + (PFN_xrVoidFunction*)(&app.FunPtrs.xrPassthroughLayerResumeFB))); + OXR(xrGetInstanceProcAddr( + instance, + "xrPassthroughLayerPauseFB", + (PFN_xrVoidFunction*)(&app.FunPtrs.xrPassthroughLayerPauseFB))); + OXR(xrGetInstanceProcAddr( + instance, + "xrPassthroughLayerSetStyleFB", + (PFN_xrVoidFunction*)(&app.FunPtrs.xrPassthroughLayerSetStyleFB))); + OXR(xrGetInstanceProcAddr( + instance, + "xrPassthroughStartFB", + (PFN_xrVoidFunction*)(&app.FunPtrs.xrPassthroughStartFB))); + OXR(xrGetInstanceProcAddr( + instance, + "xrPassthroughPauseFB", + (PFN_xrVoidFunction*)(&app.FunPtrs.xrPassthroughPauseFB))); + + /// Hook up extensions for spatial entity + OXR(xrGetInstanceProcAddr( + instance, + "xrCreateSpatialAnchorFB", + (PFN_xrVoidFunction*)(&app.FunPtrs.xrCreateSpatialAnchorFB))); + OXR(xrGetInstanceProcAddr( + instance, "xrGetSpaceUuidFB", (PFN_xrVoidFunction*)(&app.FunPtrs.xrGetSpaceUuidFB))); + OXR(xrGetInstanceProcAddr( + instance, + "xrEnumerateSpaceSupportedComponentsFB", + (PFN_xrVoidFunction*)(&app.FunPtrs.xrEnumerateSpaceSupportedComponentsFB))); + OXR(xrGetInstanceProcAddr( + instance, + "xrGetSpaceComponentStatusFB", + (PFN_xrVoidFunction*)(&app.FunPtrs.xrGetSpaceComponentStatusFB))); + OXR(xrGetInstanceProcAddr( + instance, + "xrSetSpaceComponentStatusFB", + (PFN_xrVoidFunction*)(&app.FunPtrs.xrSetSpaceComponentStatusFB))); + OXR(xrGetInstanceProcAddr( + instance, "xrSaveSpacesMETA", (PFN_xrVoidFunction*)(&app.FunPtrs.xrSaveSpacesMETA))); + OXR(xrGetInstanceProcAddr( + instance, "xrEraseSpacesMETA", (PFN_xrVoidFunction*)(&app.FunPtrs.xrEraseSpacesMETA))); + OXR(xrGetInstanceProcAddr( + instance, "xrQuerySpacesFB", (PFN_xrVoidFunction*)(&app.FunPtrs.xrQuerySpacesFB))); + OXR(xrGetInstanceProcAddr( + instance, + "xrRetrieveSpaceQueryResultsFB", + (PFN_xrVoidFunction*)(&app.FunPtrs.xrRetrieveSpaceQueryResultsFB))); + OXR(xrGetInstanceProcAddr( + instance, + "xrDiscoverSpacesMETA", + (PFN_xrVoidFunction*)(&app.FunPtrs.xrDiscoverSpacesMETA))); + OXR(xrGetInstanceProcAddr( + instance, + "xrRetrieveSpaceDiscoveryResultsMETA", + (PFN_xrVoidFunction*)(&app.FunPtrs.xrRetrieveSpaceDiscoveryResultsMETA))); + OXR(xrGetInstanceProcAddr( + instance, "xrCreateSpaceUserFB", (PFN_xrVoidFunction*)(&app.FunPtrs.xrCreateSpaceUserFB))); + OXR(xrGetInstanceProcAddr( + instance, "xrGetSpaceUserIdFB", (PFN_xrVoidFunction*)(&app.FunPtrs.xrGetSpaceUserIdFB))); + OXR(xrGetInstanceProcAddr( + instance, + "xrDestroySpaceUserFB", + (PFN_xrVoidFunction*)(&app.FunPtrs.xrDestroySpaceUserFB))); + OXR(xrGetInstanceProcAddr( + instance, "xrShareSpacesFB", (PFN_xrVoidFunction*)(&app.FunPtrs.xrShareSpacesFB))); + + // Create and start passthrough + XrPassthroughFB passthrough = XR_NULL_HANDLE; + XrPassthroughLayerFB reconPassthroughLayer = XR_NULL_HANDLE; + { + XrPassthroughCreateInfoFB ptci = {XR_TYPE_PASSTHROUGH_CREATE_INFO_FB}; + XrResult result; + OXR(result = app.FunPtrs.xrCreatePassthroughFB(app.Session, &ptci, &passthrough)); + + if (XR_SUCCEEDED(result)) { + ALOGV("Creating passthrough layer"); + XrPassthroughLayerCreateInfoFB plci = {XR_TYPE_PASSTHROUGH_LAYER_CREATE_INFO_FB}; + plci.passthrough = passthrough; + plci.purpose = XR_PASSTHROUGH_LAYER_PURPOSE_RECONSTRUCTION_FB; + OXR(app.FunPtrs.xrCreatePassthroughLayerFB(app.Session, &plci, &reconPassthroughLayer)); + } + + if (XR_SUCCEEDED(result)) { + ALOGV("Setting passthrough style"); + XrPassthroughStyleFB style{XR_TYPE_PASSTHROUGH_STYLE_FB}; + OXR(app.FunPtrs.xrPassthroughLayerResumeFB(reconPassthroughLayer)); + style.textureOpacityFactor = 0.5f; + style.edgeColor = {0.0f, 0.0f, 0.0f, 0.0f}; + OXR(app.FunPtrs.xrPassthroughLayerSetStyleFB(reconPassthroughLayer, &style)); + } + + if (result != XR_ERROR_FEATURE_UNSUPPORTED) { + OXR(result = app.FunPtrs.xrPassthroughStartFB(passthrough)); + } + } + + // Initialize lists for Shared Spatial Anchors + if (!app.ExternalDataHandler->LoadShareUserList(app.ShareUserList)) { + ALOGW("Failed to load list of users to share Spatial Anchors with"); + app.ShareUserList.clear(); + } + if (!app.ExternalDataHandler->LoadInboundSpatialAnchorList(app.InboundSpatialAnchorList)) { + ALOGW("Failed to load list of Spatial Anchors shared with current user"); + app.InboundSpatialAnchorList.clear(); + } + + // Controller button states + bool aButtonVal = false; + bool aPrevButtonVal = false; + bool bButtonVal = false; + bool bPrevButtonVal = false; + bool xButtonVal = false; + bool xPrevButtonVal = false; + bool yButtonVal = false; + bool yPrevButtonVal = false; + +#if defined(XR_USE_PLATFORM_ANDROID) + while (androidApp->destroyRequested == 0) +#else + while (true) +#endif + { +#if defined(XR_USE_PLATFORM_ANDROID) + // Read all pending events. + for (;;) { + int events; + struct android_poll_source* source; + // If the timeout is zero, returns immediately without blocking. + // If the timeout is negative, waits indefinitely until an event appears. + const int timeoutMilliseconds = (app.Resumed == false && app.SessionActive == false && + androidApp->destroyRequested == 0) + ? -1 + : 0; + if (ALooper_pollAll(timeoutMilliseconds, NULL, &events, (void**)&source) < 0) { + break; + } + + // Process this event. + if (source != NULL) { + source->process(androidApp, source); + } + } +#elif defined(XR_USE_PLATFORM_WIN32) + MSG msg; + while (::PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) > 0) { + if (msg.message == WM_QUIT) { + app.ShouldExit = true; + } else { + ::TranslateMessage(&msg); + ::DispatchMessage(&msg); + } + } +#endif + + app.HandleXrEvents(); + + if (app.ShouldExit) { + break; + } + + if (app.SessionActive == false) { + continue; + } + + input->SyncActions(); + + // Create the scene if not yet created. + // The scene is created here to be able to show a loading icon. + if (!app.AppRenderer.Scene.IsCreated()) { + // Create the scene. + app.AppRenderer.Scene.Create(); + } + + if (stageBoundsDirty) { + UpdateStageBounds(app); + stageBoundsDirty = false; + } + + if (app.ShouldDiscoverAnchors) { + // This is called after the app starts, or after Button X is pressed. + app.AppRenderer.Scene.SpaceList.clear(); + DiscoverAnchors(app); + DownloadAnchors(app); + app.ShouldDiscoverAnchors = false; + } + + // NOTE: OpenXR does not use the concept of frame indices. Instead, + // XrWaitFrame returns the predicted display time. + XrFrameWaitInfo waitFrameInfo = {XR_TYPE_FRAME_WAIT_INFO}; + + XrFrameState frameState = {XR_TYPE_FRAME_STATE}; + + OXR(xrWaitFrame(app.Session, &waitFrameInfo, &frameState)); + + // Get the HMD pose, predicted for the middle of the time period during which + // the new eye images will be displayed. The number of frames predicted ahead + // depends on the pipeline depth of the engine and the synthesis rate. + // The better the prediction, the less black will be pulled in at the edges. + XrFrameBeginInfo beginFrameDesc = {XR_TYPE_FRAME_BEGIN_INFO}; + OXR(xrBeginFrame(app.Session, &beginFrameDesc)); + + XrSpaceLocation loc = {XR_TYPE_SPACE_LOCATION}; + OXR(xrLocateSpace(app.HeadSpace, app.LocalSpace, frameState.predictedDisplayTime, &loc)); + XrPosef xfLocalFromHead = loc.pose; + + XrViewState viewState = {XR_TYPE_VIEW_STATE, NULL}; + + XrViewLocateInfo projectionInfo = {XR_TYPE_VIEW_LOCATE_INFO}; + projectionInfo.viewConfigurationType = app.ViewportConfig.viewConfigurationType; + projectionInfo.displayTime = frameState.predictedDisplayTime; + projectionInfo.space = app.HeadSpace; + + uint32_t projectionCapacityInput = NUM_EYES; + uint32_t projectionCountOutput = projectionCapacityInput; + + OXR(xrLocateViews( + app.Session, + &projectionInfo, + &viewState, + projectionCapacityInput, + &projectionCountOutput, + projections)); + + auto& scene = app.AppRenderer.Scene; + scene.CubeData.clear(); + { + ovrCubeData persistedCube; + persistedCube.ColorScale *= 0.0f; + persistedCube.ColorBias = OVR::Vector4f(1, 0.5, 0, 1); // Orange + + for (XrSpace space : scene.SpaceList) { + // If anchor was placed, just update the anchor location + // Updating it regularly will prevent drift + XrSpaceLocation persistedAnchorLoc = {XR_TYPE_SPACE_LOCATION}; + XrResult res = XR_SUCCESS; + OXR(res = xrLocateSpace( + space, + app.LocalSpace, + frameState.predictedDisplayTime, + &persistedAnchorLoc)); + if (res == XR_SUCCESS) { + OVR::Posef localFromPersistedAnchor = FromXrPosef(persistedAnchorLoc.pose); + persistedCube.Model = OVR::Matrix4f(localFromPersistedAnchor); + persistedCube.Model *= OVR::Matrix4f::Scaling(0.01f, 0.01f, 0.05f); + scene.CubeData.push_back(persistedCube); + } else { + ALOGE("Failed getting anchor pose"); + } + } + } + + // A Button: Place a world locked anchor. + // B Button: Erase and Destroy all anchors in the scene. + // X Button: Refresh anchors by querying them. + // Y Button: Share all loaded anchors. + if (input != nullptr) { + aPrevButtonVal = aButtonVal; + aButtonVal = input->A(); + if (aPrevButtonVal != aButtonVal && aPrevButtonVal) { + PlaceAnchor(app, input, frameState); + } + + bPrevButtonVal = bButtonVal; + bButtonVal = input->B(); + if (bPrevButtonVal != bButtonVal && bPrevButtonVal) { + EraseAndDestroyAnchors(app); + } + + xPrevButtonVal = xButtonVal; + xButtonVal = input->X(); + if (xPrevButtonVal != xButtonVal && xPrevButtonVal) { + app.ShouldDiscoverAnchors = true; + } + + yPrevButtonVal = yButtonVal; + yButtonVal = input->Y(); + if (yPrevButtonVal != yButtonVal && yPrevButtonVal) { + SaveAnchorsAndTrySharing(app); + } + } + + ovrAppRenderer::FrameIn frameIn; + uint32_t chainIndex = 0; + XrSwapchainImageAcquireInfo acquireInfo = {XR_TYPE_SWAPCHAIN_IMAGE_ACQUIRE_INFO, NULL}; + OXR(xrAcquireSwapchainImage(app.ColorSwapChain, &acquireInfo, &chainIndex)); + frameIn.SwapChainIndex = int(chainIndex); + + XrPosef xfLocalFromEye[NUM_EYES]; + + for (int eye = 0; eye < NUM_EYES; eye++) { + // LOG_POSE( "viewTransform", &projectionInfo.projections[eye].viewTransform ); + XrPosef xfHeadFromEye = projections[eye].pose; + XrPosef_Multiply(&xfLocalFromEye[eye], &xfLocalFromHead, &xfHeadFromEye); + + XrPosef xfEyeFromLocal; + XrPosef_Invert(&xfEyeFromLocal, &xfLocalFromEye[eye]); + + XrMatrix4x4f viewMat{}; + XrMatrix4x4f_CreateFromRigidTransform(&viewMat, &xfEyeFromLocal); + + const XrFovf fov = projections[eye].fov; + XrMatrix4x4f projMat; + XrMatrix4x4f_CreateProjectionFov(&projMat, GRAPHICS_OPENGL_ES, fov, 0.1f, 0.0f); + + frameIn.View[eye] = OvrFromXr(viewMat); + frameIn.Proj[eye] = OvrFromXr(projMat); + } + + if (app.StageSpace != XR_NULL_HANDLE) { + loc = {XR_TYPE_SPACE_LOCATION}; + OXR(xrLocateSpace( + app.StageSpace, app.LocalSpace, frameState.predictedDisplayTime, &loc)); + XrPosef xfLocalFromStage = loc.pose; + + frameIn.HasStage = true; + frameIn.StagePose = OvrFromXr(xfLocalFromStage); + frameIn.StageScale = app.StageBounds; + } else { + frameIn.HasStage = false; + } + + XrSwapchainImageWaitInfo waitInfo = {XR_TYPE_SWAPCHAIN_IMAGE_WAIT_INFO}; + waitInfo.timeout = 1000000000; /* timeout in nanoseconds */ + XrResult res = xrWaitSwapchainImage(app.ColorSwapChain, &waitInfo); + int retry = 0; + while (res == XR_TIMEOUT_EXPIRED) { + res = xrWaitSwapchainImage(app.ColorSwapChain, &waitInfo); + retry++; + ALOGV( + " Retry xrWaitSwapchainImage %d times due to XR_TIMEOUT_EXPIRED (duration %f seconds)", + retry, + waitInfo.timeout * (1E-9)); + } + + app.AppRenderer.RenderFrame(frameIn); + + XrSwapchainImageReleaseInfo releaseInfo = {XR_TYPE_SWAPCHAIN_IMAGE_RELEASE_INFO, NULL}; + OXR(xrReleaseSwapchainImage(app.ColorSwapChain, &releaseInfo)); + + // Set-up the compositor layers for this frame. + // NOTE: Multiple independent layers are allowed, but they need to be added + // in a depth consistent order. + + XrCompositionLayerProjectionView proj_views[2] = {}; + + app.LayerCount = 0; + memset(app.Layers, 0, sizeof(ovrCompositorLayer_Union) * ovrMaxLayerCount); + + // passthrough layer is backmost layer (if available) + if (reconPassthroughLayer != XR_NULL_HANDLE) { + XrCompositionLayerPassthroughFB passthrough_layer = { + XR_TYPE_COMPOSITION_LAYER_PASSTHROUGH_FB}; + passthrough_layer.layerHandle = reconPassthroughLayer; + passthrough_layer.flags = XR_COMPOSITION_LAYER_BLEND_TEXTURE_SOURCE_ALPHA_BIT; + passthrough_layer.space = XR_NULL_HANDLE; + app.Layers[app.LayerCount++].Passthrough = passthrough_layer; + } + + XrCompositionLayerProjection proj_layer = {XR_TYPE_COMPOSITION_LAYER_PROJECTION}; + proj_layer.layerFlags = XR_COMPOSITION_LAYER_BLEND_TEXTURE_SOURCE_ALPHA_BIT; + proj_layer.layerFlags |= XR_COMPOSITION_LAYER_CORRECT_CHROMATIC_ABERRATION_BIT; + proj_layer.space = app.LocalSpace; + proj_layer.viewCount = NUM_EYES; + proj_layer.views = proj_views; + + for (int eye = 0; eye < NUM_EYES; eye++) { + XrCompositionLayerProjectionView& proj_view = proj_views[eye]; + proj_view = {XR_TYPE_COMPOSITION_LAYER_PROJECTION_VIEW}; + proj_view.pose = xfLocalFromEye[eye]; + proj_view.fov = projections[eye].fov; + + proj_view.subImage.swapchain = app.ColorSwapChain; + proj_view.subImage.imageRect.offset.x = 0; + proj_view.subImage.imageRect.offset.y = 0; + proj_view.subImage.imageRect.extent.width = width; + proj_view.subImage.imageRect.extent.height = height; + proj_view.subImage.imageArrayIndex = eye; + } + + app.Layers[app.LayerCount++].Projection = proj_layer; + + // Compose the layers for this frame. + const XrCompositionLayerBaseHeader* layers[ovrMaxLayerCount] = {}; + for (int i = 0; i < app.LayerCount; i++) { + layers[i] = (const XrCompositionLayerBaseHeader*)&app.Layers[i]; + } + + XrFrameEndInfo endFrameInfo = {XR_TYPE_FRAME_END_INFO}; + endFrameInfo.displayTime = frameState.predictedDisplayTime; + endFrameInfo.environmentBlendMode = XR_ENVIRONMENT_BLEND_MODE_OPAQUE; + endFrameInfo.layerCount = app.LayerCount; + endFrameInfo.layers = layers; + + OXR(xrEndFrame(app.Session, &endFrameInfo)); + } + + input->EndSession(); + + delete input; + + app.AppRenderer.Destroy(); + + OXR(xrDestroySwapchain(app.ColorSwapChain)); + OXR(xrDestroySpace(app.HeadSpace)); + OXR(xrDestroySpace(app.LocalSpace)); + // StageSpace is optional. + if (app.StageSpace != XR_NULL_HANDLE) { + OXR(xrDestroySpace(app.StageSpace)); + } + OXR(xrDestroySession(app.Session)); + + app.Egl.DestroyContext(); + + OXR(xrDestroyInstance(instance)); + +#if defined(XR_USE_PLATFORM_ANDROID) + (*androidApp->activity->vm).DetachCurrentThread(); +#endif // defined(XR_USE_PLATFORM_ANDROID) +} diff --git a/Samples/XrSamples/XrSpatialAnchor/Src/SpatialAnchorXr.h b/Samples/XrSamples/XrSpatialAnchor/Src/SpatialAnchorXr.h new file mode 100755 index 0000000..39ca735 --- /dev/null +++ b/Samples/XrSamples/XrSpatialAnchor/Src/SpatialAnchorXr.h @@ -0,0 +1,21 @@ +#pragma once + +#if defined(ANDROID) +#include +#include +#include +#include + +#define XR_USE_GRAPHICS_API_OPENGL_ES 1 +#define XR_USE_PLATFORM_ANDROID 1 +#elif defined(WIN32) +#include "Render/GlWrapperWin32.h" + +#include +#define XR_USE_GRAPHICS_API_OPENGL 1 +#define XR_USE_PLATFORM_WIN32 1 +#endif + +#include +#include +#include diff --git a/Samples/XrSamples/XrSpatialAnchor/assets/donotedelete.txt b/Samples/XrSamples/XrSpatialAnchor/assets/donotedelete.txt new file mode 100644 index 0000000..e69de29 diff --git a/Samples/XrSamples/XrSpatialAnchor/java/com/oculus/NativeActivity.java b/Samples/XrSamples/XrSpatialAnchor/java/com/oculus/NativeActivity.java new file mode 100644 index 0000000..2591ae9 --- /dev/null +++ b/Samples/XrSamples/XrSpatialAnchor/java/com/oculus/NativeActivity.java @@ -0,0 +1,28 @@ +// Copyright (c) Facebook Technologies, LLC and its affiliates. All Rights reserved. +package com.oculus; + +/** + * When using NativeActivity, we currently need to handle loading of dependent shared libraries + * manually before a shared library that depends on them is loaded, since there is not currently a + * way to specify a shared library dependency for NativeActivity via the manifest meta-data. + * + *

The simplest method for doing so is to subclass NativeActivity with an empty activity that + * calls System.loadLibrary on the dependent libraries, which is unfortunate when the goal is to + * write a pure native C/C++ only Android activity. + * + *

A native-code only solution is to load the dependent libraries dynamically using dlopen(). + * However, there are a few considerations, see: + * https://groups.google.com/forum/#!msg/android-ndk/l2E2qh17Q6I/wj6s_6HSjaYJ + * + *

1. Only call dlopen() if you're sure it will succeed as the bionic dynamic linker will + * remember if dlopen failed and will not re-try a dlopen on the same lib a second time. + * + *

2. Must remember what libraries have already been loaded to avoid infinitely looping when + * libraries have circular dependencies. + */ +public class NativeActivity extends android.app.NativeActivity { + static { + System.loadLibrary("openxr_loader"); + System.loadLibrary("spatialanchor"); + } +} diff --git a/Samples/XrSamples/XrVirtualKeyboard/CMakeLists.txt b/Samples/XrSamples/XrVirtualKeyboard/CMakeLists.txt new file mode 100755 index 0000000..35669ee --- /dev/null +++ b/Samples/XrSamples/XrVirtualKeyboard/CMakeLists.txt @@ -0,0 +1,43 @@ +# Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved. + +project(xrvirtualkeyboard) + +if(NOT TARGET OpenXR::openxr_loader) + find_package(OpenXR REQUIRED) +endif() + +file(GLOB_RECURSE SRC_FILES + Src/*.c + Src/*.cpp +) + +if(ANDROID) + add_library(${PROJECT_NAME} MODULE ${SRC_FILES}) + target_include_directories(${PROJECT_NAME} PUBLIC ${ANDROID_NDK}/sources/android/native_app_glue) + target_link_libraries(${PROJECT_NAME} PRIVATE + android + EGL + GLESv3 + log + ktx + ) + set_target_properties(${PROJECT_NAME} PROPERTIES LINK_FLAGS "-u ANativeActivity_onCreate") +elseif(WIN32) + add_definitions(-D_USE_MATH_DEFINES) + add_executable(${PROJECT_NAME} ${SRC_FILES}) + add_custom_command(TARGET ${PROJECT_NAME} PRE_BUILD + COMMAND "${CMAKE_COMMAND}" -E copy_directory + "${CMAKE_CURRENT_LIST_DIR}/assets" + "$/assets" + VERBATIM) + + add_custom_command(TARGET ${PROJECT_NAME} PRE_BUILD + COMMAND "${CMAKE_COMMAND}" -E copy_directory + "${CMAKE_SOURCE_DIR}/SampleXrFramework/res/raw" + "$/font/res/raw" + VERBATIM) +endif() + +# Common across platforms +target_include_directories(${PROJECT_NAME} PRIVATE Src) +target_link_libraries(${PROJECT_NAME} PRIVATE samplexrframework) diff --git a/Samples/XrSamples/XrVirtualKeyboard/Projects/Android/AndroidManifest.xml b/Samples/XrSamples/XrVirtualKeyboard/Projects/Android/AndroidManifest.xml new file mode 100755 index 0000000..12ecd47 --- /dev/null +++ b/Samples/XrSamples/XrVirtualKeyboard/Projects/Android/AndroidManifest.xml @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Samples/XrSamples/XrVirtualKeyboard/Projects/Android/build.bat b/Samples/XrSamples/XrVirtualKeyboard/Projects/Android/build.bat new file mode 100755 index 0000000..facf79f --- /dev/null +++ b/Samples/XrSamples/XrVirtualKeyboard/Projects/Android/build.bat @@ -0,0 +1,29 @@ +@rem Only edit the master copy of this file in SDK_ROOT/bin/scripts/build/perproject + +@setlocal enableextensions enabledelayedexpansion + +@if not exist "build.gradle" @echo Build script must be executed from project directory. & goto :Abort + +@set P=.. + +:TryAgain + +@rem @echo P = %P% + +@if exist "%P%\bin\scripts\build\build.py.bat" goto :Found + +@if exist "%P%\bin\scripts\build" @echo "Could not find build.py.bat" & goto :Abort + +@set P=%P%\.. + +@goto :TryAgain + +:Found + +@set P=%P%\bin\scripts\build +@call %P%\build.py.bat %1 %2 %3 %4 %5 +@goto :End + +:Abort + +:End diff --git a/Samples/XrSamples/XrVirtualKeyboard/Projects/Android/build.gradle b/Samples/XrSamples/XrVirtualKeyboard/Projects/Android/build.gradle new file mode 100755 index 0000000..f493a03 --- /dev/null +++ b/Samples/XrSamples/XrVirtualKeyboard/Projects/Android/build.gradle @@ -0,0 +1,78 @@ +// Top-level build file where you can add configuration options common to all sub-projects/modules. +buildscript { + repositories { + google() + mavenCentral() + } + dependencies { + classpath "com.android.tools.build:gradle:7.0.3" + } +} + +repositories { + google() + mavenCentral() +} + +apply plugin: 'com.android.application' + +android { + compileSdk 32 + + defaultConfig { + applicationId "com.oculus.sdk.xrvirtualkeyboard" + minSdk 26 + targetSdk 32 + versionCode 1 + versionName "1.0" + + // override app plugin abiFilters for 64-bit support + externalNativeBuild { + ndk { + abiFilters 'arm64-v8a' + } + ndkBuild { + abiFilters 'arm64-v8a' + } + cmake { + targets "xrvirtualkeyboard" + } + } + } + + sourceSets { + main { + manifest.srcFile 'AndroidManifest.xml' + java.srcDirs = ['../../java'] + assets.srcDirs = ['../../assets'] + res.srcDirs = ['../../res'] + } + } + + buildTypes { + debug { + debuggable true + } + + release { + debuggable false + } + } + + externalNativeBuild { + cmake { + path file('../../../../CMakeLists.txt') + } + } + + // Enable prefab support for the OpenXR AAR + buildFeatures { + prefab true + } +} + +dependencies { + // Package/application AndroidManifest.xml properties, plus headers and libraries + // exposed to CMake + implementation 'org.khronos.openxr:openxr_loader_for_android:1.1.36' +} diff --git a/Samples/XrSamples/XrVirtualKeyboard/Projects/Android/build.py b/Samples/XrSamples/XrVirtualKeyboard/Projects/Android/build.py new file mode 100755 index 0000000..d4b6e58 --- /dev/null +++ b/Samples/XrSamples/XrVirtualKeyboard/Projects/Android/build.py @@ -0,0 +1,35 @@ +#!/usr/bin/python +# (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +# This first bit of code is common bootstrapping code +# to determine the SDK root, and to set up the import +# path for additional python code. + +# begin bootstrap +import os +import sys + + +def init(): + root = os.path.realpath(os.path.dirname(os.path.realpath(__file__))) + os.chdir(root) # make sure we are always executing from the project directory + while os.path.isdir(os.path.join(root, "bin/scripts/build")) == False: + root = os.path.realpath(os.path.join(root, "..")) + if ( + len(root) <= 5 + ): # Should catch both Posix and Windows root directories (e.g. '/' and 'C:\') + print("Unable to find SDK root. Exiting.") + sys.exit(1) + root = os.path.abspath(root) + os.environ["OCULUS_SDK_PATH"] = root + sys.path.append(root + "/bin/scripts/build") + + +init() +import ovrbuild + +ovrbuild.init() +# end bootstrap + + +ovrbuild.build() diff --git a/Samples/XrSamples/XrVirtualKeyboard/Projects/Android/gradle.properties b/Samples/XrSamples/XrVirtualKeyboard/Projects/Android/gradle.properties new file mode 100644 index 0000000..3e927b1 --- /dev/null +++ b/Samples/XrSamples/XrVirtualKeyboard/Projects/Android/gradle.properties @@ -0,0 +1,21 @@ +# Project-wide Gradle settings. +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. More details, visit +# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects +# org.gradle.parallel=true +# AndroidX package structure to make it clearer which packages are bundled with the +# Android operating system, and which are packaged with your app's APK +# https://developer.android.com/topic/libraries/support-library/androidx-rn +android.useAndroidX=true +# Enables namespacing of each library's R class so that its R class includes only the +# resources declared in the library itself and none from the library's dependencies, +# thereby reducing the size of the R class for that library +android.nonTransitiveRClass=true \ No newline at end of file diff --git a/Samples/XrSamples/XrVirtualKeyboard/Projects/Android/gradle/wrapper/gradle-wrapper.jar b/Samples/XrSamples/XrVirtualKeyboard/Projects/Android/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..7454180 Binary files /dev/null and b/Samples/XrSamples/XrVirtualKeyboard/Projects/Android/gradle/wrapper/gradle-wrapper.jar differ diff --git a/Samples/XrSamples/XrVirtualKeyboard/Projects/Android/gradle/wrapper/gradle-wrapper.properties b/Samples/XrSamples/XrVirtualKeyboard/Projects/Android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..ffed3a2 --- /dev/null +++ b/Samples/XrSamples/XrVirtualKeyboard/Projects/Android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/Samples/XrSamples/XrVirtualKeyboard/Projects/Android/gradlew b/Samples/XrSamples/XrVirtualKeyboard/Projects/Android/gradlew new file mode 100755 index 0000000..a69d9cb --- /dev/null +++ b/Samples/XrSamples/XrVirtualKeyboard/Projects/Android/gradlew @@ -0,0 +1,240 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# 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 +# +# https://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. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit + +APP_NAME="Gradle" +APP_BASE_NAME=${0##*/} + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/Samples/XrSamples/XrVirtualKeyboard/Projects/Android/gradlew.bat b/Samples/XrSamples/XrVirtualKeyboard/Projects/Android/gradlew.bat new file mode 100755 index 0000000..f127cfd --- /dev/null +++ b/Samples/XrSamples/XrVirtualKeyboard/Projects/Android/gradlew.bat @@ -0,0 +1,91 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/Samples/XrSamples/XrVirtualKeyboard/Projects/Android/settings.gradle b/Samples/XrSamples/XrVirtualKeyboard/Projects/Android/settings.gradle new file mode 100755 index 0000000..8203cd9 --- /dev/null +++ b/Samples/XrSamples/XrVirtualKeyboard/Projects/Android/settings.gradle @@ -0,0 +1 @@ +rootProject.name = "XrVirtualKeyboard" diff --git a/Samples/XrSamples/XrVirtualKeyboard/README.md b/Samples/XrSamples/XrVirtualKeyboard/README.md new file mode 100644 index 0000000..6569d68 --- /dev/null +++ b/Samples/XrSamples/XrVirtualKeyboard/README.md @@ -0,0 +1,56 @@ +# Virtual Keyboard Sample Project + +## Overview + +Virtual Keyboard enables VR developers to easily integrate a best-in-class keyboard into their applications and provides users with a consistent typing experience across Meta Quest VR applications. The API is designed so that features and improvements can be made to the keyboard through OS updates, without any modifications by the developer to leverage the latest features. + +The keyboard supports multiple input modes including far raycast based input, direct touch input, and swipe typing for both modalities. + +While developers have full control over the position and scale of the keyboard, the extension provides default sizes and positions that have been fine-tuned for usability based on user research. + +There are several components that you must set up to provide users with a rich text input experience. These components are: + +* Virtual Keyboard +* Render Model +* Hands (optional) + +The **Virtual Keyboard** is the main component that manages a keyboard in virtual space. This component allows the user to position and locate the keyboard, processes user interaction input, and provides visual and state updates back to the application. + +The **Render Model** component provides the actual keyboard model to be rendered in virtual space. Note that while the application is running, new animation and texture data may be sent by the Virtual Keyboard runtime to reflect the current state of the keyboard model. + +Though optional, we recommend enabling the **Hands** component as it assists in tracking and displaying hand models in virtual space that correspond to the user's actual hands, providing a more natural near field typing experience. + +For the complete OpenXR API specification for Virtual Keyboard, please refer to [XR_META_virtual_keyboard](https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#XR_META_virtual_keyboard). + +Additional information available on [Oculus Developers](https://developer.oculus.com/documentation/native/android/mobile-openxr-virtual-keyboard-overview/). + +## Sample Notes + +![Virtual Keyboard Native Sample](images/virtual_keyboard_native_sample.jpg) + +The sample project showcases different features of Virtual Keyboard including: + +* Hands and Controllers interaction +* Near and Far field input (via raycast and touch) +* Keyboard positioning and scaling (via analog stick) +* Swipe typing and typeahead suggestions +* Touch limiting for improved feedback + +![Virtual Keyboard Swipe Typing](images/virtual_keyboard_native_swipe.jpg) + +Here is an overview of the contents of the sample: + +#### main.cpp +The main entry point of the sample application. It contains the full definition of `XrVirtualKeyboardApp`, which derives from `OVRFW::XrApp` and implements all the app lifecycle methods. It also controls and manages the sample UI. + +#### XrVirtualKeyboardHelper.h/cpp +Helper class for accessing the `XR_META_virtual_keyboard` extension API. It holds the handles to a Virtual Keyboard instance and the corresponding `XrSpace`. + +#### XrRenderModelHelper.h/cpp +Helper class for accessing the `XR_FB_render_model` extension API. It manages the render model properties and provides functions to query the render model key and data. + +#### XrHandHelper.h +Helper class for accessing the hand tracking extensions. It provides functionalities to using hands as input and manages the hand render model. + +#### VirtualKeyboardModelRenderer.h/cpp +Helper class for rendering the Virtual Keyboard model. It keeps references to the dynamic textures and the loaded glTF model so we can apply the texture and animation updates returned by the runtime. diff --git a/Samples/XrSamples/XrVirtualKeyboard/Src/VirtualKeyboardModelRenderer.cpp b/Samples/XrSamples/XrVirtualKeyboard/Src/VirtualKeyboardModelRenderer.cpp new file mode 100755 index 0000000..492ba38 --- /dev/null +++ b/Samples/XrSamples/XrVirtualKeyboard/Src/VirtualKeyboardModelRenderer.cpp @@ -0,0 +1,368 @@ +/************************************************************************************************ +Filename : VirtualKeyboardModelRenderer.cpp +Content : Helper class for rendering the virtual keyboard model +Created : January 2023 +Authors : Peter Chan +Language : C++ +Copyright : Copyright (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. +************************************************************************************************/ + +#include "VirtualKeyboardModelRenderer.h" + +#include +#include +#include +#include +#include + +namespace { + +/// clang-format off +const char* kVertexShaderSrc = R"glsl( +attribute highp vec4 Position; +attribute highp vec2 TexCoord; + +varying lowp vec2 oTexCoord; + +void main() +{ + gl_Position = TransformVertex( Position ); + oTexCoord = TexCoord; +} +)glsl"; + +const char* kFragmentShaderSrc = R"glsl( +precision lowp float; + +uniform sampler2D Texture0; +uniform lowp vec4 BaseColorFactor; + +varying lowp vec2 oTexCoord; + +void main() +{ + lowp vec4 diffuse = texture2D( Texture0, oTexCoord ); + lowp vec3 finalColor = diffuse.xyz * BaseColorFactor.xyz; + + // apply alpha + gl_FragColor.w = diffuse.w; + // premult + gamma correction + gl_FragColor.xyz = pow(finalColor.rgb, vec3(2.2)) * gl_FragColor.w; +} +)glsl"; +/// clang-format on + +bool ParseImageUri( + const std::string& uri, + uint64_t& textureId, + uint32_t& pixelWidth, + uint32_t& pixelHeight) { + // URI format: + // metaVirtualKeyboard://texture/{textureID}?w={width}&h={height}&fmt=RGBA32 + + auto getToken = [&uri](size_t startIdx, char delimiter, std::string& token) { + const size_t endIdx = uri.find_first_of(delimiter, startIdx); + if (endIdx == std::string::npos) { + return false; + } + token = uri.substr(startIdx, endIdx - startIdx); + return true; + }; + + // Validate scheme + std::string token; + size_t index = 0; + if (!getToken(index, ':', token) || token != "metaVirtualKeyboard") { + return false; + } + + // Validate resource type + index += token.size() + 3; // skip "://" + if (!getToken(index, '/', token) || token != "texture") { + return false; + } + + // Get texture id + index += token.size() + 1; // skip "/" + if (!getToken(index, '?', token)) { + return false; + } + textureId = std::stoull(token); + + // Get pixel width + index += token.size() + 3; // skip "?w=" + if (!getToken(index, '&', token)) { + return false; + } + pixelWidth = std::stoul(token); + + // Get pixel height + index += token.size() + 3; // skip "&h=" + if (!getToken(index, '&', token)) { + return false; + } + pixelHeight = std::stoul(token); + + // Validate format + index += token.size(); + if (uri.substr(index) != "&fmt=RGBA32") { + return false; + } + + return true; +} + +OVRFW::GlTexture CreateGlTexture(uint32_t pixelWidth, uint32_t pixelHeight) { + GLuint texId; + glGenTextures(1, &texId); + glBindTexture(GL_TEXTURE_2D, texId); + glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, pixelWidth, pixelHeight); + std::vector blankBytes((size_t)pixelWidth * pixelHeight * 4); + glTexSubImage2D( + GL_TEXTURE_2D, + 0, + 0, + 0, + pixelWidth, + pixelHeight, + GL_RGBA, + GL_UNSIGNED_BYTE, + blankBytes.data()); + return OVRFW::GlTexture(texId, GL_TEXTURE_2D, pixelWidth, pixelHeight); +} + +void UpdateGlTexture(OVRFW::GlTexture texture, const uint8_t* textureData) { + glBindTexture(GL_TEXTURE_2D, texture.texture); + glTexSubImage2D( + GL_TEXTURE_2D, + 0, + 0, + 0, + texture.Width, + texture.Height, + GL_RGBA, + GL_UNSIGNED_BYTE, + textureData); +} + +const OVRFW::ModelNode* FindCollisionNode(const OVRFW::ModelFile& model) { + auto iter = + std::find_if(model.Nodes.begin(), model.Nodes.end(), [](const auto& node) { + return node.name == "collision"; + }); + if (iter == model.Nodes.end()) { + return nullptr; + } + const OVRFW::ModelNode* collisionNode = &(*iter); + return collisionNode; +} + +} // namespace + +bool VirtualKeyboardModelRenderer::Init(const std::vector& modelBuffer) { + if (modelBuffer.empty()) { + return false; + } + + OVRFW::ovrProgramParm uniformParms[] = { + {"Texture0", OVRFW::ovrProgramParmType::TEXTURE_SAMPLED}, + {"BaseColorFactor", OVRFW::ovrProgramParmType::FLOAT_VECTOR4}, + }; + progKeyboard_ = OVRFW::GlProgram::Build( + "", + kVertexShaderSrc, + "", + kFragmentShaderSrc, + uniformParms, + sizeof(uniformParms) / sizeof(OVRFW::ovrProgramParm)); + + OVRFW::MaterialParms materials = {}; + materials.ImageUriHandler = [this](OVRFW::ModelFile& modelFile, const std::string& uri) { + uint64_t textureId; + uint32_t pixelWidth; + uint32_t pixelHeight; + if (!ParseImageUri(uri, textureId, pixelWidth, pixelHeight)) { + return false; + } + + // Add texture to our model + OVRFW::ModelTexture tex; + tex.name = std::to_string(textureId); + tex.texid = CreateGlTexture(pixelWidth, pixelHeight); + modelFile.Textures.push_back(tex); + + // Register texture + textureIdMap_[textureId] = tex.texid; + ALOG("Registered texture %d, %ux%u", (int)textureId, pixelWidth, pixelHeight); + return true; + }; + OVRFW::ModelGlPrograms programs = {}; + programs.ProgSingleTexture = &progKeyboard_; + programs.ProgBaseColorPBR = &progKeyboard_; + programs.ProgSkinnedBaseColorPBR = &progKeyboard_; + programs.ProgLightMapped = &progKeyboard_; + programs.ProgBaseColorEmissivePBR = &progKeyboard_; + programs.ProgSkinnedBaseColorEmissivePBR = &progKeyboard_; + programs.ProgSimplePBR = &progKeyboard_; + programs.ProgSkinnedSimplePBR = &progKeyboard_; + + keyboardModel_ = std::unique_ptr(LoadModelFile_glB( + "keyboard", (const char*)modelBuffer.data(), modelBuffer.size(), programs, materials)); + if (keyboardModel_ == nullptr) { + return false; + } + + collisionNode_ = FindCollisionNode(*keyboardModel_); + + keyboardModelState_ = std::make_unique(); + keyboardModelState_->GenerateStateFromModelFile(keyboardModel_.get()); + + for (const auto& nodeState : keyboardModelState_->nodeStates) { + OVRFW::Model* model = nodeState.node->model; + if (model != nullptr) { + auto& gc = model->surfaces[0].surfaceDef.graphicsCommand; + gc.UniformData[0].Data = &gc.Textures[0]; + gc.UniformData[1].Data = (void*)&model->surfaces[0].material->baseColorFactor; + gc.GpuState.depthEnable = gc.GpuState.depthMaskEnable = true; + gc.GpuState.blendEnable = OVRFW::ovrGpuState::BLEND_ENABLE; + gc.GpuState.blendMode = GL_FUNC_ADD; + gc.GpuState.blendSrc = GL_ONE; + gc.GpuState.blendDst = GL_ONE_MINUS_SRC_ALPHA; + } + } + + return true; +} + +void VirtualKeyboardModelRenderer::Shutdown() { + OVRFW::GlProgram::Free(progKeyboard_); + keyboardModelState_.reset(); + keyboardModel_.reset(); + textureIdMap_.clear(); + collisionNode_ = nullptr; +} + +void VirtualKeyboardModelRenderer::Update(const OVR::Posef& pose, const OVR::Vector3f& scale) { + transform_ = OVR::Matrix4f(pose) * OVR::Matrix4f::Scaling(scale); +} + +void VirtualKeyboardModelRenderer::Render(std::vector& surfaceList) { + if (keyboardModel_ != nullptr) { + for (const auto& nodeState : keyboardModelState_->nodeStates) { + const OVRFW::Model* model = nodeState.node->model; + if (model != nullptr) { + OVRFW::ovrDrawSurface surface; + surface.surface = &(model->surfaces[0].surfaceDef); + surface.modelMatrix = transform_ * nodeState.GetGlobalTransform(); + surfaceList.push_back(surface); + } + } + } +} + +void VirtualKeyboardModelRenderer::UpdateTexture( + uint64_t textureId, + const uint8_t* textureData, + uint32_t textureWidth, + uint32_t textureHeight) { + auto iter = textureIdMap_.find(textureId); + if (iter == textureIdMap_.end()) { + ALOGE("Failed to update render model texture. Texture id %d not found.", (int)textureId); + return; + } + auto& modelTexture = iter->second; + if (modelTexture.Width != static_cast(textureWidth) || + modelTexture.Height != static_cast(textureHeight)) { + ALOGE("Invalid render model texture dimensions for id %d.", (int)textureId); + return; + } + + UpdateGlTexture(modelTexture, textureData); +} + +void VirtualKeyboardModelRenderer::SetAnimationState(int animationIndex, float fraction) { + if (animationIndex < 0 || + animationIndex >= static_cast(keyboardModel_->Animations.size())) { + ALOGE( + "Invalid animation index %i, count = %zu", + animationIndex, + keyboardModel_->Animations.size()); + return; + } + + const float timeInSeconds = + (keyboardModel_->animationEndTime - keyboardModel_->animationStartTime) * + std::clamp(fraction, 0.0f, 1.0f); + + const OVRFW::ModelAnimation& animation = keyboardModel_->Animations[animationIndex]; + for (const OVRFW::ModelAnimationChannel& channel : animation.channels) { + OVRFW::ModelAnimationTimeLineState& timeLineState = + keyboardModelState_->animationTimelineStates[channel.sampler->timeLineIndex]; + timeLineState.CalculateFrameAndFraction(timeInSeconds); + } + + ApplyAnimation(*keyboardModelState_, animationIndex); + + for (const OVRFW::ModelAnimationChannel& channel : animation.channels) { + OVRFW::ModelNodeState& nodeState = keyboardModelState_->nodeStates[channel.nodeIndex]; + nodeState.RecalculateMatrix(); + + // If animation controls weights, cache the node index so we can update the surface geo once + // all the weights are applied + if (channel.path == OVRFW::MODEL_ANIMATION_PATH_WEIGHTS) { + dirtyGeoNodeIndices_.push_back(channel.nodeIndex); + } + } +} + +void VirtualKeyboardModelRenderer::UpdateSurfaceGeo() { + for (auto nodeIndex : dirtyGeoNodeIndices_) { + OVRFW::ModelNodeState& nodeState = keyboardModelState_->nodeStates[nodeIndex]; + std::vector& surfaces = nodeState.node->model->surfaces; + for (auto& surface : surfaces) { + OVRFW::VertexAttribs attribs = surface.attribs; // copy base values + for (int w = 0; w < static_cast(nodeState.weights.size()); ++w) { + const auto& targetAttribs = surface.targets[w]; + if (!targetAttribs.position.empty()) { + float* original = (float*)attribs.position.data(); + float* target = (float*)targetAttribs.position.data(); + int posIndex = (w % 2) + (w / 2 * 3); // skip z + original[posIndex] += target[posIndex] * nodeState.weights[w]; + } + if (!targetAttribs.uv0.empty()) { + float* original = (float*)attribs.uv0.data(); + float* target = (float*)targetAttribs.uv0.data(); + int uvIndex = w - 8; // 4 pairs of (x,y) for positions + original[uvIndex] += target[uvIndex] * nodeState.weights[w]; + } + } + surface.surfaceDef.geo.Update(attribs, false); + } + } + dirtyGeoNodeIndices_.clear(); +} + +bool VirtualKeyboardModelRenderer::IsModelLoaded() const { + return keyboardModel_ != nullptr; +} + +bool VirtualKeyboardModelRenderer::IsPointNearKeyboard(const OVR::Vector3f& globalPoint) const { + if (!IsModelLoaded() || collisionNode_ == nullptr) { + return false; + } + auto localPoint = + (transform_ * collisionNode_->GetGlobalTransform()).Inverted().Transform(globalPoint); + auto bounds = GetCollisionBounds(); + // Expand in front and behind the keyboard + bounds.AddPoint(bounds.GetCenter() + OVR::Vector3f(0.0f, 0.0f, -0.25f)); + bounds.AddPoint(bounds.GetCenter() + OVR::Vector3f(0.0f, 0.0f, 0.4f)); + return bounds.Contains(localPoint); +} + +OVR::Bounds3f VirtualKeyboardModelRenderer::GetCollisionBounds() const { + OVR::Bounds3f bounds; + if (collisionNode_ != nullptr) { + bounds = OVR::Bounds3f(collisionNode_->model->surfaces[0].surfaceDef.geo.localBounds); + } + return bounds; +} diff --git a/Samples/XrSamples/XrVirtualKeyboard/Src/VirtualKeyboardModelRenderer.h b/Samples/XrSamples/XrVirtualKeyboard/Src/VirtualKeyboardModelRenderer.h new file mode 100755 index 0000000..60f500f --- /dev/null +++ b/Samples/XrSamples/XrVirtualKeyboard/Src/VirtualKeyboardModelRenderer.h @@ -0,0 +1,52 @@ +/************************************************************************************************ +Filename : VirtualKeyboardModelRenderer.h +Content : Helper class for rendering the virtual keyboard model +Created : January 2023 +Authors : Peter Chan +Language : C++ +Copyright : Copyright (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. +************************************************************************************************/ + +#pragma once + +#include +#include +#include +#include + +#include + +#include +#include + +class VirtualKeyboardModelRenderer { + public: + bool Init(const std::vector& modelBuffer); + void Shutdown(); + + void Update(const OVR::Posef& pose, const OVR::Vector3f& scale); + void Render(std::vector& surfaceList); + + void UpdateTexture( + uint64_t textureId, + const uint8_t* textureData, + uint32_t textureWidth, + uint32_t textureHeight); + void SetAnimationState(int animationIndex, float fraction); + void UpdateSurfaceGeo(); + + bool IsModelLoaded() const; + bool IsPointNearKeyboard(const OVR::Vector3f& globalPoint) const; + OVR::Bounds3f GetCollisionBounds() const; + + private: + std::unique_ptr keyboardModel_; + std::unique_ptr keyboardModelState_; + + OVRFW::GlProgram progKeyboard_; + OVR::Matrix4f transform_; + const OVRFW::ModelNode* collisionNode_ = nullptr; + + std::map textureIdMap_; + std::vector dirtyGeoNodeIndices_; +}; diff --git a/Samples/XrSamples/XrVirtualKeyboard/Src/XrHandHelper.h b/Samples/XrSamples/XrVirtualKeyboard/Src/XrHandHelper.h new file mode 100755 index 0000000..163fe3d --- /dev/null +++ b/Samples/XrSamples/XrVirtualKeyboard/Src/XrHandHelper.h @@ -0,0 +1,233 @@ +/************************************************************************************************ +Filename : XrHandHelper.h +Content : Helper Inteface for openxr hand extensions +Created : April 2021 +Authors : Federico Schliemann +Language : C++ +Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved. +************************************************************************************************/ +#pragma once + +#include "XrHelper.h" + +class XrHandHelper : public XrHelper { + public: + XrHandHelper(XrInstance instance, bool isLeft) : XrHelper(instance), isLeft_(isLeft) { + /// Hook up extensions for hand tracking + oxr(xrGetInstanceProcAddr( + instance, "xrCreateHandTrackerEXT", (PFN_xrVoidFunction*)(&xrCreateHandTrackerEXT_))); + oxr(xrGetInstanceProcAddr( + instance, "xrDestroyHandTrackerEXT", (PFN_xrVoidFunction*)(&xrDestroyHandTrackerEXT_))); + oxr(xrGetInstanceProcAddr( + instance, "xrLocateHandJointsEXT", (PFN_xrVoidFunction*)(&xrLocateHandJointsEXT_))); + /// Hook up extensions for hand rendering + oxr(xrGetInstanceProcAddr( + instance, "xrGetHandMeshFB", (PFN_xrVoidFunction*)(&xrGetHandMeshFB_))); + } + + ~XrHandHelper() override { + xrCreateHandTrackerEXT_ = nullptr; + xrDestroyHandTrackerEXT_ = nullptr; + xrLocateHandJointsEXT_ = nullptr; + xrGetHandMeshFB_ = nullptr; + }; + + /// XrHelper Interface + virtual bool SessionInit(XrSession session) override { + if (xrCreateHandTrackerEXT_) { + XrHandTrackerCreateInfoEXT createInfo{XR_TYPE_HAND_TRACKER_CREATE_INFO_EXT}; + createInfo.handJointSet = XR_HAND_JOINT_SET_DEFAULT_EXT; + createInfo.hand = isLeft_ ? XR_HAND_LEFT_EXT : XR_HAND_RIGHT_EXT; + if (!oxr(xrCreateHandTrackerEXT_(session, &createInfo, &handTracker_))) { + return false; + } + + if (xrGetHandMeshFB_) { + mesh_.type = XR_TYPE_HAND_TRACKING_MESH_FB; + mesh_.next = nullptr; + mesh_.jointCapacityInput = 0; + mesh_.jointCountOutput = 0; + mesh_.vertexCapacityInput = 0; + mesh_.vertexCountOutput = 0; + mesh_.indexCapacityInput = 0; + mesh_.indexCountOutput = 0; + if (!oxr(xrGetHandMeshFB_(handTracker_, &mesh_))) { + return false; + } + /// update sizes + mesh_.jointCapacityInput = mesh_.jointCountOutput; + mesh_.vertexCapacityInput = mesh_.vertexCountOutput; + mesh_.indexCapacityInput = mesh_.indexCountOutput; + vertexPositions_.resize(mesh_.vertexCapacityInput); + vertexNormals_.resize(mesh_.vertexCapacityInput); + vertexUVs_.resize(mesh_.vertexCapacityInput); + vertexBlendIndices_.resize(mesh_.vertexCapacityInput); + vertexBlendWeights_.resize(mesh_.vertexCapacityInput); + indices_.resize(mesh_.indexCapacityInput); + + /// skeleton + mesh_.jointBindPoses = jointBindPoses_; + mesh_.jointParents = jointParents_; + mesh_.jointRadii = jointRadii_; + /// mesh + mesh_.vertexPositions = vertexPositions_.data(); + mesh_.vertexNormals = vertexNormals_.data(); + mesh_.vertexUVs = vertexUVs_.data(); + mesh_.vertexBlendIndices = vertexBlendIndices_.data(); + mesh_.vertexBlendWeights = vertexBlendWeights_.data(); + mesh_.indices = indices_.data(); + /// get mesh + if (!oxr(xrGetHandMeshFB_(handTracker_, &mesh_))) { + return false; + } + } + return true; + } + return false; + } + + virtual bool SessionEnd() override { + if (xrDestroyHandTrackerEXT_) { + return oxr(xrDestroyHandTrackerEXT_(handTracker_)); + } + return false; + } + + virtual bool Update(XrSpace currentSpace, XrTime predictedDisplayTime) override { + if (xrLocateHandJointsEXT_) { + /// aim + aimState_.next = nullptr; + /// scale + scale_.next = &aimState_; + scale_.sensorOutput = 1.0f; + scale_.currentOutput = 1.0f; + scale_.overrideHandScale = XR_TRUE; + scale_.overrideValueInput = 1.00f; + /// locations + locations_.next = &scale_; + locations_.jointCount = XR_HAND_JOINT_COUNT_EXT; + locations_.jointLocations = jointLocations_; + XrHandJointsLocateInfoEXT locateInfo{XR_TYPE_HAND_JOINTS_LOCATE_INFO_EXT}; + locateInfo.baseSpace = currentSpace; + locateInfo.time = predictedDisplayTime; + + matJointScaledFromUnscaled_ = OVR::Matrix4f::Identity(); + + bool result = oxr(xrLocateHandJointsEXT_(handTracker_, &locateInfo, &locations_)); + if (result) { + OVR::Matrix4f rootMat = OVR::Matrix4f(FromXrPosef(WristRootPose())); + OVR::Matrix4f scaleMat = OVR::Matrix4f::Scaling(RenderScale()); + matJointScaledFromUnscaled_ = rootMat * scaleMat * rootMat.Inverted(); + } + + return result; + } + return false; + } + + static std::vector RequiredExtensionNames() { + return { + XR_EXT_HAND_TRACKING_EXTENSION_NAME, + XR_FB_HAND_TRACKING_MESH_EXTENSION_NAME, + XR_FB_HAND_TRACKING_AIM_EXTENSION_NAME}; + } + + public: + /// Own interface + bool IsLeft() const { + return isLeft_; + } + const XrHandTrackingMeshFB& Mesh() const { + return mesh_; + } + const XrHandTrackingScaleFB& Scale() const { + return scale_; + } + const XrPosef* JointBindPoses() const { + return jointBindPoses_; + } + const XrHandJointEXT* JointParents() const { + return jointParents_; + } + const XrHandJointLocationEXT* Joints() const { + return jointLocations_; + } + float RenderScale() const { + return scale_.sensorOutput; + } + bool IsTracking() const { + return (handTracker_ != XR_NULL_HANDLE); + } + bool AreLocationsActive() const { + return IsTracking() && (locations_.isActive); + } + bool IsPositionValid() const { + return jointLocations_[XR_HAND_JOINT_PALM_EXT].locationFlags & + XR_SPACE_LOCATION_POSITION_VALID_BIT; + } + const XrPosef& AimPose() const { + return aimState_.aimPose; + } + const XrPosef GetScaledJointPose(XrHandJointEXT joint) const { + OVR::Posef jointPoseWorldUnscaled = FromXrPosef(jointLocations_[joint].pose); + OVR::Matrix4f scaled = matJointScaledFromUnscaled_ * OVR::Matrix4f(jointPoseWorldUnscaled); + return ToXrPosef(OVR::Posef(scaled)); + } + const XrPosef& WristRootPose() const { + return jointLocations_[XR_HAND_JOINT_WRIST_EXT].pose; + } + bool IndexPinching() const { + return (aimState_.status & XR_HAND_TRACKING_AIM_INDEX_PINCHING_BIT_FB) != 0; + } + const XrHandTrackingAimStateFB& AimState() const { + return aimState_; + } + + void ModifyWristRoot(const XrPosef& wristRootPose) { + auto rootPose = FromXrPosef(WristRootPose()); + auto modifiedPose = FromXrPosef(wristRootPose); + if (!rootPose.IsEqual(modifiedPose)) { + const OVR::Matrix4f rootMatrix = OVR::Matrix4f(rootPose); + const OVR::Matrix4f rootMatrixOffset = OVR::Matrix4f(modifiedPose); + for (int i = 0; i < XR_HAND_JOINT_COUNT_EXT; i++) { + // Apply offset to hand joints + auto j = OVR::Matrix4f(FromXrPosef(jointLocations_[i].pose)); + jointLocations_[i].pose = ToXrPosef(OVR::Posef(rootMatrixOffset + (j - rootMatrix))); + } + } + } + + private: + bool isLeft_; + /// Hands - extension functions + PFN_xrCreateHandTrackerEXT xrCreateHandTrackerEXT_ = nullptr; + PFN_xrDestroyHandTrackerEXT xrDestroyHandTrackerEXT_ = nullptr; + PFN_xrLocateHandJointsEXT xrLocateHandJointsEXT_ = nullptr; + /// Hands - FB mesh rendering extensions + PFN_xrGetHandMeshFB xrGetHandMeshFB_ = nullptr; + /// Hands - tracker handles + XrHandTrackerEXT handTracker_ = XR_NULL_HANDLE; + /// Hands - data buffers + XrHandJointLocationEXT jointLocations_[XR_HAND_JOINT_COUNT_EXT]; + XrPosef jointBindPoses_[XR_HAND_JOINT_COUNT_EXT]; + XrHandJointEXT jointParents_[XR_HAND_JOINT_COUNT_EXT]; + float jointRadii_[XR_HAND_JOINT_COUNT_EXT]; + /// mesh storage + XrHandTrackingMeshFB mesh_{XR_TYPE_HAND_TRACKING_MESH_FB}; + std::vector vertexPositions_; + std::vector vertexNormals_; + std::vector vertexUVs_; + std::vector vertexBlendIndices_; + std::vector vertexBlendWeights_; + std::vector indices_; + /// extension nodes + /// scale + XrHandTrackingScaleFB scale_{XR_TYPE_HAND_TRACKING_SCALE_FB}; + /// aim + XrHandTrackingAimStateFB aimState_{XR_TYPE_HAND_TRACKING_AIM_STATE_FB}; + /// joint locations *before* applying hand scale + XrHandJointLocationsEXT locations_{XR_TYPE_HAND_JOINT_LOCATIONS_EXT}; + + // cached matrix for applying hand scale to joint locations + OVR::Matrix4f matJointScaledFromUnscaled_; +}; diff --git a/Samples/XrSamples/XrVirtualKeyboard/Src/XrHelper.h b/Samples/XrSamples/XrVirtualKeyboard/Src/XrHelper.h new file mode 100755 index 0000000..11858af --- /dev/null +++ b/Samples/XrSamples/XrVirtualKeyboard/Src/XrHelper.h @@ -0,0 +1,71 @@ +/************************************************************************************************ +Filename : XrHelper.h +Content : Base interface to wrap openxr extension functionality +Created : April 2021 +Authors : Federico Schliemann +Language : C++ +Copyright : Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved. +************************************************************************************************/ +#pragma once + +#if defined(ANDROID) +#define XR_USE_GRAPHICS_API_OPENGL_ES 1 +#define XR_USE_PLATFORM_ANDROID 1 +#include +#include +#else +#include "unknwn.h" +#define XR_USE_GRAPHICS_API_OPENGL 1 +#define XR_USE_PLATFORM_WIN32 1 +#endif + +#include +#include + +#include +#include +#include +#include +#include +#include + +#if !defined(XRLOG) +#define XRLOG(...) +#endif + +class XrHelper { + public: + XrHelper(XrInstance instance) : instance_(instance), lastError_(XR_SUCCESS) {} + virtual ~XrHelper() = default; + virtual bool SessionInit(XrSession session) = 0; + virtual bool SessionEnd() = 0; + virtual bool Update(XrSpace currentSpace, XrTime predictedDisplayTime) = 0; + XrResult GetLastError() const { + return lastError_; + } + + protected: + bool _oxr(XrResult xr, const char* func) { + if (XR_FAILED(xr)) { + char errorBuffer[XR_MAX_RESULT_STRING_SIZE]; + xrResultToString(instance_, xr, errorBuffer); + lastError_ = xr; + return false; + } + return true; + } + XrInstance GetInstance() const { + return instance_; + } + XrInstance GetInstance() { + return instance_; + } + + private: + XrInstance instance_; + XrResult lastError_; +}; + +#if !defined(oxr) +#define oxr(func) _oxr(func, #func) +#endif diff --git a/Samples/XrSamples/XrVirtualKeyboard/Src/XrRenderModelHelper.cpp b/Samples/XrSamples/XrVirtualKeyboard/Src/XrRenderModelHelper.cpp new file mode 100755 index 0000000..6a0b018 --- /dev/null +++ b/Samples/XrSamples/XrVirtualKeyboard/Src/XrRenderModelHelper.cpp @@ -0,0 +1,170 @@ +/************************************************************************************************ +Filename : XrRenderModelHelper.cpp +Content : Helper inteface for XR_FB_render_model extensions +Created : October 2022 +Authors : Peter Chan +Language : C++ +Copyright : Copyright (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. +************************************************************************************************/ + +#include "XrRenderModelHelper.h" + +#include + +std::vector XrRenderModelHelper::RequiredExtensionNames() { + return {XR_FB_RENDER_MODEL_EXTENSION_NAME}; +} + +XrRenderModelHelper::XrRenderModelHelper(XrInstance instance) : XrHelper(instance) { + /// Hook up extensions for device settings + oxr(xrGetInstanceProcAddr( + instance, + "xrEnumerateRenderModelPathsFB", + (PFN_xrVoidFunction*)(&xrEnumerateRenderModelPathsFB_))); + oxr(xrGetInstanceProcAddr( + instance, + "xrGetRenderModelPropertiesFB", + (PFN_xrVoidFunction*)(&xrGetRenderModelPropertiesFB_))); + oxr(xrGetInstanceProcAddr( + instance, "xrLoadRenderModelFB", (PFN_xrVoidFunction*)(&xrLoadRenderModelFB_))); +} + +bool XrRenderModelHelper::SessionInit(XrSession session) { + session_ = session; + return true; +} + +bool XrRenderModelHelper::SessionEnd() { + session_ = XR_NULL_HANDLE; + return true; +} + +bool XrRenderModelHelper::Update(XrSpace currentSpace, XrTime predictedDisplayTime) { + return true; +} + +XrRenderModelKeyFB XrRenderModelHelper::TryGetRenderModelKey(const char* modelPath) { + XrRenderModelKeyFB modelKey = XR_NULL_RENDER_MODEL_KEY_FB; + if (xrGetRenderModelPropertiesFB_ == nullptr) { + ALOGE("XrRenderModelHelper: no render model properties function"); + return modelKey; + } + + LazyInitialize(); + + auto iter = properties_.find(modelPath); + if (iter == properties_.end()) { + ALOGE("XrRenderModelHelper: model %s not available", modelPath); + return modelKey; + } + + XrRenderModelPropertiesFB& modelProp = iter->second; + modelKey = modelProp.modelKey; + if (modelKey == XR_NULL_RENDER_MODEL_KEY_FB) { + // Get properties again to see if the model has become available + XrPath xrPath; + oxr(xrStringToPath(GetInstance(), modelPath, &xrPath)); + XrRenderModelPropertiesFB prop{XR_TYPE_RENDER_MODEL_PROPERTIES_FB}; + XrRenderModelCapabilitiesRequestFB capReq{XR_TYPE_RENDER_MODEL_CAPABILITIES_REQUEST_FB}; + capReq.flags = XR_RENDER_MODEL_SUPPORTS_GLTF_2_0_SUBSET_2_BIT_FB; + prop.next = &capReq; + XrResult result = xrGetRenderModelPropertiesFB_(session_, xrPath, &prop); + if (result != XR_SUCCESS) { + ALOGE("XrRenderModelHelper: failed to load model properties for %s", modelPath); + return modelKey; + } + modelProp = prop; // Update cache + modelKey = prop.modelKey; + if (modelKey == XR_NULL_RENDER_MODEL_KEY_FB) { + ALOGW("XrRenderModelHelper: model %s is still not available", modelPath); + } + } + + return modelKey; +} + +std::vector XrRenderModelHelper::LoadRenderModel(XrRenderModelKeyFB modelKey) { + std::vector buffer; + if (xrLoadRenderModelFB_ == nullptr) { + ALOGE("XrRenderModelHelper: no render model load model function"); + return buffer; + } + + LazyInitialize(); + + if (modelKey == XR_NULL_RENDER_MODEL_KEY_FB) { + ALOGE("XrRenderModelHelper: Invalid modelKey %u", modelKey); + return buffer; + } + + XrRenderModelLoadInfoFB loadInfo = {XR_TYPE_RENDER_MODEL_LOAD_INFO_FB}; + loadInfo.modelKey = modelKey; + XrRenderModelBufferFB rmb{XR_TYPE_RENDER_MODEL_BUFFER_FB}; + if (!oxr(xrLoadRenderModelFB_(session_, &loadInfo, &rmb))) { + ALOGE("XrRenderModelHelper: FAILED to load modelKey %u on pass 1", modelKey); + return buffer; + } + + ALOG("XrRenderModelHelper: loading modelKey %u size %u", modelKey, rmb.bufferCountOutput); + + buffer.resize(rmb.bufferCountOutput); + rmb.buffer = buffer.data(); + rmb.bufferCapacityInput = rmb.bufferCountOutput; + if (!oxr(xrLoadRenderModelFB_(session_, &loadInfo, &rmb))) { + ALOGE("XrRenderModelHelper: FAILED to load modelKey %u on pass 2", modelKey); + buffer.resize(0); + return buffer; + } + + ALOG("XrRenderModelHelper: loaded modelKey %u buffer size is %u", modelKey, buffer.size()); + return buffer; +} + +void XrRenderModelHelper::LazyInitialize() { + if (isInitialized_) { + return; + } + + if (xrEnumerateRenderModelPathsFB_ == nullptr || xrGetRenderModelPropertiesFB_ == nullptr) { + ALOGE("XrRenderModelHelper: no render model extension functions"); + return; + } + + // The application *must* call xrEnumerateRenderModelPathsFB to enumerate the valid render model + // paths that are supported by the runtime before calling xrGetRenderModelPropertiesFB. + // https://registry.khronos.org/OpenXR/specs/1.0/html/xrspec.html#xrEnumerateRenderModelPathsFB + uint32_t pathCount = 0; + oxr(xrEnumerateRenderModelPathsFB_(session_, pathCount, &pathCount, nullptr)); + if (pathCount > 0) { + ALOG("XrRenderModelHelper: found %u models", pathCount); + + std::vector pathInfos(pathCount, {XR_TYPE_RENDER_MODEL_PATH_INFO_FB}); + oxr(xrEnumerateRenderModelPathsFB_(session_, pathCount, &pathCount, pathInfos.data())); + + // Try to get properties for each model + for (const auto& info : pathInfos) { + char pathString[XR_MAX_PATH_LENGTH]; + uint32_t countOutput = 0; + oxr(xrPathToString( + GetInstance(), info.path, XR_MAX_PATH_LENGTH, &countOutput, pathString)); + XrRenderModelPropertiesFB prop{XR_TYPE_RENDER_MODEL_PROPERTIES_FB}; + XrRenderModelCapabilitiesRequestFB capReq{XR_TYPE_RENDER_MODEL_CAPABILITIES_REQUEST_FB}; + capReq.flags = XR_RENDER_MODEL_SUPPORTS_GLTF_2_0_SUBSET_2_BIT_FB; + prop.next = &capReq; + if (oxr(xrGetRenderModelPropertiesFB_(session_, info.path, &prop))) { + ALOG( + "XrRenderModelHelper: found properties for %s, vendorId = %u, modelName = %s, modelKey = %u, modelVersion = %u", + pathString, + prop.vendorId, + prop.modelName, + prop.modelKey, + prop.modelVersion); + properties_[pathString] = prop; + } else { + ALOGE("XrRenderModelHelper: FAILED to load model properties for %s", pathString); + } + } + } + + isInitialized_ = true; +} diff --git a/Samples/XrSamples/XrVirtualKeyboard/Src/XrRenderModelHelper.h b/Samples/XrSamples/XrVirtualKeyboard/Src/XrRenderModelHelper.h new file mode 100755 index 0000000..9fee994 --- /dev/null +++ b/Samples/XrSamples/XrVirtualKeyboard/Src/XrRenderModelHelper.h @@ -0,0 +1,43 @@ +/************************************************************************************************ +Filename : XrRenderModelHelper.h +Content : Helper inteface for openxr XR_FB_render_model extensions +Created : October 2022 +Authors : Peter Chan +Language : C++ +Copyright : Copyright (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. +************************************************************************************************/ + +#pragma once + +#include + +#include "XrHelper.h" + +class XrRenderModelHelper : public XrHelper { + public: + static std::vector RequiredExtensionNames(); + + public: + explicit XrRenderModelHelper(XrInstance instance); + + /// XrHelper Interface + bool SessionInit(XrSession session) override; + bool SessionEnd() override; + bool Update(XrSpace currentSpace, XrTime predictedDisplayTime) override; + + XrRenderModelKeyFB TryGetRenderModelKey(const char* modelPath); + std::vector LoadRenderModel(XrRenderModelKeyFB modelKey); + + private: + void LazyInitialize(); + + XrSession session_ = XR_NULL_HANDLE; + + PFN_xrEnumerateRenderModelPathsFB xrEnumerateRenderModelPathsFB_ = nullptr; + PFN_xrGetRenderModelPropertiesFB xrGetRenderModelPropertiesFB_ = nullptr; + PFN_xrLoadRenderModelFB xrLoadRenderModelFB_ = nullptr; + + std::map properties_; + + bool isInitialized_ = false; +}; diff --git a/Samples/XrSamples/XrVirtualKeyboard/Src/XrVirtualKeyboardHelper.cpp b/Samples/XrSamples/XrVirtualKeyboard/Src/XrVirtualKeyboardHelper.cpp new file mode 100755 index 0000000..46703dc --- /dev/null +++ b/Samples/XrSamples/XrVirtualKeyboard/Src/XrVirtualKeyboardHelper.cpp @@ -0,0 +1,341 @@ +/************************************************************************************************ +Filename : XrVirtualKeyboardHelper.cpp +Content : Helper inteface for openxr XR_META_virtual_keyboard extension +Created : April 2022 +Authors : Juan Pablo León, Robert Memmott, Peter Chan, Brent Housen, Chiara Coetzee +Language : C++ +Copyright : Copyright (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. +************************************************************************************************/ + +#include "XrVirtualKeyboardHelper.h" + +#include + +std::vector XrVirtualKeyboardHelper::RequiredExtensionNames() { + return {XR_META_VIRTUAL_KEYBOARD_EXTENSION_NAME}; +} + +XrVirtualKeyboardHelper::XrVirtualKeyboardHelper(XrInstance instance) : XrHelper(instance) { + /// Hook up extensions for keyboard tracking + oxr(xrGetInstanceProcAddr( + instance, + "xrCreateVirtualKeyboardMETA", + (PFN_xrVoidFunction*)(&xrCreateVirtualKeyboardMETA_))); + oxr(xrGetInstanceProcAddr( + instance, + "xrDestroyVirtualKeyboardMETA", + (PFN_xrVoidFunction*)(&xrDestroyVirtualKeyboardMETA_))); + oxr(xrGetInstanceProcAddr( + instance, + "xrCreateVirtualKeyboardSpaceMETA", + (PFN_xrVoidFunction*)(&xrCreateVirtualKeyboardSpaceMETA_))); + oxr(xrGetInstanceProcAddr( + instance, + "xrSuggestVirtualKeyboardLocationMETA", + (PFN_xrVoidFunction*)(&xrSuggestVirtualKeyboardLocationMETA_))); + oxr(xrGetInstanceProcAddr( + instance, + "xrGetVirtualKeyboardScaleMETA", + (PFN_xrVoidFunction*)(&xrGetVirtualKeyboardScaleMETA_))); + oxr(xrGetInstanceProcAddr( + instance, + "xrSetVirtualKeyboardModelVisibilityMETA", + (PFN_xrVoidFunction*)(&xrSetVirtualKeyboardModelVisibilityMETA_))); + oxr(xrGetInstanceProcAddr( + instance, + "xrGetVirtualKeyboardModelAnimationStatesMETA", + (PFN_xrVoidFunction*)(&xrGetVirtualKeyboardModelAnimationStatesMETA_))); + oxr(xrGetInstanceProcAddr( + instance, + "xrGetVirtualKeyboardDirtyTexturesMETA", + (PFN_xrVoidFunction*)(&xrGetVirtualKeyboardDirtyTexturesMETA_))); + oxr(xrGetInstanceProcAddr( + instance, + "xrGetVirtualKeyboardTextureDataMETA", + (PFN_xrVoidFunction*)(&xrGetVirtualKeyboardTextureDataMETA_))); + oxr(xrGetInstanceProcAddr( + instance, + "xrSendVirtualKeyboardInputMETA", + (PFN_xrVoidFunction*)(&xrSendVirtualKeyboardInputMETA_))); + oxr(xrGetInstanceProcAddr( + instance, + "xrChangeVirtualKeyboardTextContextMETA", + (PFN_xrVoidFunction*)(&xrChangeVirtualKeyboardTextContext_))); +} + +bool XrVirtualKeyboardHelper::SessionInit(XrSession session) { + session_ = session; + return true; +} + +bool XrVirtualKeyboardHelper::SessionEnd() { + session_ = XR_NULL_HANDLE; + return true; +} + +bool XrVirtualKeyboardHelper::Update(XrSpace currentSpace, XrTime predictedDisplayTime) { + return true; +} + +bool XrVirtualKeyboardHelper::IsSupported() const { + XrSystemGetInfo systemGetInfo{XR_TYPE_SYSTEM_GET_INFO}; + systemGetInfo.formFactor = XR_FORM_FACTOR_HEAD_MOUNTED_DISPLAY; + + XrSystemId systemId; + XrResult result = xrGetSystem(GetInstance(), &systemGetInfo, &systemId); + if (result != XR_SUCCESS) { + if (result == XR_ERROR_FORM_FACTOR_UNAVAILABLE) { + ALOGE("Failed to get system; the specified form factor is not available. Is your headset connected?"); + } else { + ALOGE("xrGetSystem failed, error %d", result); + } + exit(1); + } + + XrSystemProperties systemProperties{XR_TYPE_SYSTEM_PROPERTIES}; + XrSystemVirtualKeyboardPropertiesMETA virtualKeyboardProps{ + XR_TYPE_SYSTEM_VIRTUAL_KEYBOARD_PROPERTIES_META}; + systemProperties.next = &virtualKeyboardProps; + result = xrGetSystemProperties(GetInstance(), systemId, &systemProperties); + if (result != XR_SUCCESS) { + ALOGE("Failed to get system properties."); + return false; + } + + return virtualKeyboardProps.supportsVirtualKeyboard != XR_FALSE; +} + +bool XrVirtualKeyboardHelper::HasVirtualKeyboard() const { + return keyboardHandle_ != XR_NULL_HANDLE; +} + +bool XrVirtualKeyboardHelper::CreateVirtualKeyboard(XrVirtualKeyboardCreateInfoMETA* createInfo) { + if (session_ == XR_NULL_HANDLE || GetInstance() == XR_NULL_HANDLE || createInfo == nullptr) { + // Session needs to be initialized, and the method available + return false; + } + if (keyboardHandle_ != XR_NULL_HANDLE) { + // Only one at the time + return false; + } + if (xrCreateVirtualKeyboardMETA_) { + return oxr(xrCreateVirtualKeyboardMETA_(session_, createInfo, &keyboardHandle_)); + } + return false; +} + +bool XrVirtualKeyboardHelper::CreateVirtualKeyboardSpace( + const XrVirtualKeyboardSpaceCreateInfoMETA* locationInfo) { + if (session_ == XR_NULL_HANDLE || GetInstance() == XR_NULL_HANDLE || + keyboardHandle_ == XR_NULL_HANDLE) { + // Session needs to be initialized, and the method available + return false; + } + if (xrCreateVirtualKeyboardSpaceMETA_) { + return oxr(xrCreateVirtualKeyboardSpaceMETA_(session_, keyboardHandle_, locationInfo, &space_)); + } + return false; +} + +bool XrVirtualKeyboardHelper::DestroyVirtualKeyboard() { + if (session_ == XR_NULL_HANDLE || GetInstance() == XR_NULL_HANDLE) { + // Session needs to be initialized, and the method available + return false; + } + if (keyboardHandle_ == XR_NULL_HANDLE) { + // No need to destroy an unexisting keyboard + return true; + } + if (xrDestroyVirtualKeyboardMETA_) { + bool result = oxr(xrDestroyVirtualKeyboardMETA_(keyboardHandle_)); + if (result) { + keyboardHandle_ = XR_NULL_HANDLE; + } + return result; + } + return false; +} + +bool XrVirtualKeyboardHelper::SuggestVirtualKeyboardLocation( + const XrVirtualKeyboardLocationInfoMETA* locationInfo) { + if (session_ == XR_NULL_HANDLE || GetInstance() == XR_NULL_HANDLE || + keyboardHandle_ == XR_NULL_HANDLE) { + // Session needs to be initialized, and the method available + return false; + } + if (xrSuggestVirtualKeyboardLocationMETA_) { + return oxr(xrSuggestVirtualKeyboardLocationMETA_(keyboardHandle_, locationInfo)); + } + return false; +} + +bool XrVirtualKeyboardHelper::GetVirtualKeyboardLocation( + XrSpace baseSpace, + XrTime time, + VirtualKeyboardLocation* keyboardLocation) { + if (session_ == XR_NULL_HANDLE || GetInstance() == XR_NULL_HANDLE || + keyboardHandle_ == XR_NULL_HANDLE) { + // Session needs to be initialized, and the method available + return false; + } + if (space_ == XR_NULL_HANDLE) { + ALOGE("Keyboard space is null handle. Call SuggestVirtualKeyboardLocation."); + return false; + } + + if (xrGetVirtualKeyboardScaleMETA_) { + XrSpaceLocation location{XR_TYPE_SPACE_LOCATION}; + auto result = xrLocateSpace(space_, baseSpace, time, &location); + if (result != XR_SUCCESS) { + ALOGE("Failed to locate virtual keyboard: %i", result); + return false; + }; + keyboardLocation->pose = location.pose; + result = xrGetVirtualKeyboardScaleMETA_(keyboardHandle_, &keyboardLocation->scale); + if (result != XR_SUCCESS) { + ALOGE("Failed to get virtual keyboard scale: %i", result); + return false; + }; + return true; + } + return false; +} + +bool XrVirtualKeyboardHelper::ShowModel(bool visible) { + if (session_ == XR_NULL_HANDLE || GetInstance() == XR_NULL_HANDLE || + keyboardHandle_ == XR_NULL_HANDLE || xrSetVirtualKeyboardModelVisibilityMETA_ == nullptr) { + // Session needs to be initialized, and the method available + return false; + } + + XrVirtualKeyboardModelVisibilitySetInfoMETA modelVisibility{ + XR_TYPE_VIRTUAL_KEYBOARD_MODEL_VISIBILITY_SET_INFO_META}; + modelVisibility.visible = visible ? XR_TRUE : XR_FALSE; + bool result = oxr(xrSetVirtualKeyboardModelVisibilityMETA_(keyboardHandle_, &modelVisibility)); + if (!result) { + return false; + } + + return result; +} + +bool XrVirtualKeyboardHelper::GetModelAnimationStates( + XrVirtualKeyboardModelAnimationStatesMETA& modelAnimationStates) { + if (session_ == XR_NULL_HANDLE || GetInstance() == XR_NULL_HANDLE || + keyboardHandle_ == XR_NULL_HANDLE || + xrGetVirtualKeyboardModelAnimationStatesMETA_ == nullptr) { + // Session needs to be initialized, and the method available + return false; + } + + modelAnimationStates = {XR_TYPE_VIRTUAL_KEYBOARD_MODEL_ANIMATION_STATES_META}; + modelAnimationStates.stateCapacityInput = 0; + bool result = + oxr(xrGetVirtualKeyboardModelAnimationStatesMETA_(keyboardHandle_, &modelAnimationStates)); + if (!result) { + return false; + } + + // If there are no animations, we are done + if (modelAnimationStates.stateCountOutput == 0) { + return true; + } + + animationStatesBuffer_.resize( + modelAnimationStates.stateCountOutput, {XR_TYPE_VIRTUAL_KEYBOARD_ANIMATION_STATE_META}); + modelAnimationStates.stateCapacityInput = modelAnimationStates.stateCountOutput; + modelAnimationStates.states = animationStatesBuffer_.data(); + result = + oxr(xrGetVirtualKeyboardModelAnimationStatesMETA_(keyboardHandle_, &modelAnimationStates)); + return result; +} + +bool XrVirtualKeyboardHelper::GetDirtyTextures(std::vector& textureIds) { + if (session_ == XR_NULL_HANDLE || GetInstance() == XR_NULL_HANDLE || + keyboardHandle_ == XR_NULL_HANDLE || xrGetVirtualKeyboardDirtyTexturesMETA_ == nullptr) { + return false; + } + + uint32_t textureIdCountOutput = 0; + bool result = oxr(xrGetVirtualKeyboardDirtyTexturesMETA_(keyboardHandle_, 0, &textureIdCountOutput, nullptr)); + if (!result) { + return false; + } + + // If nothing is dirty, we are done + if (textureIdCountOutput == 0) { + return true; + } + + textureIds.resize(textureIdCountOutput); + result = oxr(xrGetVirtualKeyboardDirtyTexturesMETA_(keyboardHandle_, textureIdCountOutput, &textureIdCountOutput, textureIds.data())); + return result; +} + +bool XrVirtualKeyboardHelper::GetTextureData( + uint64_t textureId, + XrVirtualKeyboardTextureDataMETA& textureData) { + if (session_ == XR_NULL_HANDLE || GetInstance() == XR_NULL_HANDLE || + keyboardHandle_ == XR_NULL_HANDLE || xrGetVirtualKeyboardTextureDataMETA_ == nullptr) { + // Session needs to be initialized, and the method available + return false; + } + + textureData = {XR_TYPE_VIRTUAL_KEYBOARD_TEXTURE_DATA_META}; + textureData.bufferCapacityInput = 0; + bool result = + oxr(xrGetVirtualKeyboardTextureDataMETA_(keyboardHandle_, textureId, &textureData)); + if (!result) { + return false; + } + + // No data available, try again later + if (textureData.bufferCountOutput == 0) { + return false; + } + + textureDataBuffer_.resize(textureData.bufferCountOutput); + textureData.bufferCapacityInput = textureData.bufferCountOutput; + textureData.buffer = textureDataBuffer_.data(); + result = oxr(xrGetVirtualKeyboardTextureDataMETA_(keyboardHandle_, textureId, &textureData)); + return result; +} + +bool XrVirtualKeyboardHelper::SendVirtualKeyboardInput( + XrSpace space, + XrVirtualKeyboardInputSourceMETA source, + const XrPosef& pointerPose, + bool pressed, + XrPosef* interactorRootPose) { + if (session_ == XR_NULL_HANDLE || GetInstance() == XR_NULL_HANDLE || + keyboardHandle_ == XR_NULL_HANDLE) { + // Session needs to be initialized, and the method available + return false; + } + if (xrSendVirtualKeyboardInputMETA_) { + XrVirtualKeyboardInputInfoMETA info{XR_TYPE_VIRTUAL_KEYBOARD_INPUT_INFO_META}; + info.inputSource = source; + info.inputSpace = space; + info.inputPoseInSpace = pointerPose; + if (pressed) { + info.inputState |= XR_VIRTUAL_KEYBOARD_INPUT_STATE_PRESSED_BIT_META; + } + return oxr(xrSendVirtualKeyboardInputMETA_(keyboardHandle_, &info, interactorRootPose)); + } + return false; +} + +bool XrVirtualKeyboardHelper::UpdateTextContext(const std::string& textContext) { + if (session_ == XR_NULL_HANDLE || GetInstance() == XR_NULL_HANDLE || + keyboardHandle_ == XR_NULL_HANDLE) { + // Session needs to be initialized, and the method available + return false; + } + if (xrChangeVirtualKeyboardTextContext_) { + XrVirtualKeyboardTextContextChangeInfoMETA changeInfo{ + XR_TYPE_VIRTUAL_KEYBOARD_TEXT_CONTEXT_CHANGE_INFO_META}; + changeInfo.textContext = textContext.c_str(); + return oxr(xrChangeVirtualKeyboardTextContext_(keyboardHandle_, &changeInfo)); + } + return false; +} diff --git a/Samples/XrSamples/XrVirtualKeyboard/Src/XrVirtualKeyboardHelper.h b/Samples/XrSamples/XrVirtualKeyboard/Src/XrVirtualKeyboardHelper.h new file mode 100755 index 0000000..b8e3b23 --- /dev/null +++ b/Samples/XrSamples/XrVirtualKeyboard/Src/XrVirtualKeyboardHelper.h @@ -0,0 +1,83 @@ +/************************************************************************************************ +Filename : XrVirtualKeyboardHelper.h +Content : Helper inteface for openxr XR_META_virtual_keyboard extension +Created : April 2022 +Authors : Juan Pablo León, Robert Memmott, Peter Chan, Brent Housen, Chiara Coetzee +Language : C++ +Copyright : Copyright (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. +************************************************************************************************/ + +#pragma once + +#include + +#include "XrHelper.h" + +struct VirtualKeyboardLocation { + XrPosef pose; + float scale; +}; + +class XrVirtualKeyboardHelper : public XrHelper { + public: + static std::vector RequiredExtensionNames(); + + public: + explicit XrVirtualKeyboardHelper(XrInstance instance); + + /// XrHelper Interface + bool SessionInit(XrSession session) override; + bool SessionEnd() override; + bool Update(XrSpace currentSpace, XrTime predictedDisplayTime) override; + + bool IsSupported() const; + bool HasVirtualKeyboard() const; + + // Creation + bool CreateVirtualKeyboard(XrVirtualKeyboardCreateInfoMETA* createInfo); + bool CreateVirtualKeyboardSpace(const XrVirtualKeyboardSpaceCreateInfoMETA* locationInfo); + bool DestroyVirtualKeyboard(); + + // Positioning + bool SuggestVirtualKeyboardLocation(const XrVirtualKeyboardLocationInfoMETA* locationInfo); + bool GetVirtualKeyboardLocation( + XrSpace baseSpace, + XrTime time, + VirtualKeyboardLocation* keyboardLocation); + + // Render model + bool ShowModel(bool visible); + bool GetModelAnimationStates(XrVirtualKeyboardModelAnimationStatesMETA& modelAnimationStates); + bool GetDirtyTextures(std::vector& textureIds); + bool GetTextureData(uint64_t textureId, XrVirtualKeyboardTextureDataMETA& textureData); + + // Interaction + bool SendVirtualKeyboardInput( + XrSpace space, + XrVirtualKeyboardInputSourceMETA source, + const XrPosef& pointerPose, + bool pressed, + XrPosef* interactorRootPose); + bool UpdateTextContext(const std::string& textContext); + + private: + XrSession session_ = XR_NULL_HANDLE; + XrVirtualKeyboardMETA keyboardHandle_ = XR_NULL_HANDLE; + XrSpace space_ = XR_NULL_HANDLE; + + PFN_xrCreateVirtualKeyboardMETA xrCreateVirtualKeyboardMETA_ = nullptr; + PFN_xrDestroyVirtualKeyboardMETA xrDestroyVirtualKeyboardMETA_ = nullptr; + PFN_xrCreateVirtualKeyboardSpaceMETA xrCreateVirtualKeyboardSpaceMETA_ = nullptr; + PFN_xrSuggestVirtualKeyboardLocationMETA xrSuggestVirtualKeyboardLocationMETA_ = nullptr; + PFN_xrGetVirtualKeyboardScaleMETA xrGetVirtualKeyboardScaleMETA_ = nullptr; + PFN_xrSetVirtualKeyboardModelVisibilityMETA xrSetVirtualKeyboardModelVisibilityMETA_ = nullptr; + PFN_xrGetVirtualKeyboardModelAnimationStatesMETA xrGetVirtualKeyboardModelAnimationStatesMETA_ = + nullptr; + PFN_xrGetVirtualKeyboardDirtyTexturesMETA xrGetVirtualKeyboardDirtyTexturesMETA_ = nullptr; + PFN_xrGetVirtualKeyboardTextureDataMETA xrGetVirtualKeyboardTextureDataMETA_ = nullptr; + PFN_xrSendVirtualKeyboardInputMETA xrSendVirtualKeyboardInputMETA_ = nullptr; + PFN_xrChangeVirtualKeyboardTextContextMETA xrChangeVirtualKeyboardTextContext_ = nullptr; + + std::vector animationStatesBuffer_; + std::vector textureDataBuffer_; +}; diff --git a/Samples/XrSamples/XrVirtualKeyboard/Src/main.cpp b/Samples/XrSamples/XrVirtualKeyboard/Src/main.cpp new file mode 100755 index 0000000..18321b9 --- /dev/null +++ b/Samples/XrSamples/XrVirtualKeyboard/Src/main.cpp @@ -0,0 +1,1001 @@ +/************************************************************************************************ +Filename : main.cpp +Content : Sample test app to test openxr XR_META_virtual_keyboard extension +Created : March 2022 +Authors : Chiara Coetzee, Juan Pablo León, Robert Memmott, Brent Housen, Peter Chan +Language : C++ +Copyright : Copyright (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. +************************************************************************************************/ + +#include +#include + +#include +#include +#include +#include + +#include "VirtualKeyboardModelRenderer.h" +#include "XrHandHelper.h" +#include "XrRenderModelHelper.h" +#include "XrVirtualKeyboardHelper.h" + +enum struct InputHandedness { Unknown, Left, Right }; + +class XrVirtualKeyboardApp : public OVRFW::XrApp { + private: + static constexpr std::string_view kSampleExplanation = + "Virtual Keyboard enables VR developers to easily integrate a \n" + "best-in-class keyboard into their applications and provides \n" + "users with a consistent typing experience across Meta Quest \n" + "VR applications. The API is designed so that features and \n" + "improvements can be made to the keyboard through OS \n" + "updates, without any modifications by the developer to \n" + "leverage the latest features. \n" + " \n" + "The keyboard supports multiple input modes including far \n" + "raycast based input, direct touch input, and swipe typing \n" + "for both modalities. "; + public: + XrVirtualKeyboardApp() = default; + + // Returns a list of OpenXr extensions needed for this app + virtual std::vector GetExtensions() override { + std::vector extensions = XrApp::GetExtensions(); + // Add virtual keyboard extensions + for (const auto& kbdExtension : XrVirtualKeyboardHelper::RequiredExtensionNames()) { + extensions.push_back(kbdExtension); + } + // Add hand extensions + for (const auto& handExtension : XrHandHelper::RequiredExtensionNames()) { + extensions.push_back(handExtension); + } + // Add render model extensions + for (const auto& renderModelExtension : XrRenderModelHelper::RequiredExtensionNames()) { + extensions.push_back(renderModelExtension); + } + + // Log all extensions + ALOG("XrVirtualKeyboardApp requesting extensions:"); + for (const auto& e : extensions) { + ALOG(" --> %s", e); + } + + return extensions; + } + + // Must return true if the application initializes successfully + virtual bool AppInit(const xrJava* context) override { + if (false == ui_.Init(context, GetFileSys(), false)) { + ALOG("TinyUI::Init FAILED."); + return false; + } + + // Virtual keyboard + keyboardExtensionAvailable_ = + ExtensionsArePresent(XrVirtualKeyboardHelper::RequiredExtensionNames()); + if (keyboardExtensionAvailable_) { + virtualKeyboard_ = std::make_unique(GetInstance()); + OXR(virtualKeyboard_->GetLastError()); + } + + // Hand tracking + handsExtensionAvailable_ = ExtensionsArePresent(XrHandHelper::RequiredExtensionNames()); + if (handsExtensionAvailable_) { + handL_ = std::make_unique(GetInstance(), true); + OXR(handL_->GetLastError()); + handR_ = std::make_unique(GetInstance(), false); + OXR(handR_->GetLastError()); + } + + // Render model + renderModelExtensionAvailable_ = + ExtensionsArePresent(XrRenderModelHelper::RequiredExtensionNames()); + if (renderModelExtensionAvailable_) { + renderModel_ = std::make_unique(GetInstance()); + OXR(renderModel_->GetLastError()); + } + + return true; + } + + virtual void AppShutdown(const xrJava* context) override { + renderModel_ = nullptr; + handL_ = nullptr; + handR_ = nullptr; + virtualKeyboard_ = nullptr; + + uiInitialized_ = false; + renderModelExtensionAvailable_ = false; + handsExtensionAvailable_ = false; + keyboardExtensionAvailable_ = false; + + ui_.Shutdown(); + OVRFW::XrApp::AppShutdown(context); + } + + virtual bool SessionInit() override { + // Use LocalSpace instead of Stage Space + CurrentSpace = LocalSpace; + + // Disable scene navigation + GetScene().SetFootPos({0.0f, 0.0f, 0.0f}); + FreeMove = false; + + // Init session bound objects + if (false == controllerRenderL_.Init(true)) { + ALOG("SessionInit::Init L controller renderer FAILED."); + return false; + } + if (false == controllerRenderR_.Init(false)) { + ALOG("SessionInit::Init R controller renderer FAILED."); + return false; + } + beamRenderer_.Init(GetFileSys(), nullptr, OVR::Vector4f(1.0f), 1.0f); + particleSystem_.Init(10, nullptr, OVRFW::ovrParticleSystem::GetDefaultGpuState(), false); + + if (keyboardExtensionAvailable_) { + virtualKeyboard_->SessionInit(GetSession()); + + XrVirtualKeyboardCreateInfoMETA createInfo{XR_TYPE_VIRTUAL_KEYBOARD_CREATE_INFO_META}; + bool success = virtualKeyboard_->CreateVirtualKeyboard(&createInfo); + if (!success) { + OXR(virtualKeyboard_->GetLastError()); + } + + // Create a default space to locate the keyboard + if (success) { + XrVirtualKeyboardSpaceCreateInfoMETA spaceCreateInfo{ + XR_TYPE_VIRTUAL_KEYBOARD_SPACE_CREATE_INFO_META}; + spaceCreateInfo.space = GetCurrentSpace(); + spaceCreateInfo.poseInSpace = ToXrPosef(OVR::Posef::Identity()); + success = virtualKeyboard_->CreateVirtualKeyboardSpace(&spaceCreateInfo); + if (!success) { + OXR(virtualKeyboard_->GetLastError()); + } + } + } + + if (handsExtensionAvailable_) { + handL_->SessionInit(GetSession()); + handR_->SessionInit(GetSession()); + handRendererL_.Init(&handL_->Mesh(), handL_->IsLeft()); + handRendererR_.Init(&handR_->Mesh(), handR_->IsLeft()); + } + + if (renderModelExtensionAvailable_) { + renderModel_->SessionInit(GetSession()); + } + + return true; + } + + virtual void SessionEnd() override { + if (renderModelExtensionAvailable_) { + renderModel_->SessionEnd(); + } + if (handsExtensionAvailable_) { + handL_->SessionEnd(); + handR_->SessionEnd(); + handRendererL_.Shutdown(); + handRendererR_.Shutdown(); + } + if (keyboardExtensionAvailable_) { + virtualKeyboard_->DestroyVirtualKeyboard(); + virtualKeyboard_->SessionEnd(); + } + + keyboardModelRenderer_.Shutdown(); + controllerRenderL_.Shutdown(); + controllerRenderR_.Shutdown(); + particleSystem_.Shutdown(); + beamRenderer_.Shutdown(); + } + + virtual void HandleXrEvents() override { + XrEventDataBuffer eventDataBuffer = {}; + + // Poll for events + for (;;) { + XrEventDataBaseHeader* baseEventHeader = (XrEventDataBaseHeader*)(&eventDataBuffer); + baseEventHeader->type = XR_TYPE_EVENT_DATA_BUFFER; + baseEventHeader->next = NULL; + XrResult r; + OXR(r = xrPollEvent(Instance, &eventDataBuffer)); + if (r != XR_SUCCESS) { + break; + } + + switch (baseEventHeader->type) { + case XR_TYPE_EVENT_DATA_VIRTUAL_KEYBOARD_COMMIT_TEXT_META: { + const XrEventDataVirtualKeyboardCommitTextMETA* commit_text_event = + (XrEventDataVirtualKeyboardCommitTextMETA*)(baseEventHeader); + OnCommitText(commit_text_event->text); + } break; + case XR_TYPE_EVENT_DATA_VIRTUAL_KEYBOARD_BACKSPACE_META: { + OnBackspace(); + } break; + case XR_TYPE_EVENT_DATA_VIRTUAL_KEYBOARD_ENTER_META: { + OnEnter(); + } break; + case XR_TYPE_EVENT_DATA_VIRTUAL_KEYBOARD_SHOWN_META: { + OnKeyboardShown(); + } break; + case XR_TYPE_EVENT_DATA_VIRTUAL_KEYBOARD_HIDDEN_META: { + OnKeyboardHidden(); + } break; + case XR_TYPE_EVENT_DATA_EVENTS_LOST: + ALOGV("xrPollEvent: received XR_TYPE_EVENT_DATA_EVENTS_LOST event"); + break; + case XR_TYPE_EVENT_DATA_INSTANCE_LOSS_PENDING: + ALOGV("xrPollEvent: received XR_TYPE_EVENT_DATA_INSTANCE_LOSS_PENDING event"); + break; + case XR_TYPE_EVENT_DATA_INTERACTION_PROFILE_CHANGED: + ALOGV( + "xrPollEvent: received XR_TYPE_EVENT_DATA_INTERACTION_PROFILE_CHANGED event"); + break; + case XR_TYPE_EVENT_DATA_PERF_SETTINGS_EXT: { + const XrEventDataPerfSettingsEXT* perf_settings_event = + (XrEventDataPerfSettingsEXT*)(baseEventHeader); + ALOGV( + "xrPollEvent: received XR_TYPE_EVENT_DATA_PERF_SETTINGS_EXT event: type %d subdomain %d : level %d -> level %d", + perf_settings_event->type, + perf_settings_event->subDomain, + perf_settings_event->fromLevel, + perf_settings_event->toLevel); + } break; + case XR_TYPE_EVENT_DATA_REFERENCE_SPACE_CHANGE_PENDING: + ALOGV( + "xrPollEvent: received XR_TYPE_EVENT_DATA_REFERENCE_SPACE_CHANGE_PENDING event"); + break; + case XR_TYPE_EVENT_DATA_SESSION_STATE_CHANGED: { + const XrEventDataSessionStateChanged* session_state_changed_event = + (XrEventDataSessionStateChanged*)(baseEventHeader); + ALOGV( + "xrPollEvent: received XR_TYPE_EVENT_DATA_SESSION_STATE_CHANGED: %d for session %p at time %f", + session_state_changed_event->state, + (void*)session_state_changed_event->session, + FromXrTime(session_state_changed_event->time)); + + switch (session_state_changed_event->state) { + case XR_SESSION_STATE_FOCUSED: { + Focused = true; + + // Try to load the keyboard model if it's not already loaded + if (!keyboardModelRenderer_.IsModelLoaded() && + renderModelExtensionAvailable_) { + if (modelKey_ == XR_NULL_RENDER_MODEL_KEY_FB) { + // This path should be documented in the OpenXR spec here: + // https://registry.khronos.org/OpenXR/specs/1.0/man/html/openxr.html#XrRenderModelPathInfoFB + // under "Possible Render Model Paths". + modelKey_ = renderModel_->TryGetRenderModelKey( + "/model_meta/keyboard/virtual"); + if (modelKey_ == XR_NULL_RENDER_MODEL_KEY_FB) { + ALOGE("Failed to get virtual keyboard render model key"); + } + } + if (modelKey_ != XR_NULL_RENDER_MODEL_KEY_FB) { + std::vector buffer = + renderModel_->LoadRenderModel(modelKey_); + ALOG("Model buffer.size() = %zu", buffer.size()); + if (keyboardModelRenderer_.Init(buffer)) { + keyboardModelRenderer_.Update( + currentPose_, OVR::Vector3f(currentScale_)); + ShowKeyboard(); + } else { + ALOGE("Failed to load virtual keyboard render model"); + } + } + } + } break; + case XR_SESSION_STATE_VISIBLE: + Focused = false; + break; + case XR_SESSION_STATE_READY: + case XR_SESSION_STATE_STOPPING: + HandleSessionStateChanges(session_state_changed_event->state); + break; + case XR_SESSION_STATE_EXITING: + ShouldExit = true; + break; + default: + break; + } + } break; + default: + ALOGV("xrPollEvent: Unknown event"); + break; + } + } + } + + virtual void Update(const OVRFW::ovrApplFrameIn& in) override { + InitializeUI(); + + XrSpace currentSpace = GetCurrentSpace(); + XrTime predictedDisplayTime = ToXrTime(in.PredictedDisplayTime); + + // hands + if (handsExtensionAvailable_) { + handL_->Update(currentSpace, predictedDisplayTime); + handR_->Update(currentSpace, predictedDisplayTime); + } + + // Keyboard + if (keyboardExtensionAvailable_) { + virtualKeyboard_->Update(currentSpace, predictedDisplayTime); + } + + // Render model + if (renderModelExtensionAvailable_) { + renderModel_->Update(currentSpace, predictedDisplayTime); + } + + if (in.Clicked(in.kButtonA)) { + if (!isShowingKeyboard_) { + ShowKeyboard(); + } else { + HideKeyboard(); + } + } + + UpdateUIHitTests(in); + + // Update controller poses (which may be adjusted after keyboard interaction) + leftAdjustedRemotePose_ = in.LeftRemotePose; + rightAdjustedRemotePose_ = in.RightRemotePose; + + if (isShowingKeyboard_) { + UpdateKeyboardInteractions(in); + UpdateKeyboardMoving(in); + } + + // Hands + if (handsExtensionAvailable_) { + if (handL_->AreLocationsActive()) { + handRendererL_.Update(handL_->Joints(), handL_->RenderScale()); + } + if (handR_->AreLocationsActive()) { + handRendererR_.Update(handR_->Joints(), handR_->RenderScale()); + } + } + + // Controllers + if (in.LeftRemoteTracked) { + controllerRenderL_.Update(leftAdjustedRemotePose_); + } + if (in.RightRemoteTracked) { + controllerRenderR_.Update(rightAdjustedRemotePose_); + } + + if (keyboardModelRenderer_.IsModelLoaded()) { + std::vector textureIds; + virtualKeyboard_->GetDirtyTextures(textureIds); + for (const uint64_t textureId : textureIds) { + XrVirtualKeyboardTextureDataMETA textureData{}; + if (virtualKeyboard_->GetTextureData(textureId, textureData)) { + keyboardModelRenderer_.UpdateTexture( + textureId, + textureData.buffer, + textureData.textureWidth, + textureData.textureHeight); + } + } + + XrVirtualKeyboardModelAnimationStatesMETA modelAnimationStates; + virtualKeyboard_->GetModelAnimationStates(modelAnimationStates); + for (uint32_t i = 0; i < modelAnimationStates.stateCountOutput; ++i) { + const auto& animationState = modelAnimationStates.states[i]; + keyboardModelRenderer_.SetAnimationState( + animationState.animationIndex, animationState.fraction); + } + keyboardModelRenderer_.UpdateSurfaceGeo(); + } + } + + virtual void Render(const OVRFW::ovrApplFrameIn& in, OVRFW::ovrRendererOutput& out) override { + ui_.Render(in, out); + + if (isShowingKeyboard_) { + keyboardModelRenderer_.Render(out.Surfaces); + } + + if (handsExtensionAvailable_ && handL_->AreLocationsActive() && handL_->IsPositionValid()) { + handRendererL_.Render(out.Surfaces); + } else if (in.LeftRemoteTracked) { + controllerRenderL_.Render(out.Surfaces); + } + + if (handsExtensionAvailable_ && handR_->AreLocationsActive() && handR_->IsPositionValid()) { + handRendererR_.Render(out.Surfaces); + } else if (in.RightRemoteTracked) { + controllerRenderR_.Render(out.Surfaces); + } + + // Render beams last for proper blending + particleSystem_.Frame(in, nullptr, out.FrameMatrices.CenterView); + particleSystem_.RenderEyeView( + out.FrameMatrices.CenterView, out.FrameMatrices.EyeProjection[0], out.Surfaces); + beamRenderer_.Render(in, out); + } + + private: + enum class HitTestRayDeviceNums { + LeftHand, + LeftRemote, + RightHand, + RightRemote, + }; + + void ShowKeyboard() { + if (!virtualKeyboard_->HasVirtualKeyboard()) { + return; + } + + if (!virtualKeyboard_->ShowModel(true)) { + eventLog_->SetText("Failed to show keyboard"); + return; + } + + SetKeyboardLocation(locationType_); + + // Should call this whenever the keyboard is created or when the text focus changes + virtualKeyboard_->UpdateTextContext(textInputBuffer_); + } + + void SetKeyboardLocation(XrVirtualKeyboardLocationTypeMETA locationType) { + // Update keyboard location based on the location type + XrVirtualKeyboardLocationInfoMETA locationInfo{XR_TYPE_VIRTUAL_KEYBOARD_LOCATION_INFO_META}; + locationInfo.space = GetCurrentSpace(); + locationInfo.locationType = locationType; + if (!virtualKeyboard_->SuggestVirtualKeyboardLocation(&locationInfo)) { + eventLog_->SetText("Failed to update keyboard location & scale."); + return; + } + + // Remember the current location type + locationType_ = locationType; + + // Sync local location/scale + VirtualKeyboardLocation location; + if (!virtualKeyboard_->GetVirtualKeyboardLocation( + GetCurrentSpace(), ToXrTime(OVRFW::GetTimeInSeconds()), &location)) { + eventLog_->SetText("Failed to sync keyboard location & scale."); + return; + } + currentPose_ = FromXrPosef(location.pose); + currentScale_ = location.scale; + } + + void HideKeyboard() { + virtualKeyboard_->ShowModel(false); + } + + bool ExtensionsArePresent(const std::vector& extensionList) const { + const auto extensionProperties = GetXrExtensionProperties(); + bool foundAllExtensions = true; + for (const auto& extension : extensionList) { + bool foundExtension = false; + for (const auto& extensionProperty : extensionProperties) { + if (!strcmp(extension, extensionProperty.extensionName)) { + foundExtension = true; + break; + } + } + if (!foundExtension) { + foundAllExtensions = false; + break; + } + } + return foundAllExtensions; + } + + void InitializeUI() { + if (uiInitialized_) { + return; + } + uiInitialized_ = true; + + keyboardHitTest_ = ui_.AddLabel("", {0.0f, 0.0f, 0.0f}, {100.0f, 100.0f}); + keyboardHitTest_->SetColor({0, 0, 0, 0}); + keyboardHitTest_->AddFlags(OVRFW::VRMENUOBJECT_FLAG_NO_DEPTH_MASK); + + eventLog_ = ui_.AddLabel("", {0.0f, 0.5f, -1.5f}, {600.0f, 50.0f}); + + // virtual keyboard + if (!keyboardExtensionAvailable_) { + eventLog_->SetText("Virtual Keyboard extension unavailable."); + return; + } + if (!virtualKeyboard_->IsSupported()) { + eventLog_->SetText("Virtual Keyboard not supported."); + return; + } + if (!virtualKeyboard_->HasVirtualKeyboard()) { + eventLog_->SetText("Virtual Keyboard creation failed"); + return; + } + + // Build UI + CreateSampleDescriptionPanel(); + + textInput_ = ui_.AddLabel("", {0.0f, 0.1f, -1.5f}, {600.0f, 320.0f}); + OVRFW::VRMenuFontParms fontParms = textInput_->GetFontParms(); + fontParms.AlignHoriz = OVRFW::HORIZONTAL_LEFT; + fontParms.AlignVert = OVRFW::VERTICAL_BASELINE; + fontParms.WrapWidth = 1.1f; + fontParms.MaxLines = 10; + textInput_->SetFontParms(fontParms); + textInput_->SetTextLocalPosition({-0.55f, 0.25f, 0.0f}); + + // Keyboard visibility controls + showKeyboardButton_ = ui_.AddButton( + "Show Keyboard", {-0.3f, 0.9f, -1.5f}, {300.0f, 50.0f}, [this]() { ShowKeyboard(); }); + hideKeyboardButton_ = ui_.AddButton( + "Hide Keyboard", {-0.3f, 0.8f, -1.5f}, {300.0f, 50.0f}, [this]() { HideKeyboard(); }); + + // Space buttons + localSpaceButton_ = ui_.AddButton( + "Local Space", {-0.3f, 0.7f, -1.5f}, {300.0f, 50.0f}, [this]() { SetSpace(GetLocalSpace()); }); + stageSpaceButton_ = ui_.AddButton( + "Stage Space", {0.3f, 0.7f, -1.5f}, {300.0f, 50.0f}, [this]() { SetSpace(GetStageSpace()); }); + SetSpace(GetCurrentSpace()); + + // Keyboard location controls + enableMoveKeyboardButton_ = + ui_.AddButton("Move", {0.1f, 0.9f, -1.5f}, {100.0f, 50.0f}, [this]() { + if (isShowingKeyboard_) { + isMovingKeyboard_ = true; + keyboardMoveDistance_ = 0.0f; + } + }); + showNearKeyboardButton_ = + ui_.AddButton("Near", {0.3f, 0.9f, -1.5f}, {100.0f, 50.0f}, [this]() { + SetKeyboardLocation(XR_VIRTUAL_KEYBOARD_LOCATION_TYPE_DIRECT_META); + }); + showFarKeyboardButton_ = + ui_.AddButton("Far", {0.5f, 0.9f, -1.5f}, {100.0f, 50.0f}, [this]() { + SetKeyboardLocation(XR_VIRTUAL_KEYBOARD_LOCATION_TYPE_FAR_META); + }); + + // Clear text + clearTextButton_ = + ui_.AddButton("Clear Text", {0.3f, 0.8f, -1.5f}, {300.0f, 50.0f}, [this]() { + textInputBuffer_.clear(); + textInput_->SetText(textInputBuffer_.c_str()); + eventLog_->SetText("Text Cleared"); + virtualKeyboard_->UpdateTextContext(textInputBuffer_); + }); + + ui_.SetUnhandledClickHandler([this]() { isMovingKeyboard_ = false; }); + } + + void CreateSampleDescriptionPanel() { + // Panel to provide sample description to the user for context + auto descriptionLabel = ui_.AddLabel( + static_cast(kSampleExplanation), {1.5f, 0.355f, -1.0f}, {750.0f, 400.0f}); + + // Align and size the description text for readability + OVRFW::VRMenuFontParms fontParams{}; + fontParams.Scale = 0.5f; + fontParams.AlignHoriz = OVRFW::HORIZONTAL_LEFT; + descriptionLabel->SetFontParms(fontParams); + descriptionLabel->SetTextLocalPosition({-0.65f, 0, 0}); + + // Tilt the description billboard 45 degrees towards the user + descriptionLabel->SetLocalRotation( + OVR::Quat::FromRotationVector({0, OVR::DegreeToRad(-30.0f), 0})); + } + + void SetSpace(XrSpace space) { + CurrentSpace = space; + if (CurrentSpace == GetLocalSpace()) { + DisableButton(localSpaceButton_); + EnableButton(stageSpaceButton_); + } else { + EnableButton(localSpaceButton_); + DisableButton(stageSpaceButton_); + } + } + + void EnableButton(OVRFW::VRMenuObject* button) { + button->SetSurfaceColor(0, ui_.BackgroundColor); + button->RemoveFlags(OVRFW::VRMENUOBJECT_DONT_HIT_ALL); + } + + void DisableButton(OVRFW::VRMenuObject* button) { + button->SetSurfaceColor(0, {0.1f, 0.1f, 0.1f, 1.0f}); + button->AddFlags(OVRFW::VRMENUOBJECT_DONT_HIT_ALL); + } + + void DetermineHandedness(const OVRFW::ovrApplFrameIn& in) { + if ((handsExtensionAvailable_ && handL_->AreLocationsActive()) || in.LeftRemoteTracked) { + if (currentHandedness_ == InputHandedness::Unknown || + (handsExtensionAvailable_ && handL_->IndexPinching()) || + in.LeftRemoteIndexTrigger > 0.25f) { + currentHandedness_ = InputHandedness::Left; + } + } else if (currentHandedness_ == InputHandedness::Left) { + currentHandedness_ = InputHandedness::Unknown; + } + if ((handsExtensionAvailable_ && handR_->AreLocationsActive()) || in.RightRemoteTracked) { + if (currentHandedness_ == InputHandedness::Unknown || + (handsExtensionAvailable_ && handR_->IndexPinching()) || + in.RightRemoteIndexTrigger > 0.25f) { + currentHandedness_ = InputHandedness::Right; + } + } else if (currentHandedness_ == InputHandedness::Right) { + currentHandedness_ = InputHandedness::Unknown; + } + } + + void UpdateKeyboardPosition( + const OVR::Posef activePointerPose, + OVR::Vector2f distanceScaleMod, + bool shouldFlip) { + if (keyboardMoveDistance_ == 0.0f) { + keyboardMoveDistance_ = + currentPose_.Translation.Distance(activePointerPose.Translation); + } + + float distanceScaleModDeadzone = 0.2f; + + if (std::abs(distanceScaleMod.y) > distanceScaleModDeadzone) { + auto distanceMod = (distanceScaleMod.y > 0) + ? distanceScaleMod.y - distanceScaleModDeadzone + : distanceScaleMod.y + distanceScaleModDeadzone; + keyboardMoveDistance_ *= 1.0f + distanceMod * 0.01f; + keyboardMoveDistance_ = OVR::OVRMath_Clamp(keyboardMoveDistance_, 0.1f, 100.0f); + } + + auto pointFromPointerPose = activePointerPose.Translation + + activePointerPose.Rotation.Normalized() * OVR::Vector3f(0, 0, -1) * + keyboardMoveDistance_; + + // Account for left hand input activePointerPose being flipped + auto targetRotation = (shouldFlip) + ? activePointerPose.Rotation * OVR::Quatf({0, 0, 1}, MATH_FLOAT_PI) + : activePointerPose.Rotation; + auto targetPose = OVR::Posef(targetRotation, pointFromPointerPose); + + float newScale = currentScale_; + + if (std::abs(distanceScaleMod.x) > distanceScaleModDeadzone) { + auto scaleMod = (distanceScaleMod.x > 0) + ? distanceScaleMod.x - distanceScaleModDeadzone + : distanceScaleMod.x + distanceScaleModDeadzone; + newScale = currentScale_ *= 1.0f + scaleMod * 0.01f; + newScale = OVR::OVRMath_Clamp(newScale, 0.4f, 2.0f); + } + + XrVirtualKeyboardLocationInfoMETA locationInfo{XR_TYPE_VIRTUAL_KEYBOARD_LOCATION_INFO_META}; + locationInfo.locationType = XR_VIRTUAL_KEYBOARD_LOCATION_TYPE_CUSTOM_META; + locationInfo.space = GetCurrentSpace(); + locationInfo.poseInSpace = ToXrPosef(targetPose); + locationInfo.scale = newScale; + + virtualKeyboard_->SuggestVirtualKeyboardLocation(&locationInfo); + } + + OVRFW::ovrParticleSystem::handle_t AddParticle( + const OVRFW::ovrApplFrameIn& in, + const OVR::Vector3f& position) { + return particleSystem_.AddParticle( + in, + position, + 0.0f, + OVR::Vector3f(0.0f), + OVR::Vector3f(0.0f), + beamRenderer_.PointerParticleColor, + OVRFW::ovrEaseFunc::NONE, + 0.0f, + 0.03f, + 0.1f, + 0); + } + + void UpdateUIHitTests(const OVRFW::ovrApplFrameIn& in) { + ui_.HitTestDevices().clear(); + particleSystem_.RemoveParticle(leftControllerPoint_); + particleSystem_.RemoveParticle(rightControllerPoint_); + + // The controller actions are still triggered with hand tracking + if (handsExtensionAvailable_ && handL_->IsPositionValid()) { + UpdateRemoteTrackedUIHitTest( + FromXrPosef(handL_->AimPose()), + handL_->IndexPinching() ? 1.0f : 0.0f, + handL_.get(), + HitTestRayDeviceNums::LeftHand); + } else if (in.LeftRemoteTracked) { + UpdateRemoteTrackedUIHitTest( + in.LeftRemotePointPose, + in.LeftRemoteIndexTrigger, + handL_.get(), + HitTestRayDeviceNums::LeftRemote); + leftControllerPoint_ = AddParticle(in, in.LeftRemotePointPose.Translation); + } + + if (handsExtensionAvailable_ && handR_->IsPositionValid()) { + UpdateRemoteTrackedUIHitTest( + FromXrPosef(handR_->AimPose()), + handR_->IndexPinching() ? 1.0f : 0.0f, + handR_.get(), + HitTestRayDeviceNums::RightHand); + } else if (in.RightRemoteTracked) { + UpdateRemoteTrackedUIHitTest( + in.RightRemotePointPose, + in.RightRemoteIndexTrigger, + handR_.get(), + HitTestRayDeviceNums::RightRemote); + rightControllerPoint_ = AddParticle(in, in.RightRemotePointPose.Translation); + } + + ui_.Update(in); + beamRenderer_.Update(in, ui_.HitTestDevices()); + } + + void UpdateRemoteTrackedUIHitTest( + const OVR::Posef& remotePose, + float remoteIndexTrigger, + XrHandHelper* hand, + HitTestRayDeviceNums device) { + if (isShowingKeyboard_) { + const bool controllerNearKeyboard = + keyboardModelRenderer_.IsPointNearKeyboard(remotePose.Translation); + const bool handNearKeyboard = (hand != nullptr) && hand->IsPositionValid() && + keyboardModelRenderer_.IsPointNearKeyboard( + FromXrVector3f(hand->GetScaledJointPose(XR_HAND_JOINT_INDEX_TIP_EXT).position)); + if (controllerNearKeyboard || handNearKeyboard) { + // Don't interact with UI if controller/hand near keyboard + return; + } + } + + const bool didPinch = remoteIndexTrigger > 0.25f; + ui_.AddHitTestRay(remotePose, didPinch, static_cast(device)); + } + + void UpdateHandInteraction(InputHandedness handedness, XrHandHelper& hand) { + const auto rayInputSource = (handedness == InputHandedness::Left) + ? XR_VIRTUAL_KEYBOARD_INPUT_SOURCE_HAND_RAY_LEFT_META + : XR_VIRTUAL_KEYBOARD_INPUT_SOURCE_HAND_RAY_RIGHT_META; + const auto directInputSource = (handedness == InputHandedness::Left) + ? XR_VIRTUAL_KEYBOARD_INPUT_SOURCE_HAND_DIRECT_INDEX_TIP_LEFT_META + : XR_VIRTUAL_KEYBOARD_INPUT_SOURCE_HAND_DIRECT_INDEX_TIP_RIGHT_META; + + const XrPosef& xrAimPose = hand.AimPose(); + const XrPosef& xrTouchPose = hand.GetScaledJointPose(XR_HAND_JOINT_INDEX_TIP_EXT); + const bool didPinch = hand.IndexPinching(); + XrPosef interactorRootPose = hand.WristRootPose(); + + // Send both ray and direct input and let the runtime decide which best to use + const auto result = + virtualKeyboard_->SendVirtualKeyboardInput( + GetCurrentSpace(), rayInputSource, xrAimPose, didPinch, &interactorRootPose) && + virtualKeyboard_->SendVirtualKeyboardInput( + GetCurrentSpace(), directInputSource, xrTouchPose, didPinch, &interactorRootPose); + + // Handle poke limiting + if (result) { + hand.ModifyWristRoot(interactorRootPose); + } + } + + void UpdateControllerInteraction( + InputHandedness handedness, + float remoteIndexTrigger, + const OVR::Posef& remotePointPose, + const OVR::Posef& remotePose, + OVR::Posef& adjustedRemotePose) { + const auto rayInputSource = (handedness == InputHandedness::Left) + ? XR_VIRTUAL_KEYBOARD_INPUT_SOURCE_CONTROLLER_RAY_LEFT_META + : XR_VIRTUAL_KEYBOARD_INPUT_SOURCE_CONTROLLER_RAY_RIGHT_META; + + const auto directInputSource = (handedness == InputHandedness::Left) + ? XR_VIRTUAL_KEYBOARD_INPUT_SOURCE_CONTROLLER_DIRECT_LEFT_META + : XR_VIRTUAL_KEYBOARD_INPUT_SOURCE_CONTROLLER_DIRECT_RIGHT_META; + + const auto& xrAimPose = ToXrPosef(remotePointPose); + const auto& xrTouchPose = ToXrPosef(remotePointPose); + const bool didPinch = remoteIndexTrigger > 0.25f; + XrPosef interactorRootPose = ToXrPosef(remotePose); + + // Send both ray and direct input and let the runtime decide which best to use + auto result = + virtualKeyboard_->SendVirtualKeyboardInput( + GetCurrentSpace(), rayInputSource, xrAimPose, didPinch, &interactorRootPose) && + virtualKeyboard_->SendVirtualKeyboardInput( + GetCurrentSpace(), directInputSource, xrTouchPose, didPinch, &interactorRootPose); + + // Handle poke limiting + if (result) { + adjustedRemotePose = FromXrPosef(interactorRootPose); + } + } + + void UpdateKeyboardInteractions(const OVRFW::ovrApplFrameIn& in) { + if (keyboardExtensionAvailable_ && !isMovingKeyboard_) { + if (handL_->AreLocationsActive()) { + UpdateHandInteraction(InputHandedness::Left, *handL_); + } else if (in.LeftRemoteTracked) { + UpdateControllerInteraction( + InputHandedness::Left, + in.LeftRemoteIndexTrigger, + in.LeftRemotePointPose, + in.LeftRemotePose, + leftAdjustedRemotePose_); + } + + if (handR_->AreLocationsActive()) { + UpdateHandInteraction(InputHandedness::Right, *handR_); + } else if (in.RightRemoteTracked) { + UpdateControllerInteraction( + InputHandedness::Right, + in.RightRemoteIndexTrigger, + in.RightRemotePointPose, + in.RightRemotePose, + rightAdjustedRemotePose_); + } + } + } + + void UpdateKeyboardMoving(const OVRFW::ovrApplFrameIn& in) { + DetermineHandedness(in); + if (isMovingKeyboard_) { + OVR::Posef activePointerPose = OVR::Posef::Identity(); + OVR::Vector2f distanceScaleMod = OVR::Vector2f::ZERO; + bool shouldFlip = false; + + if (currentHandedness_ == InputHandedness::Left) { + if (handsExtensionAvailable_ && handL_->AreLocationsActive()) { + activePointerPose = FromXrPosef(handL_->AimPose()); + shouldFlip = true; + } else if (in.LeftRemoteTracked) { + activePointerPose = in.LeftRemotePointPose; + distanceScaleMod = in.LeftRemoteJoystick; + } + } else { + if (handsExtensionAvailable_ && handR_->AreLocationsActive()) { + activePointerPose = FromXrPosef(handR_->AimPose()); + } else if (in.RightRemoteTracked) { + activePointerPose = in.RightRemotePointPose; + distanceScaleMod = in.RightRemoteJoystick; + } + } + UpdateKeyboardPosition(activePointerPose, distanceScaleMod, shouldFlip); + } + + // Query and update location before render + VirtualKeyboardLocation location; + if (virtualKeyboard_->GetVirtualKeyboardLocation( + GetCurrentSpace(), ToXrTime(in.PredictedDisplayTime), &location)) { + currentPose_ = FromXrPosef(location.pose); + currentScale_ = location.scale; + + if (keyboardModelRenderer_.IsModelLoaded()) { + keyboardModelRenderer_.Update(currentPose_, OVR::Vector3f(currentScale_)); + auto bounds = keyboardModelRenderer_.GetCollisionBounds(); + keyboardHitTest_->SetLocalPose(currentPose_); + auto keyboardSize = bounds.GetSize() * currentScale_; + if (keyboardSize_ != keyboardSize) { + keyboardSize_ = keyboardSize; + keyboardHitTest_->SetSurfaceDims( + 0, + {keyboardSize_.x * OVRFW::VRMenuObject::TEXELS_PER_METER, + keyboardSize_.y * OVRFW::VRMenuObject::TEXELS_PER_METER}); + keyboardHitTest_->RegenerateSurfaceGeometry(0, false); + } + } + } + } + + void OnCommitText(const std::string& text) { + ALOGV("VIRTUALKEYBOARD Text committed: %s", text.c_str()); + textInputBuffer_.append(text); + textInput_->SetText(textInputBuffer_.c_str()); + eventLog_->SetText("Text Committed: %s", text.c_str()); + } + + void OnBackspace() { + ALOGV("VIRTUALKEYBOARD Backspace"); + if (!textInputBuffer_.empty()) { + textInputBuffer_.pop_back(); + textInput_->SetText(textInputBuffer_.c_str()); + } + eventLog_->SetText("Backspace Pressed"); + } + + void OnEnter() { + ALOGV("VIRTUALKEYBOARD Enter"); + textInputBuffer_.append("\n"); + textInput_->SetText(textInputBuffer_.c_str()); + eventLog_->SetText("Enter Pressed"); + } + + void OnKeyboardShown() { + ALOGV("VIRTUALKEYBOARD Shown"); + isShowingKeyboard_ = true; + DisableButton(showKeyboardButton_); + EnableButton(hideKeyboardButton_); + EnableButton(enableMoveKeyboardButton_); + EnableButton(showNearKeyboardButton_); + EnableButton(showFarKeyboardButton_); + keyboardHitTest_->SetVisible(true); + eventLog_->SetText("Keyboard Shown"); + } + + void OnKeyboardHidden() { + ALOGV("VIRTUALKEYBOARD Hidden"); + isShowingKeyboard_ = false; + EnableButton(showKeyboardButton_); + DisableButton(hideKeyboardButton_); + DisableButton(enableMoveKeyboardButton_); + DisableButton(showNearKeyboardButton_); + DisableButton(showFarKeyboardButton_); + keyboardHitTest_->SetVisible(false); + eventLog_->SetText("Keyboard Hidden"); + } + + private: + bool keyboardExtensionAvailable_ = false; + bool handsExtensionAvailable_ = false; + bool renderModelExtensionAvailable_ = false; + bool uiInitialized_ = false; + + // hands - xr interface + std::unique_ptr handL_; + std::unique_ptr handR_; + // hands/controllers - rendering + OVRFW::HandRenderer handRendererL_; + OVRFW::HandRenderer handRendererR_; + OVRFW::ControllerRenderer controllerRenderL_; + OVRFW::ControllerRenderer controllerRenderR_; + + // keyboard - xr interface + std::unique_ptr virtualKeyboard_; + // render model - xr interface + std::unique_ptr renderModel_; + // keyboard - rendering + VirtualKeyboardModelRenderer keyboardModelRenderer_; + XrRenderModelKeyFB modelKey_ = XR_NULL_RENDER_MODEL_KEY_FB; + + // UI + OVRFW::TinyUI ui_; + OVRFW::SimpleBeamRenderer beamRenderer_; + std::vector beams_; + OVRFW::ovrParticleSystem particleSystem_; + OVRFW::ovrParticleSystem::handle_t leftControllerPoint_; + OVRFW::ovrParticleSystem::handle_t rightControllerPoint_; + + OVRFW::VRMenuObject* textInput_ = nullptr; + std::string textInputBuffer_; + OVRFW::VRMenuObject* eventLog_ = nullptr; + + OVRFW::VRMenuObject* keyboardHitTest_ = nullptr; + OVR::Vector3f keyboardSize_ = OVR::Vector3f::ZERO; + + OVRFW::VRMenuObject* showKeyboardButton_ = nullptr; + OVRFW::VRMenuObject* hideKeyboardButton_ = nullptr; + bool isShowingKeyboard_ = false; + + OVRFW::VRMenuObject* localSpaceButton_ = nullptr; + OVRFW::VRMenuObject* stageSpaceButton_ = nullptr; + + OVRFW::VRMenuObject* enableMoveKeyboardButton_ = nullptr; + OVRFW::VRMenuObject* showNearKeyboardButton_ = nullptr; + OVRFW::VRMenuObject* showFarKeyboardButton_ = nullptr; + bool isMovingKeyboard_ = false; + float keyboardMoveDistance_ = 0.0f; + XrVirtualKeyboardLocationTypeMETA locationType_ = XR_VIRTUAL_KEYBOARD_LOCATION_TYPE_DIRECT_META; + + OVRFW::VRMenuObject* clearTextButton_ = nullptr; + + InputHandedness currentHandedness_ = InputHandedness::Unknown; + + OVR::Posef currentPose_ = OVR::Posef::Identity(); + float currentScale_ = 1.0f; + OVR::Posef leftAdjustedRemotePose_ = OVR::Posef::Identity(); + OVR::Posef rightAdjustedRemotePose_ = OVR::Posef::Identity(); +}; + +ENTRY_POINT(XrVirtualKeyboardApp) diff --git a/Samples/XrSamples/XrVirtualKeyboard/assets/assets.txt b/Samples/XrSamples/XrVirtualKeyboard/assets/assets.txt new file mode 100644 index 0000000..2cc30f7 --- /dev/null +++ b/Samples/XrSamples/XrVirtualKeyboard/assets/assets.txt @@ -0,0 +1 @@ +This file is a placeholder. diff --git a/Samples/XrSamples/XrVirtualKeyboard/assets/panel.ktx b/Samples/XrSamples/XrVirtualKeyboard/assets/panel.ktx new file mode 100644 index 0000000..deb13e8 Binary files /dev/null and b/Samples/XrSamples/XrVirtualKeyboard/assets/panel.ktx differ diff --git a/Samples/XrSamples/XrVirtualKeyboard/java/MainActivity.java b/Samples/XrSamples/XrVirtualKeyboard/java/MainActivity.java new file mode 100644 index 0000000..03b50ae --- /dev/null +++ b/Samples/XrSamples/XrVirtualKeyboard/java/MainActivity.java @@ -0,0 +1,28 @@ +// Copyright (c) Facebook Technologies, LLC and its affiliates. All Rights reserved. +package com.oculus.sdk.xrvirtualkeyboard; + +/** + * When using NativeActivity, we currently need to handle loading of dependent shared libraries + * manually before a shared library that depends on them is loaded, since there is not currently a + * way to specify a shared library dependency for NativeActivity via the manifest meta-data. + * + *

The simplest method for doing so is to subclass NativeActivity with an empty activity that + * calls System.loadLibrary on the dependent libraries, which is unfortunate when the goal is to + * write a pure native C/C++ only Android activity. + * + *

A native-code only solution is to load the dependent libraries dynamically using dlopen(). + * However, there are a few considerations, see: + * https://groups.google.com/forum/#!msg/android-ndk/l2E2qh17Q6I/wj6s_6HSjaYJ + * + *

1. Only call dlopen() if you're sure it will succeed as the bionic dynamic linker will + * remember if dlopen failed and will not re-try a dlopen on the same lib a second time. + * + *

2. Must remember what libraries have already been loaded to avoid infinitely looping when + * libraries have circular dependencies. + */ +public class MainActivity extends android.app.NativeActivity { + static { + System.loadLibrary("openxr_loader"); + System.loadLibrary("xrvirtualkeyboard"); + } +} diff --git a/Samples/XrSamples/XrVirtualKeyboard/res/values/strings.xml b/Samples/XrSamples/XrVirtualKeyboard/res/values/strings.xml new file mode 100644 index 0000000..f992393 --- /dev/null +++ b/Samples/XrSamples/XrVirtualKeyboard/res/values/strings.xml @@ -0,0 +1,4 @@ + + + Xr Virtual Keyboard Sample + diff --git a/Samples/bin/scripts/build/build.py.bat b/Samples/bin/scripts/build/build.py.bat new file mode 100755 index 0000000..6b8a704 --- /dev/null +++ b/Samples/bin/scripts/build/build.py.bat @@ -0,0 +1 @@ +@"%~dp0\..\python.bat" build.py %1 %2 %3 %4 %5 diff --git a/Samples/bin/scripts/build/ovrbuild.py b/Samples/bin/scripts/build/ovrbuild.py new file mode 100755 index 0000000..a80f23b --- /dev/null +++ b/Samples/bin/scripts/build/ovrbuild.py @@ -0,0 +1,372 @@ +#!/usr/bin/python +# (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +import argparse +import hashlib +import locale +import os +import shlex +import shutil +import sys +import time +import zipfile +from shutil import rmtree +from subprocess import PIPE, Popen +from sys import argv, exit +from time import sleep +from xml.dom import minidom + +import util + + +class CommandOptions: + def __init__(self, args): + self.should_clean = args.type == "clean" + self.is_debug_build = args.type == "debug" + self.is_retail_build = args.type == "retail" + self.should_install = args.should_install and not self.should_clean + self.clear_logcat = args.clear_logcat + self.loglevel = args.loglevel + self.profile = args.profile + self.use_gradle_daemon = args.use_gradle_daemon + self.keystore_path = args.keystore_path + self.keystore_pswd = args.keystore_pswd + self.keyalias = args.keyalias + self.keyalias_pswd = args.keyalias_pswd + self.scan = args.scan + self.build_cache = args.build_cache + self.configure_on_demand = args.configure_on_demand + self.parallel = args.parallel + + @classmethod + def parse(cls, argv): + args = cls.parser.parse_args(argv) + validate_ok, validation_msg = cls.validate(args) + if not validate_ok: + if validation_msg is not None: + sys.stderr.write(validation_msg) + cls.parser.print_help() + exit(1) + return None + return CommandOptions(args) + + @classmethod + def validate(cls, args): + if args.type == "retail": + if None in ( + args.keystore_path, + args.keystore_pswd, + args.keyalias, + args.keyalias_pswd, + ): + return ( + False, + 'ERROR: When building "retail", --keystore_path, --keystore_pswd, --keyalias,' + " --keyalias_pswd are required.\n", + ) + return (True, None) + + parser = argparse.ArgumentParser(description="Build a project and its dependencies") + parser.add_argument( + "type", type=str, help="the type of build", nargs="?", default="release" + ) + parser.add_argument( + "-n", + help="don't install the built APK", + dest="should_install", + action="store_false", + ) + parser.add_argument( + "-c", + help="clear logcat before running the app", + dest="clear_logcat", + action="store_true", + ) + parser.add_argument( + "-log", + type=str, + help="specify gradle log level [quiet,lifecycle,info,debug]", + dest="loglevel", + default="quiet", + ) + parser.add_argument( + "-p", + help="run gradle with profiling enabled", + dest="profile", + action="store_true", + ) + parser.add_argument( + "--no-daemon", + help="don't use the gradle daemon when building", + dest="use_gradle_daemon", + action="store_false", + ) + parser.add_argument( + "--keystore_path", + type=str, + help="The path to the keystore used for signing", + dest="keystore_path", + action="store", + ) + parser.add_argument( + "--keystore_pswd", + type=str, + help="The password for the keystore", + dest="keystore_pswd", + action="store", + ) + parser.add_argument( + "--keyalias", + type=str, + help="The private key used for signing", + dest="keyalias", + action="store", + ) + parser.add_argument( + "--keyalias_pswd", + type=str, + help="The password for the private key", + dest="keyalias_pswd", + action="store", + ) + parser.add_argument( + "--scan", + help="Perform a Gradle build scan", + dest="scan", + action="store_true", + ) + parser.add_argument( + "--no-build-cache", + help="Disable Gradle build cache", + dest="build_cache", + action="store_false", + ) + parser.add_argument( + "--no-configure-on-demand", + help="Disable Gradle configure on demand", + dest="configure_on_demand", + action="store_false", + ) + parser.add_argument( + "--no-parallel", + help="Disable Gradle parallel builds", + dest="parallel", + action="store_false", + ) + + +class BuildFailedException(Exception): + def __init__(self, message): + self.message = message + + +class NoSourceException(Exception): + pass + + +STRING_TYPES = [str] + + +def print_command_line(cmdline): + if type(cmdline) in STRING_TYPES: + print(cmdline) + else: + printable = "" + for cmd in cmdline: + printable += cmd + printable += " " + print(printable) + + +def build_command_list(cmdline, shell): + """ + Returns a list of command-line arguments for subprocess.Popen + + If cmdline is already an array of arguments, the returned array is just + encoded for the current shell character set. If cmdline is a string, + the arguments are split along whitespace boundaries then encoded. + + :param cmdline: A string or array of command line arguments + """ + if not cmdline: + return [] + encoding = locale.getpreferredencoding() if shell else "utf-8" + cmds = shlex.split(cmdline) if type(cmdline) in STRING_TYPES else cmdline + return map(lambda x: str(x).encode(encoding, "ignore"), cmds) + + +def call(cmdline, targetDir=".", suppressErrors=False, grabStdOut=False, verbose=True): + print_command_line(cmdline) + + useShell = os.name != "posix" + cmds = build_command_list(cmdline, useShell) + + with util.chdir(targetDir): + if verbose: + print(" ".join(map(bytes.decode, cmds))) + if grabStdOut: + p = Popen(cmdline, stdout=PIPE, stderr=PIPE, shell=useShell) + else: + p = Popen(cmdline, stderr=PIPE, shell=useShell) + (out, err) = p.communicate() + if grabStdOut and verbose: + print(out) + if not p.returncode == 0: + # if this is not a source build, there will be no 'assemble' task for the root gradle to complete, so it + # will throw an exception. Rather than have the script determine whether a source build is necessary before + # executing the command, we choose to run it anyway and catch the exception, + gradleTask = ( + "clean" + if command_options.should_clean + else ( + "assembleDebug" + if command_options.is_debug_build + else "assembleRelease" + ) + ) + err_decoded = err.decode("utf-8") + if ( + "Task '%s' not found in root project 'OculusRoot'" % gradleTask + ) in err_decoded: + raise NoSourceException(targetDir) + error_string = "command (%s) failed with returncode: %d" % ( + cmdline, + p.returncode, + ) + if verbose: + print(err_decoded) + if suppressErrors: + print(error_string) + else: + raise BuildFailedException(error_string) + + return (p.returncode, out, err) + + +def check_call(cmdline): + try: + call(cmdline, suppressErrors=False, grabStdOut=True, verbose=False) + return True + except Exception as e: + return False + + +def init(options_parser=CommandOptions.parse): + global command_options + command_options = options_parser(sys.argv[1:]) + + ndk_envars = ["ANDROID_NDK", "NDKROOT", "ANDROID_NDK_HOME"] + sdk_envars = ["ANDROID_HOME"] + + # Check to see if we have the right tools installed. + if not ( + any(map(lambda x: os.environ.get(x), ndk_envars)) + or check_call(["ndk-build", "--version"]) + ): + print( + "ndk-build not found! Make sure ANDROID_NDK_HOME is set for command line builds" + ) + if not ( + any(map(lambda x: os.environ.get(x), sdk_envars)) + or check_call(["adb", "version"]) + ): + print("adb not found! Make sure ANDROID_HOME is set for command line builds") + + +def gradle_command(): + # Use the wrapper so people don't need to install Gradle + # Handle directory structure types for building from an app project and building + # from within a lib project + paths = [".", "../../", "../../../", "../../../../", "../../../../../"] + for path in paths: + if os.path.exists(os.path.join(path, "gradlew")): + return os.path.abspath(os.path.join(path, "gradlew")) + return None + + +def find_gradle_root_project(): + # Handle both directory structure types: ones with Projects/Android and ones without + paths = [".", "../../", "../../../"] + for path in paths: + # settings.gradle files indicate a Gradle app (or 'root project') + # whereas build.gradle files indicate a Gradle project/module that + # typically the app will pull in via settings.gradle. + if os.path.exists(os.path.join(path, "settings.gradle")): + return os.path.join(path, "build.gradle") + return None + + +def run_gradle_task(opts, task, args=None): + """ + Forks a sub-process to execute a gradle build task + + :param opts: Parsed command line options + :param task: Gradle task name + :param args: Array of additional arguments to supply to gradle build + """ + flags = [task] + flags.append("--daemon" if opts.use_gradle_daemon else "--no-daemon") + # lifecycle logging is enabled when a log level is not specified. + if opts.loglevel != "lifecycle": + flags.append("-%s" % opts.loglevel) + if opts.profile: + flags.append("--profile") + if opts.scan: + flags.append("--scan") + if opts.clear_logcat: + flags.append("-Pclear_logcat") + if opts.build_cache: + flags.append("--build-cache") + if opts.configure_on_demand: + flags.append("--configure-on-demand") + if opts.parallel: + flags.append("--parallel") + + gradle_file_path = find_gradle_root_project() + with util.chdir(os.path.dirname(gradle_file_path)): + beginTime = time.time() + command = [gradle_command()] + flags + (args or []) + call(command) + endTime = time.time() + deltaTime = endTime - beginTime + print("Gradle took %f seconds" % deltaTime) + + +def build_in_dir(targetDir, args=[]): + with util.chdir(targetDir): + print("\n\nbuilding in " + targetDir) + if os.path.exists("build.gradle"): + if command_options.should_clean: + run_gradle_task(command_options, "clean") + elif command_options.is_debug_build: + run_gradle_task(command_options, "assembleDebug", args) + else: + run_gradle_task(command_options, "assembleRelease", args) + print("\n\nfinished building in " + targetDir) + + +def build(): + try: + # print gradle version + run_gradle_task(command_options, "--version") + # print ndk version + print("ANDROID_NDK_HOME: %s" % os.environ.get("ANDROID_NDK_HOME")) + + # set flags for build + flags = [] + if command_options.should_install: + flags.append("-Pshould_install") + if command_options.keystore_path: + flags.append("-Pkey.store=%s" % command_options.keystore_path) + if command_options.keystore_pswd: + flags.append("-Pkey.store.password=%s" % command_options.keystore_pswd) + if command_options.keyalias: + flags.append("-Pkey.alias=%s" % command_options.keyalias) + if command_options.keyalias_pswd: + flags.append("-Pkey.alias.password=%s" % command_options.keyalias_pswd) + + # build the application + build_in_dir(".", flags) + except BuildFailedException as e: + print(e.message) + exit(-1) diff --git a/Samples/bin/scripts/build/ovrbuild_keystore.py b/Samples/bin/scripts/build/ovrbuild_keystore.py new file mode 100755 index 0000000..cd15cc6 --- /dev/null +++ b/Samples/bin/scripts/build/ovrbuild_keystore.py @@ -0,0 +1,119 @@ +#!/usr/bin/env python + +import os, socket, sys + +workingDir = os.getcwd() + + +def init(): + root = os.path.realpath(os.path.dirname(os.path.realpath(__file__))) + os.chdir(root) # make sure we are always executing from the project directory + while os.path.isdir(os.path.join(root, "bin/scripts/build")) == False: + root = os.path.realpath(os.path.join(root, "..")) + if ( + len(root) <= 5 + ): # Should catch both Posix and Windows root directories (e.g. '/' and 'C:\') + print("Unable to find SDK root. Exiting.") + sys.exit(1) + root = os.path.abspath(root) + os.environ["OCULUS_SDK_PATH"] = root + sys.path.append(root + "/bin/scripts/build") + + +init() +import ovrbuild + +ovrbuild.init() + + +def generate_distinguished_name(): + """ + Generates a moderately unique X.509 distinguished name + + :return: Array of RDN tuples + """ + user = os.environ.get("USERNAME", os.environ.get("USER", "Unknown")) + host = socket.gethostname() + return [("CN", "ovrbuild"), ("CN", user), ("OU", host)] + + +def encode_distinguished_name(dn): + """ + Creates a canonical X.509 distinguished name string from an array of RDNs + + :param dn: Distinguished name stored as array of RDN tuples + """ + # escape embedded commas in any RDN so that they don't cause problems for + # keytool's command-line parser + return ",".join(map(lambda x: x[0] + "=" + x[1].replace(",", r"\,"), dn)) + + +def create_keystore(execfn, path, alias, storepw, keypw, replace=False, sdn=None): + """ + Creates a JKS keystore with a single private key entry + + :param execfn: Function to execute shell command, f(List[Tuple]) => Tuple + :param path: Path to overwrite with the new keystore + :param alias: Alias for the added key entry + :param storepw: Encryption password for the keystore. Must be >= 6 chars + :param keypw: Encryption password for the private key. >= 6 chars. + :param replace: Overwrite existing keystore file if it exists + :param sdn: Array of RDN tuples for the subject distinguished name + """ + if not (path and alias and storepw and keypw): + raise ValueError("Missing argument") + if len(storepw) < 6 or len(keypw) < 6: + raise ValueError("Insufficient password length") + if os.path.exists(path) and not replace: + return None + + if os.path.exists(path): + os.unlink(path) + + cmd = [ + "keytool", + "-v", + "-genkey", + "-keyalg", + "RSA", + "-keystore", + path, + "-storepass", + storepw, + "-alias", + alias, + "-keypass", + keypw, + "-validity", + "10000", + "-dname", + encode_distinguished_name(sdn or generate_distinguished_name()), + ] + + return execfn(cmd) + + +def genDebugKeystore(): + # verify 'keytool' is on the PATH before trying to execute it. + if not ovrbuild.check_call(["keytool"]): + raise EnvironmentError("keytool not found! Verify JDK bin folder is on PATH!") + + os.chdir(workingDir) # make sure we are always executing from the project directory + path = os.path.join(".", "android.debug.keystore") + debug_props = { + "keystore": path, + "alias": "androiddebugkey", + "storepass": "android", + "keypass": "android", + } + create_keystore( + lambda x: ovrbuild.call(x), + debug_props["keystore"], + debug_props["alias"], + debug_props["storepass"], + debug_props["keypass"], + replace=False, + ) + + +genDebugKeystore() diff --git a/Samples/bin/scripts/build/ovrbuild_keystore.py.bat b/Samples/bin/scripts/build/ovrbuild_keystore.py.bat new file mode 100755 index 0000000..e2c6358 --- /dev/null +++ b/Samples/bin/scripts/build/ovrbuild_keystore.py.bat @@ -0,0 +1 @@ +@%~dp0..\python.bat %~dp0ovrbuild_keystore.py diff --git a/Samples/bin/scripts/build/perproject/build.py b/Samples/bin/scripts/build/perproject/build.py new file mode 100755 index 0000000..d4b6e58 --- /dev/null +++ b/Samples/bin/scripts/build/perproject/build.py @@ -0,0 +1,35 @@ +#!/usr/bin/python +# (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. + +# This first bit of code is common bootstrapping code +# to determine the SDK root, and to set up the import +# path for additional python code. + +# begin bootstrap +import os +import sys + + +def init(): + root = os.path.realpath(os.path.dirname(os.path.realpath(__file__))) + os.chdir(root) # make sure we are always executing from the project directory + while os.path.isdir(os.path.join(root, "bin/scripts/build")) == False: + root = os.path.realpath(os.path.join(root, "..")) + if ( + len(root) <= 5 + ): # Should catch both Posix and Windows root directories (e.g. '/' and 'C:\') + print("Unable to find SDK root. Exiting.") + sys.exit(1) + root = os.path.abspath(root) + os.environ["OCULUS_SDK_PATH"] = root + sys.path.append(root + "/bin/scripts/build") + + +init() +import ovrbuild + +ovrbuild.init() +# end bootstrap + + +ovrbuild.build() diff --git a/Samples/bin/scripts/build/perproject/build.py.bat b/Samples/bin/scripts/build/perproject/build.py.bat new file mode 100755 index 0000000..facf79f --- /dev/null +++ b/Samples/bin/scripts/build/perproject/build.py.bat @@ -0,0 +1,29 @@ +@rem Only edit the master copy of this file in SDK_ROOT/bin/scripts/build/perproject + +@setlocal enableextensions enabledelayedexpansion + +@if not exist "build.gradle" @echo Build script must be executed from project directory. & goto :Abort + +@set P=.. + +:TryAgain + +@rem @echo P = %P% + +@if exist "%P%\bin\scripts\build\build.py.bat" goto :Found + +@if exist "%P%\bin\scripts\build" @echo "Could not find build.py.bat" & goto :Abort + +@set P=%P%\.. + +@goto :TryAgain + +:Found + +@set P=%P%\bin\scripts\build +@call %P%\build.py.bat %1 %2 %3 %4 %5 +@goto :End + +:Abort + +:End diff --git a/Samples/bin/scripts/build/util.py b/Samples/bin/scripts/build/util.py new file mode 100755 index 0000000..af5c71a --- /dev/null +++ b/Samples/bin/scripts/build/util.py @@ -0,0 +1,13 @@ +import os + + +class chdir: + def __init__(self, path): + self.path = path + + def __enter__(self): + self.old_dir = os.getcwd() + os.chdir(self.path) + + def __exit__(self, exc_type, exc_value, traceback): + os.chdir(self.old_dir) diff --git a/Samples/bin/scripts/python.bat b/Samples/bin/scripts/python.bat new file mode 100755 index 0000000..52b56d9 --- /dev/null +++ b/Samples/bin/scripts/python.bat @@ -0,0 +1,14 @@ +@rem This script attempts to locate python.exe and executes it +@rem with the requested command-line parameters. + +@setlocal enableextensions enabledelayedexpansion + +@set P= + +@python --version 2>NUL +@if errorlevel 1 ( + @echo. + @echo ERROR^: Cannot find python. Make sure it is installed and has been added to your 'Path' system environment variable. +) else ( + @python %1 %2 %3 %4 %5 %6 +) diff --git a/Samples/build.gradle b/Samples/build.gradle new file mode 100755 index 0000000..2bb0e6e --- /dev/null +++ b/Samples/build.gradle @@ -0,0 +1,6 @@ +buildscript { + repositories { + google() + mavenCentral() + } +} diff --git a/Samples/gradle.properties b/Samples/gradle.properties new file mode 100644 index 0000000..3e927b1 --- /dev/null +++ b/Samples/gradle.properties @@ -0,0 +1,21 @@ +# Project-wide Gradle settings. +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. More details, visit +# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects +# org.gradle.parallel=true +# AndroidX package structure to make it clearer which packages are bundled with the +# Android operating system, and which are packaged with your app's APK +# https://developer.android.com/topic/libraries/support-library/androidx-rn +android.useAndroidX=true +# Enables namespacing of each library's R class so that its R class includes only the +# resources declared in the library itself and none from the library's dependencies, +# thereby reducing the size of the R class for that library +android.nonTransitiveRClass=true \ No newline at end of file diff --git a/Samples/gradle/wrapper/gradle-wrapper.jar b/Samples/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..7454180 Binary files /dev/null and b/Samples/gradle/wrapper/gradle-wrapper.jar differ diff --git a/Samples/gradle/wrapper/gradle-wrapper.properties b/Samples/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..ffed3a2 --- /dev/null +++ b/Samples/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/Samples/gradlew b/Samples/gradlew new file mode 100755 index 0000000..a69d9cb --- /dev/null +++ b/Samples/gradlew @@ -0,0 +1,240 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# 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 +# +# https://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. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit + +APP_NAME="Gradle" +APP_BASE_NAME=${0##*/} + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/Samples/gradlew.bat b/Samples/gradlew.bat new file mode 100755 index 0000000..f127cfd --- /dev/null +++ b/Samples/gradlew.bat @@ -0,0 +1,91 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/Samples/settings.gradle.kts b/Samples/settings.gradle.kts new file mode 100644 index 0000000..11f9ab3 --- /dev/null +++ b/Samples/settings.gradle.kts @@ -0,0 +1,12 @@ +// Copyright (c) Facebook Technologies, LLC and its affiliates. All rights reserved. +rootProject.name = ("MetaOpenXRSDK") + +val folderPath = "XrSamples/" + +File(folderPath).listFiles()?.forEach { file -> + if (file.isDirectory) { + val dir = File("XrSamples/${file.name}/Projects/Android") + include(":${file.name}") + project(":${file.name}").projectDir = dir + } +}