From 09709900614cf44ad405c97ca2eb4c0a2a93bb61 Mon Sep 17 00:00:00 2001 From: Nick Fisher Date: Fri, 1 Mar 2024 22:46:00 +0800 Subject: [PATCH 01/52] update tests --- test/cpp/test.cpp | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/test/cpp/test.cpp b/test/cpp/test.cpp index 8612b512..1de753ef 100644 --- a/test/cpp/test.cpp +++ b/test/cpp/test.cpp @@ -63,7 +63,20 @@ int main(int argc, char** argv) { auto sceneManager = SceneManager(&loader, engine, scene, nullptr); - auto shapes = sceneManager.loadGlb("../example/assets/shapes/shapes.glb", 1); + auto shapes = sceneManager.loadGlb("../example/assets/shapes/shapes.glb", 2); + + sceneManager.remove(shapes); + + shapes = sceneManager.loadGlb("../example/assets/shapes/shapes.glb", 2); + + auto instanceCount = sceneManager.getInstanceCount(shapes); + assert(instanceCount == 2); + + EntityId instances[instanceCount]; + + sceneManager.getInstances(shapes, instances); + + sceneManager.transformToUnitCube(shapes); auto morphTargetNames = sceneManager.getMorphTargetNames(shapes, "Cylinder"); assert(morphTargetNames->size() == 4); @@ -73,5 +86,9 @@ int main(int argc, char** argv) { morphTargetNames = sceneManager.getMorphTargetNames(shapes, "Cone"); assert(morphTargetNames->size() == 8); + math::mat4f boneTransform; + sceneManager.setBoneTransform(shapes, "Cylinder", 0, "Bone", boneTransform); + + sceneManager.destroyAll(); } \ No newline at end of file From 9b9a0b7ce55d56b9b40060b843bdbe10e79ae7f5 Mon Sep 17 00:00:00 2001 From: Nick Fisher Date: Fri, 1 Mar 2024 22:46:23 +0800 Subject: [PATCH 02/52] split component managers --- .../components/AnimationComponentManager.hpp | 244 ++++++++++++++++++ ...onents.h => CollisionComponentManager.hpp} | 20 +- 2 files changed, 255 insertions(+), 9 deletions(-) create mode 100644 ios/include/components/AnimationComponentManager.hpp rename ios/include/components/{StandardComponents.h => CollisionComponentManager.hpp} (93%) diff --git a/ios/include/components/AnimationComponentManager.hpp b/ios/include/components/AnimationComponentManager.hpp new file mode 100644 index 00000000..98f1eb29 --- /dev/null +++ b/ios/include/components/AnimationComponentManager.hpp @@ -0,0 +1,244 @@ +#pragma once + +#include "Log.hpp" + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +template class std::vector; +namespace flutter_filament { + using namespace filament; + using namespace filament::gltfio; + using namespace utils; + using namespace std::chrono; + + typedef std::chrono::time_point time_point_t; + + enum AnimationType { + MORPH, BONE, GLTF + }; + + struct AnimationStatus { + time_point_t start = time_point_t::max(); + bool loop = false; + bool reverse = false; + float durationInSecs = 0; + }; + + struct GltfAnimation : AnimationStatus { + int index = -1; + }; + + // + // Use this to construct a dynamic (i.e. non-glTF embedded) morph target animation. + // + struct MorphAnimation : AnimationStatus { + utils::Entity meshTarget; + int numFrames = -1; + float frameLengthInMs = 0; + std::vector frameData; + std::vector morphIndices; + int lengthInFrames; + }; + + // + // Use this to construct a dynamic (i.e. non-glTF embedded) bone/joint animation. + // + struct BoneAnimation : AnimationStatus { + size_t boneIndex; + std::vector meshTargets; + size_t skinIndex = 0; + int lengthInFrames; + float frameLengthInMs = 0; + std::vector frameData; + }; + + struct AnimationComponent { + FilamentInstance* instance; + std::vector initialJointTransforms; + std::vector gltfAnimations; + std::vector morphAnimations; + std::vector boneAnimations; + + // the index of the last active glTF animation, + // used to cross-fade + int fadeGltfAnimationIndex = -1; + float fadeDuration = 0.0f; + float fadeOutAnimationStart = 0.0f; + + }; + + + class AnimationComponentManager : public utils::SingleInstanceComponentManager { + + filament::TransformManager& _transformManager; + filament::RenderableManager& _renderableManager; + + public: + AnimationComponentManager( + filament::TransformManager& transformManager, + filament::RenderableManager& renderableManager) : + _transformManager(transformManager), + _renderableManager(renderableManager) {}; + + void addAnimationComponent(FilamentInstance* instance) { + const auto joints = instance->getJointsAt(0); + AnimationComponent animationComponent; + animationComponent.instance = instance; + for(int i = 0; i < instance->getJointCountAt(0); i++) { + const auto joint = joints[i]; + const auto& jointTransformInstance = _transformManager.getInstance(joint); + const auto& jointTransform = _transformManager.getTransform(jointTransformInstance); + animationComponent.initialJointTransforms.push_back(jointTransform); + } + auto componentInstance = addComponent(instance->getRoot()); + this->elementAt<0>(componentInstance) = animationComponent; + } + + void update() + { + auto now = high_resolution_clock::now(); + + for(auto it = begin(); it < end(); it++) { + const auto& entity = getEntity(it); + + auto componentInstance = getInstance(entity); + auto& animationComponent = elementAt<0>(componentInstance); + + auto animator = animationComponent.instance->getAnimator(); + auto& gltfAnimations = animationComponent.gltfAnimations; + auto& morphAnimations = animationComponent.morphAnimations; + auto& boneAnimations = animationComponent.boneAnimations; + + for (int i = ((int)gltfAnimations.size()) - 1; i >= 0; i--) { + + auto animationStatus = animationComponent.gltfAnimations[i]; + + auto elapsedInSecs = float(std::chrono::duration_cast(now - animationStatus.start).count()) / 1000.0f; + + if (!animationStatus.loop && elapsedInSecs >= animationStatus.durationInSecs) + { + animator->applyAnimation(animationStatus.index, animationStatus.durationInSecs - 0.001); + animator->updateBoneMatrices(); + gltfAnimations.erase(gltfAnimations.begin() + i); + animationComponent.fadeGltfAnimationIndex = -1; + continue; + } + animator->applyAnimation(animationStatus.index, elapsedInSecs); + + if (animationComponent.fadeGltfAnimationIndex != -1 && elapsedInSecs < animationComponent.fadeDuration) + { + // cross-fade + auto fadeFromTime = animationComponent.fadeOutAnimationStart + elapsedInSecs; + auto alpha = elapsedInSecs / animationComponent.fadeDuration; + animator->applyCrossFade(animationComponent.fadeGltfAnimationIndex, fadeFromTime, alpha); + } + } + + animator->updateBoneMatrices(); + + for (int i = (int)morphAnimations.size() - 1; i >= 0; i--) { + + auto animationStatus = morphAnimations[i]; + + auto elapsedInSecs = float(std::chrono::duration_cast(now - animationStatus.start).count()) / 1000.0f; + + if (!animationStatus.loop && elapsedInSecs >= animationStatus.durationInSecs) + { + morphAnimations.erase(morphAnimations.begin() + i); + continue; + } + + + int frameNumber = static_cast(elapsedInSecs * 1000.0f / animationStatus.frameLengthInMs) % animationStatus.lengthInFrames; + // offset from the end if reverse + if (animationStatus.reverse) + { + frameNumber = animationStatus.lengthInFrames - frameNumber; + } + auto baseOffset = frameNumber * animationStatus.morphIndices.size(); + for (int i = 0; i < animationStatus.morphIndices.size(); i++) + { + auto morphIndex = animationStatus.morphIndices[i]; + // set the weights appropriately + _renderableManager.setMorphWeights( + _renderableManager.getInstance(animationStatus.meshTarget), + animationStatus.frameData.data() + baseOffset + i, + 1, + morphIndex); + } + } + for (int i = (int)boneAnimations.size() - 1; i >= 0; i--) { + auto animationStatus = boneAnimations[i]; + + auto elapsedInSecs = float(std::chrono::duration_cast(now - animationStatus.start).count()) / 1000.0f; + + if (!animationStatus.loop && elapsedInSecs >= animationStatus.durationInSecs) + { + boneAnimations.erase(boneAnimations.begin() + i); + continue; + } + + float elapsedFrames = elapsedInSecs * 1000.0f / animationStatus.frameLengthInMs; + + int currFrame = static_cast(elapsedFrames) % animationStatus.lengthInFrames; + float delta = elapsedFrames - currFrame; + int nextFrame = currFrame; + auto restLocalTransform = animationComponent.initialJointTransforms[animationStatus.boneIndex]; + + // offset from the end if reverse + if (animationStatus.reverse) + { + currFrame = animationStatus.lengthInFrames - currFrame; + if(currFrame > 0) { + nextFrame = currFrame - 1; + } else { + nextFrame = 0; + } + } else { + if(currFrame < animationStatus.lengthInFrames - 1) { + nextFrame = currFrame + 1; + } else { + nextFrame = currFrame; + } + } + + // simple linear interpolation + math::mat4f curr = (1 - delta) * (restLocalTransform * animationStatus.frameData[currFrame]); + math::mat4f next = delta * (restLocalTransform * animationStatus.frameData[nextFrame]); + math::mat4f localTransform = curr + next; + + const Entity joint = animationComponent.instance->getJointsAt(animationStatus.skinIndex)[animationStatus.boneIndex]; + + auto jointTransform = _transformManager.getInstance(joint); + + _transformManager.setTransform(jointTransform, localTransform); + + animator->updateBoneMatrices(); + + if (animationStatus.loop && elapsedInSecs >= animationStatus.durationInSecs) + { + animationStatus.start = now; + } + } + } + } + }; + +} diff --git a/ios/include/components/StandardComponents.h b/ios/include/components/CollisionComponentManager.hpp similarity index 93% rename from ios/include/components/StandardComponents.h rename to ios/include/components/CollisionComponentManager.hpp index 31da4ab4..74d580a0 100644 --- a/ios/include/components/StandardComponents.h +++ b/ios/include/components/CollisionComponentManager.hpp @@ -1,5 +1,4 @@ -#ifndef _STANDARD_COMPONENTS_H -#define _STANDARD_COMPONENTS_H +#pragma once #include "utils/Entity.h" #include "utils/EntityInstance.h" @@ -9,7 +8,7 @@ #include "gltfio/FilamentInstance.h" #include "Log.hpp" -namespace polyvox +namespace flutter_filament { typedef void(*CollisionCallback)(int32_t entityId1, int32_t entityId2) ; @@ -19,11 +18,15 @@ class CollisionComponentManager : public utils::SingleInstanceComponentManager collides(EntityId transformingEntityId, filament::Aabb sourceBox) { + std::vector collides(utils::Entity transformingEntity, filament::Aabb sourceBox) { auto sourceCorners = sourceBox.getCorners(); std::vector collisionAxes; for(auto it = begin(); it < end(); it++) { auto entity = getEntity(it); + + if(entity == transformingEntity) { + continue; + } auto targetXformInstance = _transformManager.getInstance(entity); auto targetXform = _transformManager.getWorldTransform(targetXformInstance); auto targetBox = elementAt<0>(it).transform(targetXform); @@ -40,10 +43,10 @@ class CollisionComponentManager : public utils::SingleInstanceComponentManager(it); if(callback) { - callback(utils::Entity::smuggle(entity), transformingEntityId); + callback(utils::Entity::smuggle(entity), utils::Entity::smuggle(transformingEntity)); } } } @@ -92,7 +95,6 @@ class CollisionComponentManager : public utils::SingleInstanceComponentManager Date: Fri, 1 Mar 2024 22:46:53 +0800 Subject: [PATCH 03/52] namespace changes --- ios/include/material/FileMaterialProvider.hpp | 34 +++++++++++-------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/ios/include/material/FileMaterialProvider.hpp b/ios/include/material/FileMaterialProvider.hpp index 64d63074..dacf41ab 100644 --- a/ios/include/material/FileMaterialProvider.hpp +++ b/ios/include/material/FileMaterialProvider.hpp @@ -1,6 +1,7 @@ #ifndef FILE_MATERIAL_PROVIDER #define FILE_MATERIAL_PROVIDER +#include #include #include #include @@ -8,31 +9,34 @@ #include #include #include +#include "Log.hpp" -namespace polyvox { - class FileMaterialProvider : public MaterialProvider { +namespace flutter_filament { - Material* _m; - const Material* _ms[1]; - Texture* mDummyTexture = nullptr; + + class FileMaterialProvider : public filament::gltfio::MaterialProvider { + + filament::Material* _m; + const filament::Material* _ms[1]; + filament::Texture* mDummyTexture = nullptr; public: - FileMaterialProvider(Engine* engine, const void* const data, const size_t size) { - _m = Material::Builder() + FileMaterialProvider(filament::Engine* engine, const void* const data, const size_t size) { + _m = filament::Material::Builder() .package(data, size) .build(*engine); _ms[0] = _m; unsigned char texels[4] = {}; - mDummyTexture = Texture::Builder() + mDummyTexture = filament::Texture::Builder() .width(1).height(1) - .format(Texture::InternalFormat::RGBA8) + .format(filament::Texture::InternalFormat::RGBA8) .build(*engine); - Texture::PixelBufferDescriptor pbd(texels, sizeof(texels), Texture::Format::RGBA, - Texture::Type::UBYTE); + filament::Texture::PixelBufferDescriptor pbd(texels, sizeof(texels), filament::Texture::Format::RGBA, + filament::Texture::Type::UBYTE); mDummyTexture->setImage(*engine, 0, std::move(pbd)); } - filament::MaterialInstance* createMaterialInstance(MaterialKey* config, UvMap* uvmap, + filament::MaterialInstance* createMaterialInstance(filament::gltfio::MaterialKey* config, filament::gltfio::UvMap* uvmap, const char* label = "material", const char* extras = nullptr) { auto getUvIndex = [uvmap](uint8_t srcIndex, bool hasTexture) -> int { @@ -40,14 +44,14 @@ namespace polyvox { }; auto instance = _m->createInstance(); - math::mat3f identity; + filament::math::mat3f identity; instance->setParameter("baseColorUvMatrix", identity); instance->setParameter("normalUvMatrix", identity); instance->setParameter("baseColorIndex", getUvIndex(config->baseColorUV, config->hasBaseColorTexture)); instance->setParameter("normalIndex", getUvIndex(config->normalUV, config->hasNormalTexture)); if(config->hasNormalTexture) { - TextureSampler sampler; + filament::TextureSampler sampler; instance->setParameter("normalMap", mDummyTexture, sampler); instance->setParameter("baseColorMap", mDummyTexture, sampler); } else { @@ -60,7 +64,7 @@ namespace polyvox { /** * Creates or fetches a compiled Filament material corresponding to the given config. */ - virtual Material* getMaterial(MaterialKey* config, UvMap* uvmap, const char* label = "material") { + virtual filament::Material* getMaterial(filament::gltfio::MaterialKey* config, filament::gltfio::UvMap* uvmap, const char* label = "material") { return _m; } From 6525dea6c26c58483c389166933600feb3552a83 Mon Sep 17 00:00:00 2001 From: Nick Fisher Date: Fri, 1 Mar 2024 22:47:01 +0800 Subject: [PATCH 04/52] namespace changes --- windows/flutter_filament_plugin.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/windows/flutter_filament_plugin.cpp b/windows/flutter_filament_plugin.cpp index 2c23bbd0..7c3d4d96 100644 --- a/windows/flutter_filament_plugin.cpp +++ b/windows/flutter_filament_plugin.cpp @@ -44,10 +44,11 @@ #include "wgl_context.h" #endif -using namespace std::chrono_literals; - namespace flutter_filament { + using namespace std::chrono_literals; + + void FlutterFilamentPlugin::RegisterWithRegistrar( flutter::PluginRegistrarWindows *registrar) { auto channel = From 9295059885005b927f428be7b82edbdeb268f50c Mon Sep 17 00:00:00 2001 From: Nick Fisher Date: Fri, 1 Mar 2024 22:47:35 +0800 Subject: [PATCH 05/52] namespace changes, setCameraRotation accepts quat, getMainCamera --- ios/include/FilamentViewer.hpp | 35 +++++++++++++++++----------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/ios/include/FilamentViewer.hpp b/ios/include/FilamentViewer.hpp index 8324e1e8..ce908396 100644 --- a/ios/include/FilamentViewer.hpp +++ b/ios/include/FilamentViewer.hpp @@ -35,17 +35,16 @@ #include "SceneManager.hpp" #include "ThreadPool.hpp" -using namespace std; -using namespace filament; -using namespace filament::math; -using namespace gltfio; -using namespace camutils; - -typedef int32_t EntityId; - -namespace polyvox +namespace flutter_filament { + typedef std::chrono::time_point time_point_t; + + using namespace std::chrono; + using namespace filament; + using namespace filament::math; + using namespace gltfio; + using namespace camutils; enum ToneMapping { @@ -56,6 +55,8 @@ namespace polyvox class FilamentViewer { + + typedef int32_t EntityId; public: FilamentViewer(const void *context, const ResourceLoaderWrapper *const resourceLoaderWrapper, void *const platform = nullptr, const char *uberArchivePath = nullptr); ~FilamentViewer(); @@ -83,6 +84,7 @@ namespace polyvox bool setCamera(EntityId asset, const char *nodeName); void setMainCamera(); + EntityId getMainCamera(); void setCameraFov(double fovDegrees, double aspect); void createSwapChain(const void *surface, uint32_t width, uint32_t height); @@ -103,7 +105,7 @@ namespace polyvox void setViewFrustumCulling(bool enabled); void setCameraExposure(float aperture, float shutterSpeed, float sensitivity); void setCameraPosition(float x, float y, float z); - void setCameraRotation(float rads, float x, float y, float z); + void setCameraRotation(float w, float x, float y, float z); const math::mat4 getCameraModelMatrix(); const math::mat4 getCameraViewMatrix(); const math::mat4 getCameraProjectionMatrix(); @@ -134,6 +136,7 @@ namespace polyvox void setRecordingOutputDirectory(const char* path); void setAntiAliasing(bool msaaEnabled, bool fxaaEnabled, bool taaEnabled); + void setDepthOfField(); EntityId createGeometry(float* vertices, uint32_t numVertices, uint16_t* indices, uint32_t numIndices, const char* materialPath); @@ -158,11 +161,9 @@ namespace polyvox SceneManager *_sceneManager = nullptr; - NameComponentManager *_ncm = nullptr; - std::mutex mtx; // mutex to ensure thread safety when removing assets - vector _lights; + std::vector _lights; Texture *_skyboxTexture = nullptr; Skybox *_skybox = nullptr; Texture *_iblTexture = nullptr; @@ -199,10 +200,10 @@ namespace polyvox IndexBuffer *_imageIb = nullptr; Material *_imageMaterial = nullptr; TextureSampler _imageSampler; - void loadKtx2Texture(string path, ResourceBuffer data); - void loadKtxTexture(string path, ResourceBuffer data); - void loadPngTexture(string path, ResourceBuffer data); - void loadTextureFromPath(string path); + void loadKtx2Texture(std::string path, ResourceBuffer data); + void loadKtxTexture(std::string path, ResourceBuffer data); + void loadPngTexture(std::string path, ResourceBuffer data); + void loadTextureFromPath(std::string path); void savePng(void* data, size_t size, int frameNumber); time_point_t _recordingStartTime = std::chrono::high_resolution_clock::now(); From 6c6bcfe7a425f307035f5a93d41a65248e08d7a3 Mon Sep 17 00:00:00 2001 From: Nick Fisher Date: Fri, 1 Mar 2024 22:48:39 +0800 Subject: [PATCH 06/52] work --- example/assets/shapes/shapes.blend | 4 +- example/assets/shapes/shapes.glb | 4 +- example/lib/main.dart | 103 +- example/lib/menus/asset_submenu.dart | 49 +- example/lib/menus/camera_submenu.dart | 8 +- example/lib/picker_result_widget.dart | 7 +- ios/include/FlutterFilamentApi.h | 9 +- ios/include/FlutterFilamentFFIApi.h | 32 +- ios/include/SceneAsset.hpp | 93 -- ios/include/SceneManager.hpp | 78 +- ios/include/StreamBufferAdapter.hpp | 11 +- ios/src/FilamentViewer.cpp | 68 +- ios/src/FlutterFilamentApi.cpp | 43 +- ios/src/FlutterFilamentFFIApi.cpp | 73 +- ios/src/SceneManager.cpp | 1132 ++++++++--------- ios/src/StreamBufferAdapter.cpp | 28 +- lib/camera/camera_orientation.dart | 15 + lib/filament_controller.dart | 52 +- lib/filament_controller_ffi.dart | 124 +- lib/generated_bindings.dart | 98 +- lib/hardware/hardware_keyboard_poll.dart | 42 + lib/lights/light_options.dart | 29 + lib/widgets/camera_options_widget.dart | 136 +- lib/widgets/filament_gesture_detector.dart | 18 +- .../filament_gesture_detector_desktop.dart | 34 +- .../filament_gesture_detector_mobile.dart | 31 +- lib/widgets/light_slider.dart | 150 ++- linux/flutter_filament_plugin.cc | 6 +- .../flutter_filament/resource_loader.hpp | 5 +- macos/flutter_filament.podspec | 2 +- 30 files changed, 1432 insertions(+), 1052 deletions(-) delete mode 100644 ios/include/SceneAsset.hpp create mode 100644 lib/camera/camera_orientation.dart create mode 100644 lib/hardware/hardware_keyboard_poll.dart create mode 100644 lib/lights/light_options.dart diff --git a/example/assets/shapes/shapes.blend b/example/assets/shapes/shapes.blend index 27743c63..31aa068a 100644 --- a/example/assets/shapes/shapes.blend +++ b/example/assets/shapes/shapes.blend @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ab0cada556723be0d138d7b1cadb5e315a273524db0468e0c4255d8d2b0c1c2d -size 1222992 +oid sha256:983af4eb524f24b17bf081ae40a7217b60415122ab02cbf02c0186fbf069d52a +size 1251408 diff --git a/example/assets/shapes/shapes.glb b/example/assets/shapes/shapes.glb index 7be024c3..015701e5 100644 --- a/example/assets/shapes/shapes.glb +++ b/example/assets/shapes/shapes.glb @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b728c8fe0ce9eb06290dc37034404a952df8ea360b014b76c82fe8b9d695e85a -size 113124 +oid sha256:715793a3cf36ccf5608c347b961d0f2549e54edb4ba5227a5a008dc1d3445e83 +size 116948 diff --git a/example/lib/main.dart b/example/lib/main.dart index ed9431fe..2de07159 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -73,8 +73,6 @@ class ExampleWidgetState extends State { static FilamentEntity? flightHelmet; static FilamentEntity? buster; - static List? animations; - static FilamentEntity? directionalLight; static bool loop = false; @@ -90,8 +88,6 @@ class ExampleWidgetState extends State { setState(() { _filamentController = FilamentControllerFFI(); }); - await Future.delayed(const Duration(milliseconds: 100)); - WidgetsBinding.instance.addPostFrameCallback((timeStamp) async { await _filamentController!.createViewer(); _createEntityLoadListener(); @@ -101,8 +97,7 @@ class ExampleWidgetState extends State { await _filamentController!.setRendering(true); assets.add( await _filamentController!.loadGlb("assets/shapes/shapes.glb")); - ExampleWidgetState.animations = - await _filamentController!.getAnimationNames(assets.first); + await _filamentController! .setCameraManipulatorOptions(zoomSpeed: 1.0); @@ -123,7 +118,6 @@ class ExampleWidgetState extends State { _listener = _filamentController!.onLoad.listen((FilamentEntity entity) async { assets.add(entity); - animations = await _filamentController!.getAnimationNames(entity); if (mounted) { setState(() {}); } @@ -136,37 +130,65 @@ class ExampleWidgetState extends State { final _sharedFocusNode = FocusNode(); Widget _assetEntry(FilamentEntity entity) { - return Row(children: [ - Text("Asset ${entity}"), - IconButton( - tooltip: "Transform to unit cube", - onPressed: () async { - await _filamentController!.transformToUnitCube(entity); - }, - icon: const Icon(Icons.settings_overscan_outlined)), - IconButton( - onPressed: () async { - _transformController?.dispose(); - if (_controlled == entity) { - _controlled = null; - } else { - _controlled = entity; - _transformController?.dispose(); - _transformController = - EntityTransformController(_filamentController!, entity); - } - setState(() {}); - }, - icon: Icon(Icons.control_camera, - color: _controlled == entity ? Colors.green : Colors.black)), - IconButton( - onPressed: () async { - await _filamentController!.removeEntity(entity); - assets.remove(entity); - setState(() {}); - }, - icon: const Icon(Icons.cancel_sharp)), - ]); + return FutureBuilder( + future: _filamentController!.getAnimationNames(entity), + builder: (_, animations) { + if (animations.data == null) { + return Container(); + } + return Row(children: [ + Text("Asset ${entity}"), + IconButton( + iconSize: 14, + tooltip: "Transform to unit cube", + onPressed: () async { + await _filamentController!.transformToUnitCube(entity); + }, + icon: const Icon(Icons.settings_overscan_outlined)), + IconButton( + iconSize: 14, + tooltip: "Attach mouse control", + onPressed: () async { + _transformController?.dispose(); + if (_controlled == entity) { + _controlled = null; + } else { + _controlled = entity; + _transformController?.dispose(); + _transformController = + EntityTransformController(_filamentController!, entity); + } + setState(() {}); + }, + icon: Icon(Icons.control_camera, + color: + _controlled == entity ? Colors.green : Colors.black)), + IconButton( + iconSize: 14, + tooltip: "Remove", + onPressed: () async { + await _filamentController!.removeEntity(entity); + assets.remove(entity); + setState(() {}); + }, + icon: const Icon(Icons.cancel_sharp)), + if (animations.data!.isNotEmpty) + PopupMenuButton( + tooltip: "Animations", + itemBuilder: (_) => animations.data! + .map((a) => PopupMenuItem( + value: a, + child: Text(a), + )) + .toList(), + onSelected: (value) async { + print("Playing animation $value"); + await _filamentController! + .playAnimation(entity, animations.data!.indexOf(value!)); + }, + ) + ]); + }); } @override @@ -222,8 +244,9 @@ class ExampleWidgetState extends State { ), GestureDetector( onTap: () async { - await _filamentController! - .loadGlb('assets/shapes/shapes.glb'); + await _filamentController!.loadGlb( + 'assets/shapes/shapes.glb', + numInstances: 10); }, child: Container( color: Colors.transparent, diff --git a/example/lib/menus/asset_submenu.dart b/example/lib/menus/asset_submenu.dart index 7aa1d052..358cfebb 100644 --- a/example/lib/menus/asset_submenu.dart +++ b/example/lib/menus/asset_submenu.dart @@ -43,16 +43,25 @@ class _AssetSubmenuState extends State { child: const Text('Find Cylinder entity by name')), MenuItemButton( onPressed: () async { - Timer.periodic(const Duration(milliseconds: 50), (_) async { - await widget.controller.setBoneTransform( - ExampleWidgetState.assets.last, - "Cylinder", - "Bone", - Matrix4.rotationX(pi / 2)); - }); + await widget.controller.addBoneAnimation( + ExampleWidgetState.assets.last, + BoneAnimationData([ + "Bone" + ], [ + "Cylinder" + ], [ + [v.Quaternion.axisAngle(v.Vector3(1, 1, 1), pi / 2)] + ], [ + [v.Vector3.zero()] + ], 16)); }, child: const Text('Set bone transform for Cylinder (pi/2 rotation X)')), + MenuItemButton( + onPressed: () async { + await widget.controller.resetBones(ExampleWidgetState.assets.last); + }, + child: const Text('Reset bones for Cylinder')), MenuItemButton( onPressed: () async { await widget.controller.addBoneAnimation( @@ -72,9 +81,11 @@ class _AssetSubmenuState extends State { }, child: const Text('Set bone transform animation for Cylinder')), MenuItemButton( + closeOnActivate: false, onPressed: () async { var names = await widget.controller.getMorphTargetNames( ExampleWidgetState.assets.last, "Cylinder"); + print("NAMES : $names"); await showDialog( context: context, builder: (ctx) { @@ -134,26 +145,6 @@ class _AssetSubmenuState extends State { child: Text( "Toggle animation looping ${ExampleWidgetState.loop ? "OFF" : "ON"}")) ]; - if (ExampleWidgetState.animations != null) { - children.addAll(ExampleWidgetState.animations!.map((a) => MenuItemButton( - onPressed: () { - widget.controller.playAnimation(ExampleWidgetState.assets.last, - ExampleWidgetState.animations!.indexOf(a), - replaceActive: true, - crossfade: 0.5, - loop: ExampleWidgetState.loop); - }, - child: Text( - "play animation ${ExampleWidgetState.animations!.indexOf(a)} (replace/fade)")))); - children.addAll(ExampleWidgetState.animations!.map((a) => MenuItemButton( - onPressed: () { - widget.controller.playAnimation(ExampleWidgetState.assets.last, - ExampleWidgetState.animations!.indexOf(a), - replaceActive: false, loop: ExampleWidgetState.loop); - }, - child: Text( - "Play animation ${ExampleWidgetState.animations!.indexOf(a)} (noreplace)")))); - } return SubmenuButton(menuChildren: children, child: const Text("Shapes")); } @@ -213,8 +204,6 @@ class _AssetSubmenuState extends State { force: true); await widget.controller .playAnimation(ExampleWidgetState.buster!, 0, loop: true); - ExampleWidgetState.animations = await widget.controller - .getAnimationNames(ExampleWidgetState.assets.last); } else { await widget.controller .removeEntity(ExampleWidgetState.buster!); @@ -242,7 +231,7 @@ class _AssetSubmenuState extends State { : 'Remove flight helmet')), MenuItemButton( onPressed: () { - widget.controller.setBackgroundColor(const Color(0xFF73C9FA)); + widget.controller.setBackgroundColor(const Color(0xAA73C9FA)); }, child: const Text("Set background color")), MenuItemButton( diff --git a/example/lib/menus/camera_submenu.dart b/example/lib/menus/camera_submenu.dart index 9ab4464c..ba424e6e 100644 --- a/example/lib/menus/camera_submenu.dart +++ b/example/lib/menus/camera_submenu.dart @@ -2,7 +2,7 @@ import 'dart:async'; import 'dart:math'; import 'package:flutter/material.dart'; - +import 'package:vector_math/vector_math_64.dart' as v; import 'package:flutter_filament/filament_controller.dart'; import 'package:flutter_filament_example/main.dart'; @@ -99,7 +99,8 @@ class _CameraSubmenuState extends State { MenuItemButton( onPressed: () async { widget.controller.setCameraPosition(0.0, 0.0, 0.0); - widget.controller.setCameraRotation(0, 0.0, 1.0, 0.0); + widget.controller.setCameraRotation( + v.Quaternion.axisAngle(v.Vector3(0, 0.0, 1.0), 0.0)); }, child: const Text('Move to 0,0,0, facing towards 0,0,-1'), ), @@ -119,7 +120,8 @@ class _CameraSubmenuState extends State { ), MenuItemButton( onPressed: () { - widget.controller.setCameraRotation(pi / 4, 0.0, 1.0, 0.0); + widget.controller.setCameraRotation( + v.Quaternion.axisAngle(v.Vector3(0, 1, 0), pi / 4)); }, child: const Text("Rotate camera 45 degrees around y axis"), ), diff --git a/example/lib/picker_result_widget.dart b/example/lib/picker_result_widget.dart index 01544d46..dca58d90 100644 --- a/example/lib/picker_result_widget.dart +++ b/example/lib/picker_result_widget.dart @@ -9,11 +9,8 @@ class PickerResultWidget extends StatelessWidget { @override Widget build(BuildContext context) { return StreamBuilder( - stream: controller.pickResult.map((FilamentEntity? entityId) { - if (entityId == null) { - return null; - } - return controller.getNameForEntity(entityId); + stream: controller.pickResult.map((result) { + return controller.getNameForEntity(result.entity); }), builder: (ctx, snapshot) => snapshot.data == null ? Container() diff --git a/ios/include/FlutterFilamentApi.h b/ios/include/FlutterFilamentApi.h index fed53bc2..a1474b43 100644 --- a/ios/include/FlutterFilamentApi.h +++ b/ios/include/FlutterFilamentApi.h @@ -73,9 +73,14 @@ extern "C" FLUTTER_PLUGIN_EXPORT EntityId add_light(const void *const viewer, uint8_t type, float colour, float intensity, float posX, float posY, float posZ, float dirX, float dirY, float dirZ, bool shadows); FLUTTER_PLUGIN_EXPORT void remove_light(const void *const viewer, EntityId entityId); FLUTTER_PLUGIN_EXPORT void clear_lights(const void *const viewer); - FLUTTER_PLUGIN_EXPORT EntityId load_glb(void *sceneManager, const char *assetPath, bool unlit); + FLUTTER_PLUGIN_EXPORT EntityId load_glb(void *sceneManager, const char *assetPath, int numInstances); + FLUTTER_PLUGIN_EXPORT EntityId load_glb_from_buffer(void *sceneManager, const void* const data, size_t length); FLUTTER_PLUGIN_EXPORT EntityId load_gltf(void *sceneManager, const char *assetPath, const char *relativePath); + FLUTTER_PLUGIN_EXPORT EntityId create_instance(void *sceneManager, EntityId id); + FLUTTER_PLUGIN_EXPORT int get_instance_count(void *sceneManager, EntityId entityId); + FLUTTER_PLUGIN_EXPORT void get_instances(void *sceneManager, EntityId entityId, EntityId *out); FLUTTER_PLUGIN_EXPORT void set_main_camera(const void *const viewer); + FLUTTER_PLUGIN_EXPORT EntityId get_main_camera(const void *const viewer); FLUTTER_PLUGIN_EXPORT bool set_camera(const void *const viewer, EntityId asset, const char *nodeName); FLUTTER_PLUGIN_EXPORT void set_view_frustum_culling(const void *const viewer, bool enabled); FLUTTER_PLUGIN_EXPORT void render( @@ -159,7 +164,7 @@ extern "C" FLUTTER_PLUGIN_EXPORT void set_camera_exposure(const void *const viewer, float aperture, float shutterSpeed, float sensitivity); FLUTTER_PLUGIN_EXPORT void set_camera_position(const void *const viewer, float x, float y, float z); FLUTTER_PLUGIN_EXPORT void get_camera_position(const void *const viewer); - FLUTTER_PLUGIN_EXPORT void set_camera_rotation(const void *const viewer, float rads, float x, float y, float z); + FLUTTER_PLUGIN_EXPORT void set_camera_rotation(const void *const viewer, float w, float x, float y, float z); FLUTTER_PLUGIN_EXPORT void set_camera_model_matrix(const void *const viewer, const float *const matrix); FLUTTER_PLUGIN_EXPORT const double *const get_camera_model_matrix(const void *const viewer); FLUTTER_PLUGIN_EXPORT const double *const get_camera_view_matrix(const void *const viewer); diff --git a/ios/include/FlutterFilamentFFIApi.h b/ios/include/FlutterFilamentFFIApi.h index 006db42a..097f369a 100644 --- a/ios/include/FlutterFilamentFFIApi.h +++ b/ios/include/FlutterFilamentFFIApi.h @@ -38,34 +38,36 @@ FLUTTER_PLUGIN_EXPORT void remove_ibl_ffi(void* const viewer); FLUTTER_PLUGIN_EXPORT EntityId add_light_ffi(void* const viewer, uint8_t type, float colour, float intensity, float posX, float posY, float posZ, float dirX, float dirY, float dirZ, bool shadows); FLUTTER_PLUGIN_EXPORT void remove_light_ffi(void* const viewer, EntityId entityId); FLUTTER_PLUGIN_EXPORT void clear_lights_ffi(void* const viewer); -FLUTTER_PLUGIN_EXPORT EntityId load_glb_ffi(void* const assetManager, const char *assetPath, bool unlit); -FLUTTER_PLUGIN_EXPORT EntityId load_gltf_ffi(void* const assetManager, const char *assetPath, const char *relativePath); +FLUTTER_PLUGIN_EXPORT EntityId load_glb_ffi(void* const sceneManager, const char *assetPath, int numInstances); +FLUTTER_PLUGIN_EXPORT EntityId load_glb_from_buffer_ffi(void* const sceneManager, const void* const data, size_t length, int numInstances); +FLUTTER_PLUGIN_EXPORT EntityId load_gltf_ffi(void* const sceneManager, const char *assetPath, const char *relativePath); +FLUTTER_PLUGIN_EXPORT EntityId create_instance_ffi(void* const sceneManager, EntityId entityId); FLUTTER_PLUGIN_EXPORT void remove_entity_ffi(void* const viewer, EntityId asset); FLUTTER_PLUGIN_EXPORT void clear_entities_ffi(void* const viewer); FLUTTER_PLUGIN_EXPORT bool set_camera_ffi(void* const viewer, EntityId asset, const char *nodeName); FLUTTER_PLUGIN_EXPORT void apply_weights_ffi( - void* const assetManager, + void* const sceneManager, EntityId asset, const char *const entityName, float *const weights, int count ); -FLUTTER_PLUGIN_EXPORT void play_animation_ffi(void* const assetManager, EntityId asset, int index, bool loop, bool reverse, bool replaceActive, float crossfade); -FLUTTER_PLUGIN_EXPORT void set_animation_frame_ffi(void* const assetManager, EntityId asset, int animationIndex, int animationFrame); -FLUTTER_PLUGIN_EXPORT void stop_animation_ffi(void* const assetManager, EntityId asset, int index); -FLUTTER_PLUGIN_EXPORT int get_animation_count_ffi(void* const assetManager, EntityId asset); -FLUTTER_PLUGIN_EXPORT void get_animation_name_ffi(void* const assetManager, EntityId asset, char *const outPtr, int index); -FLUTTER_PLUGIN_EXPORT void get_morph_target_name_ffi(void* const assetManager, EntityId asset, const char *meshName, char *const outPtr, int index); -FLUTTER_PLUGIN_EXPORT int get_morph_target_name_count_ffi(void* const assetManager, EntityId asset, const char *meshName); -FLUTTER_PLUGIN_EXPORT void set_morph_target_weights_ffi(void* const assetManager, +FLUTTER_PLUGIN_EXPORT void play_animation_ffi(void* const sceneManager, EntityId asset, int index, bool loop, bool reverse, bool replaceActive, float crossfade); +FLUTTER_PLUGIN_EXPORT void set_animation_frame_ffi(void* const sceneManager, EntityId asset, int animationIndex, int animationFrame); +FLUTTER_PLUGIN_EXPORT void stop_animation_ffi(void* const sceneManager, EntityId asset, int index); +FLUTTER_PLUGIN_EXPORT int get_animation_count_ffi(void* const sceneManager, EntityId asset); +FLUTTER_PLUGIN_EXPORT void get_animation_name_ffi(void* const sceneManager, EntityId asset, char *const outPtr, int index); +FLUTTER_PLUGIN_EXPORT void get_morph_target_name_ffi(void* const sceneManager, EntityId asset, const char *meshName, char *const outPtr, int index); +FLUTTER_PLUGIN_EXPORT int get_morph_target_name_count_ffi(void* const sceneManager, EntityId asset, const char *meshName); +FLUTTER_PLUGIN_EXPORT void set_morph_target_weights_ffi(void* const sceneManager, EntityId asset, const char *const entityName, const float *const morphData, int numWeights ); FLUTTER_PLUGIN_EXPORT bool set_morph_animation_ffi( - void *assetManager, + void *sceneManager, EntityId asset, const char *const entityName, const float *const morphData, @@ -74,13 +76,13 @@ FLUTTER_PLUGIN_EXPORT bool set_morph_animation_ffi( int numFrames, float frameLengthInMs); FLUTTER_PLUGIN_EXPORT bool set_bone_transform_ffi( - void *assetManager, + void *sceneManager, EntityId asset, const char *entityName, const float *const transform, const char *boneName); FLUTTER_PLUGIN_EXPORT void add_bone_animation_ffi( - void *assetManager, + void *sceneManager, EntityId asset, const float *const frameData, int numFrames, @@ -91,7 +93,7 @@ FLUTTER_PLUGIN_EXPORT void add_bone_animation_ffi( bool isModelSpace); FLUTTER_PLUGIN_EXPORT void set_post_processing_ffi(void* const viewer, bool enabled); FLUTTER_PLUGIN_EXPORT void pick_ffi(void* const viewer, int x, int y, EntityId* entityId); -FLUTTER_PLUGIN_EXPORT void reset_to_rest_pose_ffi(void* const assetManager, EntityId entityId); +FLUTTER_PLUGIN_EXPORT void reset_to_rest_pose_ffi(void* const sceneManager, EntityId entityId); FLUTTER_PLUGIN_EXPORT void ios_dummy_ffi(); FLUTTER_PLUGIN_EXPORT EntityId create_geometry_ffi(void* const viewer, float* vertices, int numVertices, uint16_t* indices, int numIndices, const char* materialPath); diff --git a/ios/include/SceneAsset.hpp b/ios/include/SceneAsset.hpp deleted file mode 100644 index 3b34940e..00000000 --- a/ios/include/SceneAsset.hpp +++ /dev/null @@ -1,93 +0,0 @@ -#pragma once - -#include "Log.hpp" - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include -#include -#include -#include - -extern "C" { - #include "FlutterFilamentApi.h" -} -template class std::vector; -namespace polyvox { - using namespace filament; - using namespace filament::gltfio; - using namespace utils; - using namespace std; - - typedef std::chrono::time_point time_point_t; - - enum AnimationType { - MORPH, BONE, GLTF - }; - - struct AnimationStatus { - time_point_t start = time_point_t::max(); - bool loop = false; - bool reverse = false; - float durationInSecs = 0; - }; - - struct GltfAnimation : AnimationStatus { - int index = -1; - }; - - // - // Use this to construct a dynamic (i.e. non-glTF embedded) morph target animation. - // - struct MorphAnimation : AnimationStatus { - utils::Entity meshTarget; - int numFrames = -1; - float frameLengthInMs = 0; - vector frameData; - vector morphIndices; - int lengthInFrames; - }; - - // - // Use this to construct a dynamic (i.e. non-glTF embedded) bone/joint animation. - // - struct BoneAnimation : AnimationStatus { - size_t boneIndex; - vector meshTargets; - size_t skinIndex = 0; - int lengthInFrames; - float frameLengthInMs = 0; - vector frameData; - }; - - struct SceneAsset { - FilamentAsset* asset = nullptr; - vector initialJointTransforms; - vector gltfAnimations; - vector morphAnimations; - vector boneAnimations; - - // the index of the last active glTF animation, - // used to cross-fade - int fadeGltfAnimationIndex = -1; - float fadeDuration = 0.0f; - float fadeOutAnimationStart = 0.0f; - - // a slot to preload textures - filament::Texture* texture = nullptr; - - SceneAsset( - FilamentAsset* asset - ) : asset(asset) {} - }; -} diff --git a/ios/include/SceneManager.hpp b/ios/include/SceneManager.hpp index cbac0282..b89e23b2 100644 --- a/ios/include/SceneManager.hpp +++ b/ios/include/SceneManager.hpp @@ -1,41 +1,64 @@ #pragma once #include +#include +#include +#include #include #include #include +#include #include -#include "SceneAsset.hpp" +#include "utils/NameComponentManager.h" #include "ResourceBuffer.hpp" -#include "components/StandardComponents.h" +#include "components/CollisionComponentManager.hpp" +#include "components/AnimationComponentManager.hpp" -typedef int32_t EntityId; +#include "tsl/robin_map.h" -namespace polyvox +namespace flutter_filament { + typedef int32_t EntityId; + using namespace filament; using namespace filament::gltfio; + using namespace utils; + using std::vector; + using std::unique_ptr; + using std::string; + class SceneManager { public: SceneManager(const ResourceLoaderWrapper *const loader, - NameComponentManager *ncm, Engine *engine, Scene *scene, const char *uberArchivePath); ~SceneManager(); + + EntityId loadGltf(const char *uri, const char *relativeResourcePath); - EntityId loadGlb(const char *uri, bool unlit); - FilamentAsset *getAssetByEntityId(EntityId entityId); + + //// + /// @brief + /// @param uri + /// @param numInstances + /// @return an Entity representing the FilamentAsset associated with the loaded FilamentAsset. + /// + EntityId loadGlb(const char *uri, int numInstances); + EntityId loadGlbFromBuffer(const uint8_t* data, size_t length, int numInstances=1); + EntityId createInstance(EntityId entityId); + void remove(EntityId entity); void destroyAll(); unique_ptr> getAnimationNames(EntityId entity); float getAnimationDuration(EntityId entity, int animationIndex); - unique_ptr> getMorphTargetNames(EntityId entity, const char *meshName); + + unique_ptr> getMorphTargetNames(EntityId entity, const char *name); void transformToUnitCube(EntityId e); inline void updateTransform(EntityId e); void setScale(EntityId e, float scale); @@ -45,8 +68,8 @@ namespace polyvox void queueRotationUpdate(EntityId e, float rads, float x, float y, float z, float w, bool relative); const utils::Entity *getCameraEntities(EntityId e); size_t getCameraEntityCount(EntityId e); - const utils::Entity *getLightEntities(EntityId e) const noexcept; - size_t getLightEntityCount(EntityId e) const noexcept; + const utils::Entity *getLightEntities(EntityId e) noexcept; + size_t getLightEntityCount(EntityId e) noexcept; void updateAnimations(); void updateTransforms(); void testCollisions(EntityId entity); @@ -107,29 +130,48 @@ namespace polyvox void addCollisionComponent(EntityId entity, void (*onCollisionCallback)(const EntityId entityId1, const EntityId entityId2), bool affectsCollidingTransform); void removeCollisionComponent(EntityId entityId); void setParent(EntityId child, EntityId parent); + void addAnimatableComponent(EntityId entity); + + /// @brief returns the number of instances of the FilamentAsset represented by the given entity. + /// @param entityId + /// @return + int getInstanceCount(EntityId entityId); + + /// @brief returns an array containing all instances of the FilamentAsset represented by the given entity. + /// @param entityId + /// @return + void getInstances(EntityId entityId, EntityId* out); + + friend class FilamentViewer; private: - AssetLoader *_assetLoader = nullptr; + gltfio::AssetLoader *_assetLoader = nullptr; const ResourceLoaderWrapper *const _resourceLoaderWrapper; - NameComponentManager *_ncm = nullptr; Engine *_engine; Scene *_scene; - MaterialProvider *_ubershaderProvider = nullptr; + gltfio::MaterialProvider *_ubershaderProvider = nullptr; gltfio::ResourceLoader *_gltfResourceLoader = nullptr; gltfio::TextureProvider *_stbDecoder = nullptr; gltfio::TextureProvider *_ktxDecoder = nullptr; std::mutex _mutex; - vector _assets; - tsl::robin_map _entityIdLookup; - tsl::robin_map> _transformUpdates; - std::vector _nonTransformableCollidableEntities; + utils::NameComponentManager* _ncm; + tsl::robin_map< + EntityId, + gltfio::FilamentInstance*> _instances; + tsl::robin_map _assets; + tsl::robin_map> _transformUpdates; + + AnimationComponentManager* _animationComponentManager = nullptr; CollisionComponentManager* _collisionComponentManager = nullptr; + gltfio::FilamentInstance* getInstanceByEntityId(EntityId entityId); + gltfio::FilamentAsset* getAssetByEntityId(EntityId entityId); + utils::Entity findEntityByName( - SceneAsset asset, + const gltfio::FilamentInstance* instance, const char *entityName); }; diff --git a/ios/include/StreamBufferAdapter.hpp b/ios/include/StreamBufferAdapter.hpp index f1931bc8..8e1cca43 100644 --- a/ios/include/StreamBufferAdapter.hpp +++ b/ios/include/StreamBufferAdapter.hpp @@ -5,9 +5,8 @@ #include #include -namespace polyvox { +namespace flutter_filament { - using namespace std; // // A generic adapter to expose any contiguous section of memory as a std::streambuf. @@ -20,15 +19,15 @@ namespace polyvox { ~StreamBufferAdapter() { } - streamsize size(); + std::streamsize size(); private: int_type uflow() override; int_type underflow() override; int_type pbackfail(int_type ch) override; - streampos seekoff(streamoff off, ios_base::seekdir way, ios_base::openmode which) override; - streampos seekpos(streampos sp, ios_base::openmode which) override; - streamsize showmanyc() override; + std::streampos seekoff(std::streamoff off, std::ios_base::seekdir way, std::ios_base::openmode which) override; + std::streampos seekpos(std::streampos sp, std::ios_base::openmode which) override; + std::streamsize showmanyc() override; }; diff --git a/ios/src/FilamentViewer.cpp b/ios/src/FilamentViewer.cpp index 4eb6a6c8..3ffcc394 100644 --- a/ios/src/FilamentViewer.cpp +++ b/ios/src/FilamentViewer.cpp @@ -91,11 +91,6 @@ #include "TimeIt.hpp" #include "ThreadPool.hpp" -using namespace filament; -using namespace filament::math; -using namespace gltfio; -using namespace utils; -using namespace image; namespace filament { @@ -103,9 +98,18 @@ namespace filament class LightManager; } // namespace filament -namespace polyvox +namespace flutter_filament { + using namespace filament; + using namespace filament::math; + using namespace gltfio; + using namespace utils; + using namespace image; + using namespace std::chrono; + + using std::string; + // const float kAperture = 1.0f; // const float kShutterSpeed = 1.0f; // const float kSensitivity = 50.0f; @@ -135,6 +139,8 @@ namespace polyvox _engine = Engine::create(Engine::Backend::OPENGL, (backend::Platform *)platform, (void *)sharedContext, nullptr); #endif + _engine->setAutomaticInstancingEnabled(true); + _renderer = _engine->createRenderer(); _frameInterval = 1000.0f / 60.0f; @@ -151,6 +157,7 @@ namespace polyvox Log("Main camera created"); _view = _engine->createView(); + Log("View created"); setToneMapping(ToneMapping::ACES); @@ -200,15 +207,11 @@ namespace polyvox _view->setDynamicResolutionOptions(options); setAntiAliasing(false, true, false); - EntityManager &em = EntityManager::get(); - _ncm = new NameComponentManager(em); - _sceneManager = new SceneManager( _resourceLoaderWrapper, - _ncm, _engine, _scene, uberArchivePath); @@ -339,7 +342,7 @@ namespace polyvox int32_t FilamentViewer::addLight(LightManager::Type t, float colour, float intensity, float posX, float posY, float posZ, float dirX, float dirY, float dirZ, bool shadows) { auto light = EntityManager::get().create(); - LightManager::Builder(t) + auto builder = LightManager::Builder(t) .color(Color::cct(colour)) .intensity(intensity) .position(math::float3(posX, posY, posZ)) @@ -377,7 +380,7 @@ namespace polyvox _lights.clear(); } - static bool endsWith(string path, string ending) + static bool endsWith(std::string path, std::string ending) { return path.compare(path.length() - ending.length(), ending.length(), ending) == 0; } @@ -435,7 +438,7 @@ namespace polyvox void FilamentViewer::loadPngTexture(string path, ResourceBuffer rb) { - polyvox::StreamBufferAdapter sb((char *)rb.data, (char *)rb.data + rb.size); + flutter_filament::StreamBufferAdapter sb((char *)rb.data, (char *)rb.data + rb.size); std::istream inputStream(&sb); @@ -480,7 +483,7 @@ namespace polyvox void FilamentViewer::loadTextureFromPath(string path) { - string ktxExt(".ktx"); + std::string ktxExt(".ktx"); string ktx2Ext(".ktx2"); string pngExt(".png"); @@ -802,10 +805,22 @@ namespace polyvox cam.setFocusDistance(_cameraFocusDistance); } + /// + /// + /// void FilamentViewer::setMainCamera() { _view->setCamera(_mainCamera); } + /// + /// + /// + EntityId FilamentViewer::getMainCamera() { + return Entity::smuggle(_mainCamera->getEntity()); + } + + + /// /// Sets the active camera to the GLTF camera node specified by [name] (or if null, the first camera found under that node). /// N.B. Blender will generally export a three-node hierarchy - @@ -832,18 +847,16 @@ namespace polyvox utils::Entity target; if (!cameraName) - { - auto inst = _ncm->getInstance(cameras[0]); - const char *name = _ncm->getName(inst); + { target = cameras[0]; + const char *name = asset->getName(target); Log("No camera specified, using first camera node found (%s)", name); } else { for (int j = 0; j < count; j++) { - auto inst = _ncm->getInstance(cameras[j]); - const char *name = _ncm->getName(inst); + const char *name = asset->getName(cameras[j]); if (strcmp(name, cameraName) == 0) { target = cameras[j]; @@ -1138,7 +1151,7 @@ namespace polyvox std::string filename = stringStream.str(); - ofstream wf(filename, ios::out | ios::binary); + std::ofstream wf(filename, std::ios::out | std::ios::binary); LinearImage image(toLinearWithAlpha(vp.width, vp.height, vp.width * 4, static_cast(buf))); @@ -1228,7 +1241,7 @@ namespace polyvox Camera &cam = _view->getCamera(); _cameraPosition = math::mat4f::translation(math::float3(x, y, z)); - cam.setModelMatrix(_cameraPosition * _cameraRotation); + cam.setModelMatrix(_cameraRotation * _cameraPosition); } void FilamentViewer::moveCameraToAsset(EntityId entityId) @@ -1249,11 +1262,11 @@ namespace polyvox Log("Moved camera to %f %f %f, lookAt %f %f %f, near %f far %f", eye[0], eye[1], eye[2], lookAt[0], lookAt[1], lookAt[2], cam.getNear(), cam.getCullingFar()); } - void FilamentViewer::setCameraRotation(float rads, float x, float y, float z) + void FilamentViewer::setCameraRotation(float w, float x, float y, float z) { Camera &cam = _view->getCamera(); - _cameraRotation = math::mat4f::rotation(rads, math::float3(x, y, z)); - cam.setModelMatrix(_cameraPosition * _cameraRotation); + _cameraRotation = math::mat4f(math::quatf(w, x, y, z)); + cam.setModelMatrix(_cameraRotation * _cameraPosition); } void FilamentViewer::setCameraModelMatrix(const float *const matrix) @@ -1455,7 +1468,10 @@ namespace polyvox void FilamentViewer::pick(uint32_t x, uint32_t y, EntityId *entityId) { _view->pick(x, y, [=](filament::View::PickingQueryResult const &result) - { *entityId = Entity::smuggle(result.renderable); }); + { + + *entityId = Entity::smuggle(result.renderable); + }); } EntityId FilamentViewer::createGeometry(float *vertices, uint32_t numVertices, uint16_t *indices, uint32_t numIndices, const char* materialPath) @@ -1523,4 +1539,4 @@ namespace polyvox return Entity::smuggle(renderable); } -} // namespace polyvox +} // namespace flutter_filament diff --git a/ios/src/FlutterFilamentApi.cpp b/ios/src/FlutterFilamentApi.cpp index cd8c9ac9..175f7448 100644 --- a/ios/src/FlutterFilamentApi.cpp +++ b/ios/src/FlutterFilamentApi.cpp @@ -8,7 +8,7 @@ #include #include -using namespace polyvox; +using namespace flutter_filament; extern "C" { @@ -114,9 +114,26 @@ extern "C" ((FilamentViewer *)viewer)->clearLights(); } - FLUTTER_PLUGIN_EXPORT EntityId load_glb(void *sceneManager, const char *assetPath, bool unlit) + FLUTTER_PLUGIN_EXPORT EntityId load_glb(void *sceneManager, const char *assetPath, int numInstances) { - return ((SceneManager *)sceneManager)->loadGlb(assetPath, unlit); + return ((SceneManager *)sceneManager)->loadGlb(assetPath, numInstances); + } + + FLUTTER_PLUGIN_EXPORT EntityId load_glb_from_buffer(void *sceneManager, const void* const data, size_t length) + { + return ((SceneManager *)sceneManager)->loadGlbFromBuffer((const uint8_t*)data, length); + } + + FLUTTER_PLUGIN_EXPORT EntityId create_instance(void *sceneManager, EntityId entityId) { + return ((SceneManager *)sceneManager)->createInstance(entityId); + } + + FLUTTER_PLUGIN_EXPORT int get_instance_count(void *sceneManager, EntityId entityId) { + return ((SceneManager*)sceneManager)->getInstanceCount(entityId); + } + + FLUTTER_PLUGIN_EXPORT void get_instances(void *sceneManager, EntityId entityId, EntityId *out) { + return ((SceneManager*)sceneManager)->getInstances(entityId, out); } FLUTTER_PLUGIN_EXPORT EntityId load_gltf(void *sceneManager, const char *assetPath, const char *relativePath) @@ -129,6 +146,12 @@ extern "C" return ((FilamentViewer *)viewer)->setMainCamera(); } + + FLUTTER_PLUGIN_EXPORT EntityId get_main_camera(const void *const viewer) + { + return ((FilamentViewer *)viewer)->getMainCamera(); + } + FLUTTER_PLUGIN_EXPORT bool set_camera(const void *const viewer, EntityId asset, const char *nodeName) { return ((FilamentViewer *)viewer)->setCamera(asset, nodeName); @@ -236,9 +259,9 @@ extern "C" ((FilamentViewer *)viewer)->setCameraPosition(x, y, z); } - FLUTTER_PLUGIN_EXPORT void set_camera_rotation(const void *const viewer, float rads, float x, float y, float z) + FLUTTER_PLUGIN_EXPORT void set_camera_rotation(const void *const viewer, float w, float x, float y, float z) { - ((FilamentViewer *)viewer)->setCameraRotation(rads, x, y, z); + ((FilamentViewer *)viewer)->setCameraRotation(w, x, y, z); } FLUTTER_PLUGIN_EXPORT void set_camera_model_matrix(const void *const viewer, const float *const matrix) @@ -448,20 +471,20 @@ extern "C" int index) { auto names = ((SceneManager *)sceneManager)->getAnimationNames(asset); - string name = names->at(index); + std::string name = names->at(index); strcpy(outPtr, name.c_str()); } FLUTTER_PLUGIN_EXPORT int get_morph_target_name_count(void *sceneManager, EntityId asset, const char *meshName) { - unique_ptr> names = ((SceneManager *)sceneManager)->getMorphTargetNames(asset, meshName); + std::unique_ptr> names = ((SceneManager *)sceneManager)->getMorphTargetNames(asset, meshName); return (int)names->size(); } FLUTTER_PLUGIN_EXPORT void get_morph_target_name(void *sceneManager, EntityId asset, const char *meshName, char *const outPtr, int index) { - unique_ptr> names = ((SceneManager *)sceneManager)->getMorphTargetNames(asset, meshName); - string name = names->at(index); + std::unique_ptr> names = ((SceneManager *)sceneManager)->getMorphTargetNames(asset, meshName); + std::string name = names->at(index); strcpy(outPtr, name.c_str()); } @@ -572,7 +595,7 @@ extern "C" FLUTTER_PLUGIN_EXPORT EntityId find_child_entity_by_name(void *const sceneManager, const EntityId parent, const char* name) { auto entity = ((SceneManager*)sceneManager)->findChildEntityByName(parent, name); - return Entity::smuggle(entity); + return utils::Entity::smuggle(entity); } FLUTTER_PLUGIN_EXPORT void set_parent(void *const sceneManager, EntityId child, EntityId parent) { diff --git a/ios/src/FlutterFilamentFFIApi.cpp b/ios/src/FlutterFilamentFFIApi.cpp index bae93f73..1ee2dcc3 100644 --- a/ios/src/FlutterFilamentFFIApi.cpp +++ b/ios/src/FlutterFilamentFFIApi.cpp @@ -31,7 +31,7 @@ extern "C" #include #endif -using namespace polyvox; +using namespace flutter_filament; using namespace std::chrono_literals; class RenderLoop { @@ -262,21 +262,30 @@ set_background_color_ffi(void *const viewer, const float r, const float g, fut.wait(); } -FLUTTER_PLUGIN_EXPORT EntityId load_gltf_ffi(void *const assetManager, +FLUTTER_PLUGIN_EXPORT EntityId load_gltf_ffi(void *const sceneManager, const char *path, const char *relativeResourcePath) { std::packaged_task lambda([&]() mutable { - return load_gltf(assetManager, path, relativeResourcePath); + return load_gltf(sceneManager, path, relativeResourcePath); }); auto fut = _rl->add_task(lambda); fut.wait(); return fut.get(); } -FLUTTER_PLUGIN_EXPORT EntityId load_glb_ffi(void *const assetManager, - const char *path, bool unlit) { +FLUTTER_PLUGIN_EXPORT EntityId load_glb_ffi(void *const sceneManager, + const char *path, int numInstances) { std::packaged_task lambda( - [&]() mutable { return load_glb(assetManager, path, unlit); }); + [&]() mutable { return load_glb(sceneManager, path, numInstances); }); + auto fut = _rl->add_task(lambda); + fut.wait(); + return fut.get(); +} + +FLUTTER_PLUGIN_EXPORT EntityId load_glb_from_buffer_ffi(void *const sceneManager, + const void *const data, size_t length, int numInstances) { + std::packaged_task lambda( + [&]() mutable { return load_glb_from_buffer(sceneManager, data, length); }); auto fut = _rl->add_task(lambda); fut.wait(); return fut.get(); @@ -388,20 +397,20 @@ FLUTTER_PLUGIN_EXPORT bool set_camera_ffi(void *const viewer, EntityId asset, } FLUTTER_PLUGIN_EXPORT void -get_morph_target_name_ffi(void *assetManager, EntityId asset, +get_morph_target_name_ffi(void *sceneManager, EntityId asset, const char *meshName, char *const outPtr, int index) { std::packaged_task lambda([&] { - get_morph_target_name(assetManager, asset, meshName, outPtr, index); + get_morph_target_name(sceneManager, asset, meshName, outPtr, index); }); auto fut = _rl->add_task(lambda); fut.wait(); } FLUTTER_PLUGIN_EXPORT int -get_morph_target_name_count_ffi(void *assetManager, EntityId asset, +get_morph_target_name_count_ffi(void *sceneManager, EntityId asset, const char *meshName) { std::packaged_task lambda([&] { - return get_morph_target_name_count(assetManager, asset, meshName); + return get_morph_target_name_count(sceneManager, asset, meshName); }); auto fut = _rl->add_task(lambda); fut.wait(); @@ -410,52 +419,52 @@ get_morph_target_name_count_ffi(void *assetManager, EntityId asset, -FLUTTER_PLUGIN_EXPORT void play_animation_ffi(void *const assetManager, +FLUTTER_PLUGIN_EXPORT void play_animation_ffi(void *const sceneManager, EntityId asset, int index, bool loop, bool reverse, bool replaceActive, float crossfade) { std::packaged_task lambda([&] { - play_animation(assetManager, asset, index, loop, reverse, replaceActive, + play_animation(sceneManager, asset, index, loop, reverse, replaceActive, crossfade); }); auto fut = _rl->add_task(lambda); fut.wait(); } -FLUTTER_PLUGIN_EXPORT void set_animation_frame_ffi(void *const assetManager, +FLUTTER_PLUGIN_EXPORT void set_animation_frame_ffi(void *const sceneManager, EntityId asset, int animationIndex, int animationFrame) { std::packaged_task lambda([&] { - set_animation_frame(assetManager, asset, animationIndex, animationFrame); + set_animation_frame(sceneManager, asset, animationIndex, animationFrame); }); auto fut = _rl->add_task(lambda); fut.wait(); } -FLUTTER_PLUGIN_EXPORT void stop_animation_ffi(void *const assetManager, +FLUTTER_PLUGIN_EXPORT void stop_animation_ffi(void *const sceneManager, EntityId asset, int index) { std::packaged_task lambda( - [&] { stop_animation(assetManager, asset, index); }); + [&] { stop_animation(sceneManager, asset, index); }); auto fut = _rl->add_task(lambda); fut.wait(); } -FLUTTER_PLUGIN_EXPORT int get_animation_count_ffi(void *const assetManager, +FLUTTER_PLUGIN_EXPORT int get_animation_count_ffi(void *const sceneManager, EntityId asset) { std::packaged_task lambda( - [&] { return get_animation_count(assetManager, asset); }); + [&] { return get_animation_count(sceneManager, asset); }); auto fut = _rl->add_task(lambda); fut.wait(); return fut.get(); } -FLUTTER_PLUGIN_EXPORT void get_animation_name_ffi(void *const assetManager, +FLUTTER_PLUGIN_EXPORT void get_animation_name_ffi(void *const sceneManager, EntityId asset, char *const outPtr, int index) { std::packaged_task lambda( - [&] { get_animation_name(assetManager, asset, outPtr, index); }); + [&] { get_animation_name(sceneManager, asset, outPtr, index); }); auto fut = _rl->add_task(lambda); fut.wait(); } @@ -476,27 +485,27 @@ FLUTTER_PLUGIN_EXPORT void pick_ffi(void *const viewer, int x, int y, } FLUTTER_PLUGIN_EXPORT const char * -get_name_for_entity_ffi(void *const assetManager, const EntityId entityId) { +get_name_for_entity_ffi(void *const sceneManager, const EntityId entityId) { std::packaged_task lambda( - [&] { return get_name_for_entity(assetManager, entityId); }); + [&] { return get_name_for_entity(sceneManager, entityId); }); auto fut = _rl->add_task(lambda); fut.wait(); return fut.get(); } -void set_morph_target_weights_ffi(void *const assetManager, +void set_morph_target_weights_ffi(void *const sceneManager, EntityId asset, const char *const entityName, const float *const morphData, int numWeights) { std::packaged_task lambda( - [&] { return set_morph_target_weights(assetManager, asset, entityName, morphData, numWeights); }); + [&] { return set_morph_target_weights(sceneManager, asset, entityName, morphData, numWeights); }); auto fut = _rl->add_task(lambda); fut.wait(); } bool set_morph_animation_ffi( - void *assetManager, + void *sceneManager, EntityId asset, const char *const entityName, const float *const morphData, @@ -506,7 +515,7 @@ bool set_morph_animation_ffi( float frameLengthInMs) { std::packaged_task lambda( [&] { - return set_morph_animation(assetManager, asset, entityName, morphData, morphIndices, numMorphTargets, numFrames, frameLengthInMs); + return set_morph_animation(sceneManager, asset, entityName, morphData, morphIndices, numMorphTargets, numFrames, frameLengthInMs); }); auto fut = _rl->add_task(lambda); fut.wait(); @@ -515,27 +524,27 @@ bool set_morph_animation_ffi( FLUTTER_PLUGIN_EXPORT bool set_bone_transform_ffi( - void *assetManager, + void *sceneManager, EntityId asset, const char *entityName, const float *const transform, const char *boneName) { std::packaged_task lambda( - [&] { return set_bone_transform(assetManager, asset, entityName, transform, boneName); }); + [&] { return set_bone_transform(sceneManager, asset, entityName, transform, boneName); }); auto fut = _rl->add_task(lambda); fut.wait(); return fut.get(); } -FLUTTER_PLUGIN_EXPORT void reset_to_rest_pose_ffi(void* const assetManager, EntityId entityId) { +FLUTTER_PLUGIN_EXPORT void reset_to_rest_pose_ffi(void* const sceneManager, EntityId entityId) { std::packaged_task lambda( - [&] { return reset_to_rest_pose(assetManager, entityId); }); + [&] { return reset_to_rest_pose(sceneManager, entityId); }); auto fut = _rl->add_task(lambda); fut.wait(); } FLUTTER_PLUGIN_EXPORT void add_bone_animation_ffi( - void *assetManager, + void *sceneManager, EntityId asset, const float *const frameData, int numFrames, @@ -547,7 +556,7 @@ FLUTTER_PLUGIN_EXPORT void add_bone_animation_ffi( std::packaged_task lambda( [=] { - add_bone_animation(assetManager, asset, frameData, numFrames, boneName, meshNames, numMeshTargets, frameLengthInMs, isModelSpace); + add_bone_animation(sceneManager, asset, frameData, numFrames, boneName, meshNames, numMeshTargets, frameLengthInMs, isModelSpace); }); auto fut = _rl->add_task(lambda); fut.wait(); diff --git a/ios/src/SceneManager.cpp b/ios/src/SceneManager.cpp index 1f9e5b1f..bb358ca9 100644 --- a/ios/src/SceneManager.cpp +++ b/ios/src/SceneManager.cpp @@ -8,6 +8,8 @@ #include #include +#include + #include #include #include @@ -18,7 +20,6 @@ #include #include "StreamBufferAdapter.hpp" -#include "SceneAsset.hpp" #include "Log.hpp" #include "SceneManager.hpp" @@ -30,23 +31,22 @@ extern "C" #include "material/image.h" } -namespace polyvox + +namespace flutter_filament { - using namespace std; using namespace std::chrono; using namespace image; using namespace utils; using namespace filament; using namespace filament::gltfio; + using std::unique_ptr; SceneManager::SceneManager(const ResourceLoaderWrapper *const resourceLoaderWrapper, - NameComponentManager *ncm, Engine *engine, Scene *scene, const char *uberArchivePath) : _resourceLoaderWrapper(resourceLoaderWrapper), - _ncm(ncm), _engine(engine), _scene(scene) { @@ -74,16 +74,20 @@ namespace polyvox } Log("Created ubershader provider."); - EntityManager &em = EntityManager::get(); + utils::EntityManager &em = utils::EntityManager::get(); + + _ncm = new NameComponentManager(em); _assetLoader = AssetLoader::create({_engine, _ubershaderProvider, _ncm, &em}); _gltfResourceLoader->addTextureProvider ("image/ktx2", _ktxDecoder); _gltfResourceLoader->addTextureProvider("image/png", _stbDecoder); _gltfResourceLoader->addTextureProvider("image/jpeg", _stbDecoder); - const auto& tm = _engine->getTransformManager(); + auto& tm = _engine->getTransformManager(); _collisionComponentManager = new CollisionComponentManager(tm); + _animationComponentManager = new AnimationComponentManager(tm, _engine->getRenderableManager()); + } SceneManager::~SceneManager() @@ -94,12 +98,32 @@ namespace polyvox AssetLoader::destroy(&_assetLoader); } + int SceneManager::getInstanceCount(EntityId entityId) { + auto* asset = getAssetByEntityId(entityId); + if(!asset) { + return -1; + } + + return asset->getAssetInstanceCount(); + } + + void SceneManager::getInstances(EntityId entityId, EntityId* out) { + auto* asset = getAssetByEntityId(entityId); + if(!asset) { + return; + } + auto* instances = asset->getAssetInstances(); + for(int i=0; i < asset->getAssetInstanceCount(); i++) { + auto instanceEntity = instances[i]->getRoot(); + out[i] = Entity::smuggle(instanceEntity); + } + } + EntityId SceneManager::loadGltf(const char *uri, const char *relativeResourcePath) { ResourceBuffer rbuf = _resourceLoaderWrapper->load(uri); - // Parse the glTF file and create Filament entities. FilamentAsset *asset = _assetLoader->createAsset((uint8_t *)rbuf.data, rbuf.size); if (!asset) @@ -115,7 +139,7 @@ namespace polyvox for (size_t i = 0; i < resourceUriCount; i++) { - string uri = string(relativeResourcePath) + string("/") + string(resourceUris[i]); + std::string uri = std::string(relativeResourcePath) + std::string("/") + std::string(resourceUris[i]); Log("Loading resource URI from relative path %s", resourceUris[i], uri.c_str()); ResourceBuffer buf = _resourceLoaderWrapper->load(uri.c_str()); @@ -157,25 +181,13 @@ namespace polyvox inst->getAnimator()->updateBoneMatrices(); inst->recomputeBoundingBoxes(); + _animationComponentManager->addAnimationComponent(inst); + asset->releaseSourceData(); - - SceneAsset sceneAsset(asset); - - const auto joints = inst->getJointsAt(0); - - TransformManager &transformManager = _engine->getTransformManager(); - - for(int i = 0; i < inst->getJointCountAt(0); i++) { - const auto joint = joints[i]; - const auto& jointTransformInstance = transformManager.getInstance(joint); - const auto& jointTransform = transformManager.getTransform(jointTransformInstance); - sceneAsset.initialJointTransforms.push_back(jointTransform); - } - + EntityId eid = Entity::smuggle(asset->getRoot()); - _entityIdLookup.emplace(eid, _assets.size()); - _assets.push_back(sceneAsset); + _assets.emplace(eid, asset); for (auto &rb : resourceBuffers) { @@ -188,15 +200,18 @@ namespace polyvox return eid; } - EntityId SceneManager::loadGlb(const char *uri, bool unlit) - { + EntityId SceneManager::loadGlbFromBuffer(const uint8_t* data, size_t length, int numInstances) { - ResourceBuffer rbuf = _resourceLoaderWrapper->load(uri); + Log("Loading GLB from buffer of length %d", length); - Log("Loaded GLB data (%d bytes) from URI %s", rbuf.size, uri); - - FilamentAsset *asset = _assetLoader->createAsset( - (const uint8_t *)rbuf.data, rbuf.size); + FilamentAsset *asset = nullptr; + if(numInstances > 1) { + FilamentInstance* instances[numInstances]; + asset = _assetLoader->createInstancedAsset((const uint8_t *)data, length, instances, numInstances); + } else { + asset = _assetLoader->createAsset( + (const uint8_t *)data, length); + } if (!asset) { @@ -221,7 +236,6 @@ namespace polyvox if (!_gltfResourceLoader->loadResources(asset)) { Log("Unknown error loading glb asset"); - _resourceLoaderWrapper->free(rbuf); return 0; } #endif @@ -229,53 +243,79 @@ namespace polyvox auto lights = asset->getLightEntities(); _scene->addEntities(lights, asset->getLightEntityCount()); - FilamentInstance *inst = asset->getInstance(); - - inst->getAnimator()->updateBoneMatrices(); - - inst->recomputeBoundingBoxes(); - auto box = inst->getBoundingBox(); - auto verts = box.extent(); - Log("AABB extent for %s is %f %f %f", uri, verts.x, verts.y, verts.z); - - asset->releaseSourceData(); - - _resourceLoaderWrapper->free(rbuf); - - SceneAsset sceneAsset(asset); - - const auto joints = inst->getJointsAt(0); - - TransformManager &transformManager = _engine->getTransformManager(); - - for(int i = 0; i < inst->getJointCountAt(0); i++) { - const auto joint = joints[i]; - const auto& jointTransformInstance = transformManager.getInstance(joint); - const auto& jointTransform = transformManager.getTransform(jointTransformInstance); - sceneAsset.initialJointTransforms.push_back(jointTransform); + for(int i =0; i < asset->getAssetInstanceCount(); i++) { + FilamentInstance *inst = asset->getAssetInstances()[i]; + inst->getAnimator()->updateBoneMatrices(); + inst->recomputeBoundingBoxes(); + auto instanceEntity = inst->getRoot(); + auto instanceEntityId = Entity::smuggle(instanceEntity); + _instances.emplace(instanceEntityId, inst); + addAnimatableComponent(instanceEntityId); } + asset->releaseSourceData(); + EntityId eid = Entity::smuggle(asset->getRoot()); - - _entityIdLookup.emplace(eid, _assets.size()); - _assets.push_back(sceneAsset); - + _assets.emplace(eid, asset); return eid; } + void SceneManager::addAnimatableComponent(EntityId entityId) { + + auto* instance = getInstanceByEntityId(entityId); + if (!instance) + { + auto* asset = getAssetByEntityId(entityId); + if(asset) { + instance = asset->getInstance(); + } else { + return; + } + } + + _animationComponentManager->addAnimationComponent(instance); + } + + EntityId SceneManager::createInstance(EntityId entityId) { + std::lock_guard lock(_mutex); + + const auto &pos = _assets.find(entityId); + if (pos == _assets.end()) + { + Log("Couldn't find asset under specified entity id."); + return false; + } + const auto asset = pos->second; + auto instance = _assetLoader->createInstance(asset); + + return Entity::smuggle(instance->getRoot()); + } + + EntityId SceneManager::loadGlb(const char *uri, int numInstances) + { + ResourceBuffer rbuf = _resourceLoaderWrapper->load(uri); + auto entity = loadGlbFromBuffer((const uint8_t*)rbuf.data, rbuf.size, numInstances); + _resourceLoaderWrapper->free(rbuf); + return entity; + } + bool SceneManager::hide(EntityId entityId, const char *meshName) { - - auto asset = getAssetByEntityId(entityId); - if (!asset) + auto* instance = getInstanceByEntityId(entityId); + if (!instance) { - return false; + auto* asset = getAssetByEntityId(entityId); + if(asset) { + instance = asset->getInstance(); + } else { + return false; + } } utils::Entity entity; if(meshName) { - entity = findEntityByName(asset, meshName); + entity = findEntityByName(instance, meshName); Log("Hiding child entity under name %s ", meshName); if (entity.isNull()) { Log("Failed to hide entity; specified mesh name does not exist under the target entity, or the target entity itself is no longer valid."); @@ -284,8 +324,8 @@ namespace polyvox _scene->remove(entity); } else { Log("Hiding all child entities"); - auto* entities = asset->getEntities(); - for(int i =0; i < asset->getEntityCount(); i++) { + auto* entities = instance->getEntities(); + for(int i =0; i < instance->getEntityCount(); i++) { auto entity = entities[i]; _scene->remove(entity); } @@ -296,17 +336,21 @@ namespace polyvox bool SceneManager::reveal(EntityId entityId, const char *meshName) { - auto asset = getAssetByEntityId(entityId); - if (!asset) + auto* instance = getInstanceByEntityId(entityId); + if (!instance) { - Log("No asset found under entity ID"); - return false; + auto* asset = getAssetByEntityId(entityId); + if(asset) { + instance = asset->getInstance(); + } else { + return false; + } } utils::Entity entity; if(meshName) { - entity = findEntityByName(asset, meshName); + entity = findEntityByName(instance, meshName); if (entity.isNull()) { Log("Failed to reveal entity; specified mesh name does not exist under the target entity, or the target entity itself is no longer valid."); @@ -315,14 +359,13 @@ namespace polyvox _scene->addEntity(entity); } else { Log("Revealing all child entities"); - auto* entities = asset->getEntities(); - for(int i =0; i < asset->getEntityCount(); i++) { + auto* entities = instance->getEntities(); + for(int i =0; i < instance->getEntityCount(); i++) { auto entity = entities[i]; _scene->addEntity(entity); } } - return true; } @@ -330,154 +373,38 @@ namespace polyvox { for (auto &asset : _assets) { - _scene->removeEntities(asset.asset->getEntities(), - asset.asset->getEntityCount()); - _scene->removeEntities(asset.asset->getLightEntities(), - asset.asset->getLightEntityCount()); - _assetLoader->destroyAsset(asset.asset); + _scene->removeEntities(asset.second->getEntities(), + asset.second->getEntityCount()); + _scene->removeEntities(asset.second->getLightEntities(), + asset.second->getLightEntityCount()); + _assetLoader->destroyAsset(asset.second); } _assets.clear(); } - FilamentAsset *SceneManager::getAssetByEntityId(EntityId entityId) + FilamentInstance *SceneManager::getInstanceByEntityId(EntityId entityId) { - const auto &pos = _entityIdLookup.find(entityId); - if (pos == _entityIdLookup.end()) + const auto &pos = _instances.find(entityId); + if (pos == _instances.end()) { + Log("Failed to find FilamentInstance for entity %d", entityId); return nullptr; } - return _assets[pos->second].asset; + return pos->second; } - void SceneManager::updateAnimations() + FilamentAsset *SceneManager::getAssetByEntityId(EntityId entityId) { - std::lock_guard lock(_mutex); - RenderableManager &rm = _engine->getRenderableManager(); - - auto now = high_resolution_clock::now(); - - for (auto &asset : _assets) + const auto &pos = _assets.find(entityId); + if (pos == _assets.end()) { - - for (int i = ((int)asset.gltfAnimations.size()) - 1; i >= 0; i--) { - - auto animationStatus = asset.gltfAnimations[i]; - - auto elapsedInSecs = float(std::chrono::duration_cast(now - animationStatus.start).count()) / 1000.0f; - - if (!animationStatus.loop && elapsedInSecs >= animationStatus.durationInSecs) - { - asset.asset->getInstance()->getAnimator()->applyAnimation(animationStatus.index, animationStatus.durationInSecs - 0.001); - asset.asset->getInstance()->getAnimator()->updateBoneMatrices(); - asset.gltfAnimations.erase(asset.gltfAnimations.begin() + i); - asset.fadeGltfAnimationIndex = -1; - continue; - } - asset.asset->getInstance()->getAnimator()->applyAnimation(animationStatus.index, elapsedInSecs); - - if (asset.fadeGltfAnimationIndex != -1 && elapsedInSecs < asset.fadeDuration) - { - // cross-fade - auto fadeFromTime = asset.fadeOutAnimationStart + elapsedInSecs; - auto alpha = elapsedInSecs / asset.fadeDuration; - asset.asset->getInstance()->getAnimator()->applyCrossFade(asset.fadeGltfAnimationIndex, fadeFromTime, alpha); - } - } - - asset.asset->getInstance()->getAnimator()->updateBoneMatrices(); - - - for (int i = (int)asset.morphAnimations.size() - 1; i >= 0; i--) { - - auto animationStatus = asset.morphAnimations[i]; - - auto elapsedInSecs = float(std::chrono::duration_cast(now - animationStatus.start).count()) / 1000.0f; - - if (!animationStatus.loop && elapsedInSecs >= animationStatus.durationInSecs) - { - asset.morphAnimations.erase(asset.morphAnimations.begin() + i); - continue; - } - - - int frameNumber = static_cast(elapsedInSecs * 1000.0f / animationStatus.frameLengthInMs) % animationStatus.lengthInFrames; - // offset from the end if reverse - if (animationStatus.reverse) - { - frameNumber = animationStatus.lengthInFrames - frameNumber; - } - auto baseOffset = frameNumber * animationStatus.morphIndices.size(); - for (int i = 0; i < animationStatus.morphIndices.size(); i++) - { - auto morphIndex = animationStatus.morphIndices[i]; - // set the weights appropriately - rm.setMorphWeights( - rm.getInstance(animationStatus.meshTarget), - animationStatus.frameData.data() + baseOffset + i, - 1, - morphIndex); - } - } - - for (int i = (int)asset.boneAnimations.size() - 1; i >= 0; i--) { - auto animationStatus = asset.boneAnimations[i]; - - auto elapsedInSecs = float(std::chrono::duration_cast(now - animationStatus.start).count()) / 1000.0f; - - if (!animationStatus.loop && elapsedInSecs >= animationStatus.durationInSecs) - { - asset.boneAnimations.erase(asset.boneAnimations.begin() + i); - continue; - } - - float elapsedFrames = elapsedInSecs * 1000.0f / animationStatus.frameLengthInMs; - - int currFrame = static_cast(elapsedFrames) % animationStatus.lengthInFrames; - float delta = elapsedFrames - currFrame; - int nextFrame = currFrame; - auto restLocalTransform = asset.initialJointTransforms[animationStatus.boneIndex]; - - // offset from the end if reverse - if (animationStatus.reverse) - { - currFrame = animationStatus.lengthInFrames - currFrame; - if(currFrame > 0) { - nextFrame = currFrame - 1; - } else { - nextFrame = 0; - } - } else { - if(currFrame < animationStatus.lengthInFrames - 1) { - nextFrame = currFrame + 1; - } else { - nextFrame = currFrame; - } - } - - // simple linear interpolation - math::mat4f curr = (1 - delta) * (restLocalTransform * animationStatus.frameData[currFrame]); - math::mat4f next = delta * (restLocalTransform * animationStatus.frameData[nextFrame]); - math::mat4f localTransform = curr + next; - - auto filamentInstance = asset.asset->getInstance(); - TransformManager &transformManager = _engine->getTransformManager(); - - const Entity joint = filamentInstance->getJointsAt(animationStatus.skinIndex)[animationStatus.boneIndex]; - - auto jointTransform = transformManager.getInstance(joint); - - transformManager.setTransform(jointTransform, localTransform); - - asset.asset->getInstance()->getAnimator()->updateBoneMatrices(); - - if (animationStatus.loop && elapsedInSecs >= animationStatus.durationInSecs) - { - animationStatus.start = now; - } - } + Log("Failed to find FilamentAsset for entity %d", entityId); + return nullptr; } + return pos->second; } + // TODO - we really don't want to be looking up the bone index/entity by name every single frame // - could use findChildEntityByName // - or is it better to add an option for "streaming" mode where we can just return a reference to a mat4 and then update the values directly? @@ -485,15 +412,18 @@ namespace polyvox { std::lock_guard lock(_mutex); - const auto &pos = _entityIdLookup.find(entityId); - if (pos == _entityIdLookup.end()) - { - Log("Couldn't find asset under specified entity id."); - return false; - } - SceneAsset &sceneAsset = _assets[pos->second]; + auto* instance = getInstanceByEntityId(entityId); - const auto &entity = findEntityByName(sceneAsset, entityName); + if(!instance) { + auto* asset = getAssetByEntityId(entityId); + if(asset) { + instance = asset->getInstance(); + } else { + return false; + } + } + + const auto &entity = findEntityByName(instance, entityName); if(entity.isNull()) { Log("Failed to find entity %s.", entityName); @@ -511,17 +441,15 @@ namespace polyvox TransformManager &transformManager = _engine->getTransformManager(); - const auto &filamentInstance = sceneAsset.asset->getInstance(); - - size_t skinCount = filamentInstance->getSkinCount(); + size_t skinCount = instance->getSkinCount(); if (skinCount > 1) { Log("WARNING - skin count > 1 not currently implemented. This will probably not work"); } - size_t numJoints = filamentInstance->getJointCountAt(skinIndex); - auto joints = filamentInstance->getJointsAt(skinIndex); + size_t numJoints = instance->getJointCountAt(skinIndex); + auto joints = instance->getJointsAt(skinIndex); int boneIndex = -1; for (int i = 0; i < numJoints; i++) { @@ -537,7 +465,7 @@ namespace polyvox return false; } - utils::Entity joint = filamentInstance->getJointsAt(skinIndex)[boneIndex]; + utils::Entity joint = instance->getJointsAt(skinIndex)[boneIndex]; if (joint.isNull()) { @@ -545,7 +473,7 @@ namespace polyvox return false; } - const auto& inverseBindMatrix = filamentInstance->getInverseBindMatricesAt(skinIndex)[boneIndex]; + const auto& inverseBindMatrix = instance->getInverseBindMatricesAt(skinIndex)[boneIndex]; auto jointTransform = transformManager.getInstance(joint); auto globalJointTransform = transformManager.getWorldTransform(jointTransform); @@ -570,83 +498,87 @@ namespace polyvox void SceneManager::remove(EntityId entityId) { std::lock_guard lock(_mutex); - const auto &pos = _entityIdLookup.find(entityId); - if (pos == _entityIdLookup.end()) - { - Log("Couldn't find asset under specified entity id."); - return; - } - auto assetIndex = pos->second; - SceneAsset &sceneAsset = _assets[assetIndex]; - - Log("Removing entity %d at asset index %d", entityId, assetIndex); - - for(auto entityPos : _entityIdLookup) { - if(entityPos.second > pos->second) { - _entityIdLookup[entityPos.first] = entityPos.second-1; - } - } - - _entityIdLookup.erase(entityId); - - auto entityCount = sceneAsset.asset->getEntityCount(); - const auto* entities = sceneAsset.asset->getEntities(); auto entity = Entity::import(entityId); + if(_animationComponentManager->hasComponent(entity)) { + _animationComponentManager->removeComponent(entity); + } + if(_collisionComponentManager->hasComponent(entity)) { _collisionComponentManager->removeComponent(entity); } - - for(int i = 0; i < entityCount; i++) { - auto entity = entities[i]; - if(_collisionComponentManager->hasComponent(entity)) { - _collisionComponentManager->removeComponent(entity); - } - } - auto root = sceneAsset.asset->getRoot(); - if(_collisionComponentManager->hasComponent(root)) { - _collisionComponentManager->removeComponent(root); - } - _nonTransformableCollidableEntities.erase(std::remove_if(_nonTransformableCollidableEntities.begin(), _nonTransformableCollidableEntities.end(), - [=](EntityId entityId2) - { return entityId == entityId2; }), - _nonTransformableCollidableEntities.end()); - - _scene->removeEntities(entities, entityCount); - - auto lightCount =sceneAsset.asset->getLightEntityCount(); - if(lightCount > 0) { - _scene->removeEntities(sceneAsset.asset->getLightEntities(), - sceneAsset.asset->getLightEntityCount()); - } + _scene->remove(entity); - _assetLoader->destroyAsset(sceneAsset.asset); + const auto* instance = getInstanceByEntityId(entityId); + + if(instance) { + _instances.erase(entityId); + _scene->removeEntities(instance->getEntities(), instance->getEntityCount()); + for(int i = 0; i < instance->getEntityCount(); i++) { + auto childEntity = instance->getEntities()[i]; + if(_collisionComponentManager->hasComponent(childEntity)) { + _collisionComponentManager->removeComponent(childEntity); + } + if(_animationComponentManager->hasComponent(childEntity)) { + _animationComponentManager->removeComponent(childEntity); + } + } + // if this a FilamentAsset Entity + } else { + auto* asset = getAssetByEntityId(entityId); - if (sceneAsset.texture) - { - _engine->destroy(sceneAsset.texture); + if(!asset) { + Log("ERROR: could not find FilamentInstance or FilamentAsset associated with the given entity id"); + return; + } + _assets.erase(entityId); + + _scene->removeEntities(asset->getEntities(), asset->getEntityCount()); + + _animationComponentManager->removeComponent(asset->getInstance()->getRoot()); + + for(int i = 0; i < asset->getEntityCount(); i++) { + auto childEntity = asset->getEntities()[i]; + if(_collisionComponentManager->hasComponent(childEntity)) { + _collisionComponentManager->removeComponent(childEntity); + } + if(_animationComponentManager->hasComponent(childEntity)) { + _animationComponentManager->removeComponent(childEntity); + } + } + + auto lightCount = asset->getLightEntityCount(); + if(lightCount > 0) { + _scene->removeEntities(asset->getLightEntities(), + asset->getLightEntityCount()); + } + _assetLoader->destroyAsset(asset); } - EntityManager &em = EntityManager::get(); + + // if (sceneAsset.texture) + // { + // _engine->destroy(sceneAsset.texture); + // } + + utils::EntityManager &em = utils::EntityManager::get(); em.destroy(entity); - _assets.erase(std::remove_if(_assets.begin(), _assets.end(), - [=](SceneAsset &asset) - { return asset.asset == sceneAsset.asset; }), - _assets.end()); } void SceneManager::setMorphTargetWeights(EntityId entityId, const char *const entityName, const float *const weights, const int count) { - const auto &pos = _entityIdLookup.find(entityId); - if (pos == _entityIdLookup.end()) - { - Log("ERROR: asset not found for entity."); - return; - } - auto &asset = _assets[pos->second]; + auto* instance = getInstanceByEntityId(entityId); - auto entity = findEntityByName(asset, entityName); + if(!instance) { + auto asset = getAssetByEntityId(entityId); + if(!asset) { + return; + } + instance = asset->getInstance(); + } + + auto entity = findEntityByName(instance, entityName); if (!entity) { Log("Warning: failed to find entity %s", entityName); @@ -672,15 +604,15 @@ namespace polyvox utils::Entity SceneManager::findChildEntityByName(EntityId entityId, const char *entityName) { std::lock_guard lock(_mutex); - const auto &pos = _entityIdLookup.find(entityId); - if (pos == _entityIdLookup.end()) + const auto &pos = _instances.find(entityId); + if (pos == _instances.end()) { Log("Couldn't find asset under specified entity id."); return utils::Entity(); } - SceneAsset &sceneAsset = _assets[pos->second]; + const auto* instance = pos->second; - const auto entity = findEntityByName(sceneAsset, entityName); + const auto entity = findEntityByName(instance, entityName); if(entity.isNull()) { Log("Failed to find entity %s.", entityName); @@ -691,12 +623,12 @@ namespace polyvox } - utils::Entity SceneManager::findEntityByName(SceneAsset asset, const char *entityName) - { + utils::Entity SceneManager::findEntityByName(const FilamentInstance *instance, const char *entityName) + { utils::Entity entity; - for (size_t i = 0, c = asset.asset->getEntityCount(); i != c; ++i) + for (size_t i = 0, c = instance->getEntityCount(); i != c; ++i) { - auto entity = asset.asset->getEntities()[i]; + auto entity = instance->getEntities()[i]; auto nameInstance = _ncm->getInstance(entity); if (!nameInstance.isValid()) { @@ -726,18 +658,21 @@ namespace polyvox { std::lock_guard lock(_mutex); - const auto &pos = _entityIdLookup.find(entityId); - if (pos == _entityIdLookup.end()) + auto* instance = getInstanceByEntityId(entityId); + if (!instance) { - Log("ERROR: asset not found for entity."); - return false; + auto* asset = getAssetByEntityId(entityId); + if(asset) { + instance = asset->getInstance(); + } else { + return false; + } } - auto &asset = _assets[pos->second]; - auto entity = findEntityByName(asset, entityName); + auto entity = findEntityByName(instance, entityName); if (!entity) { - Log("Warning: failed to find entity %s", entityName); + Log("ERROR: failed to find entity %s", entityName); return false; } @@ -761,21 +696,30 @@ namespace polyvox morphAnimation.durationInSecs * 1000.0f / frameLengthInMs ); - asset.morphAnimations.emplace_back(morphAnimation); + + auto animationComponentInstance = _animationComponentManager->getInstance(instance->getRoot()); + auto& animationComponent = _animationComponentManager->elementAt<0>(animationComponentInstance); + auto& morphAnimations = animationComponent.morphAnimations; + + morphAnimations.emplace_back(morphAnimation); return true; } bool SceneManager::setMaterialColor(EntityId entityId, const char *meshName, int materialIndex, const float r, const float g, const float b, const float a) { - const auto &pos = _entityIdLookup.find(entityId); - if (pos == _entityIdLookup.end()) + auto* instance = getInstanceByEntityId(entityId); + if (!instance) { - Log("ERROR: asset not found for entity."); - return false; + auto* asset = getAssetByEntityId(entityId); + if(asset) { + instance = asset->getInstance(); + } else { + return false; + } } - auto &asset = _assets[pos->second]; - auto entity = findEntityByName(asset, meshName); + + auto entity = findEntityByName(instance, meshName); RenderableManager &rm = _engine->getRenderableManager(); @@ -803,31 +747,36 @@ namespace polyvox void SceneManager::resetBones(EntityId entityId) { std::lock_guard lock(_mutex); - const auto &pos = _entityIdLookup.find(entityId); - if (pos == _entityIdLookup.end()) + auto* instance = getInstanceByEntityId(entityId); + if (!instance) { - Log("ERROR: asset not found for entity."); - return; + auto* asset = getAssetByEntityId(entityId); + if(asset) { + instance = asset->getInstance(); + } else { + return; + } } - auto &asset = _assets[pos->second]; - auto filamentInstance = asset.asset->getInstance(); - filamentInstance->getAnimator()->resetBoneMatrices(); + instance->getAnimator()->resetBoneMatrices(); - auto skinCount = filamentInstance->getSkinCount(); + auto skinCount = instance->getSkinCount(); TransformManager &transformManager = _engine->getTransformManager(); - + + auto animationComponentInstance = _animationComponentManager->getInstance(instance->getRoot()); + auto& animationComponent = _animationComponentManager->elementAt<0>(animationComponentInstance); + for(int skinIndex = 0; skinIndex < skinCount; skinIndex++) { - for(int i =0; i < filamentInstance->getJointCountAt(skinIndex);i++) { - const Entity joint = filamentInstance->getJointsAt(skinIndex)[i]; - auto restLocalTransform = asset.initialJointTransforms[i]; + for(int i =0; i < instance->getJointCountAt(skinIndex);i++) { + const Entity joint = instance->getJointsAt(skinIndex)[i]; + auto restLocalTransform = animationComponent.initialJointTransforms[i]; auto jointTransform = transformManager.getInstance(joint); transformManager.setTransform(jointTransform, restLocalTransform); } } - filamentInstance->getAnimator()->updateBoneMatrices(); - filamentInstance->getAnimator()->resetBoneMatrices(); + instance->getAnimator()->updateBoneMatrices(); + instance->getAnimator()->resetBoneMatrices(); } @@ -842,17 +791,18 @@ namespace polyvox { std::lock_guard lock(_mutex); - const auto &pos = _entityIdLookup.find(entityId); - if (pos == _entityIdLookup.end()) + auto* instance = getInstanceByEntityId(entityId); + if (!instance) { - Log("ERROR: asset not found for entity."); - return false; + auto* asset = getAssetByEntityId(entityId); + if(asset) { + instance = asset->getInstance(); + } else { + return false; + } } - auto &asset = _assets[pos->second]; - - auto filamentInstance = asset.asset->getInstance(); - size_t skinCount = filamentInstance->getSkinCount(); + size_t skinCount = instance->getSkinCount(); if (skinCount > 1) { @@ -860,8 +810,8 @@ namespace polyvox } int skinIndex = 0; - const utils::Entity *joints = filamentInstance->getJointsAt(skinIndex); - size_t numJoints = filamentInstance->getJointCountAt(skinIndex); + const utils::Entity *joints = instance->getJointsAt(skinIndex); + size_t numJoints = instance->getJointCountAt(skinIndex); BoneAnimation animation; bool found = false; @@ -883,7 +833,7 @@ namespace polyvox animation.frameData.clear(); - const auto& inverseBindMatrix = filamentInstance->getInverseBindMatricesAt(skinIndex)[animation.boneIndex]; + const auto& inverseBindMatrix = instance->getInverseBindMatricesAt(skinIndex)[animation.boneIndex]; const auto& bindMatrix = inverse(inverseBindMatrix); math::float3 trans; math::quatf rot; @@ -912,13 +862,7 @@ namespace polyvox frameData[(i*16)+13], frameData[(i*16)+14], frameData[(i*16)+15]); - // if(i == numFrames - 1) { Log("Model transform for bone %s is \n%f %f %f %f\n%f %f %f %f\n%f %f %f %f\n%f %f %f %f\n", boneName, - // frame[0][0],frame[1][0],frame[2][0],frame[3][0], - // frame[0][1],frame[1][1],frame[2][1],frame[3][1], - // frame[0][2],frame[1][2],frame[2][2],frame[3][2], - // frame[0][3],frame[1][3],frame[2][3],frame[3][3]); - // } - + if(isModelSpace) { frame = (math::mat4f(rot) * frame) * math::mat4f(brot); } @@ -930,7 +874,7 @@ namespace polyvox animation.meshTargets.clear(); for (int i = 0; i < numMeshTargets; i++) { - auto entity = findEntityByName(asset, meshNames[i]); + auto entity = findEntityByName(instance, meshNames[i]); if (!entity) { Log("Mesh target %s for bone animation could not be found", meshNames[i]); @@ -945,12 +889,18 @@ namespace polyvox animation.lengthInFrames = numFrames; animation.frameLengthInMs = frameLengthInMs; animation.skinIndex = 0; - asset.boneAnimations.push_back(animation); + + + auto animationComponentInstance = _animationComponentManager->getInstance(instance->getRoot()); + auto& animationComponent = _animationComponentManager->elementAt<0>(animationComponentInstance); + auto& boneAnimations = animationComponent.boneAnimations; + + boneAnimations.emplace_back(animation); return true; } - void SceneManager::playAnimation(EntityId e, int index, bool loop, bool reverse, bool replaceActive, float crossfade) + void SceneManager::playAnimation(EntityId entityId, int index, bool loop, bool reverse, bool replaceActive, float crossfade) { std::lock_guard lock(_mutex); @@ -959,30 +909,44 @@ namespace polyvox Log("ERROR: glTF animation index must be greater than zero."); return; } - const auto &pos = _entityIdLookup.find(e); - if (pos == _entityIdLookup.end()) + + auto* instance = getInstanceByEntityId(entityId); + if (!instance) { - Log("ERROR: asset not found for entity."); + auto* asset = getAssetByEntityId(entityId); + if(asset) { + instance = asset->getInstance(); + } else { + return; + } + } + + if(!_animationComponentManager->hasComponent(instance->getRoot())) { + Log("ERROR: specified entity is not animatable (has no animation component attached)."); return; } - auto &asset = _assets[pos->second]; + + auto animationComponentInstance = _animationComponentManager->getInstance(instance->getRoot()); + + + auto& animationComponent = _animationComponentManager->elementAt<0>(animationComponentInstance); if (replaceActive) { - if (asset.gltfAnimations.size() > 0) + if (animationComponent.gltfAnimations.size() > 0) { - auto &last = asset.gltfAnimations.back(); - asset.fadeGltfAnimationIndex = last.index; - asset.fadeDuration = crossfade; + auto &last = animationComponent.gltfAnimations.back(); + animationComponent.fadeGltfAnimationIndex = last.index; + animationComponent.fadeDuration = crossfade; auto now = high_resolution_clock::now(); auto elapsedInSecs = float(std::chrono::duration_cast(now - last.start).count()) / 1000.0f; - asset.fadeOutAnimationStart = elapsedInSecs; - asset.gltfAnimations.clear(); + animationComponent.fadeOutAnimationStart = elapsedInSecs; + animationComponent.gltfAnimations.clear(); } else { - asset.fadeGltfAnimationIndex = -1; - asset.fadeDuration = 0.0f; + animationComponent.fadeGltfAnimationIndex = -1; + animationComponent.fadeDuration = 0.0f; } } else if (crossfade > 0) @@ -992,8 +956,8 @@ namespace polyvox } else { - asset.fadeGltfAnimationIndex = -1; - asset.fadeDuration = 0.0f; + animationComponent.fadeGltfAnimationIndex = -1; + animationComponent.fadeDuration = 0.0f; } GltfAnimation animation; @@ -1001,186 +965,189 @@ namespace polyvox animation.start = std::chrono::high_resolution_clock::now(); animation.loop = loop; animation.reverse = reverse; - animation.durationInSecs = asset.asset->getInstance()->getAnimator()->getAnimationDuration(index); + animation.durationInSecs = instance->getAnimator()->getAnimationDuration(index); - asset.gltfAnimations.push_back(animation); + animationComponent.gltfAnimations.push_back(animation); } - void SceneManager::stopAnimation(EntityId entityId, int index) - { + void SceneManager::stopAnimation(EntityId entityId, int index) { std::lock_guard lock(_mutex); - const auto &pos = _entityIdLookup.find(entityId); - if (pos == _entityIdLookup.end()) - { - Log("ERROR: asset not found for entity."); + const auto *instance = getInstanceByEntityId(entityId); + if(!instance) { return; } - auto &asset = _assets[pos->second]; - asset.gltfAnimations.erase(std::remove_if(asset.gltfAnimations.begin(), - asset.gltfAnimations.end(), + auto animationComponentInstance = _animationComponentManager->getInstance(instance->getRoot()); + auto& animationComponent = _animationComponentManager->elementAt<0>(animationComponentInstance); + + animationComponent.gltfAnimations.erase(std::remove_if(animationComponent.gltfAnimations.begin(), + animationComponent.gltfAnimations.end(), [=](GltfAnimation &anim) { return anim.index == index; }), - asset.gltfAnimations.end()); + animationComponent.gltfAnimations.end()); } void SceneManager::loadTexture(EntityId entity, const char *resourcePath, int renderableIndex) { - const auto &pos = _entityIdLookup.find(entity); - if (pos == _entityIdLookup.end()) - { - Log("ERROR: asset not found for entity."); - return; - } - auto &asset = _assets[pos->second]; + // const auto &pos = _instances.find(entity); + // if (pos == _instances.end()) + // { + // Log("ERROR: asset not found for entity."); + // return; + // } + // const auto *instance = pos->second; - Log("Loading texture at %s for renderableIndex %d", resourcePath, renderableIndex); + // Log("Loading texture at %s for renderableIndex %d", resourcePath, renderableIndex); - string rp(resourcePath); + // string rp(resourcePath); - if (asset.texture) - { - _engine->destroy(asset.texture); - asset.texture = nullptr; - } + // if (asset.texture) + // { + // _engine->destroy(asset.texture); + // asset.texture = nullptr; + // } - ResourceBuffer imageResource = _resourceLoaderWrapper->load(rp.c_str()); + // ResourceBuffer imageResource = _resourceLoaderWrapper->load(rp.c_str()); - StreamBufferAdapter sb((char *)imageResource.data, (char *)imageResource.data + imageResource.size); + // StreamBufferAdapter sb((char *)imageResource.data, (char *)imageResource.data + imageResource.size); - istream *inputStream = new std::istream(&sb); + // istream *inputStream = new std::istream(&sb); - LinearImage *image = new LinearImage(ImageDecoder::decode( - *inputStream, rp.c_str(), ImageDecoder::ColorSpace::SRGB)); + // LinearImage *image = new LinearImage(ImageDecoder::decode( + // *inputStream, rp.c_str(), ImageDecoder::ColorSpace::SRGB)); - if (!image->isValid()) - { - Log("Invalid image : %s", rp.c_str()); - delete inputStream; - _resourceLoaderWrapper->free(imageResource); - return; - } + // if (!image->isValid()) + // { + // Log("Invalid image : %s", rp.c_str()); + // delete inputStream; + // _resourceLoaderWrapper->free(imageResource); + // return; + // } - uint32_t channels = image->getChannels(); - uint32_t w = image->getWidth(); - uint32_t h = image->getHeight(); - asset.texture = Texture::Builder() - .width(w) - .height(h) - .levels(0xff) - .format(channels == 3 ? Texture::InternalFormat::RGB16F - : Texture::InternalFormat::RGBA16F) - .sampler(Texture::Sampler::SAMPLER_2D) - .build(*_engine); + // uint32_t channels = image->getChannels(); + // uint32_t w = image->getWidth(); + // uint32_t h = image->getHeight(); + // asset.texture = Texture::Builder() + // .width(w) + // .height(h) + // .levels(0xff) + // .format(channels == 3 ? Texture::InternalFormat::RGB16F + // : Texture::InternalFormat::RGBA16F) + // .sampler(Texture::Sampler::SAMPLER_2D) + // .build(*_engine); - Texture::PixelBufferDescriptor::Callback freeCallback = [](void *buf, size_t, - void *data) - { - delete reinterpret_cast(data); - }; + // Texture::PixelBufferDescriptor::Callback freeCallback = [](void *buf, size_t, + // void *data) + // { + // delete reinterpret_cast(data); + // }; - Texture::PixelBufferDescriptor buffer( - image->getPixelRef(), size_t(w * h * channels * sizeof(float)), - channels == 3 ? Texture::Format::RGB : Texture::Format::RGBA, - Texture::Type::FLOAT, freeCallback); + // Texture::PixelBufferDescriptor buffer( + // image->getPixelRef(), size_t(w * h * channels * sizeof(float)), + // channels == 3 ? Texture::Format::RGB : Texture::Format::RGBA, + // Texture::Type::FLOAT, freeCallback); - asset.texture->setImage(*_engine, 0, std::move(buffer)); - MaterialInstance *const *inst = asset.asset->getInstance()->getMaterialInstances(); - size_t mic = asset.asset->getInstance()->getMaterialInstanceCount(); - Log("Material instance count : %d", mic); + // asset.texture->setImage(*_engine, 0, std::move(buffer)); + // MaterialInstance *const *inst = instance->getMaterialInstances(); + // size_t mic = instance->getMaterialInstanceCount(); + // Log("Material instance count : %d", mic); - auto sampler = TextureSampler(); - inst[0]->setParameter("baseColorIndex", 0); - inst[0]->setParameter("baseColorMap", asset.texture, sampler); - delete inputStream; + // auto sampler = TextureSampler(); + // inst[0]->setParameter("baseColorIndex", 0); + // inst[0]->setParameter("baseColorMap", asset.texture, sampler); + // delete inputStream; - _resourceLoaderWrapper->free(imageResource); + // _resourceLoaderWrapper->free(imageResource); } - void SceneManager::setAnimationFrame(EntityId entity, int animationIndex, int animationFrame) + void SceneManager::setAnimationFrame(EntityId entityId, int animationIndex, int animationFrame) { - const auto &pos = _entityIdLookup.find(entity); - if (pos == _entityIdLookup.end()) - { - Log("ERROR: asset not found for entity."); - return; - } - auto &asset = _assets[pos->second]; + auto* instance = getInstanceByEntityId(entityId); auto offset = 60 * animationFrame * 1000; // TODO - don't hardcore 60fps framerate - asset.asset->getInstance()->getAnimator()->applyAnimation(animationIndex, offset); - asset.asset->getInstance()->getAnimator()->updateBoneMatrices(); + instance->getAnimator()->applyAnimation(animationIndex, offset); + instance->getAnimator()->updateBoneMatrices(); } float SceneManager::getAnimationDuration(EntityId entity, int animationIndex) { - const auto &pos = _entityIdLookup.find(entity); + const auto &pos = _instances.find(entity); - unique_ptr> names = make_unique>(); + unique_ptr> names = std::make_unique>(); - if (pos == _entityIdLookup.end()) + if (pos == _instances.end()) { Log("ERROR: asset not found for entity id."); return -1.0f; } - auto &asset = _assets[pos->second]; - return asset.asset->getInstance()->getAnimator()->getAnimationDuration(animationIndex); + auto *instance = pos->second; + return instance->getAnimator()->getAnimationDuration(animationIndex); } - unique_ptr> SceneManager::getAnimationNames(EntityId entity) + unique_ptr> SceneManager::getAnimationNames(EntityId entity) { - const auto &pos = _entityIdLookup.find(entity); + const auto &pos = _instances.find(entity); - unique_ptr> names = make_unique>(); + unique_ptr> names = std::make_unique>(); - if (pos == _entityIdLookup.end()) + FilamentInstance* instance; + + if (pos != _instances.end()) { - Log("ERROR: asset not found for entity id."); - return names; + instance = pos->second; + } else { + const auto& assetPos = _assets.find(entity); + if(assetPos != _assets.end()) { + instance = assetPos->second->getInstance(); + } else { + Log("Could not resolve entity ID %d to FilamentInstance or FilamentAsset"); + return names; + } } - auto &asset = _assets[pos->second]; - size_t count = asset.asset->getInstance()->getAnimator()->getAnimationCount(); + size_t count = instance->getAnimator()->getAnimationCount(); for (size_t i = 0; i < count; i++) { - names->push_back(asset.asset->getInstance()->getAnimator()->getAnimationName(i)); + names->push_back(instance->getAnimator()->getAnimationName(i)); } return names; } - unique_ptr> SceneManager::getMorphTargetNames(EntityId entity, const char *meshName) + unique_ptr> SceneManager::getMorphTargetNames(EntityId entityId, const char *meshName) { - unique_ptr> names = make_unique>(); + unique_ptr> names = std::make_unique>(); - const auto &pos = _entityIdLookup.find(entity); - if (pos == _entityIdLookup.end()) - { - Log("ERROR: asset not found for entity."); - return names; + const auto *instance = getInstanceByEntityId(entityId); + if(!instance) { + auto asset = getAssetByEntityId(entityId); + if(!asset) { + return names; + } + instance = asset->getInstance(); } - auto &asset = _assets[pos->second]; - const utils::Entity *entities = asset.asset->getEntities(); + const auto *asset = instance->getAsset(); - for (int i = 0; i < asset.asset->getEntityCount(); i++) + const utils::Entity *entities = asset->getEntities(); + + for (int i = 0; i < asset->getEntityCount(); i++) { utils::Entity e = entities[i]; - auto inst = _ncm->getInstance(e); - const char *name = _ncm->getName(inst); + const char *name = asset->getName(e); if (name && strcmp(name, meshName) == 0) { - size_t count = asset.asset->getMorphTargetCountAt(e); + size_t count = asset->getMorphTargetCountAt(e); for (int j = 0; j < count; j++) { - const char *morphName = asset.asset->getMorphTargetNameAt(e, j); + const char *morphName = asset->getMorphTargetNameAt(e, j); names->push_back(morphName); } break; @@ -1189,25 +1156,27 @@ namespace polyvox return names; } - void SceneManager::transformToUnitCube(EntityId entity) + void SceneManager::transformToUnitCube(EntityId entityId) { - const auto &pos = _entityIdLookup.find(entity); - if (pos == _entityIdLookup.end()) - { - Log("ERROR: asset not found for entity."); - return; + const auto *instance = getInstanceByEntityId(entityId); + if(!instance) { + auto asset = getAssetByEntityId(entityId); + if(asset) { + instance = asset->getInstance(); + } else { + return; + } } - auto &asset = _assets[pos->second]; auto &tm = _engine->getTransformManager(); - FilamentInstance *inst = asset.asset->getInstance(); - auto aabb = inst->getBoundingBox(); + + auto aabb = instance->getBoundingBox(); auto center = aabb.center(); auto halfExtent = aabb.extent(); auto maxExtent = max(halfExtent) * 2; auto scaleFactor = 2.0f / maxExtent; auto transform = math::mat4f::scaling(scaleFactor) * math::mat4f::translation(-center); - tm.setTransform(tm.getInstance(inst->getRoot()), transform); + tm.setTransform(tm.getInstance(instance->getRoot()), transform); } void SceneManager::setParent(EntityId childEntityId, EntityId parentEntityId) { @@ -1219,44 +1188,50 @@ namespace polyvox const auto& parentInstance = tm.getInstance(parent); const auto& childInstance = tm.getInstance(child); tm.setParent(childInstance, parentInstance); - - } void SceneManager::addCollisionComponent(EntityId entityId, void(*onCollisionCallback)(const EntityId entityId1, const EntityId entityId2), bool affectsTransform) { std::lock_guard lock(_mutex); - const auto &pos = _entityIdLookup.find(entityId); - if (pos == _entityIdLookup.end()) - { - Log("ERROR: asset not found for entity."); - return; - } - auto &asset = _assets[pos->second]; - asset.asset->getAssetInstances(); - auto collisionInstance = _collisionComponentManager->addComponent(asset.asset->getRoot()); - _collisionComponentManager->elementAt<0>(collisionInstance) = asset.asset->getInstance()->getBoundingBox(); + const auto *instance = getInstanceByEntityId(entityId); + if(!instance) { + auto asset = getAssetByEntityId(entityId); + if(!asset) { + return; + } else { + instance = asset->getInstance(); + + } + } + auto collisionInstance = _collisionComponentManager->addComponent(instance->getRoot()); + _collisionComponentManager->elementAt<0>(collisionInstance) = instance->getBoundingBox(); _collisionComponentManager->elementAt<1>(collisionInstance) = onCollisionCallback; _collisionComponentManager->elementAt<2>(collisionInstance) = affectsTransform; } void SceneManager::testCollisions(EntityId entityId) { - const auto &pos = _entityIdLookup.find(entityId); - if (pos == _entityIdLookup.end()) - { - Log("ERROR: asset not found for entity."); - return; + const auto *instance = getInstanceByEntityId(entityId); + if(!instance) { + auto asset = getAssetByEntityId(entityId); + if(!asset) { + instance = asset->getInstance(); + } } - auto &asset = _assets[pos->second]; + const auto entity = Entity::import(entityId); const auto& tm = _engine->getTransformManager(); - auto transformInstance = tm.getInstance(asset.asset->getRoot()); + auto transformInstance = tm.getInstance(instance->getRoot()); auto worldTransform = tm.getWorldTransform(transformInstance); - auto aabb = asset.asset->getInstance()->getBoundingBox(); + // Log("World transform for %d is %f %f %f", entityId, worldTransform[3][0], worldTransform[3][1], worldTransform[3][2]); + auto aabb = instance->getBoundingBox(); aabb = aabb.transform(worldTransform); - _collisionComponentManager->collides(entityId, aabb); + _collisionComponentManager->collides(entity, aabb); } + void SceneManager::updateAnimations() { + std::lock_guard lock(_mutex); + _animationComponentManager->update(); + } void SceneManager::updateTransforms() { std::lock_guard lock(_mutex); @@ -1264,15 +1239,26 @@ namespace polyvox auto &tm = _engine->getTransformManager(); for ( const auto &[entityId, transformUpdate]: _transformUpdates ) { - const auto &pos = _entityIdLookup.find(entityId); - if (pos == _entityIdLookup.end()) + const auto &pos = _instances.find(entityId); + + bool isCollidable = true; + Entity entity; + filament::TransformManager::Instance transformInstance; + filament::math::mat4f transform; + Aabb boundingBox; + if (pos == _instances.end()) { - Log("ERROR: asset not found for entity."); - continue; + Log("WARNING: SceneAsset not found for entity."); + isCollidable = false; + entity = Entity::import(entityId); + } else { + const auto *instance = pos->second; + entity = instance->getRoot(); + boundingBox = instance->getBoundingBox(); } - auto &asset = _assets[pos->second]; - auto transformInstance = tm.getInstance(asset.asset->getRoot()); - auto transform = tm.getTransform(transformInstance); + + transformInstance = tm.getInstance(entity); + transform = tm.getTransform(transformInstance); math::float3 newTranslation = std::get<0>(transformUpdate); bool newTranslationRelative = std::get<1>(transformUpdate); @@ -1304,21 +1290,23 @@ namespace polyvox } transform = composeMatrix(translation, rotation, scale); - auto bb = asset.asset->getBoundingBox(); - auto transformedBB = bb.transform(transform); - - auto collisionAxes = _collisionComponentManager->collides(entityId, transformedBB); - if(collisionAxes.size() == 1) { - auto globalAxis = collisionAxes[0]; - globalAxis *= norm(relativeTranslation); - auto newRelativeTranslation = relativeTranslation + globalAxis; - translation -= relativeTranslation; - translation += newRelativeTranslation; - transform = composeMatrix(translation, rotation, scale); - } else if(collisionAxes.size() > 1) { - translation -= relativeTranslation; - transform = composeMatrix(translation, rotation, scale); + if(isCollidable) { + auto transformedBB = boundingBox.transform(transform); + + auto collisionAxes = _collisionComponentManager->collides(entity, transformedBB); + + if(collisionAxes.size() == 1) { + auto globalAxis = collisionAxes[0]; + globalAxis *= norm(relativeTranslation); + auto newRelativeTranslation = relativeTranslation + globalAxis; + translation -= relativeTranslation; + translation += newRelativeTranslation; + transform = composeMatrix(translation, rotation, scale); + } else if(collisionAxes.size() > 1) { + translation -= relativeTranslation; + transform = composeMatrix(translation, rotation, scale); + } } tm.setTransform(transformInstance, transform); } @@ -1400,15 +1388,15 @@ namespace polyvox const auto &pos = _transformUpdates.find(entity); if (pos == _transformUpdates.end()) { - _transformUpdates.emplace(entity, make_tuple(math::float3(), true, math::quatf(1.0f), true, 1.0f)); + _transformUpdates.emplace(entity, std::make_tuple(math::float3(), true, math::quatf(1.0f), true, 1.0f)); } auto curr = _transformUpdates[entity]; - auto& trans = get<0>(curr); + auto& trans = std::get<0>(curr); trans.x = x; trans.y = y; trans.z = z; - auto& isRelative = get<1>(curr); + auto& isRelative = std::get<1>(curr); isRelative = relative; _transformUpdates[entity] = curr; } @@ -1419,7 +1407,7 @@ namespace polyvox const auto &pos = _transformUpdates.find(entity); if (pos == _transformUpdates.end()) { - _transformUpdates.emplace(entity, make_tuple(math::float3(), true, math::quatf(1.0f), true, 1.0f)); + _transformUpdates.emplace(entity, std::make_tuple(math::float3(), true, math::quatf(1.0f), true, 1.0f)); } auto curr = _transformUpdates[entity]; auto& rot = std::get<2>(curr); @@ -1427,57 +1415,57 @@ namespace polyvox rot.x = x; rot.y = y; rot.z = z; - auto& isRelative = get<3>(curr); + auto& isRelative = std::get<3>(curr); isRelative = relative; _transformUpdates[entity] = curr; } - const utils::Entity *SceneManager::getCameraEntities(EntityId entity) + const utils::Entity *SceneManager::getCameraEntities(EntityId entityId) { - const auto &pos = _entityIdLookup.find(entity); - if (pos == _entityIdLookup.end()) - { - Log("ERROR: asset not found for entity."); - return nullptr; + const auto *instance = getInstanceByEntityId(entityId); + if(!instance) { + auto asset = getAssetByEntityId(entityId); + if(!asset) { + instance = asset->getInstance(); + } } - auto &asset = _assets[pos->second]; - return asset.asset->getCameraEntities(); + return instance->getAsset()->getCameraEntities(); } - size_t SceneManager::getCameraEntityCount(EntityId entity) + size_t SceneManager::getCameraEntityCount(EntityId entityId) { - const auto &pos = _entityIdLookup.find(entity); - if (pos == _entityIdLookup.end()) - { - Log("ERROR: asset not found for entity."); - return 0; + const auto *instance = getInstanceByEntityId(entityId); + if(!instance) { + auto asset = getAssetByEntityId(entityId); + if(!asset) { + instance = asset->getInstance(); + } } - auto &asset = _assets[pos->second]; - return asset.asset->getCameraEntityCount(); + return instance->getAsset()->getCameraEntityCount(); } - const utils::Entity *SceneManager::getLightEntities(EntityId entity) const noexcept + const utils::Entity *SceneManager::getLightEntities(EntityId entityId) noexcept { - const auto &pos = _entityIdLookup.find(entity); - if (pos == _entityIdLookup.end()) - { - Log("ERROR: asset not found for entity."); - return nullptr; + const auto *instance = getInstanceByEntityId(entityId); + if(!instance) { + auto asset = getAssetByEntityId(entityId); + if(!asset) { + instance = asset->getInstance(); + } } - auto &asset = _assets[pos->second]; - return asset.asset->getLightEntities(); + return instance->getAsset()->getLightEntities(); } - size_t SceneManager::getLightEntityCount(EntityId entity) const noexcept + size_t SceneManager::getLightEntityCount(EntityId entityId) noexcept { - const auto &pos = _entityIdLookup.find(entity); - if (pos == _entityIdLookup.end()) - { - Log("ERROR: asset not found for entity."); - return 0; + const auto *instance = getInstanceByEntityId(entityId); + if(!instance) { + auto asset = getAssetByEntityId(entityId); + if(!asset) { + instance = asset->getInstance(); + } } - auto &asset = _assets[pos->second]; - return asset.asset->getLightEntityCount(); + return instance->getAsset()->getLightEntityCount(); } const char *SceneManager::getNameForEntity(EntityId entityId) @@ -1493,42 +1481,46 @@ namespace polyvox } int SceneManager::getEntityCount(EntityId entityId, bool renderableOnly) { - const auto &pos = _entityIdLookup.find(entityId); - if (pos == _entityIdLookup.end()) - { - Log("ERROR: asset not found for entity."); - return 0; + const auto *instance = getInstanceByEntityId(entityId); + if(!instance) { + auto asset = getAssetByEntityId(entityId); + if(!asset) { + instance = asset->getInstance(); + } else { + return 0; + } } - auto &asset = _assets[pos->second]; if(renderableOnly) { int count = 0; const auto& rm = _engine->getRenderableManager(); - const Entity *entities = asset.asset->getEntities(); - for(int i=0; i < asset.asset->getEntityCount(); i++) { + const Entity *entities = instance->getEntities(); + for(int i=0; i < instance->getEntityCount(); i++) { if(rm.hasComponent(entities[i])) { count++; } } return count; } - return asset.asset->getEntityCount(); + return instance->getEntityCount(); } const char* SceneManager::getEntityNameAt(EntityId entityId, int index, bool renderableOnly) { - const auto &pos = _entityIdLookup.find(entityId); - if (pos == _entityIdLookup.end()) - { - Log("ERROR: asset not found for entity."); - return nullptr; + const auto *instance = getInstanceByEntityId(entityId); + if(!instance) { + auto asset = getAssetByEntityId(entityId); + if(!asset) { + instance = asset->getInstance(); + } else { + return nullptr; + } } - auto &asset = _assets[pos->second]; int found = -1; if(renderableOnly) { int count = 0; const auto& rm = _engine->getRenderableManager(); - const Entity *entities = asset.asset->getEntities(); - for(int i=0; i < asset.asset->getEntityCount(); i++) { + const Entity *entities = instance->getEntities(); + for(int i=0; i < instance->getEntityCount(); i++) { if(rm.hasComponent(entities[i])) { if(count == index) { found = i; @@ -1541,14 +1533,14 @@ namespace polyvox found = index; } - if(found >= asset.asset->getEntityCount()) { + if(found >= instance->getEntityCount()) { Log("ERROR: index %d greater than number of child entities.", found); return nullptr; } - const utils::Entity entity = asset.asset->getEntities()[found]; + const utils::Entity entity = instance->getEntities()[found]; auto inst = _ncm->getInstance(entity); return _ncm->getName(inst); } -} // namespace polyvox +} // namespace flutter_filament diff --git a/ios/src/StreamBufferAdapter.cpp b/ios/src/StreamBufferAdapter.cpp index 76bfeb71..fcbc6cd7 100644 --- a/ios/src/StreamBufferAdapter.cpp +++ b/ios/src/StreamBufferAdapter.cpp @@ -3,9 +3,7 @@ #include #include -using namespace std; - -namespace polyvox { +namespace flutter_filament { class StreamBufferAdapter : public std::streambuf { @@ -14,14 +12,14 @@ class StreamBufferAdapter : public std::streambuf ~StreamBufferAdapter() { } - streamsize size(); + std::streamsize size(); private: int_type uflow() override; int_type underflow() override; int_type pbackfail(int_type ch) override; - streampos seekoff(streamoff off, ios_base::seekdir way, ios_base::openmode which) override; - streampos seekpos(streampos sp, ios_base::openmode which) override; + std::streampos seekoff(std::streamoff off, std::ios_base::seekdir way, std::ios_base::openmode which) override; + std::streampos seekpos(std::streampos sp, std::ios_base::openmode which) override; std::streamsize showmanyc() override; }; @@ -31,11 +29,11 @@ StreamBufferAdapter::StreamBufferAdapter(const char *begin, const char *end) setg((char*)begin, (char*)begin, (char*)end); } -streamsize StreamBufferAdapter::size() { +std::streamsize StreamBufferAdapter::size() { return egptr() - eback(); } -streambuf::int_type StreamBufferAdapter::underflow() +std::streambuf::int_type StreamBufferAdapter::underflow() { if (gptr() == egptr()) { return traits_type::eof(); @@ -43,7 +41,7 @@ streambuf::int_type StreamBufferAdapter::underflow() return *(gptr()); } -streambuf::int_type StreamBufferAdapter::uflow() +std::streambuf::int_type StreamBufferAdapter::uflow() { if (gptr() == egptr()) { return traits_type::eof(); @@ -53,7 +51,7 @@ streambuf::int_type StreamBufferAdapter::uflow() return *(gptr()); } -streambuf::int_type StreamBufferAdapter::pbackfail(int_type ch) +std::streambuf::int_type StreamBufferAdapter::pbackfail(int_type ch) { if (gptr() == eback() || (ch != traits_type::eof() && ch != gptr()[-1])) return traits_type::eof(); @@ -61,15 +59,15 @@ streambuf::int_type StreamBufferAdapter::pbackfail(int_type ch) return *(gptr()); } -streamsize StreamBufferAdapter::showmanyc() +std::streamsize StreamBufferAdapter::showmanyc() { return egptr() - gptr(); } -streampos StreamBufferAdapter::seekoff(streamoff off, ios_base::seekdir way, ios_base::openmode which = ios_base::in) { - if(way == ios_base::beg) { +std::streampos StreamBufferAdapter::seekoff(std::streamoff off, std::ios_base::seekdir way, std::ios_base::openmode which = std::ios_base::in) { + if(way == std::ios_base::beg) { setg(eback(), eback()+off, egptr()); - } else if(way == ios_base::cur) { + } else if(way == std::ios_base::cur) { gbump(off); } else { setg(eback(), egptr()-off, egptr()); @@ -77,7 +75,7 @@ streampos StreamBufferAdapter::seekoff(streamoff off, ios_base::seekdir way, ios return gptr() - eback(); } -streampos StreamBufferAdapter::seekpos(streampos sp, ios_base::openmode which = ios_base::in) { +std::streampos StreamBufferAdapter::seekpos(std::streampos sp, std::ios_base::openmode which = std::ios_base::in) { return seekoff(sp - pos_type(off_type(0)), std::ios_base::beg, which); } } \ No newline at end of file diff --git a/lib/camera/camera_orientation.dart b/lib/camera/camera_orientation.dart new file mode 100644 index 00000000..3e800836 --- /dev/null +++ b/lib/camera/camera_orientation.dart @@ -0,0 +1,15 @@ +import 'package:vector_math/vector_math_64.dart' as v; + +class CameraOrientation { + v.Vector3 position = v.Vector3(0, 0, 0); + + var rotationX = 0.0; + var rotationY = 0.0; + var rotationZ = 0.0; + + v.Quaternion compose() { + return v.Quaternion.axisAngle(v.Vector3(0, 0, 1), rotationZ) * + v.Quaternion.axisAngle(v.Vector3(0, 1, 0), rotationY) * + v.Quaternion.axisAngle(v.Vector3(1, 0, 0), rotationX); + } +} diff --git a/lib/filament_controller.dart b/lib/filament_controller.dart index aecfa87b..445cf0c1 100644 --- a/lib/filament_controller.dart +++ b/lib/filament_controller.dart @@ -1,6 +1,7 @@ // ignore_for_file: constant_identifier_names import 'dart:async'; +import 'dart:typed_data'; import 'dart:ui' as ui; import 'package:flutter/widgets.dart'; @@ -12,6 +13,9 @@ import 'package:vector_math/vector_math_64.dart'; // a handle that can be safely passed back to the rendering layer to manipulate an Entity typedef FilamentEntity = int; +// "picking" means clicking/tapping on the viewport, and unprojecting the X/Y coordinate to determine whether any renderable entities were present at those coordinates. +typedef FilamentPickResult = ({FilamentEntity entity, double x, double y}); + enum ToneMapper { ACES, FILMIC, LINEAR } // see filament Manipulator.h for more details @@ -65,7 +69,7 @@ abstract class FilamentController { /// This may be a broadcast stream, so you should ensure you have subscribed to this stream before calling [pick]. /// If [pick] is called without an active subscription to this stream, the results will be silently discarded. /// - Stream get pickResult; + Stream get pickResult; /// /// Whether the controller is currently rendering at [framerate]. @@ -206,7 +210,35 @@ abstract class FilamentController { /// /// Load the .glb asset at the given path and insert into the scene. /// - Future loadGlb(String path, {bool unlit = false}); + Future loadGlb(String path, {int numInstances = 1}); + + /// + /// Load the .glb asset from the specified path (either Flutter asset URI or filepath) and insert into the scene. + /// If [cache] is true, the contents of the path will be cached locally and re-used for any future calls to load that asset. + /// See also [evictCache]. + /// + Future loadGlbFromBuffer(String path, + {bool cache = false, int numInstances = 1}); + + /// + /// Create a new instance of [entity]. + /// + Future createInstance(FilamentEntity entity); + + /// + /// Returns the number of instances of the asset associated with [entity]. + /// + Future getInstanceCount(FilamentEntity entity); + + /// + /// Returns all instances of [entity]. + /// + Future> getInstances(FilamentEntity entity); + + /// + /// Frees all cached resources loaded via [loadGlbFromBuffer]. + /// + Future evictCache(); /// /// Load the .gltf asset at the given path and insert into the scene. @@ -281,16 +313,11 @@ abstract class FilamentController { Future resetBones(FilamentEntity entity); /// - /// Starts animating a bone (joint) according to the specified [animation]. + /// Transforms the bone(s)/joint(s) according [animation]. + /// To set the instantaneous transform, just use a single frame. /// Future addBoneAnimation(FilamentEntity entity, BoneAnimationData animation); - /// - /// Sets the local joint transform for the bone at the given index in [entity] for the mesh under [meshName]. - /// - Future setBoneTransform( - FilamentEntity entity, String meshName, String boneName, Matrix4 data); - /// /// Removes/destroys the specified entity from the scene. /// [entity] will no longer be a valid handle after this method is called; ensure you immediately discard all references once this method is complete. @@ -350,6 +377,11 @@ abstract class FilamentController { /// Future setMainCamera(); + /// + /// Returns the entity associated with the main camera. + /// + Future getMainCamera(); + /// /// Sets the current scene camera to the glTF camera under [name] in [entity]. /// @@ -450,7 +482,7 @@ abstract class FilamentController { /// /// Rotate the camera by [rads] around the given axis. Note this is not persistent - any viewport navigation will reset the camera transform. /// - Future setCameraRotation(double rads, double x, double y, double z); + Future setCameraRotation(Quaternion quaternion); /// /// Sets the camera model matrix. diff --git a/lib/filament_controller_ffi.dart b/lib/filament_controller_ffi.dart index 5218e30f..44e8799a 100644 --- a/lib/filament_controller_ffi.dart +++ b/lib/filament_controller_ffi.dart @@ -1,6 +1,7 @@ import 'dart:async'; import 'dart:ffi'; import 'dart:io'; +import 'dart:typed_data'; import 'dart:ui' as ui; import 'dart:developer' as dev; import 'package:flutter/services.dart'; @@ -52,8 +53,9 @@ class FilamentControllerFFI extends FilamentController { final hasViewer = ValueNotifier(false); @override - Stream get pickResult => _pickResultController.stream; - final _pickResultController = StreamController.broadcast(); + Stream get pickResult => _pickResultController.stream; + final _pickResultController = + StreamController.broadcast(); int? _resizingWidth; int? _resizingHeight; @@ -545,8 +547,82 @@ class FilamentControllerFFI extends FilamentController { _lights.clear(); } + final _assetCache = , int)>{}; + @override - Future loadGlb(String path, {bool unlit = false}) async { + Future loadGlbFromBuffer(String path, + {bool cache = false, int numInstances = 1}) async { + late (Pointer, int) data; + + if (cache && _assetCache.containsKey(path)) { + data = _assetCache[path]!; + } else { + late ByteData asset; + if (path.startsWith("file://")) { + var raw = File(path.replaceAll("file://", "")).readAsBytesSync(); + asset = raw.buffer.asByteData(raw.offsetInBytes); + } else { + asset = await rootBundle.load(path.replaceAll("asset://", "")); + } + + var ptr = allocator(asset.lengthInBytes); + for (int i = 0; i < asset.lengthInBytes; i++) { + ptr[i] = asset.getUint8(i); + } + + data = (ptr.cast(), asset.lengthInBytes); + } + var entity = load_glb_from_buffer_ffi( + _sceneManager!, data.$1, data.$2, numInstances); + if (!cache) { + allocator.free(data.$1); + } else { + _assetCache[path] = data; + } + if (entity == _FILAMENT_ASSET_ERROR) { + throw Exception("Failed to load GLB from path $path"); + } + return entity; + } + + @override + Future createInstance(FilamentEntity entity) async { + var created = create_instance(_sceneManager!, entity); + if (created == _FILAMENT_ASSET_ERROR) { + throw Exception("Failed to create instance"); + } + return created; + } + + @override + Future getInstanceCount(FilamentEntity entity) async { + return get_instance_count(_sceneManager!, entity); + } + + @override + Future> getInstances(FilamentEntity entity) async { + var count = await getInstanceCount(entity); + var out = allocator(count); + get_instances(_sceneManager!, entity, out); + var instances = []; + for (int i = 0; i < count; i++) { + instances.add(out[i]); + } + allocator.free(out); + return instances; + } + + @override + Future evictCache() async { + for (final value in _assetCache.values) { + allocator.free(value.$1); + } + _assetCache.clear(); + } + + @override + Future loadGlb(String path, + {bool unlit = false, int numInstances = 1}) async { if (_viewer == null) { throw Exception("No viewer available, ignoring"); } @@ -554,7 +630,7 @@ class FilamentControllerFFI extends FilamentController { throw Exception("Not yet implemented"); } final pathPtr = path.toNativeUtf8().cast(); - var entity = load_glb_ffi(_sceneManager!, pathPtr, unlit); + var entity = load_glb_ffi(_sceneManager!, pathPtr, numInstances); allocator.free(pathPtr); if (entity == _FILAMENT_ASSET_ERROR) { throw Exception("An error occurred loading the asset at $path"); @@ -910,6 +986,10 @@ class FilamentControllerFFI extends FilamentController { set_main_camera(_viewer!); } + Future getMainCamera() async { + return get_main_camera(_viewer!); + } + @override Future setCamera(FilamentEntity entity, String? name) async { if (_viewer == null) { @@ -1027,11 +1107,12 @@ class FilamentControllerFFI extends FilamentController { } @override - Future setCameraRotation(double rads, double x, double y, double z) async { + Future setCameraRotation(Quaternion quaternion) async { if (_viewer == null) { throw Exception("No viewer available, ignoring"); } - set_camera_rotation(_viewer!, rads, x, y, z); + set_camera_rotation( + _viewer!, quaternion.w, quaternion.x, quaternion.y, quaternion.z); } @override @@ -1158,13 +1239,13 @@ class FilamentControllerFFI extends FilamentController { } @override - Future reveal(FilamentEntity entity, String meshName) async { + Future reveal(FilamentEntity entity, String? meshName) async { if (_viewer == null) { throw Exception("No viewer available, ignoring"); } - final meshNamePtr = meshName.toNativeUtf8().cast(); - final result = reveal_mesh(_sceneManager!, entity, meshNamePtr) != 1; + final meshNamePtr = meshName?.toNativeUtf8().cast() ?? nullptr; + final result = reveal_mesh(_sceneManager!, entity, meshNamePtr) == 1; allocator.free(meshNamePtr); if (!result) { throw Exception("Failed to reveal mesh $meshName"); @@ -1199,7 +1280,8 @@ class FilamentControllerFFI extends FilamentController { } } var entityId = outPtr.value; - _pickResultController.add(entityId); + _pickResultController + .add((entity: entityId, x: x.toDouble(), y: y.toDouble())); allocator.free(outPtr); } @@ -1331,28 +1413,6 @@ class FilamentControllerFFI extends FilamentController { return frustum; } - @override - Future setBoneTransform(FilamentEntity entity, String meshName, - String boneName, Matrix4 data) async { - var ptr = allocator(16); - for (int i = 0; i < 16; i++) { - ptr.elementAt(i).value = data.storage[i]; - } - - var meshNamePtr = meshName.toNativeUtf8(allocator: allocator).cast(); - var boneNamePtr = boneName.toNativeUtf8(allocator: allocator).cast(); - - var result = set_bone_transform_ffi( - _sceneManager!, entity, meshNamePtr, ptr, boneNamePtr); - - allocator.free(ptr); - allocator.free(meshNamePtr); - allocator.free(boneNamePtr); - if (!result) { - throw Exception("Failed to set bone transform. See logs for details"); - } - } - @override Future getChildEntity( FilamentEntity parent, String childName) async { diff --git a/lib/generated_bindings.dart b/lib/generated_bindings.dart index 2a33add6..d1c86dda 100644 --- a/lib/generated_bindings.dart +++ b/lib/generated_bindings.dart @@ -183,11 +183,21 @@ external void clear_lights( @ffi.Native< EntityId Function(ffi.Pointer, ffi.Pointer, - ffi.Bool)>(symbol: 'load_glb', assetId: 'flutter_filament_plugin') + ffi.Int)>(symbol: 'load_glb', assetId: 'flutter_filament_plugin') external int load_glb( ffi.Pointer sceneManager, ffi.Pointer assetPath, - bool unlit, + int numInstances, +); + +@ffi.Native< + EntityId Function( + ffi.Pointer, ffi.Pointer, ffi.Size)>( + symbol: 'load_glb_from_buffer', assetId: 'flutter_filament_plugin') +external int load_glb_from_buffer( + ffi.Pointer sceneManager, + ffi.Pointer data, + int length, ); @ffi.Native< @@ -200,12 +210,42 @@ external int load_gltf( ffi.Pointer relativePath, ); +@ffi.Native, EntityId)>( + symbol: 'create_instance', assetId: 'flutter_filament_plugin') +external int create_instance( + ffi.Pointer sceneManager, + int id, +); + +@ffi.Native, EntityId)>( + symbol: 'get_instance_count', assetId: 'flutter_filament_plugin') +external int get_instance_count( + ffi.Pointer sceneManager, + int entityId, +); + +@ffi.Native< + ffi.Int Function( + ffi.Pointer, EntityId, ffi.Pointer)>( + symbol: 'get_instances', assetId: 'flutter_filament_plugin') +external int get_instances( + ffi.Pointer sceneManager, + int entityId, + ffi.Pointer out, +); + @ffi.Native)>( symbol: 'set_main_camera', assetId: 'flutter_filament_plugin') external void set_main_camera( ffi.Pointer viewer, ); +@ffi.Native)>( + symbol: 'get_main_camera', assetId: 'flutter_filament_plugin') +external int get_main_camera( + ffi.Pointer viewer, +); + @ffi.Native< ffi.Bool Function( ffi.Pointer, EntityId, ffi.Pointer)>( @@ -653,7 +693,7 @@ external void get_camera_position( symbol: 'set_camera_rotation', assetId: 'flutter_filament_plugin') external void set_camera_rotation( ffi.Pointer viewer, - double rads, + double w, double x, double y, double z, @@ -1140,11 +1180,22 @@ external void clear_lights_ffi( @ffi.Native< EntityId Function(ffi.Pointer, ffi.Pointer, - ffi.Bool)>(symbol: 'load_glb_ffi', assetId: 'flutter_filament_plugin') + ffi.Int)>(symbol: 'load_glb_ffi', assetId: 'flutter_filament_plugin') external int load_glb_ffi( - ffi.Pointer assetManager, + ffi.Pointer sceneManager, ffi.Pointer assetPath, - bool unlit, + int numInstances, +); + +@ffi.Native< + EntityId Function( + ffi.Pointer, ffi.Pointer, ffi.Size, ffi.Int)>( + symbol: 'load_glb_from_buffer_ffi', assetId: 'flutter_filament_plugin') +external int load_glb_from_buffer_ffi( + ffi.Pointer sceneManager, + ffi.Pointer data, + int length, + int numInstances, ); @ffi.Native< @@ -1152,11 +1203,18 @@ external int load_glb_ffi( ffi.Pointer)>( symbol: 'load_gltf_ffi', assetId: 'flutter_filament_plugin') external int load_gltf_ffi( - ffi.Pointer assetManager, + ffi.Pointer sceneManager, ffi.Pointer assetPath, ffi.Pointer relativePath, ); +@ffi.Native, EntityId)>( + symbol: 'create_instance_ffi', assetId: 'flutter_filament_plugin') +external int create_instance_ffi( + ffi.Pointer sceneManager, + int entityId, +); + @ffi.Native, EntityId)>( symbol: 'remove_entity_ffi', assetId: 'flutter_filament_plugin') external void remove_entity_ffi( @@ -1185,7 +1243,7 @@ external bool set_camera_ffi( ffi.Pointer, ffi.Pointer, ffi.Int)>( symbol: 'apply_weights_ffi', assetId: 'flutter_filament_plugin') external void apply_weights_ffi( - ffi.Pointer assetManager, + ffi.Pointer sceneManager, int asset, ffi.Pointer entityName, ffi.Pointer weights, @@ -1197,7 +1255,7 @@ external void apply_weights_ffi( ffi.Bool, ffi.Bool, ffi.Float)>( symbol: 'play_animation_ffi', assetId: 'flutter_filament_plugin') external void play_animation_ffi( - ffi.Pointer assetManager, + ffi.Pointer sceneManager, int asset, int index, bool loop, @@ -1210,7 +1268,7 @@ external void play_animation_ffi( ffi.Void Function(ffi.Pointer, EntityId, ffi.Int, ffi.Int)>( symbol: 'set_animation_frame_ffi', assetId: 'flutter_filament_plugin') external void set_animation_frame_ffi( - ffi.Pointer assetManager, + ffi.Pointer sceneManager, int asset, int animationIndex, int animationFrame, @@ -1219,7 +1277,7 @@ external void set_animation_frame_ffi( @ffi.Native, EntityId, ffi.Int)>( symbol: 'stop_animation_ffi', assetId: 'flutter_filament_plugin') external void stop_animation_ffi( - ffi.Pointer assetManager, + ffi.Pointer sceneManager, int asset, int index, ); @@ -1227,7 +1285,7 @@ external void stop_animation_ffi( @ffi.Native, EntityId)>( symbol: 'get_animation_count_ffi', assetId: 'flutter_filament_plugin') external int get_animation_count_ffi( - ffi.Pointer assetManager, + ffi.Pointer sceneManager, int asset, ); @@ -1236,7 +1294,7 @@ external int get_animation_count_ffi( ffi.Pointer, EntityId, ffi.Pointer, ffi.Int)>( symbol: 'get_animation_name_ffi', assetId: 'flutter_filament_plugin') external void get_animation_name_ffi( - ffi.Pointer assetManager, + ffi.Pointer sceneManager, int asset, ffi.Pointer outPtr, int index, @@ -1247,7 +1305,7 @@ external void get_animation_name_ffi( ffi.Pointer, ffi.Pointer, ffi.Int)>( symbol: 'get_morph_target_name_ffi', assetId: 'flutter_filament_plugin') external void get_morph_target_name_ffi( - ffi.Pointer assetManager, + ffi.Pointer sceneManager, int asset, ffi.Pointer meshName, ffi.Pointer outPtr, @@ -1260,7 +1318,7 @@ external void get_morph_target_name_ffi( symbol: 'get_morph_target_name_count_ffi', assetId: 'flutter_filament_plugin') external int get_morph_target_name_count_ffi( - ffi.Pointer assetManager, + ffi.Pointer sceneManager, int asset, ffi.Pointer meshName, ); @@ -1270,7 +1328,7 @@ external int get_morph_target_name_count_ffi( ffi.Pointer, ffi.Pointer, ffi.Int)>( symbol: 'set_morph_target_weights_ffi', assetId: 'flutter_filament_plugin') external void set_morph_target_weights_ffi( - ffi.Pointer assetManager, + ffi.Pointer sceneManager, int asset, ffi.Pointer entityName, ffi.Pointer morphData, @@ -1289,7 +1347,7 @@ external void set_morph_target_weights_ffi( ffi.Float)>( symbol: 'set_morph_animation_ffi', assetId: 'flutter_filament_plugin') external bool set_morph_animation_ffi( - ffi.Pointer assetManager, + ffi.Pointer sceneManager, int asset, ffi.Pointer entityName, ffi.Pointer morphData, @@ -1308,7 +1366,7 @@ external bool set_morph_animation_ffi( ffi.Pointer)>( symbol: 'set_bone_transform_ffi', assetId: 'flutter_filament_plugin') external bool set_bone_transform_ffi( - ffi.Pointer assetManager, + ffi.Pointer sceneManager, int asset, ffi.Pointer entityName, ffi.Pointer transform, @@ -1328,7 +1386,7 @@ external bool set_bone_transform_ffi( ffi.Bool)>( symbol: 'add_bone_animation_ffi', assetId: 'flutter_filament_plugin') external void add_bone_animation_ffi( - ffi.Pointer assetManager, + ffi.Pointer sceneManager, int asset, ffi.Pointer frameData, int numFrames, @@ -1360,7 +1418,7 @@ external void pick_ffi( @ffi.Native, EntityId)>( symbol: 'reset_to_rest_pose_ffi', assetId: 'flutter_filament_plugin') external void reset_to_rest_pose_ffi( - ffi.Pointer assetManager, + ffi.Pointer sceneManager, int entityId, ); diff --git a/lib/hardware/hardware_keyboard_poll.dart b/lib/hardware/hardware_keyboard_poll.dart new file mode 100644 index 00000000..c5473627 --- /dev/null +++ b/lib/hardware/hardware_keyboard_poll.dart @@ -0,0 +1,42 @@ +import 'dart:async'; + +import 'package:flutter/services.dart'; +import 'package:flutter_filament/entities/entity_transform_controller.dart'; +import 'package:flutter_filament/filament_controller.dart'; + +class HardwareKeyboardPoll { + final EntityTransformController _controller; + late Timer _timer; + HardwareKeyboardPoll(this._controller) { + _timer = Timer.periodic(const Duration(milliseconds: 16), (_) { + print(RawKeyboard.instance.keysPressed); + if (RawKeyboard.instance.keysPressed.contains(LogicalKeyboardKey.keyW)) { + _controller.forwardPressed(); + } else { + _controller.forwardReleased(); + } + + if (RawKeyboard.instance.keysPressed.contains(LogicalKeyboardKey.keyS)) { + _controller.backPressed(); + } else { + _controller.backReleased(); + } + + if (RawKeyboard.instance.keysPressed.contains(LogicalKeyboardKey.keyA)) { + _controller.strafeLeftPressed(); + } else { + _controller.strafeLeftReleased(); + } + + if (RawKeyboard.instance.keysPressed.contains(LogicalKeyboardKey.keyD)) { + _controller.strafeRightPressed(); + } else { + _controller.strafeRightReleased(); + } + }); + } + + void dispose() { + _timer.cancel(); + } +} diff --git a/lib/lights/light_options.dart b/lib/lights/light_options.dart new file mode 100644 index 00000000..76f7b187 --- /dev/null +++ b/lib/lights/light_options.dart @@ -0,0 +1,29 @@ +import 'package:vector_math/vector_math_64.dart' as v; + +class LightOptions { + String? iblPath; + double iblIntensity; + int directionalType; + double directionalColor; + double directionalIntensity; + bool directionalCastShadows; + late v.Vector3 directionalPosition; + late v.Vector3 directionalDirection; + + LightOptions( + {required this.iblPath, + required this.iblIntensity, + required this.directionalType, + required this.directionalColor, + required this.directionalIntensity, + required this.directionalCastShadows, + v.Vector3? directionalDirection, + v.Vector3? directionalPosition}) { + this.directionalDirection = directionalDirection == null + ? v.Vector3(0, -1, 0) + : directionalDirection; + this.directionalPosition = directionalPosition == null + ? v.Vector3(0, 100, 0) + : directionalPosition; + } +} diff --git a/lib/widgets/camera_options_widget.dart b/lib/widgets/camera_options_widget.dart index a0fe7fa4..c2273fa4 100644 --- a/lib/widgets/camera_options_widget.dart +++ b/lib/widgets/camera_options_widget.dart @@ -1,16 +1,20 @@ -import 'dart:math'; - import 'package:flutter/material.dart'; import 'package:flutter/widgets.dart'; +import 'package:flutter_filament/camera/camera_orientation.dart'; import 'package:flutter_filament/filament_controller.dart'; -import 'package:vector_math/vector_math_64.dart' as v; +import 'dart:math'; +import 'package:vector_math/vector_math_64.dart' as v64; class CameraOptionsWidget extends StatefulWidget { final FilamentController controller; + final CameraOrientation cameraOrientation; final List<({FilamentEntity entity, String name})> cameras; CameraOptionsWidget( - {super.key, required this.controller, required this.cameras}) {} + {super.key, + required this.controller, + required this.cameras, + required this.cameraOrientation}) {} @override State createState() => _CameraOptionsWidgetState(); @@ -45,6 +49,7 @@ class _CameraOptionsWidgetState extends State { @override void didUpdateWidget(CameraOptionsWidget oldWidget) { + super.didUpdateWidget(oldWidget); if (oldWidget.cameras.length != widget.cameras.length) { setState(() {}); } @@ -55,9 +60,20 @@ class _CameraOptionsWidgetState extends State { double.parse(_apertureController.text), double.parse(_speedController.text), double.parse(_sensitivityController.text)); + await widget.controller.setCameraPosition( + widget.cameraOrientation.position.x, + widget.cameraOrientation.position.y, + widget.cameraOrientation.position.z); + var rotation = widget.cameraOrientation.compose(); + await widget.controller.setCameraRotation(rotation); + print( + "Camera : ${widget.cameraOrientation.position} ${widget.cameraOrientation.rotationX} ${widget.cameraOrientation.rotationY} ${widget.cameraOrientation.rotationZ}"); setState(() {}); } + double _bloom = 0.0; + + double _focalLength = 26.0; @override Widget build(BuildContext context) { return Theme( @@ -81,6 +97,118 @@ class _CameraOptionsWidgetState extends State { Expanded( child: TextField(controller: _sensitivityController)), ]), + Row(children: [ + Text("Bloom: ${_bloom.toStringAsFixed(2)}"), + Slider( + value: _bloom, + min: 0.0, + max: 1.0, + onChanged: (v) async { + setState(() { + _bloom = v; + }); + await widget.controller.setBloom(_bloom); + }) + ]), + Row(children: [ + Text("Focal length"), + Slider( + label: _focalLength.toString(), + value: _focalLength, + min: 1.0, + max: 100.0, + onChanged: (v) async { + setState(() { + _focalLength = v; + }); + await widget.controller + .setCameraFocalLength(_focalLength); + }) + ]), + Row(children: [ + Text("X"), + Slider( + label: widget.cameraOrientation.position.x.toString(), + value: widget.cameraOrientation.position.x, + min: -100.0, + max: 100.0, + onChanged: (v) async { + setState(() { + widget.cameraOrientation.position.x = v; + }); + _set(); + }) + ]), + Row(children: [ + Text("Y"), + Slider( + label: widget.cameraOrientation.position.y.toString(), + value: widget.cameraOrientation.position.y, + min: -100.0, + max: 100.0, + onChanged: (v) async { + setState(() { + widget.cameraOrientation.position.y = v; + }); + _set(); + }) + ]), + Row(children: [ + Text("Z"), + Slider( + label: widget.cameraOrientation.position.z.toString(), + value: widget.cameraOrientation.position.z, + min: -100.0, + max: 100.0, + onChanged: (v) async { + setState(() { + widget.cameraOrientation.position.z = v; + }); + _set(); + }) + ]), + Row(children: [ + Text("ROTX"), + Slider( + label: widget.cameraOrientation.rotationX.toString(), + value: widget.cameraOrientation.rotationX, + min: -pi, + max: pi, + onChanged: (value) async { + setState(() { + widget.cameraOrientation.rotationX = value; + }); + _set(); + }) + ]), + Row(children: [ + Text("ROTY"), + Slider( + label: widget.cameraOrientation.rotationY.toString(), + value: widget.cameraOrientation.rotationY, + min: -pi, + max: pi, + onChanged: (v) async { + setState(() { + widget.cameraOrientation.rotationY = v; + }); + _set(); + }), + ]), + Row(children: [ + Text("ROTZ"), + Slider( + label: widget.cameraOrientation.rotationZ.toString(), + value: widget.cameraOrientation.rotationZ, + min: -pi, + max: pi, + onChanged: (v) async { + setState(() { + widget.cameraOrientation.rotationZ = v; + }); + _set(); + }) + ]), Wrap( children: [ GestureDetector( diff --git a/lib/widgets/filament_gesture_detector.dart b/lib/widgets/filament_gesture_detector.dart index 10f54e52..978cd7dc 100644 --- a/lib/widgets/filament_gesture_detector.dart +++ b/lib/widgets/filament_gesture_detector.dart @@ -32,16 +32,22 @@ class FilamentGestureDetector extends StatelessWidget { final bool showControlOverlay; /// - /// If false, all gestures will be ignored. + /// If false, gestures will not manipulate the active camera. /// - final bool enabled; + final bool enableCamera; + + /// + /// If false, pointer down events will not trigger hit-testing (picking). + /// + final bool enablePicking; const FilamentGestureDetector( {Key? key, required this.controller, this.child, this.showControlOverlay = false, - this.enabled = true}) + this.enableCamera = true, + this.enablePicking = true}) : super(key: key); @override @@ -53,14 +59,16 @@ class FilamentGestureDetector extends StatelessWidget { controller: controller, child: child, showControlOverlay: showControlOverlay, - listenerEnabled: enabled, + enableCamera: enableCamera, + enablePicking: enablePicking, ); } else { return FilamentGestureDetectorMobile( controller: controller, child: child, showControlOverlay: showControlOverlay, - listenerEnabled: enabled, + enableCamera: enableCamera, + enablePicking: enablePicking, ); } } diff --git a/lib/widgets/filament_gesture_detector_desktop.dart b/lib/widgets/filament_gesture_detector_desktop.dart index 40157e32..6265bc0c 100644 --- a/lib/widgets/filament_gesture_detector_desktop.dart +++ b/lib/widgets/filament_gesture_detector_desktop.dart @@ -28,16 +28,22 @@ class FilamentGestureDetectorDesktop extends StatefulWidget { final bool showControlOverlay; /// - /// If false, all gestures will be ignored. + /// If false, gestures will not manipulate the active camera. /// - final bool listenerEnabled; + final bool enableCamera; + + /// + /// If false, pointer down events will not trigger hit-testing (picking). + /// + final bool enablePicking; const FilamentGestureDetectorDesktop( {Key? key, required this.controller, this.child, this.showControlOverlay = false, - this.listenerEnabled = true}) + this.enableCamera = true, + this.enablePicking = true}) : super(key: key); @override @@ -57,7 +63,8 @@ class _FilamentGestureDetectorDesktopState @override void didUpdateWidget(FilamentGestureDetectorDesktop oldWidget) { if (widget.showControlOverlay != oldWidget.showControlOverlay || - widget.listenerEnabled != oldWidget.listenerEnabled) { + widget.enableCamera != oldWidget.enableCamera || + widget.enablePicking != oldWidget.enablePicking) { setState(() {}); } @@ -86,12 +93,9 @@ class _FilamentGestureDetectorDesktopState @override Widget build(BuildContext context) { - if (!widget.listenerEnabled) { - return widget.child ?? Container(); - } return Listener( onPointerSignal: (PointerSignalEvent pointerSignal) async { - if (pointerSignal is PointerScrollEvent) { + if (pointerSignal is PointerScrollEvent && widget.enableCamera) { _zoom(pointerSignal); } else { throw Exception("TODO"); @@ -108,20 +112,20 @@ class _FilamentGestureDetectorDesktopState onPointerMove: (PointerMoveEvent d) async { // if this is the first move event, we need to call rotateStart/panStart to set the first coordinates if (!_pointerMoving) { - if (d.buttons == kTertiaryButton) { + if (d.buttons == kTertiaryButton && widget.enableCamera) { widget.controller .rotateStart(d.localPosition.dx, d.localPosition.dy); - } else { + } else if (widget.enableCamera) { widget.controller .panStart(d.localPosition.dx, d.localPosition.dy); } } // set the _pointerMoving flag so we don't call rotateStart/panStart on future move events _pointerMoving = true; - if (d.buttons == kTertiaryButton) { + if (d.buttons == kTertiaryButton && widget.enableCamera) { widget.controller .rotateUpdate(d.localPosition.dx, d.localPosition.dy); - } else { + } else if (widget.enableCamera) { widget.controller.panUpdate(d.localPosition.dx, d.localPosition.dy); } }, @@ -130,12 +134,12 @@ class _FilamentGestureDetectorDesktopState // 2) if _pointerMoving is false, this is interpreted as a pick // same applies to middle mouse button, but this is ignored as a pick onPointerUp: (PointerUpEvent d) async { - if (d.buttons == kTertiaryButton) { + if (d.buttons == kTertiaryButton && widget.enableCamera) { widget.controller.rotateEnd(); } else { - if (_pointerMoving) { + if (_pointerMoving && widget.enableCamera) { widget.controller.panEnd(); - } else { + } else if (widget.enablePicking) { widget.controller .pick(d.localPosition.dx.toInt(), d.localPosition.dy.toInt()); } diff --git a/lib/widgets/filament_gesture_detector_mobile.dart b/lib/widgets/filament_gesture_detector_mobile.dart index 117bdc9f..3645a082 100644 --- a/lib/widgets/filament_gesture_detector_mobile.dart +++ b/lib/widgets/filament_gesture_detector_mobile.dart @@ -28,9 +28,14 @@ class FilamentGestureDetectorMobile extends StatefulWidget { final bool showControlOverlay; /// - /// If false, all gestures will be ignored. + /// If false, gestures will not manipulate the active camera. /// - final bool listenerEnabled; + final bool enableCamera; + + /// + /// If false, pointer down events will not trigger hit-testing (picking). + /// + final bool enablePicking; final double zoomDelta; @@ -39,7 +44,8 @@ class FilamentGestureDetectorMobile extends StatefulWidget { required this.controller, this.child, this.showControlOverlay = false, - this.listenerEnabled = true, + this.enableCamera = true, + this.enablePicking = true, this.zoomDelta = 1}) : super(key: key); @@ -105,7 +111,8 @@ class _FilamentGestureDetectorMobileState @override void didUpdateWidget(FilamentGestureDetectorMobile oldWidget) { if (widget.showControlOverlay != oldWidget.showControlOverlay || - widget.listenerEnabled != oldWidget.listenerEnabled) { + widget.enableCamera != oldWidget.enableCamera || + widget.enablePicking != oldWidget.enablePicking) { setState(() {}); } @@ -122,10 +129,6 @@ class _FilamentGestureDetectorMobileState // - inner is a Listener for all other gestures (including scroll zoom on desktop) @override Widget build(BuildContext context) { - if (!widget.listenerEnabled) { - return widget.child ?? Container(); - } - return Stack(children: [ Positioned.fill( child: GestureDetector( @@ -136,10 +139,10 @@ class _FilamentGestureDetectorMobileState }); }, onScaleStart: (d) async { - if (d.pointerCount == 2) { + if (d.pointerCount == 2 && widget.enableCamera) { _scaling = true; await widget.controller.zoomBegin(); - } else if (!_scaling) { + } else if (!_scaling && widget.enableCamera) { if (_rotateOnPointerMove) { widget.controller.rotateStart( d.localFocalPoint.dx, d.localFocalPoint.dy); @@ -150,7 +153,7 @@ class _FilamentGestureDetectorMobileState } }, onScaleUpdate: (ScaleUpdateDetails d) async { - if (d.pointerCount == 2) { + if (d.pointerCount == 2 && widget.enableCamera) { if (d.horizontalScale != _lastScale) { widget.controller.zoomUpdate( d.localFocalPoint.dx, @@ -158,7 +161,7 @@ class _FilamentGestureDetectorMobileState d.horizontalScale > _lastScale ? 0.1 : -0.1); _lastScale = d.horizontalScale; } - } else if (!_scaling) { + } else if (!_scaling && widget.enableCamera) { if (_rotateOnPointerMove) { widget.controller .rotateUpdate(d.focalPoint.dx, d.focalPoint.dy); @@ -169,9 +172,9 @@ class _FilamentGestureDetectorMobileState } }, onScaleEnd: (d) async { - if (d.pointerCount == 2) { + if (d.pointerCount == 2 && widget.enableCamera) { widget.controller.zoomEnd(); - } else if (!_scaling) { + } else if (!_scaling && widget.enableCamera) { if (_rotateOnPointerMove) { widget.controller.rotateEnd(); } else { diff --git a/lib/widgets/light_slider.dart b/lib/widgets/light_slider.dart index 564c7565..56e25b24 100644 --- a/lib/widgets/light_slider.dart +++ b/lib/widgets/light_slider.dart @@ -3,72 +3,52 @@ import 'dart:math'; import 'package:flutter/material.dart'; import 'package:flutter/widgets.dart'; import 'package:flutter_filament/filament_controller.dart'; +import 'package:flutter_filament/lights/light_options.dart'; import 'package:vector_math/vector_math_64.dart' as v; class LightSliderWidget extends StatefulWidget { final FilamentController controller; - late final v.Vector3 initialPosition; - late final v.Vector3 initialDirection; - final int initialType; - final double initialColor; - final double initialIntensity; - final bool initialCastShadows; + final LightOptions options; final bool showControls; LightSliderWidget( {super.key, required this.controller, - this.initialType = 0, - this.initialColor = 6500, - this.initialIntensity = 100000, - this.initialCastShadows = true, this.showControls = false, - v.Vector3? initialDirection, - v.Vector3? initialPosition}) { - this.initialDirection = initialDirection ?? v.Vector3(0, 0.5, -1); - this.initialPosition = initialPosition ?? v.Vector3(0, 0.5, 1); - } - + required this.options}); @override - State createState() => _IblRotationSliderWidgetState(); + State createState() => _LightSliderWidgetState(); } -class _IblRotationSliderWidgetState extends State { - v.Vector3 lightPos = v.Vector3(1, 0.1, 1); - v.Vector3 lightDir = v.Vector3(-1, 0.1, 0); - bool castShadows = true; - int type = 0; - double color = 6500; - double intensity = 100000; +class _LightSliderWidgetState extends State { FilamentEntity? _light; @override void initState() { - type = widget.initialType; - castShadows = widget.initialCastShadows; - color = widget.initialColor; - lightPos = widget.initialPosition; - lightDir = widget.initialDirection; - intensity = widget.initialIntensity; _set(); super.initState(); } Future _set() async { - if (_light != null) await widget.controller.removeLight(_light!); + await widget.controller.clearLights(); + + if (widget.options.iblPath != null) { + _light = await widget.controller.loadIbl(widget.options.iblPath!, + intensity: widget.options.iblIntensity); + } _light = await widget.controller.addLight( - type, - color, - intensity, - lightPos.x, - lightPos.y, - lightPos.z, - lightDir.x, - lightDir.y, - lightDir.z, - castShadows); + widget.options.directionalType, + widget.options.directionalColor, + widget.options.directionalIntensity, + widget.options.directionalPosition.x, + widget.options.directionalPosition.y, + widget.options.directionalPosition.z, + widget.options.directionalDirection.x, + widget.options.directionalDirection.y, + widget.options.directionalDirection.z, + widget.options.directionalCastShadows); setState(() {}); } @@ -87,35 +67,39 @@ class _IblRotationSliderWidgetState extends State { showValueIndicator: ShowValueIndicator.always, valueIndicatorTextStyle: TextStyle(color: Colors.black)), child: Column(mainAxisSize: MainAxisSize.min, children: [ + Text("Directional"), Row(children: [ Expanded( child: Slider( - label: "POSX", - value: lightPos.x, + label: + "POSX ${widget.options.directionalPosition.x}", + value: widget.options.directionalPosition.x, min: -10.0, max: 10.0, onChanged: (value) { - lightPos.x = value; + widget.options.directionalPosition.x = value; _set(); })), Expanded( child: Slider( - label: "POSY", - value: lightPos.y, - min: -10.0, - max: 10.0, + label: + "POSY ${widget.options.directionalPosition.y}", + value: widget.options.directionalPosition.y, + min: -100.0, + max: 100.0, onChanged: (value) { - lightPos.y = value; + widget.options.directionalPosition.y = value; _set(); })), Expanded( child: Slider( - label: "POSZ", - value: lightPos.z, - min: -10.0, - max: 10.0, + label: + "POSZ ${widget.options.directionalPosition.z}", + value: widget.options.directionalPosition.z, + min: -100.0, + max: 100.0, onChanged: (value) { - lightPos.z = value; + widget.options.directionalPosition.z = value; _set(); })) ]), @@ -123,58 +107,58 @@ class _IblRotationSliderWidgetState extends State { Expanded( child: Slider( label: "DIRX", - value: lightDir.x, - min: -10.0, - max: 10.0, + value: widget.options.directionalDirection.x, + min: -1.0, + max: 1.0, onChanged: (value) { - lightDir.x = value; + widget.options.directionalDirection.x = value; _set(); })), Expanded( child: Slider( label: "DIRY", - value: lightDir.y, - min: -10.0, - max: 10.0, + value: widget.options.directionalDirection.y, + min: -1.0, + max: 1.0, onChanged: (value) { - lightDir.y = value; + widget.options.directionalDirection.y = value; _set(); })), Expanded( child: Slider( label: "DIRZ", - value: lightDir.z, - min: -10.0, - max: 10.0, + value: widget.options.directionalDirection.z, + min: -1.0, + max: 1.0, onChanged: (value) { - lightDir.z = value; + widget.options.directionalDirection.z = value; _set(); })) ]), Slider( label: "Color", - value: color, + value: widget.options.directionalColor, min: 0, max: 16000, onChanged: (value) { - color = value; + widget.options.directionalColor = value; _set(); }), Slider( - label: "Intensity", - value: intensity, + label: "Intensity ${widget.options.directionalIntensity}", + value: widget.options.directionalIntensity, min: 0, max: 1000000, onChanged: (value) { - intensity = value; + widget.options.directionalIntensity = value; _set(); }), DropdownButton( onChanged: (v) { - this.type = v; + this.widget.options.directionalType = v; _set(); }, - value: type, + value: this.widget.options.directionalType, items: List.generate( 5, (idx) => DropdownMenuItem( @@ -182,13 +166,27 @@ class _IblRotationSliderWidgetState extends State { child: Text("$idx"), ))), Row(children: [ - Text("Shadows: $castShadows"), + Text( + "Shadows: ${this.widget.options.directionalCastShadows}"), Checkbox( - value: castShadows, + value: widget.options.directionalCastShadows, onChanged: (v) { - this.castShadows = v!; + this.widget.options.directionalCastShadows = v!; _set(); }) + ]), + Text("Indirect"), + Row(children: [ + Expanded( + child: Slider( + label: "Intensity ${widget.options.iblIntensity}", + value: widget.options.iblIntensity, + min: 0.0, + max: 200000, + onChanged: (value) { + widget.options.iblIntensity = value; + _set(); + })), ]) ])))); } diff --git a/linux/flutter_filament_plugin.cc b/linux/flutter_filament_plugin.cc index 33f3ed9a..69055014 100644 --- a/linux/flutter_filament_plugin.cc +++ b/linux/flutter_filament_plugin.cc @@ -43,7 +43,7 @@ struct _FlutterFilamentPlugin { double width = 0; double height = 0; bool rendering = false; - polyvox::FilamentViewer* viewer; + flutter_filament::FilamentViewer* viewer; }; G_DEFINE_TYPE(FlutterFilamentPlugin, flutter_filament_plugin, g_object_get_type()) @@ -71,7 +71,7 @@ static FlMethodResponse* _create_filament_viewer(FlutterFilamentPlugin* self, Fl self->height = height; auto context = glXGetCurrentContext(); - self->viewer = (polyvox::FilamentViewer*)create_filament_viewer( + self->viewer = (flutter_filament::FilamentViewer*)create_filament_viewer( (void*)context, callback ); @@ -698,7 +698,7 @@ static FlMethodResponse* _get_morph_target_names(FlutterFilamentPlugin* self, Fl static FlMethodResponse* _set_tone_mapping(FlutterFilamentPlugin* self, FlMethodCall* method_call) { FlValue* args = fl_method_call_get_args(method_call); - polyvox::ToneMapping toneMapping = static_cast(fl_value_get_int(args)); + flutter_filament::ToneMapping toneMapping = static_cast(fl_value_get_int(args)); set_tone_mapping(self->viewer, toneMapping); return FL_METHOD_RESPONSE(fl_method_success_response_new(fl_value_new_bool(true))); } diff --git a/linux/include/flutter_filament/resource_loader.hpp b/linux/include/flutter_filament/resource_loader.hpp index 9bac451c..2ba1af5e 100644 --- a/linux/include/flutter_filament/resource_loader.hpp +++ b/linux/include/flutter_filament/resource_loader.hpp @@ -12,14 +12,13 @@ #include "ResourceBuffer.hpp" -using namespace std; -static map _file_assets; +static std::map _file_assets; static uint32_t _i = 0; ResourceBuffer loadResource(const char* name) { - std::cout << "LOADING RESOURCE" << std::endl; + std::cout << "LOADING RESOURCE" << std::endl; char cwd[PATH_MAX]; if (getcwd(cwd, sizeof(cwd)) != NULL) { diff --git a/macos/flutter_filament.podspec b/macos/flutter_filament.podspec index 0134458b..7853340f 100644 --- a/macos/flutter_filament.podspec +++ b/macos/flutter_filament.podspec @@ -14,7 +14,7 @@ A new Flutter plugin project. s.author = { 'Your Company' => 'email@example.com' } s.source = { :path => '.' } - s.source_files = 'Classes/*', 'src/*', "src/camutils/*", 'include/filament/*', 'include/*', 'include/material/*.c' + s.source_files = 'Classes/*', 'src/*', "src/camutils/*", 'include/filament/*', 'include/*', 'include/components/*', 'include/material/*.c' s.public_header_files = 'include/SwiftFlutterFilamentPlugin-Bridging-Header.h', 'include/FlutterFilamentApi.h', 'include/FlutterFilamentFFIApi.h', 'include/ResourceBuffer.hpp' #, 'include/filament/*' s.dependency 'FlutterMacOS' From 40ec8accf752a0d8c1d2641edee15bc774399091 Mon Sep 17 00:00:00 2001 From: Nick Fisher Date: Mon, 4 Mar 2024 08:32:22 +0800 Subject: [PATCH 07/52] expose addAnimationComponent and dont add by default on loadGlb --- ios/include/SceneManager.hpp | 2 +- ios/src/FlutterFilamentApi.cpp | 4 ++++ ios/src/SceneManager.cpp | 38 ++++++++++++++++---------------- lib/filament_controller.dart | 12 +++++++--- lib/filament_controller_ffi.dart | 8 ++++++- lib/generated_bindings.dart | 11 +++++++-- 6 files changed, 49 insertions(+), 26 deletions(-) diff --git a/ios/include/SceneManager.hpp b/ios/include/SceneManager.hpp index b89e23b2..8281f861 100644 --- a/ios/include/SceneManager.hpp +++ b/ios/include/SceneManager.hpp @@ -130,7 +130,7 @@ namespace flutter_filament void addCollisionComponent(EntityId entity, void (*onCollisionCallback)(const EntityId entityId1, const EntityId entityId2), bool affectsCollidingTransform); void removeCollisionComponent(EntityId entityId); void setParent(EntityId child, EntityId parent); - void addAnimatableComponent(EntityId entity); + void addAnimationComponent(EntityId entity); /// @brief returns the number of instances of the FilamentAsset represented by the given entity. /// @param entityId diff --git a/ios/src/FlutterFilamentApi.cpp b/ios/src/FlutterFilamentApi.cpp index 175f7448..5adc4249 100644 --- a/ios/src/FlutterFilamentApi.cpp +++ b/ios/src/FlutterFilamentApi.cpp @@ -589,6 +589,10 @@ extern "C" ((SceneManager*)sceneManager)->addCollisionComponent(entityId, onCollisionCallback, affectsCollidingTransform); } + FLUTTER_PLUGIN_EXPORT void add_animation_component(void *const sceneManager, EntityId entityId) { + ((SceneManager*)sceneManager)->addAnimationComponent(entityId); + } + FLUTTER_PLUGIN_EXPORT EntityId create_geometry(void *const viewer, float* vertices, int numVertices, uint16_t* indices, int numIndices, const char* materialPath) { return ((FilamentViewer*)viewer)->createGeometry(vertices, (size_t)numVertices, indices, numIndices, materialPath); } diff --git a/ios/src/SceneManager.cpp b/ios/src/SceneManager.cpp index bb358ca9..e96f2085 100644 --- a/ios/src/SceneManager.cpp +++ b/ios/src/SceneManager.cpp @@ -250,7 +250,6 @@ namespace flutter_filament auto instanceEntity = inst->getRoot(); auto instanceEntityId = Entity::smuggle(instanceEntity); _instances.emplace(instanceEntityId, inst); - addAnimatableComponent(instanceEntityId); } asset->releaseSourceData(); @@ -260,7 +259,7 @@ namespace flutter_filament return eid; } - void SceneManager::addAnimatableComponent(EntityId entityId) { + void SceneManager::addAnimationComponent(EntityId entityId) { auto* instance = getInstanceByEntityId(entityId); if (!instance) @@ -387,7 +386,7 @@ namespace flutter_filament const auto &pos = _instances.find(entityId); if (pos == _instances.end()) { - Log("Failed to find FilamentInstance for entity %d", entityId); + // Log("Failed to find FilamentInstance for entity %d", entityId); return nullptr; } return pos->second; @@ -398,7 +397,7 @@ namespace flutter_filament const auto &pos = _assets.find(entityId); if (pos == _assets.end()) { - Log("Failed to find FilamentAsset for entity %d", entityId); + // Log("Failed to find FilamentAsset for entity %d", entityId); return nullptr; } return pos->second; @@ -561,9 +560,9 @@ namespace flutter_filament // { // _engine->destroy(sceneAsset.texture); // } - - utils::EntityManager &em = utils::EntityManager::get(); - em.destroy(entity); +// +// utils::EntityManager &em = utils::EntityManager::get(); +// em.destroy(entity); } void SceneManager::setMorphTargetWeights(EntityId entityId, const char *const entityName, const float *const weights, const int count) @@ -604,13 +603,16 @@ namespace flutter_filament utils::Entity SceneManager::findChildEntityByName(EntityId entityId, const char *entityName) { std::lock_guard lock(_mutex); - const auto &pos = _instances.find(entityId); - if (pos == _instances.end()) + auto* instance = getInstanceByEntityId(entityId); + if (!instance) { - Log("Couldn't find asset under specified entity id."); - return utils::Entity(); + auto* asset = getAssetByEntityId(entityId); + if(!asset) { + return utils::Entity(); + } + instance = asset->getInstance(); } - const auto* instance = pos->second; + const auto entity = findEntityByName(instance, entityName); @@ -1199,7 +1201,6 @@ namespace flutter_filament return; } else { instance = asset->getInstance(); - } } auto collisionInstance = _collisionComponentManager->addComponent(instance->getRoot()); @@ -1212,20 +1213,20 @@ namespace flutter_filament const auto *instance = getInstanceByEntityId(entityId); if(!instance) { auto asset = getAssetByEntityId(entityId); - if(!asset) { + if(asset) { instance = asset->getInstance(); - } + } else { + return; + } } - const auto entity = Entity::import(entityId); const auto& tm = _engine->getTransformManager(); auto transformInstance = tm.getInstance(instance->getRoot()); auto worldTransform = tm.getWorldTransform(transformInstance); - // Log("World transform for %d is %f %f %f", entityId, worldTransform[3][0], worldTransform[3][1], worldTransform[3][2]); auto aabb = instance->getBoundingBox(); aabb = aabb.transform(worldTransform); - _collisionComponentManager->collides(entity, aabb); + _collisionComponentManager->collides(instance->getRoot(), aabb); } void SceneManager::updateAnimations() { @@ -1248,7 +1249,6 @@ namespace flutter_filament Aabb boundingBox; if (pos == _instances.end()) { - Log("WARNING: SceneAsset not found for entity."); isCollidable = false; entity = Entity::import(entityId); } else { diff --git a/lib/filament_controller.dart b/lib/filament_controller.dart index 445cf0c1..954a7603 100644 --- a/lib/filament_controller.dart +++ b/lib/filament_controller.dart @@ -610,12 +610,18 @@ abstract class FilamentController { /// Future setRecordingOutputDirectory(String outputDirectory); - // Stream get keyboardFocusRequested; - // void requestKeyboardFocus(); - + /// + /// Attach the keyboard/mouse to [entity]. + /// Future control(FilamentEntity entity, {double? translationSpeed, String? forwardAnimation}); + /// + /// An [entity] will only be animatable after an animation component is attached. + /// Any calls to [playAnimation]/[setBoneAnimation]/[setMorphAnimation] will have no visual effect until [addAnimationComponent] has been called on the instance. + /// + Future addAnimationComponent(FilamentEntity entity); + /// /// Makes [collidableEntity] collidable with /// (a) any entity whose transform is being controlled (via [control]) or diff --git a/lib/filament_controller_ffi.dart b/lib/filament_controller_ffi.dart index 44e8799a..5a69a3c8 100644 --- a/lib/filament_controller_ffi.dart +++ b/lib/filament_controller_ffi.dart @@ -1269,7 +1269,8 @@ class FilamentControllerFFI extends FilamentController { final outPtr = allocator(1); outPtr.value = 0; - pick_ffi(_viewer!, x, textureDetails.value!.height - y, outPtr); + pick_ffi(_viewer!, (x * _pixelRatio).toInt(), + textureDetails.value!.height - (y * _pixelRatio).toInt(), outPtr); int wait = 0; while (outPtr.value == 0) { await Future.delayed(const Duration(milliseconds: 32)); @@ -1497,6 +1498,11 @@ class FilamentControllerFFI extends FilamentController { } } + @override + Future addAnimationComponent(FilamentEntity entity) async { + add_animation_component(_sceneManager!, entity); + } + @override Future createGeometry( List vertices, List indices, String? materialPath) async { diff --git a/lib/generated_bindings.dart b/lib/generated_bindings.dart index d1c86dda..617a9286 100644 --- a/lib/generated_bindings.dart +++ b/lib/generated_bindings.dart @@ -225,10 +225,10 @@ external int get_instance_count( ); @ffi.Native< - ffi.Int Function( + ffi.Void Function( ffi.Pointer, EntityId, ffi.Pointer)>( symbol: 'get_instances', assetId: 'flutter_filament_plugin') -external int get_instances( +external void get_instances( ffi.Pointer sceneManager, int entityId, ffi.Pointer out, @@ -931,6 +931,13 @@ external void add_collision_component( bool affectsCollidingTransform, ); +@ffi.Native, EntityId)>( + symbol: 'add_animation_component', assetId: 'flutter_filament_plugin') +external void add_animation_component( + ffi.Pointer sceneManager, + int entityId, +); + @ffi.Native< EntityId Function(ffi.Pointer, ffi.Pointer, ffi.Int, ffi.Pointer, ffi.Int, ffi.Pointer)>( From 06d8df03949242f19b11b631c575862830486221 Mon Sep 17 00:00:00 2001 From: Nick Fisher Date: Mon, 4 Mar 2024 08:32:38 +0800 Subject: [PATCH 08/52] expose addAnimationComponent and dont add by default on loadGlb --- ios/include/FlutterFilamentApi.h | 1 + 1 file changed, 1 insertion(+) diff --git a/ios/include/FlutterFilamentApi.h b/ios/include/FlutterFilamentApi.h index a1474b43..c2479265 100644 --- a/ios/include/FlutterFilamentApi.h +++ b/ios/include/FlutterFilamentApi.h @@ -194,6 +194,7 @@ extern "C" FLUTTER_PLUGIN_EXPORT void ios_dummy(); FLUTTER_PLUGIN_EXPORT void flutter_filament_free(void *ptr); FLUTTER_PLUGIN_EXPORT void add_collision_component(void *const sceneManager, EntityId entityId, void (*callback)(const EntityId entityId1, const EntityId entityId2), bool affectsCollidingTransform); + FLUTTER_PLUGIN_EXPORT void add_animation_component(void *const sceneManager, EntityId entityId); FLUTTER_PLUGIN_EXPORT EntityId create_geometry(void *const viewer, float* vertices, int numVertices, uint16_t* indices, int numIndices, const char* materialPath); FLUTTER_PLUGIN_EXPORT void set_parent(void *const sceneManager, EntityId child, EntityId parent); From b8314dcdd8ca3f8e73804e1b463a2c9760159180 Mon Sep 17 00:00:00 2001 From: Nick Fisher Date: Mon, 4 Mar 2024 08:32:52 +0800 Subject: [PATCH 09/52] logging --- ios/src/FilamentViewer.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ios/src/FilamentViewer.cpp b/ios/src/FilamentViewer.cpp index 3ffcc394..d8daeb8b 100644 --- a/ios/src/FilamentViewer.cpp +++ b/ios/src/FilamentViewer.cpp @@ -1044,8 +1044,7 @@ namespace flutter_filament if (secsSinceLastFpsCheck >= 1) { auto fps = _frameCount / secsSinceLastFpsCheck; - Log("%ffps (_frameCount %d, secs since last check %f)", fps, _frameCount, secsSinceLastFpsCheck); - // Log("1 sec average for asset animation update %f", _elapsed / _frameCount); + Log("%ffps", fps); _frameCount = 0; _skippedFrames = 0; _fpsCounterStartTime = now; From 1680573a1edcd889750d96affd2f9d890f21c7c4 Mon Sep 17 00:00:00 2001 From: Nick Fisher Date: Mon, 4 Mar 2024 08:35:00 +0800 Subject: [PATCH 10/52] hardcode 5ms window for rendering --- ios/src/FlutterFilamentFFIApi.cpp | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/ios/src/FlutterFilamentFFIApi.cpp b/ios/src/FlutterFilamentFFIApi.cpp index 1ee2dcc3..020971d1 100644 --- a/ios/src/FlutterFilamentFFIApi.cpp +++ b/ios/src/FlutterFilamentFFIApi.cpp @@ -45,7 +45,7 @@ public: float elapsed = float(std::chrono::duration_cast(now - last).count()); - while(elapsed < 3 * _frameIntervalInMilliseconds / 4) { + while(elapsed < _frameIntervalInMilliseconds - 5) { std::function task; std::unique_lock lock(_access); @@ -70,6 +70,25 @@ public: // Log("Took %f milliseconds for render", float(std::chrono::duration_cast(frameEnd - frameStart).count())); } + elapsed = float(std::chrono::duration_cast(now - last).count()); + + while(elapsed < _frameIntervalInMilliseconds) { + std::function task; + std::unique_lock lock(_access); + if (_tasks.empty()) { + _cond.wait_for(lock, std::chrono::duration(1)); + now = std::chrono::high_resolution_clock::now(); + elapsed = float(std::chrono::duration_cast(now - last).count()); + continue; + } + task = std::move(_tasks.front()); + _tasks.pop_front(); + task(); + + now = std::chrono::high_resolution_clock::now(); + elapsed = float(std::chrono::duration_cast(now - last).count()); + } + last = now; } }); @@ -139,6 +158,7 @@ public: void setFrameIntervalInMilliseconds(float frameIntervalInMilliseconds) { _frameIntervalInMilliseconds = frameIntervalInMilliseconds; + Log("Set _frameIntervalInMilliseconds to %f", _frameIntervalInMilliseconds); } template From 61299e4587769d9fc79f44519fbec59ef01da7d7 Mon Sep 17 00:00:00 2001 From: Nick Fisher Date: Mon, 4 Mar 2024 15:40:44 +0800 Subject: [PATCH 11/52] add stopAnimationByName --- lib/filament_controller.dart | 2 ++ lib/filament_controller_ffi.dart | 6 ++++++ 2 files changed, 8 insertions(+) diff --git a/lib/filament_controller.dart b/lib/filament_controller.dart index 954a7603..59bf2f5f 100644 --- a/lib/filament_controller.dart +++ b/lib/filament_controller.dart @@ -365,7 +365,9 @@ abstract class FilamentController { Future setAnimationFrame( FilamentEntity entity, int index, int animationFrame); + Future stopAnimation(FilamentEntity entity, int animationIndex); + Future stopAnimationByName(FilamentEntity entity, String name); /// /// Sets the current scene camera to the glTF camera under [name] in [entity]. diff --git a/lib/filament_controller_ffi.dart b/lib/filament_controller_ffi.dart index 5a69a3c8..925611ac 100644 --- a/lib/filament_controller_ffi.dart +++ b/lib/filament_controller_ffi.dart @@ -950,6 +950,12 @@ class FilamentControllerFFI extends FilamentController { _sceneManager!, entity, index, loop, reverse, replaceActive, crossfade); } + @override + Future stopAnimationByName(FilamentEntity entity, String name) async { + var animations = await getAnimationNames(entity); + await stopAnimation(entity, animations.indexOf(name)); + } + @override Future playAnimationByName(FilamentEntity entity, String name, {bool loop = false, From d8ae4c0949026532cecfa3a3cab61f92c1d07c21 Mon Sep 17 00:00:00 2001 From: Nick Fisher Date: Mon, 4 Mar 2024 15:40:58 +0800 Subject: [PATCH 12/52] logging --- ios/src/SceneManager.cpp | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/ios/src/SceneManager.cpp b/ios/src/SceneManager.cpp index e96f2085..07eb2886 100644 --- a/ios/src/SceneManager.cpp +++ b/ios/src/SceneManager.cpp @@ -315,14 +315,14 @@ namespace flutter_filament if(meshName) { entity = findEntityByName(instance, meshName); - Log("Hiding child entity under name %s ", meshName); + // Log("Hiding child entity under name %s ", meshName); if (entity.isNull()) { Log("Failed to hide entity; specified mesh name does not exist under the target entity, or the target entity itself is no longer valid."); return false; } _scene->remove(entity); } else { - Log("Hiding all child entities"); + // Log("Hiding all child entities"); auto* entities = instance->getEntities(); for(int i =0; i < instance->getEntityCount(); i++) { auto entity = entities[i]; @@ -357,7 +357,7 @@ namespace flutter_filament } _scene->addEntity(entity); } else { - Log("Revealing all child entities"); + // Log("Revealing all child entities"); auto* entities = instance->getEntities(); for(int i =0; i < instance->getEntityCount(); i++) { auto entity = entities[i]; @@ -1075,17 +1075,16 @@ namespace flutter_filament float SceneManager::getAnimationDuration(EntityId entity, int animationIndex) { - const auto &pos = _instances.find(entity); + auto* instance = getInstanceByEntityId(entity); - unique_ptr> names = std::make_unique>(); - - if (pos == _instances.end()) + if (!instance) { - Log("ERROR: asset not found for entity id."); - return -1.0f; + auto* asset = getAssetByEntityId(entity); + if(!asset) { + return -1.0f; + } + instance = asset->getInstance(); } - - auto *instance = pos->second; return instance->getAnimator()->getAnimationDuration(animationIndex); } @@ -1182,7 +1181,7 @@ namespace flutter_filament } void SceneManager::setParent(EntityId childEntityId, EntityId parentEntityId) { - Log("Parenting child %d to %d", childEntityId, parentEntityId); + // Log("Parenting child %d to %d", childEntityId, parentEntityId); auto& tm = _engine->getTransformManager(); const auto child = Entity::import(childEntityId); const auto parent = Entity::import(parentEntityId); From 403ea40d09bc10e2e1c1dd964dadbf8f72c33dc6 Mon Sep 17 00:00:00 2001 From: Nick Fisher Date: Mon, 4 Mar 2024 15:41:11 +0800 Subject: [PATCH 13/52] resize fix --- lib/widgets/filament_widget.dart | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/widgets/filament_widget.dart b/lib/widgets/filament_widget.dart index 20fa071a..b6343df3 100644 --- a/lib/widgets/filament_widget.dart +++ b/lib/widgets/filament_widget.dart @@ -182,6 +182,7 @@ class _SizedFilamentWidgetState extends State<_SizedFilamentWidget> { } finally { _resizing = false; completer.complete(); + _resizeTimer?.cancel(); } }); return completer.future; @@ -190,7 +191,9 @@ class _SizedFilamentWidgetState extends State<_SizedFilamentWidget> { @override void didUpdateWidget(_SizedFilamentWidget oldWidget) { super.didUpdateWidget(oldWidget); - _resize(); + if (widget.height != oldWidget.height || widget.width != oldWidget.width) { + _resize(); + } } @override From f80d92bf5b7f1d2bf9de80d4eb7920b451d16bf5 Mon Sep 17 00:00:00 2001 From: Nick Fisher Date: Mon, 4 Mar 2024 15:41:35 +0800 Subject: [PATCH 14/52] move pick to pointerdown for better latency --- .../filament_gesture_detector_desktop.dart | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/lib/widgets/filament_gesture_detector_desktop.dart b/lib/widgets/filament_gesture_detector_desktop.dart index 6265bc0c..ceef215f 100644 --- a/lib/widgets/filament_gesture_detector_desktop.dart +++ b/lib/widgets/filament_gesture_detector_desktop.dart @@ -95,8 +95,10 @@ class _FilamentGestureDetectorDesktopState Widget build(BuildContext context) { return Listener( onPointerSignal: (PointerSignalEvent pointerSignal) async { - if (pointerSignal is PointerScrollEvent && widget.enableCamera) { - _zoom(pointerSignal); + if (pointerSignal is PointerScrollEvent) { + if (widget.enableCamera) { + _zoom(pointerSignal); + } } else { throw Exception("TODO"); } @@ -107,7 +109,13 @@ class _FilamentGestureDetectorDesktopState // ignore all pointer down events // so we can wait to see if the pointer will be held/moved (interpreted as rotate/pan), // or if this is a single mousedown event (interpreted as viewport pick) - onPointerDown: (d) async {}, + onPointerDown: (d) async { + if (d.buttons != kTertiaryButton && widget.enablePicking) { + widget.controller + .pick(d.localPosition.dx.toInt(), d.localPosition.dy.toInt()); + } + _pointerMoving = false; + }, // holding/moving the left mouse button is interpreted as a pan, middle mouse button as a rotate onPointerMove: (PointerMoveEvent d) async { // if this is the first move event, we need to call rotateStart/panStart to set the first coordinates @@ -139,9 +147,6 @@ class _FilamentGestureDetectorDesktopState } else { if (_pointerMoving && widget.enableCamera) { widget.controller.panEnd(); - } else if (widget.enablePicking) { - widget.controller - .pick(d.localPosition.dx.toInt(), d.localPosition.dy.toInt()); } } _pointerMoving = false; From e2a56f41369a2bdfe3e8511be3c459e1a8ddfbd3 Mon Sep 17 00:00:00 2001 From: Nick Fisher Date: Mon, 4 Mar 2024 21:43:35 +0800 Subject: [PATCH 15/52] fix bad copy-paste bug --- ios/src/SceneManager.cpp | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/ios/src/SceneManager.cpp b/ios/src/SceneManager.cpp index 07eb2886..675ce9e1 100644 --- a/ios/src/SceneManager.cpp +++ b/ios/src/SceneManager.cpp @@ -1424,9 +1424,11 @@ namespace flutter_filament const auto *instance = getInstanceByEntityId(entityId); if(!instance) { auto asset = getAssetByEntityId(entityId); - if(!asset) { + if(asset) { instance = asset->getInstance(); - } + } else { + return nullptr; + } } return instance->getAsset()->getCameraEntities(); } @@ -1436,9 +1438,11 @@ namespace flutter_filament const auto *instance = getInstanceByEntityId(entityId); if(!instance) { auto asset = getAssetByEntityId(entityId); - if(!asset) { + if(asset) { instance = asset->getInstance(); - } + } else { + return -1; + } } return instance->getAsset()->getCameraEntityCount(); } @@ -1448,9 +1452,11 @@ namespace flutter_filament const auto *instance = getInstanceByEntityId(entityId); if(!instance) { auto asset = getAssetByEntityId(entityId); - if(!asset) { + if(asset) { instance = asset->getInstance(); - } + } else { + return nullptr; + } } return instance->getAsset()->getLightEntities(); } @@ -1460,9 +1466,11 @@ namespace flutter_filament const auto *instance = getInstanceByEntityId(entityId); if(!instance) { auto asset = getAssetByEntityId(entityId); - if(!asset) { + if(asset) { instance = asset->getInstance(); - } + } else { + return -1; + } } return instance->getAsset()->getLightEntityCount(); } @@ -1483,7 +1491,7 @@ namespace flutter_filament const auto *instance = getInstanceByEntityId(entityId); if(!instance) { auto asset = getAssetByEntityId(entityId); - if(!asset) { + if(asset) { instance = asset->getInstance(); } else { return 0; @@ -1507,7 +1515,7 @@ namespace flutter_filament const auto *instance = getInstanceByEntityId(entityId); if(!instance) { auto asset = getAssetByEntityId(entityId); - if(!asset) { + if(asset) { instance = asset->getInstance(); } else { return nullptr; From 5287c108f93de0ac9aa14dd64cf6942e3b5cb942 Mon Sep 17 00:00:00 2001 From: Nick Fisher Date: Mon, 4 Mar 2024 21:43:56 +0800 Subject: [PATCH 16/52] rename getMeshNames to getChildEntities --- lib/filament_controller.dart | 5 +++-- lib/filament_controller_ffi.dart | 9 +++++---- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/lib/filament_controller.dart b/lib/filament_controller.dart index 59bf2f5f..105deee4 100644 --- a/lib/filament_controller.dart +++ b/lib/filament_controller.dart @@ -597,9 +597,10 @@ abstract class FilamentController { FilamentEntity parent, String childName); /// - /// Lists all child meshes under the given entity. + /// List all child entities under the given entity. /// - Future> getMeshNames(FilamentEntity entity, {bool async = true}); + Future> getChildEntities(FilamentEntity entity, + {bool renderableOnly = true}); /// /// If [recording] is set to true, each frame the framebuffer/texture will be written to /tmp/output_*.png. diff --git a/lib/filament_controller_ffi.dart b/lib/filament_controller_ffi.dart index 925611ac..0beb5c3e 100644 --- a/lib/filament_controller_ffi.dart +++ b/lib/filament_controller_ffi.dart @@ -1436,12 +1436,13 @@ class FilamentControllerFFI extends FilamentController { return childEntity; } - Future> getMeshNames(FilamentEntity entity, - {bool async = false}) async { - var count = get_entity_count(_sceneManager!, entity, true); + @override + Future> getChildEntities(FilamentEntity entity, + {bool renderableOnly = false}) async { + var count = get_entity_count(_sceneManager!, entity, renderableOnly); var names = []; for (int i = 0; i < count; i++) { - var name = get_entity_name_at(_sceneManager!, entity, i, true); + var name = get_entity_name_at(_sceneManager!, entity, i, renderableOnly); if (name == nullptr) { throw Exception("Failed to find mesh at index $i"); } From 0c9e051bafb6fb66ca765fd3664c6ed6428aeff1 Mon Sep 17 00:00:00 2001 From: Nick Fisher Date: Tue, 5 Mar 2024 10:21:26 +0800 Subject: [PATCH 17/52] rename plugin files --- ios/Classes/{PolyvoxFilamentPlugin.h => FlutterFilamentPlugin.h} | 0 ios/Classes/{PolyvoxFilamentPlugin.m => FlutterFilamentPlugin.m} | 0 ...lyvoxFilamentPlugin.swift => SwiftFlutterFilamentPlugin.swift} | 0 ...lyvoxFilamentPlugin.swift => SwiftFlutterFilamentPlugin.swift} | 0 4 files changed, 0 insertions(+), 0 deletions(-) rename ios/Classes/{PolyvoxFilamentPlugin.h => FlutterFilamentPlugin.h} (100%) rename ios/Classes/{PolyvoxFilamentPlugin.m => FlutterFilamentPlugin.m} (100%) rename ios/Classes/{SwiftPolyvoxFilamentPlugin.swift => SwiftFlutterFilamentPlugin.swift} (100%) rename macos/Classes/{SwiftPolyvoxFilamentPlugin.swift => SwiftFlutterFilamentPlugin.swift} (100%) diff --git a/ios/Classes/PolyvoxFilamentPlugin.h b/ios/Classes/FlutterFilamentPlugin.h similarity index 100% rename from ios/Classes/PolyvoxFilamentPlugin.h rename to ios/Classes/FlutterFilamentPlugin.h diff --git a/ios/Classes/PolyvoxFilamentPlugin.m b/ios/Classes/FlutterFilamentPlugin.m similarity index 100% rename from ios/Classes/PolyvoxFilamentPlugin.m rename to ios/Classes/FlutterFilamentPlugin.m diff --git a/ios/Classes/SwiftPolyvoxFilamentPlugin.swift b/ios/Classes/SwiftFlutterFilamentPlugin.swift similarity index 100% rename from ios/Classes/SwiftPolyvoxFilamentPlugin.swift rename to ios/Classes/SwiftFlutterFilamentPlugin.swift diff --git a/macos/Classes/SwiftPolyvoxFilamentPlugin.swift b/macos/Classes/SwiftFlutterFilamentPlugin.swift similarity index 100% rename from macos/Classes/SwiftPolyvoxFilamentPlugin.swift rename to macos/Classes/SwiftFlutterFilamentPlugin.swift From 246494404c7ba363a98e6dae3e876e8bb8369a75 Mon Sep 17 00:00:00 2001 From: Nick Fisher Date: Tue, 5 Mar 2024 10:21:51 +0800 Subject: [PATCH 18/52] explicitly include iomanip header for iOS build using dart native --- ios/src/FilamentViewer.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/ios/src/FilamentViewer.cpp b/ios/src/FilamentViewer.cpp index d8daeb8b..e1a7d358 100644 --- a/ios/src/FilamentViewer.cpp +++ b/ios/src/FilamentViewer.cpp @@ -82,6 +82,7 @@ #include #include #include +#include #include "Log.hpp" From 32fe5d9083ef34f85d9afb669357c9e7790a3ee9 Mon Sep 17 00:00:00 2001 From: Nick Fisher Date: Tue, 5 Mar 2024 10:22:12 +0800 Subject: [PATCH 19/52] remove unused test linux file --- linux/test/polyvox_filament_plugin_test.cc | 31 ---------------------- 1 file changed, 31 deletions(-) delete mode 100644 linux/test/polyvox_filament_plugin_test.cc diff --git a/linux/test/polyvox_filament_plugin_test.cc b/linux/test/polyvox_filament_plugin_test.cc deleted file mode 100644 index 40c63fa6..00000000 --- a/linux/test/polyvox_filament_plugin_test.cc +++ /dev/null @@ -1,31 +0,0 @@ -#include -#include -#include - -#include "include/flutter_filament/flutter_filament_plugin.h" -#include "flutter_filament_plugin_private.h" - -// This demonstrates a simple unit test of the C portion of this plugin's -// implementation. -// -// Once you have built the plugin's example app, you can run these tests -// from the command line. For instance, for a plugin called my_plugin -// built for x64 debug, run: -// $ build/linux/x64/debug/plugins/my_plugin/my_plugin_test - -namespace flutter_filament { -namespace test { - -TEST(FlutterFilamentPlugin, GetPlatformVersion) { - g_autoptr(FlMethodResponse) response = get_platform_version(); - ASSERT_NE(response, nullptr); - ASSERT_TRUE(FL_IS_METHOD_SUCCESS_RESPONSE(response)); - FlValue* result = fl_method_success_response_get_result( - FL_METHOD_SUCCESS_RESPONSE(response)); - ASSERT_EQ(fl_value_get_type(result), FL_VALUE_TYPE_STRING); - // The full string varies, so just validate that it has the right format. - EXPECT_THAT(fl_value_get_string(result), testing::StartsWith("Linux ")); -} - -} // namespace test -} // namespace flutter_filament From 39fa9387e6b4711d8584bf1acdc73f2559f2807f Mon Sep 17 00:00:00 2001 From: Nick Fisher Date: Tue, 5 Mar 2024 10:30:42 +0800 Subject: [PATCH 20/52] expose removeCollisionComponent --- ios/include/FlutterFilamentApi.h | 1 + ios/src/FlutterFilamentApi.cpp | 4 ++++ lib/filament_controller.dart | 17 ++++++++++------- lib/filament_controller_ffi.dart | 18 +++++++++++++----- lib/generated_bindings.dart | 7 +++++++ 5 files changed, 35 insertions(+), 12 deletions(-) diff --git a/ios/include/FlutterFilamentApi.h b/ios/include/FlutterFilamentApi.h index c2479265..6c084a69 100644 --- a/ios/include/FlutterFilamentApi.h +++ b/ios/include/FlutterFilamentApi.h @@ -194,6 +194,7 @@ extern "C" FLUTTER_PLUGIN_EXPORT void ios_dummy(); FLUTTER_PLUGIN_EXPORT void flutter_filament_free(void *ptr); FLUTTER_PLUGIN_EXPORT void add_collision_component(void *const sceneManager, EntityId entityId, void (*callback)(const EntityId entityId1, const EntityId entityId2), bool affectsCollidingTransform); + FLUTTER_PLUGIN_EXPORT void remove_collision_component(void *const sceneManager, EntityId entityId); FLUTTER_PLUGIN_EXPORT void add_animation_component(void *const sceneManager, EntityId entityId); FLUTTER_PLUGIN_EXPORT EntityId create_geometry(void *const viewer, float* vertices, int numVertices, uint16_t* indices, int numIndices, const char* materialPath); diff --git a/ios/src/FlutterFilamentApi.cpp b/ios/src/FlutterFilamentApi.cpp index 5adc4249..31882754 100644 --- a/ios/src/FlutterFilamentApi.cpp +++ b/ios/src/FlutterFilamentApi.cpp @@ -589,6 +589,10 @@ extern "C" ((SceneManager*)sceneManager)->addCollisionComponent(entityId, onCollisionCallback, affectsCollidingTransform); } + FLUTTER_PLUGIN_EXPORT void remove_collision_component(void *const sceneManager, EntityId entityId) { + ((SceneManager*)sceneManager)->removeCollisionComponent(entityId); + } + FLUTTER_PLUGIN_EXPORT void add_animation_component(void *const sceneManager, EntityId entityId) { ((SceneManager*)sceneManager)->addAnimationComponent(entityId); } diff --git a/lib/filament_controller.dart b/lib/filament_controller.dart index 105deee4..0110ab56 100644 --- a/lib/filament_controller.dart +++ b/lib/filament_controller.dart @@ -626,15 +626,18 @@ abstract class FilamentController { Future addAnimationComponent(FilamentEntity entity); /// - /// Makes [collidableEntity] collidable with - /// (a) any entity whose transform is being controlled (via [control]) or - /// (b) any entity that has been marked as non-transformable but collidable (via [markNonTransformableCollidable]) - /// These are differentiated because a collision will affect the transform for controlled entities - /// (e.g. if a controlled entity collides with a wall, we ignore the control update to the transform so it doesn't go through the wall) + /// Makes [entity] collidable. + /// This allows you to call [testCollisions] with any other entity ("entity B") to see if [entity] has collided with entity B. The callback will be invoked if so. + /// Alternatively, if [affectsTransform] is true and this entity collides with another entity, any queued position updates to the latter entity will be ignored. /// - Future addCollisionComponent(FilamentEntity collidableEntity, + Future addCollisionComponent(FilamentEntity entity, {void Function(int entityId1, int entityId2)? callback, - bool affectsCollingTransform = false}); + bool affectsTransform = false}); + + /// + /// Removes the collision component from [entity], meaning this will no longer be tested when [testCollisions] or [queuePositionUpdate] is called with another entity. + /// + Future removeCollisionComponent(FilamentEntity entity); /// /// Creates a (renderable) entity with the specified geometry and adds to the scene. diff --git a/lib/filament_controller_ffi.dart b/lib/filament_controller_ffi.dart index 0beb5c3e..f1a6df5b 100644 --- a/lib/filament_controller_ffi.dart +++ b/lib/filament_controller_ffi.dart @@ -1485,10 +1485,12 @@ class FilamentControllerFFI extends FilamentController { return transformController; } + final _collisions = {}; + @override - Future addCollisionComponent(FilamentEntity collidableEntity, + Future addCollisionComponent(FilamentEntity entity, {void Function(int entityId1, int entityId2)? callback, - bool affectsCollingTransform = false}) async { + bool affectsTransform = false}) async { if (_sceneManager == null) { throw Exception("SceneManager must be non-null"); } @@ -1497,14 +1499,20 @@ class FilamentControllerFFI extends FilamentController { if (callback != null) { var ptr = NativeCallable< Void Function(Int32 entityId1, Int32 entityId2)>.listener(callback); - add_collision_component(_sceneManager!, collidableEntity, - ptr.nativeFunction, affectsCollingTransform); + add_collision_component( + _sceneManager!, entity, ptr.nativeFunction, affectsTransform); + _collisions[entity] = ptr; } else { add_collision_component( - _sceneManager!, collidableEntity, nullptr, affectsCollingTransform); + _sceneManager!, entity, nullptr, affectsTransform); } } + @override + Future removeCollisionComponent(FilamentEntity entity) async { + remove_collision_component(_sceneManager!, entity); + } + @override Future addAnimationComponent(FilamentEntity entity) async { add_animation_component(_sceneManager!, entity); diff --git a/lib/generated_bindings.dart b/lib/generated_bindings.dart index 617a9286..bdc9b2c0 100644 --- a/lib/generated_bindings.dart +++ b/lib/generated_bindings.dart @@ -931,6 +931,13 @@ external void add_collision_component( bool affectsCollidingTransform, ); +@ffi.Native, EntityId)>( + symbol: 'remove_collision_component', assetId: 'flutter_filament_plugin') +external void remove_collision_component( + ffi.Pointer sceneManager, + int entityId, +); + @ffi.Native, EntityId)>( symbol: 'add_animation_component', assetId: 'flutter_filament_plugin') external void add_animation_component( From adfb607eb719c37c2a99c5e7f50f5f130c65af2f Mon Sep 17 00:00:00 2001 From: Nick Fisher Date: Tue, 5 Mar 2024 13:51:57 +0800 Subject: [PATCH 21/52] make all FFI calls async with completers --- ios/include/FlutterFilamentFFIApi.h | 195 +++++++----- ios/include/SceneManager.hpp | 11 +- ios/src/FlutterFilamentApi.cpp | 2 +- ios/src/FlutterFilamentFFIApi.cpp | 300 +++++++++--------- ios/src/SceneManager.cpp | 14 + ios/src/StreamBufferAdapter.cpp | 2 +- lib/filament_controller_ffi.dart | 219 ++++++++++--- lib/generated_bindings.dart | 186 +++++++---- .../filament_gesture_detector_mobile.dart | 13 +- macos/flutter_filament.podspec | 2 +- 10 files changed, 598 insertions(+), 346 deletions(-) diff --git a/ios/include/FlutterFilamentFFIApi.h b/ios/include/FlutterFilamentFFIApi.h index 097f369a..22dbb4a7 100644 --- a/ios/include/FlutterFilamentFFIApi.h +++ b/ios/include/FlutterFilamentFFIApi.h @@ -4,98 +4,119 @@ #include "FlutterFilamentApi.h" #ifdef __cplusplus -extern "C" { +extern "C" +{ #endif -/// -/// This header replicates most of the methods in FlutterFilamentApi.h, and is only intended to be used to generate client FFI bindings. -/// The intention is that calling one of these methods will call its respective method in FlutterFilamentApi.h, but wrapped in some kind of thread runner to ensure thread safety. -/// + /// + /// This header replicates most of the methods in FlutterFilamentApi.h, and is only intended to be used to generate client FFI bindings. + /// The intention is that calling one of these methods will call its respective method in FlutterFilamentApi.h, but wrapped in some kind of thread runner to ensure thread safety. + /// -typedef int32_t EntityId; -typedef void (*FilamentRenderCallback)(void* const owner); + typedef int32_t EntityId; + typedef void (*FilamentRenderCallback)(void *const owner); -FLUTTER_PLUGIN_EXPORT void* const create_filament_viewer_ffi(void* const context, void* const platform, const char* uberArchivePath, const ResourceLoaderWrapper* const loader, void (*renderCallback)(void* const renderCallbackOwner), void* const renderCallbackOwner); -FLUTTER_PLUGIN_EXPORT void create_swap_chain_ffi(void* const viewer, void* const surface, uint32_t width, uint32_t height); -FLUTTER_PLUGIN_EXPORT void destroy_swap_chain_ffi(void* const viewer); -FLUTTER_PLUGIN_EXPORT void create_render_target_ffi(void* const viewer, intptr_t nativeTextureId, uint32_t width, uint32_t height); -FLUTTER_PLUGIN_EXPORT void destroy_filament_viewer_ffi(void* const viewer); -FLUTTER_PLUGIN_EXPORT void render_ffi(void* const viewer); -FLUTTER_PLUGIN_EXPORT FilamentRenderCallback make_render_callback_fn_pointer(FilamentRenderCallback); -FLUTTER_PLUGIN_EXPORT void set_rendering_ffi(void* const viewer, bool rendering); -FLUTTER_PLUGIN_EXPORT void set_frame_interval_ffi(float frameInterval); -FLUTTER_PLUGIN_EXPORT void update_viewport_and_camera_projection_ffi(void* const viewer, const uint32_t width, const uint32_t height, const float scaleFactor); -FLUTTER_PLUGIN_EXPORT void set_background_color_ffi(void* const viewer, const float r, const float g, const float b, const float a); -FLUTTER_PLUGIN_EXPORT void clear_background_image_ffi(void* const viewer); -FLUTTER_PLUGIN_EXPORT void set_background_image_ffi(void* const viewer, const char *path, bool fillHeight); -FLUTTER_PLUGIN_EXPORT void set_background_image_position_ffi(void* const viewer, float x, float y, bool clamp); -FLUTTER_PLUGIN_EXPORT void set_tone_mapping_ffi(void* const viewer, int toneMapping); -FLUTTER_PLUGIN_EXPORT void set_bloom_ffi(void* const viewer, float strength); -FLUTTER_PLUGIN_EXPORT void load_skybox_ffi(void* const viewer, const char *skyboxPath); -FLUTTER_PLUGIN_EXPORT void load_ibl_ffi(void* const viewer, const char *iblPath, float intensity); -FLUTTER_PLUGIN_EXPORT void remove_skybox_ffi(void* const viewer); -FLUTTER_PLUGIN_EXPORT void remove_ibl_ffi(void* const viewer); -FLUTTER_PLUGIN_EXPORT EntityId add_light_ffi(void* const viewer, uint8_t type, float colour, float intensity, float posX, float posY, float posZ, float dirX, float dirY, float dirZ, bool shadows); -FLUTTER_PLUGIN_EXPORT void remove_light_ffi(void* const viewer, EntityId entityId); -FLUTTER_PLUGIN_EXPORT void clear_lights_ffi(void* const viewer); -FLUTTER_PLUGIN_EXPORT EntityId load_glb_ffi(void* const sceneManager, const char *assetPath, int numInstances); -FLUTTER_PLUGIN_EXPORT EntityId load_glb_from_buffer_ffi(void* const sceneManager, const void* const data, size_t length, int numInstances); -FLUTTER_PLUGIN_EXPORT EntityId load_gltf_ffi(void* const sceneManager, const char *assetPath, const char *relativePath); -FLUTTER_PLUGIN_EXPORT EntityId create_instance_ffi(void* const sceneManager, EntityId entityId); -FLUTTER_PLUGIN_EXPORT void remove_entity_ffi(void* const viewer, EntityId asset); -FLUTTER_PLUGIN_EXPORT void clear_entities_ffi(void* const viewer); -FLUTTER_PLUGIN_EXPORT bool set_camera_ffi(void* const viewer, EntityId asset, const char *nodeName); -FLUTTER_PLUGIN_EXPORT void apply_weights_ffi( - void* const sceneManager, - EntityId asset, - const char *const entityName, - float *const weights, - int count - ); + FLUTTER_PLUGIN_EXPORT void create_filament_viewer_ffi( + void *const context, + void *const platform, + const char *uberArchivePath, + const ResourceLoaderWrapper *const loader, + void (*renderCallback)(void *const renderCallbackOwner), + void *const renderCallbackOwner, + void (*callback)(void* const viewer) + ); + FLUTTER_PLUGIN_EXPORT void create_swap_chain_ffi(void *const viewer, void *const surface, uint32_t width, uint32_t height, void (*onComplete)()); + FLUTTER_PLUGIN_EXPORT void destroy_swap_chain_ffi(void *const viewer, void (*onComplete)()); + FLUTTER_PLUGIN_EXPORT void create_render_target_ffi(void *const viewer, intptr_t nativeTextureId, uint32_t width, uint32_t height, void (*onComplete)()); + FLUTTER_PLUGIN_EXPORT void destroy_filament_viewer_ffi(void *const viewer); + FLUTTER_PLUGIN_EXPORT void render_ffi(void *const viewer); + FLUTTER_PLUGIN_EXPORT FilamentRenderCallback make_render_callback_fn_pointer(FilamentRenderCallback); + FLUTTER_PLUGIN_EXPORT void set_rendering_ffi(void *const viewer, bool rendering); + FLUTTER_PLUGIN_EXPORT void set_frame_interval_ffi(float frameInterval); + FLUTTER_PLUGIN_EXPORT void update_viewport_and_camera_projection_ffi(void *const viewer, const uint32_t width, const uint32_t height, const float scaleFactor, void (*onComplete)()); + FLUTTER_PLUGIN_EXPORT void set_background_color_ffi(void *const viewer, const float r, const float g, const float b, const float a); + FLUTTER_PLUGIN_EXPORT void clear_background_image_ffi(void *const viewer); + FLUTTER_PLUGIN_EXPORT void set_background_image_ffi(void *const viewer, const char *path, bool fillHeight); + FLUTTER_PLUGIN_EXPORT void set_background_image_position_ffi(void *const viewer, float x, float y, bool clamp); + FLUTTER_PLUGIN_EXPORT void set_tone_mapping_ffi(void *const viewer, int toneMapping); + FLUTTER_PLUGIN_EXPORT void set_bloom_ffi(void *const viewer, float strength); + FLUTTER_PLUGIN_EXPORT void load_skybox_ffi(void *const viewer, const char *skyboxPath); + FLUTTER_PLUGIN_EXPORT void load_ibl_ffi(void *const viewer, const char *iblPath, float intensity); + FLUTTER_PLUGIN_EXPORT void remove_skybox_ffi(void *const viewer); + FLUTTER_PLUGIN_EXPORT void remove_ibl_ffi(void *const viewer); + FLUTTER_PLUGIN_EXPORT void add_light_ffi( + void *const viewer, + uint8_t type, + float colour, + float intensity, + float posX, + float posY, + float posZ, + float dirX, + float dirY, + float dirZ, + bool shadows, + void (*callback)(EntityId)); + FLUTTER_PLUGIN_EXPORT void remove_light_ffi(void *const viewer, EntityId entityId); + FLUTTER_PLUGIN_EXPORT void clear_lights_ffi(void *const viewer); + FLUTTER_PLUGIN_EXPORT void load_glb_ffi(void *const sceneManager, const char *assetPath, int numInstances, void (*callback)(EntityId)); + FLUTTER_PLUGIN_EXPORT void load_glb_from_buffer_ffi(void *const sceneManager, const void *const data, size_t length, int numInstances, void (*callback)(EntityId)); + FLUTTER_PLUGIN_EXPORT void load_gltf_ffi(void *const sceneManager, const char *assetPath, const char *relativePath, void (*callback)(EntityId)); + FLUTTER_PLUGIN_EXPORT void create_instance_ffi(void *const sceneManager, EntityId entityId, void (*callback)(EntityId)); + FLUTTER_PLUGIN_EXPORT void remove_entity_ffi(void *const viewer, EntityId asset, void (*callback)()); + FLUTTER_PLUGIN_EXPORT void clear_entities_ffi(void *const viewer, void (*callback)()); + FLUTTER_PLUGIN_EXPORT void set_camera_ffi(void *const viewer, EntityId asset, const char *nodeName, void (*callback)(bool)); + FLUTTER_PLUGIN_EXPORT void apply_weights_ffi( + void *const sceneManager, + EntityId asset, + const char *const entityName, + float *const weights, + int count); -FLUTTER_PLUGIN_EXPORT void play_animation_ffi(void* const sceneManager, EntityId asset, int index, bool loop, bool reverse, bool replaceActive, float crossfade); -FLUTTER_PLUGIN_EXPORT void set_animation_frame_ffi(void* const sceneManager, EntityId asset, int animationIndex, int animationFrame); -FLUTTER_PLUGIN_EXPORT void stop_animation_ffi(void* const sceneManager, EntityId asset, int index); -FLUTTER_PLUGIN_EXPORT int get_animation_count_ffi(void* const sceneManager, EntityId asset); -FLUTTER_PLUGIN_EXPORT void get_animation_name_ffi(void* const sceneManager, EntityId asset, char *const outPtr, int index); -FLUTTER_PLUGIN_EXPORT void get_morph_target_name_ffi(void* const sceneManager, EntityId asset, const char *meshName, char *const outPtr, int index); -FLUTTER_PLUGIN_EXPORT int get_morph_target_name_count_ffi(void* const sceneManager, EntityId asset, const char *meshName); -FLUTTER_PLUGIN_EXPORT void set_morph_target_weights_ffi(void* const sceneManager, - EntityId asset, - const char *const entityName, - const float *const morphData, - int numWeights - ); -FLUTTER_PLUGIN_EXPORT bool set_morph_animation_ffi( - void *sceneManager, - EntityId asset, - const char *const entityName, - const float *const morphData, - const int *const morphIndices, - int numMorphTargets, - int numFrames, - float frameLengthInMs); -FLUTTER_PLUGIN_EXPORT bool set_bone_transform_ffi( - void *sceneManager, - EntityId asset, - const char *entityName, - const float *const transform, - const char *boneName); -FLUTTER_PLUGIN_EXPORT void add_bone_animation_ffi( - void *sceneManager, - EntityId asset, - const float *const frameData, - int numFrames, - const char *const boneName, - const char **const meshNames, - int numMeshTargets, - float frameLengthInMs, - bool isModelSpace); -FLUTTER_PLUGIN_EXPORT void set_post_processing_ffi(void* const viewer, bool enabled); -FLUTTER_PLUGIN_EXPORT void pick_ffi(void* const viewer, int x, int y, EntityId* entityId); -FLUTTER_PLUGIN_EXPORT void reset_to_rest_pose_ffi(void* const sceneManager, EntityId entityId); -FLUTTER_PLUGIN_EXPORT void ios_dummy_ffi(); -FLUTTER_PLUGIN_EXPORT EntityId create_geometry_ffi(void* const viewer, float* vertices, int numVertices, uint16_t* indices, int numIndices, const char* materialPath); + FLUTTER_PLUGIN_EXPORT void play_animation_ffi(void *const sceneManager, EntityId asset, int index, bool loop, bool reverse, bool replaceActive, float crossfade); + FLUTTER_PLUGIN_EXPORT void set_animation_frame_ffi(void *const sceneManager, EntityId asset, int animationIndex, int animationFrame); + FLUTTER_PLUGIN_EXPORT void stop_animation_ffi(void *const sceneManager, EntityId asset, int index); + FLUTTER_PLUGIN_EXPORT void get_animation_count_ffi(void *const sceneManager, EntityId asset, void (*callback)(int)); + FLUTTER_PLUGIN_EXPORT void get_animation_name_ffi(void *const sceneManager, EntityId asset, char *const outPtr, int index, void (*callback)()); + FLUTTER_PLUGIN_EXPORT void get_morph_target_name_ffi(void *const sceneManager, EntityId asset, const char *meshName, char *const outPtr, int index, void (*callback)()); + FLUTTER_PLUGIN_EXPORT void get_morph_target_name_count_ffi(void *const sceneManager, EntityId asset, const char *meshName, void (*callback)(int32_t)); + FLUTTER_PLUGIN_EXPORT void set_morph_target_weights_ffi(void *const sceneManager, + EntityId asset, + const char *const entityName, + const float *const morphData, + int numWeights); + FLUTTER_PLUGIN_EXPORT void set_morph_animation_ffi( + void *sceneManager, + EntityId asset, + const char *const entityName, + const float *const morphData, + const int *const morphIndices, + int numMorphTargets, + int numFrames, + float frameLengthInMs, + void (*callback)(bool)); + FLUTTER_PLUGIN_EXPORT void set_bone_transform_ffi( + void *sceneManager, + EntityId asset, + const char *entityName, + const float *const transform, + const char *boneName, + void (*callback)(bool)); + FLUTTER_PLUGIN_EXPORT void add_bone_animation_ffi( + void *sceneManager, + EntityId asset, + const float *const frameData, + int numFrames, + const char *const boneName, + const char **const meshNames, + int numMeshTargets, + float frameLengthInMs, + bool isModelSpace); + FLUTTER_PLUGIN_EXPORT void set_post_processing_ffi(void *const viewer, bool enabled); + FLUTTER_PLUGIN_EXPORT void pick_ffi(void *const viewer, int x, int y, EntityId *entityId); + FLUTTER_PLUGIN_EXPORT void reset_to_rest_pose_ffi(void *const sceneManager, EntityId entityId); + FLUTTER_PLUGIN_EXPORT void ios_dummy_ffi(); + FLUTTER_PLUGIN_EXPORT void create_geometry_ffi(void *const viewer, float *vertices, int numVertices, uint16_t *indices, int numIndices, const char *materialPath, void (*callback)(EntityId)); #ifdef __cplusplus } diff --git a/ios/include/SceneManager.hpp b/ios/include/SceneManager.hpp index 8281f861..52465eb3 100644 --- a/ios/include/SceneManager.hpp +++ b/ios/include/SceneManager.hpp @@ -44,9 +44,9 @@ namespace flutter_filament EntityId loadGltf(const char *uri, const char *relativeResourcePath); //// - /// @brief - /// @param uri - /// @param numInstances + /// @brief Load the GLB from the specified path, optionally creating multiple instances. + /// @param uri the path to the asset. Should be either asset:// (representing a Flutter asset), or file:// (representing a filesystem file). + /// @param numInstances the number of instances to create. /// @return an Entity representing the FilamentAsset associated with the loaded FilamentAsset. /// EntityId loadGlb(const char *uri, int numInstances); @@ -103,7 +103,7 @@ namespace flutter_filament /// @param meshName an array of mesh names under [entity] that should be animated /// @param numMeshTargets the number of meshes under [meshName] /// @param frameLengthInMs the length of each frame in ms - /// @return + /// @return true if the bone animation was successfully enqueued bool addBoneAnimation( EntityId entity, const float *const frameData, @@ -134,12 +134,11 @@ namespace flutter_filament /// @brief returns the number of instances of the FilamentAsset represented by the given entity. /// @param entityId - /// @return + /// @return the number of instances int getInstanceCount(EntityId entityId); /// @brief returns an array containing all instances of the FilamentAsset represented by the given entity. /// @param entityId - /// @return void getInstances(EntityId entityId, EntityId* out); friend class FilamentViewer; diff --git a/ios/src/FlutterFilamentApi.cpp b/ios/src/FlutterFilamentApi.cpp index 31882754..bf92d288 100644 --- a/ios/src/FlutterFilamentApi.cpp +++ b/ios/src/FlutterFilamentApi.cpp @@ -598,7 +598,7 @@ extern "C" } FLUTTER_PLUGIN_EXPORT EntityId create_geometry(void *const viewer, float* vertices, int numVertices, uint16_t* indices, int numIndices, const char* materialPath) { - return ((FilamentViewer*)viewer)->createGeometry(vertices, (size_t)numVertices, indices, numIndices, materialPath); + return ((FilamentViewer*)viewer)->createGeometry(vertices, (uint32_t)numVertices, indices, numIndices, materialPath); } FLUTTER_PLUGIN_EXPORT EntityId find_child_entity_by_name(void *const sceneManager, const EntityId parent, const char* name) { diff --git a/ios/src/FlutterFilamentFFIApi.cpp b/ios/src/FlutterFilamentFFIApi.cpp index 020971d1..a380d348 100644 --- a/ios/src/FlutterFilamentFFIApi.cpp +++ b/ios/src/FlutterFilamentFFIApi.cpp @@ -64,10 +64,9 @@ public: } if (_rendering) { - auto frameStart = std::chrono::high_resolution_clock::now(); + // auto frameStart = std::chrono::high_resolution_clock::now(); doRender(); - auto frameEnd = std::chrono::high_resolution_clock::now(); - // Log("Took %f milliseconds for render", float(std::chrono::duration_cast(frameEnd - frameStart).count())); + // auto frameEnd = std::chrono::high_resolution_clock::now(); } elapsed = float(std::chrono::duration_cast(now - last).count()); @@ -98,10 +97,12 @@ public: _t->join(); } - void *const createViewer(void *const context, void *const platform, + void createViewer(void *const context, void *const platform, const char *uberArchivePath, const ResourceLoaderWrapper *const loader, - void (*renderCallback)(void *), void *const owner) { + void (*renderCallback)(void *), + void *const owner, + void (*callback)(void* const) ) { _renderCallback = renderCallback; _renderCallbackOwner = owner; std::packaged_task lambda([=]() mutable { @@ -117,34 +118,30 @@ public: #else _viewer = new FilamentViewer(context, loader, platform, uberArchivePath); #endif + callback(_viewer); return _viewer; }); auto fut = add_task(lambda); - fut.wait(); - _viewer = fut.get(); - return (void *const)_viewer; } void destroyViewer() { - std::packaged_task lambda([&]() mutable { + std::packaged_task lambda([=]() mutable { _rendering = false; destroy_filament_viewer(_viewer); _viewer = nullptr; }); auto fut = add_task(lambda); - fut.wait(); } void setRendering(bool rendering) { std::packaged_task lambda( - [&]() mutable { this->_rendering = rendering; }); + [=]() mutable { this->_rendering = rendering; }); auto fut = add_task(lambda); - fut.wait(); } void doRender() { - auto now = std::chrono::high_resolution_clock::now(); - auto nanos = std::chrono::duration_cast(now.time_since_epoch()).count(); + // auto now = std::chrono::high_resolution_clock::now(); + // auto nanos = std::chrono::duration_cast(now.time_since_epoch()).count(); render(_viewer, 0, nullptr, nullptr, nullptr); _lastRenderTime = std::chrono::high_resolution_clock::now(); if(_renderCallback) { @@ -189,16 +186,17 @@ extern "C" { static RenderLoop *_rl; -FLUTTER_PLUGIN_EXPORT void *const create_filament_viewer_ffi( +FLUTTER_PLUGIN_EXPORT void create_filament_viewer_ffi( void *const context, void *const platform, const char *uberArchivePath, const ResourceLoaderWrapper *const loader, void (*renderCallback)(void *const renderCallbackOwner), - void *const renderCallbackOwner) { + void *const renderCallbackOwner, + void (*callback)(void* const) ) { if (!_rl) { _rl = new RenderLoop(); } - return _rl->createViewer(context, platform, uberArchivePath, loader, - renderCallback, renderCallbackOwner); + _rl->createViewer(context, platform, uberArchivePath, loader, + renderCallback, renderCallbackOwner, callback); } FLUTTER_PLUGIN_EXPORT void destroy_filament_viewer_ffi(void *const viewer) { @@ -208,44 +206,49 @@ FLUTTER_PLUGIN_EXPORT void destroy_filament_viewer_ffi(void *const viewer) { FLUTTER_PLUGIN_EXPORT void create_swap_chain_ffi(void *const viewer, void *const surface, uint32_t width, - uint32_t height) { - Log("Creating swapchain %dx%d", width, height); + uint32_t height, + void (*onComplete)()) { + Log("Creating swapchain %dx%d with viewer %d", width, height, viewer); std::packaged_task lambda( - [&]() mutable { create_swap_chain(viewer, surface, width, height); }); + [=]() mutable { + create_swap_chain(viewer, surface, width, height); + onComplete(); + }); auto fut = _rl->add_task(lambda); - fut.wait(); } -FLUTTER_PLUGIN_EXPORT void destroy_swap_chain_ffi(void *const viewer) { +FLUTTER_PLUGIN_EXPORT void destroy_swap_chain_ffi(void *const viewer, void (*onComplete)()) { Log("Destroying swapchain"); std::packaged_task lambda( - [&]() mutable { + [=]() mutable { destroy_swap_chain(viewer); + onComplete(); }); auto fut = _rl->add_task(lambda); - fut.wait(); } FLUTTER_PLUGIN_EXPORT void create_render_target_ffi(void *const viewer, intptr_t nativeTextureId, uint32_t width, - uint32_t height) { - std::packaged_task lambda([&]() mutable { + uint32_t height, + void (*onComplete)()) { + std::packaged_task lambda([=]() mutable { create_render_target(viewer, nativeTextureId, width, height); + onComplete(); }); auto fut = _rl->add_task(lambda); - fut.wait(); } FLUTTER_PLUGIN_EXPORT void update_viewport_and_camera_projection_ffi( void *const viewer, const uint32_t width, const uint32_t height, - const float scaleFactor) { + const float scaleFactor, + void (*onComplete)()) { Log("Update viewport %dx%d", width, height); - std::packaged_task lambda([&]() mutable { + std::packaged_task lambda([=]() mutable { update_viewport_and_camera_projection(viewer, width, height, scaleFactor); + onComplete(); }); auto fut = _rl->add_task(lambda); - fut.wait(); } FLUTTER_PLUGIN_EXPORT void set_rendering_ffi(void *const viewer, @@ -268,173 +271,173 @@ set_frame_interval_ffi(float frameIntervalInMilliseconds) { } FLUTTER_PLUGIN_EXPORT void render_ffi(void *const viewer) { - std::packaged_task lambda([&]() mutable { _rl->doRender(); }); + std::packaged_task lambda([=]() mutable { _rl->doRender(); }); auto fut = _rl->add_task(lambda); - fut.wait(); } FLUTTER_PLUGIN_EXPORT void set_background_color_ffi(void *const viewer, const float r, const float g, const float b, const float a) { std::packaged_task lambda( - [&]() mutable { set_background_color(viewer, r, g, b, a); }); + [=]() mutable { set_background_color(viewer, r, g, b, a); }); auto fut = _rl->add_task(lambda); - fut.wait(); } -FLUTTER_PLUGIN_EXPORT EntityId load_gltf_ffi(void *const sceneManager, +FLUTTER_PLUGIN_EXPORT void load_gltf_ffi(void *const sceneManager, const char *path, - const char *relativeResourcePath) { - std::packaged_task lambda([&]() mutable { - return load_gltf(sceneManager, path, relativeResourcePath); + const char *relativeResourcePath, + void (*callback)(EntityId)) { + std::packaged_task lambda([=]() mutable { + auto entity = load_gltf(sceneManager, path, relativeResourcePath); + callback(entity); + return entity; }); auto fut = _rl->add_task(lambda); - fut.wait(); - return fut.get(); } -FLUTTER_PLUGIN_EXPORT EntityId load_glb_ffi(void *const sceneManager, - const char *path, int numInstances) { +FLUTTER_PLUGIN_EXPORT void load_glb_ffi(void *const sceneManager, + const char *path, int numInstances, void (*callback)(EntityId)) { std::packaged_task lambda( - [&]() mutable { return load_glb(sceneManager, path, numInstances); }); + [=]() mutable { + auto entity = load_glb(sceneManager, path, numInstances); + callback(entity); + return entity; + }); auto fut = _rl->add_task(lambda); - fut.wait(); - return fut.get(); } -FLUTTER_PLUGIN_EXPORT EntityId load_glb_from_buffer_ffi(void *const sceneManager, - const void *const data, size_t length, int numInstances) { +FLUTTER_PLUGIN_EXPORT void load_glb_from_buffer_ffi(void *const sceneManager, + const void *const data, size_t length, int numInstances, void (*callback)(EntityId)) { std::packaged_task lambda( - [&]() mutable { return load_glb_from_buffer(sceneManager, data, length); }); + [=]() mutable { + auto entity = load_glb_from_buffer(sceneManager, data, length); + callback(entity); + return entity; + }); auto fut = _rl->add_task(lambda); - fut.wait(); - return fut.get(); } FLUTTER_PLUGIN_EXPORT void clear_background_image_ffi(void *const viewer) { - std::packaged_task lambda([&] { clear_background_image(viewer); }); + std::packaged_task lambda([=] { clear_background_image(viewer); }); auto fut = _rl->add_task(lambda); - fut.wait(); } FLUTTER_PLUGIN_EXPORT void set_background_image_ffi(void *const viewer, const char *path, bool fillHeight) { std::packaged_task lambda( - [&] { set_background_image(viewer, path, fillHeight); }); + [=] { set_background_image(viewer, path, fillHeight); }); auto fut = _rl->add_task(lambda); - fut.wait(); } FLUTTER_PLUGIN_EXPORT void set_background_image_position_ffi(void *const viewer, float x, float y, bool clamp) { std::packaged_task lambda( - [&] { set_background_image_position(viewer, x, y, clamp); }); + [=] { set_background_image_position(viewer, x, y, clamp); }); auto fut = _rl->add_task(lambda); - fut.wait(); } FLUTTER_PLUGIN_EXPORT void set_tone_mapping_ffi(void *const viewer, int toneMapping) { std::packaged_task lambda( - [&] { set_tone_mapping(viewer, toneMapping); }); + [=] { set_tone_mapping(viewer, toneMapping); }); auto fut = _rl->add_task(lambda); - fut.wait(); } FLUTTER_PLUGIN_EXPORT void set_bloom_ffi(void *const viewer, float strength) { - std::packaged_task lambda([&] { set_bloom(viewer, strength); }); + std::packaged_task lambda([=] { set_bloom(viewer, strength); }); auto fut = _rl->add_task(lambda); - fut.wait(); } FLUTTER_PLUGIN_EXPORT void load_skybox_ffi(void *const viewer, const char *skyboxPath) { - std::packaged_task lambda([&] { load_skybox(viewer, skyboxPath); }); + std::packaged_task lambda([=] { load_skybox(viewer, skyboxPath); }); auto fut = _rl->add_task(lambda); - fut.wait(); } + FLUTTER_PLUGIN_EXPORT void load_ibl_ffi(void *const viewer, const char *iblPath, float intensity) { std::packaged_task lambda( - [&] { load_ibl(viewer, iblPath, intensity); }); + [=] { load_ibl(viewer, iblPath, intensity); }); auto fut = _rl->add_task(lambda); - fut.wait(); } FLUTTER_PLUGIN_EXPORT void remove_skybox_ffi(void *const viewer) { - std::packaged_task lambda([&] { remove_skybox(viewer); }); + std::packaged_task lambda([=] { remove_skybox(viewer); }); auto fut = _rl->add_task(lambda); - fut.wait(); } FLUTTER_PLUGIN_EXPORT void remove_ibl_ffi(void *const viewer) { - std::packaged_task lambda([&] { remove_ibl(viewer); }); + std::packaged_task lambda([=] { remove_ibl(viewer); }); auto fut = _rl->add_task(lambda); - fut.wait(); } -EntityId add_light_ffi(void *const viewer, uint8_t type, float colour, +void add_light_ffi(void *const viewer, uint8_t type, float colour, float intensity, float posX, float posY, float posZ, - float dirX, float dirY, float dirZ, bool shadows) { - std::packaged_task lambda([&] { - return add_light(viewer, type, colour, intensity, posX, posY, posZ, dirX, + float dirX, float dirY, float dirZ, bool shadows, void (*callback)(EntityId)) { + std::packaged_task lambda([=] { + auto entity = add_light(viewer, type, colour, intensity, posX, posY, posZ, dirX, dirY, dirZ, shadows); + callback(entity); + return entity; }); auto fut = _rl->add_task(lambda); - fut.wait(); - return fut.get(); } FLUTTER_PLUGIN_EXPORT void remove_light_ffi(void *const viewer, EntityId entityId) { - std::packaged_task lambda([&] { remove_light(viewer, entityId); }); + std::packaged_task lambda([=] { remove_light(viewer, entityId); }); auto fut = _rl->add_task(lambda); - fut.wait(); } FLUTTER_PLUGIN_EXPORT void clear_lights_ffi(void *const viewer) { - std::packaged_task lambda([&] { clear_lights(viewer); }); + std::packaged_task lambda([=] { clear_lights(viewer); }); auto fut = _rl->add_task(lambda); - fut.wait(); } FLUTTER_PLUGIN_EXPORT void remove_entity_ffi(void *const viewer, - EntityId asset) { - std::packaged_task lambda([&] { remove_entity(viewer, asset); }); + EntityId asset, void (*callback)()) { + std::packaged_task lambda([=] { + remove_entity(viewer, asset); + callback(); + }); auto fut = _rl->add_task(lambda); - fut.wait(); -} -FLUTTER_PLUGIN_EXPORT void clear_entities_ffi(void *const viewer) { - std::packaged_task lambda([&] { clear_entities(viewer); }); - auto fut = _rl->add_task(lambda); - fut.wait(); } -FLUTTER_PLUGIN_EXPORT bool set_camera_ffi(void *const viewer, EntityId asset, - const char *nodeName) { - std::packaged_task lambda( - [&] { return set_camera(viewer, asset, nodeName); }); +FLUTTER_PLUGIN_EXPORT void clear_entities_ffi(void *const viewer, void (*callback)()) { + std::packaged_task lambda([=] { + clear_entities(viewer); + callback(); + }); + auto fut = _rl->add_task(lambda); +} + +FLUTTER_PLUGIN_EXPORT void set_camera_ffi(void *const viewer, EntityId asset, + const char *nodeName, void (*callback)(bool)) { + std::packaged_task lambda( + [=] { + auto success = set_camera(viewer, asset, nodeName); + callback(success); + return success; + }); auto fut = _rl->add_task(lambda); - fut.wait(); - return fut.get(); } FLUTTER_PLUGIN_EXPORT void get_morph_target_name_ffi(void *sceneManager, EntityId asset, - const char *meshName, char *const outPtr, int index) { - std::packaged_task lambda([&] { + const char *meshName, char *const outPtr, int index, void (*callback)()) { + std::packaged_task lambda([=] { get_morph_target_name(sceneManager, asset, meshName, outPtr, index); + callback(); }); auto fut = _rl->add_task(lambda); - fut.wait(); } -FLUTTER_PLUGIN_EXPORT int +FLUTTER_PLUGIN_EXPORT void get_morph_target_name_count_ffi(void *sceneManager, EntityId asset, - const char *meshName) { - std::packaged_task lambda([&] { - return get_morph_target_name_count(sceneManager, asset, meshName); + const char *meshName, void (*callback)(int)) { + std::packaged_task lambda([=] { + auto count = get_morph_target_name_count(sceneManager, asset, meshName); + callback(count); + return count; }); auto fut = _rl->add_task(lambda); - fut.wait(); - return fut.get(); } @@ -444,73 +447,77 @@ FLUTTER_PLUGIN_EXPORT void play_animation_ffi(void *const sceneManager, bool loop, bool reverse, bool replaceActive, float crossfade) { - std::packaged_task lambda([&] { + std::packaged_task lambda([=] { play_animation(sceneManager, asset, index, loop, reverse, replaceActive, crossfade); }); auto fut = _rl->add_task(lambda); - fut.wait(); } FLUTTER_PLUGIN_EXPORT void set_animation_frame_ffi(void *const sceneManager, EntityId asset, int animationIndex, int animationFrame) { - std::packaged_task lambda([&] { + std::packaged_task lambda([=] { set_animation_frame(sceneManager, asset, animationIndex, animationFrame); }); auto fut = _rl->add_task(lambda); - fut.wait(); } FLUTTER_PLUGIN_EXPORT void stop_animation_ffi(void *const sceneManager, EntityId asset, int index) { std::packaged_task lambda( - [&] { stop_animation(sceneManager, asset, index); }); + [=] { stop_animation(sceneManager, asset, index); }); auto fut = _rl->add_task(lambda); - fut.wait(); } -FLUTTER_PLUGIN_EXPORT int get_animation_count_ffi(void *const sceneManager, - EntityId asset) { +FLUTTER_PLUGIN_EXPORT void get_animation_count_ffi(void *const sceneManager, + EntityId asset, + void (*callback)(int)) { std::packaged_task lambda( - [&] { return get_animation_count(sceneManager, asset); }); + [=] { + auto count = get_animation_count(sceneManager, asset); + callback(count); + return count; + }); auto fut = _rl->add_task(lambda); - fut.wait(); - return fut.get(); } + FLUTTER_PLUGIN_EXPORT void get_animation_name_ffi(void *const sceneManager, EntityId asset, char *const outPtr, - int index) { + int index, + void (*callback)()) { std::packaged_task lambda( - [&] { get_animation_name(sceneManager, asset, outPtr, index); }); + [=] { + get_animation_name(sceneManager, asset, outPtr, index); + callback(); + }); auto fut = _rl->add_task(lambda); - fut.wait(); } FLUTTER_PLUGIN_EXPORT void set_post_processing_ffi(void *const viewer, bool enabled) { std::packaged_task lambda( - [&] { set_post_processing(viewer, enabled); }); + [=] { set_post_processing(viewer, enabled); }); auto fut = _rl->add_task(lambda); - fut.wait(); } FLUTTER_PLUGIN_EXPORT void pick_ffi(void *const viewer, int x, int y, EntityId *entityId) { - std::packaged_task lambda([&] { pick(viewer, x, y, entityId); }); + std::packaged_task lambda([=] { pick(viewer, x, y, entityId); }); auto fut = _rl->add_task(lambda); - fut.wait(); } -FLUTTER_PLUGIN_EXPORT const char * -get_name_for_entity_ffi(void *const sceneManager, const EntityId entityId) { +FLUTTER_PLUGIN_EXPORT void +get_name_for_entity_ffi(void *const sceneManager, const EntityId entityId, void (*callback)(const char*)) { std::packaged_task lambda( - [&] { return get_name_for_entity(sceneManager, entityId); }); + [=] { + auto name = get_name_for_entity(sceneManager, entityId); + callback(name); + return name; + }); auto fut = _rl->add_task(lambda); - fut.wait(); - return fut.get(); } void set_morph_target_weights_ffi(void *const sceneManager, @@ -519,12 +526,13 @@ void set_morph_target_weights_ffi(void *const sceneManager, const float *const morphData, int numWeights) { std::packaged_task lambda( - [&] { return set_morph_target_weights(sceneManager, asset, entityName, morphData, numWeights); }); + [=] { + set_morph_target_weights(sceneManager, asset, entityName, morphData, numWeights); + }); auto fut = _rl->add_task(lambda); - fut.wait(); } -bool set_morph_animation_ffi( +void set_morph_animation_ffi( void *sceneManager, EntityId asset, const char *const entityName, @@ -532,35 +540,38 @@ bool set_morph_animation_ffi( const int *const morphIndices, int numMorphTargets, int numFrames, - float frameLengthInMs) { + float frameLengthInMs, + void (*callback)(bool)) { std::packaged_task lambda( - [&] { - return set_morph_animation(sceneManager, asset, entityName, morphData, morphIndices, numMorphTargets, numFrames, frameLengthInMs); + [=] { + auto success = set_morph_animation(sceneManager, asset, entityName, morphData, morphIndices, numMorphTargets, numFrames, frameLengthInMs); + callback(success); + return success; }); auto fut = _rl->add_task(lambda); - fut.wait(); - return fut.get(); } -FLUTTER_PLUGIN_EXPORT bool set_bone_transform_ffi( +FLUTTER_PLUGIN_EXPORT void set_bone_transform_ffi( void *sceneManager, EntityId asset, const char *entityName, const float *const transform, - const char *boneName) { + const char *boneName, + void (*callback)(bool)) { std::packaged_task lambda( - [&] { return set_bone_transform(sceneManager, asset, entityName, transform, boneName); }); + [=] { + auto success = set_bone_transform(sceneManager, asset, entityName, transform, boneName); + callback(success); + return success; + }); auto fut = _rl->add_task(lambda); - fut.wait(); - return fut.get(); } FLUTTER_PLUGIN_EXPORT void reset_to_rest_pose_ffi(void* const sceneManager, EntityId entityId) { std::packaged_task lambda( - [&] { return reset_to_rest_pose(sceneManager, entityId); }); + [=] { return reset_to_rest_pose(sceneManager, entityId); }); auto fut = _rl->add_task(lambda); - fut.wait(); } FLUTTER_PLUGIN_EXPORT void add_bone_animation_ffi( @@ -579,19 +590,18 @@ FLUTTER_PLUGIN_EXPORT void add_bone_animation_ffi( add_bone_animation(sceneManager, asset, frameData, numFrames, boneName, meshNames, numMeshTargets, frameLengthInMs, isModelSpace); }); auto fut = _rl->add_task(lambda); - fut.wait(); } FLUTTER_PLUGIN_EXPORT void ios_dummy_ffi() { Log("Dummy called"); } -FLUTTER_PLUGIN_EXPORT EntityId create_geometry_ffi(void* const viewer, float* vertices, int numVertices, uint16_t* indices, int numIndices, const char* materialPath) { +FLUTTER_PLUGIN_EXPORT void create_geometry_ffi(void* const viewer, float* vertices, int numVertices, uint16_t* indices, int numIndices, const char* materialPath, void (*callback)(EntityId) ) { std::packaged_task lambda( [=] { - return create_geometry(viewer, vertices, numVertices, indices, numIndices, materialPath); + auto entity = create_geometry(viewer, vertices, numVertices, indices, numIndices, materialPath); + callback(entity); + return entity; }); auto fut = _rl->add_task(lambda); - fut.wait(); - return fut.get(); } } diff --git a/ios/src/SceneManager.cpp b/ios/src/SceneManager.cpp index 675ce9e1..01f312c7 100644 --- a/ios/src/SceneManager.cpp +++ b/ios/src/SceneManager.cpp @@ -1208,6 +1208,20 @@ namespace flutter_filament _collisionComponentManager->elementAt<2>(collisionInstance) = affectsTransform; } + void SceneManager::removeCollisionComponent(EntityId entityId) { + std::lock_guard lock(_mutex); + const auto *instance = getInstanceByEntityId(entityId); + if(!instance) { + auto asset = getAssetByEntityId(entityId); + if(!asset) { + return; + } else { + instance = asset->getInstance(); + } + } + _collisionComponentManager->removeComponent(instance->getRoot()); + } + void SceneManager::testCollisions(EntityId entityId) { const auto *instance = getInstanceByEntityId(entityId); if(!instance) { diff --git a/ios/src/StreamBufferAdapter.cpp b/ios/src/StreamBufferAdapter.cpp index fcbc6cd7..7ba83a71 100644 --- a/ios/src/StreamBufferAdapter.cpp +++ b/ios/src/StreamBufferAdapter.cpp @@ -68,7 +68,7 @@ std::streampos StreamBufferAdapter::seekoff(std::streamoff off, std::ios_base::s if(way == std::ios_base::beg) { setg(eback(), eback()+off, egptr()); } else if(way == std::ios_base::cur) { - gbump(off); + gbump((int)off); } else { setg(eback(), egptr()-off, egptr()); } diff --git a/lib/filament_controller_ffi.dart b/lib/filament_controller_ffi.dart index f1a6df5b..5a5facc8 100644 --- a/lib/filament_controller_ffi.dart +++ b/lib/filament_controller_ffi.dart @@ -4,6 +4,7 @@ import 'dart:io'; import 'dart:typed_data'; import 'dart:ui' as ui; import 'dart:developer' as dev; +import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; import 'package:ffi/ffi.dart'; import 'package:flutter/widgets.dart'; @@ -140,7 +141,7 @@ class FilamentControllerFFI extends FilamentController { Future setFrameRate(int framerate) async { final interval = 1000.0 / framerate; set_frame_interval_ffi(interval); - dev.log("Set frame interval to $interval"); + print("Set frame interval to $interval"); } @override @@ -182,7 +183,82 @@ class FilamentControllerFFI extends FilamentController { await _channel.invokeMethod( "destroyTexture", textureDetails.value!.textureId); } - dev.log("Texture destroyed"); + print("Texture destroyed"); + } + + Future _withVoidCallback( + Function(Pointer>) func) async { + final completer = Completer(); + // ignore: prefer_function_declarations_over_variables + void Function() callback = () { + completer.complete(); + }; + final nativeCallable = NativeCallable.listener(callback); + func.call(nativeCallable.nativeFunction); + await completer.future; + nativeCallable.close(); + } + + Future> _withVoidPointerCallback( + Function(Pointer)>>) + func) async { + final completer = Completer>(); + // ignore: prefer_function_declarations_over_variables + void Function(Pointer) callback = (Pointer ptr) { + completer.complete(ptr); + }; + final nativeCallable = + NativeCallable)>.listener(callback); + func.call(nativeCallable.nativeFunction); + await completer.future; + nativeCallable.close(); + return completer.future; + } + + Future _withBoolCallback( + Function(Pointer>) func) async { + final completer = Completer(); + // ignore: prefer_function_declarations_over_variables + void Function(bool) callback = (bool result) { + completer.complete(result); + }; + final nativeCallable = + NativeCallable.listener(callback); + func.call(nativeCallable.nativeFunction); + await completer.future; + nativeCallable.close(); + return completer.future; + } + + Future _withIntCallback( + Function(Pointer>) func) async { + final completer = Completer(); + // ignore: prefer_function_declarations_over_variables + void Function(int) callback = (int result) { + completer.complete(result); + }; + final nativeCallable = + NativeCallable.listener(callback); + func.call(nativeCallable.nativeFunction); + await completer.future; + nativeCallable.close(); + return completer.future; + } + + Future _withCharPtrCallback( + Function(Pointer)>>) + func) async { + final completer = Completer(); + // ignore: prefer_function_declarations_over_variables + void Function(Pointer) callback = (Pointer result) { + completer.complete(result.cast().toDartString()); + }; + final nativeCallable = + NativeCallable)>.listener(callback); + func.call(nativeCallable.nativeFunction); + await completer.future; + nativeCallable.close(); + return completer.future; } bool _creating = false; @@ -190,6 +266,7 @@ class FilamentControllerFFI extends FilamentController { /// /// Called by `FilamentWidget`. You do not need to call this yourself. /// + /// @override Future createViewer() async { if (_creating) { @@ -234,43 +311,60 @@ class FilamentControllerFFI extends FilamentController { var renderingSurface = await _createRenderingSurface(); - dev.log("Got rendering surface"); + print("Got rendering surface"); final uberarchivePtr = uberArchivePath?.toNativeUtf8().cast() ?? nullptr; - _viewer = create_filament_viewer_ffi( - Pointer.fromAddress(renderingSurface.sharedContext), - _driver, - uberarchivePtr, - loader, - renderCallback, - renderCallbackOwner); + _viewer = await _withVoidPointerCallback((callback) => + create_filament_viewer_ffi( + Pointer.fromAddress(renderingSurface.sharedContext), + _driver, + uberarchivePtr, + loader, + renderCallback, + renderCallbackOwner, + callback)); + allocator.free(uberarchivePtr); - dev.log("Created viewer"); + + print("Created viewer ${_viewer!.address}"); if (_viewer!.address == 0) { throw Exception("Failed to create viewer. Check logs for details"); } _sceneManager = get_scene_manager(_viewer!); - create_swap_chain_ffi(_viewer!, renderingSurface.surface, - _rect.value!.width.toInt(), _rect.value!.height.toInt()); - dev.log("Created swap chain"); + await _withVoidCallback((callback) { + create_swap_chain_ffi(_viewer!, renderingSurface.surface, + _rect.value!.width.toInt(), _rect.value!.height.toInt(), callback); + }); + + print("Created swap chain"); if (renderingSurface.textureHandle != 0) { - dev.log( + print( "Creating render target from native texture ${renderingSurface.textureHandle}"); - create_render_target_ffi(_viewer!, renderingSurface.textureHandle, - _rect.value!.width.toInt(), _rect.value!.height.toInt()); + await _withVoidCallback((callback) => create_render_target_ffi( + _viewer!, + renderingSurface.textureHandle, + _rect.value!.width.toInt(), + _rect.value!.height.toInt(), + callback)); } textureDetails.value = TextureDetails( textureId: renderingSurface.flutterTextureId, width: _rect.value!.width.toInt(), height: _rect.value!.height.toInt()); - dev.log("texture details ${textureDetails.value}"); - update_viewport_and_camera_projection_ffi( - _viewer!, _rect.value!.width.toInt(), _rect.value!.height.toInt(), 1.0); + print("texture details ${textureDetails.value}"); + await _withVoidCallback((callback) { + update_viewport_and_camera_projection_ffi( + _viewer!, + _rect.value!.width.toInt(), + _rect.value!.height.toInt(), + 1.0, + callback); + }); hasViewer.value = true; _creating = false; } @@ -365,7 +459,8 @@ class FilamentControllerFFI extends FilamentController { set_rendering_ffi(_viewer!, false); if (!_usesBackingWindow) { - destroy_swap_chain_ffi(_viewer!); + await _withVoidCallback( + (callback) => destroy_swap_chain_ffi(_viewer!, callback)); } if (requiresTextureWidget) { @@ -374,7 +469,7 @@ class FilamentControllerFFI extends FilamentController { "destroyTexture", textureDetails.value!.textureId); } } else if (Platform.isWindows) { - dev.log("Resizing window with rect ${_rect.value}"); + print("Resizing window with rect ${_rect.value}"); await _channel.invokeMethod("resizeWindow", [ _rect.value!.width, _rect.value!.height, @@ -390,15 +485,23 @@ class FilamentControllerFFI extends FilamentController { } if (!_usesBackingWindow) { - create_swap_chain_ffi(_viewer!, renderingSurface.surface, - _rect.value!.width.toInt(), _rect.value!.height.toInt()); + _withVoidCallback((callback) => create_swap_chain_ffi( + _viewer!, + renderingSurface.surface, + _rect.value!.width.toInt(), + _rect.value!.height.toInt(), + callback)); } if (renderingSurface.textureHandle != 0) { - dev.log( + print( "Creating render target from native texture ${renderingSurface.textureHandle}"); - create_render_target_ffi(_viewer!, renderingSurface.textureHandle, - _rect.value!.width.toInt(), _rect.value!.height.toInt()); + _withVoidCallback((callback) => create_render_target_ffi( + _viewer!, + renderingSurface.textureHandle, + _rect.value!.width.toInt(), + _rect.value!.height.toInt(), + callback)); } textureDetails.value = TextureDetails( @@ -406,8 +509,12 @@ class FilamentControllerFFI extends FilamentController { width: _rect.value!.width.toInt(), height: _rect.value!.height.toInt()); - update_viewport_and_camera_projection_ffi(_viewer!, - _rect.value!.width.toInt(), _rect.value!.height.toInt(), 1.0); + _withVoidCallback((callback) => update_viewport_and_camera_projection_ffi( + _viewer!, + _rect.value!.width.toInt(), + _rect.value!.height.toInt(), + 1.0, + callback)); await setRendering(_rendering); } finally { @@ -518,8 +625,19 @@ class FilamentControllerFFI extends FilamentController { if (_viewer == null) { throw Exception("No viewer available, ignoring"); } - var entity = add_light_ffi(_viewer!, type, colour, intensity, posX, posY, - posZ, dirX, dirY, dirZ, castShadows); + var entity = await _withIntCallback((callback) => add_light_ffi( + _viewer!, + type, + colour, + intensity, + posX, + posY, + posZ, + dirX, + dirY, + dirZ, + castShadows, + callback)); _onLoadController.sink.add(entity); _lights.add(entity); return entity; @@ -572,8 +690,8 @@ class FilamentControllerFFI extends FilamentController { data = (ptr.cast(), asset.lengthInBytes); } - var entity = load_glb_from_buffer_ffi( - _sceneManager!, data.$1, data.$2, numInstances); + var entity = await _withIntCallback((callback) => load_glb_from_buffer_ffi( + _sceneManager!, data.$1, data.$2, numInstances, callback)); if (!cache) { allocator.free(data.$1); } else { @@ -587,7 +705,8 @@ class FilamentControllerFFI extends FilamentController { @override Future createInstance(FilamentEntity entity) async { - var created = create_instance(_sceneManager!, entity); + var created = await _withIntCallback( + (callback) => create_instance(_sceneManager!, entity)); if (created == _FILAMENT_ASSET_ERROR) { throw Exception("Failed to create instance"); } @@ -630,7 +749,8 @@ class FilamentControllerFFI extends FilamentController { throw Exception("Not yet implemented"); } final pathPtr = path.toNativeUtf8().cast(); - var entity = load_glb_ffi(_sceneManager!, pathPtr, numInstances); + var entity = await _withIntCallback((callback) => + load_glb_ffi(_sceneManager!, pathPtr, numInstances, callback)); allocator.free(pathPtr); if (entity == _FILAMENT_ASSET_ERROR) { throw Exception("An error occurred loading the asset at $path"); @@ -653,8 +773,8 @@ class FilamentControllerFFI extends FilamentController { final pathPtr = path.toNativeUtf8().cast(); final relativeResourcePathPtr = relativeResourcePath.toNativeUtf8().cast(); - var entity = - load_gltf_ffi(_sceneManager!, pathPtr, relativeResourcePathPtr); + var entity = await _withIntCallback((callback) => load_gltf_ffi( + _sceneManager!, pathPtr, relativeResourcePathPtr, callback)); allocator.free(pathPtr); allocator.free(relativeResourcePathPtr); if (entity == _FILAMENT_ASSET_ERROR) { @@ -739,8 +859,9 @@ class FilamentControllerFFI extends FilamentController { } var names = []; var meshNamePtr = meshName.toNativeUtf8().cast(); - var count = - get_morph_target_name_count_ffi(_sceneManager!, entity, meshNamePtr); + var count = await _withIntCallback((callback) => + get_morph_target_name_count_ffi( + _sceneManager!, entity, meshNamePtr, callback)); var outPtr = allocator(255); for (int i = 0; i < count; i++) { get_morph_target_name(_sceneManager!, entity, meshNamePtr, outPtr, i); @@ -760,9 +881,11 @@ class FilamentControllerFFI extends FilamentController { var names = []; var outPtr = allocator(255); for (int i = 0; i < animationCount; i++) { - get_animation_name_ffi(_sceneManager!, entity, outPtr, i); + await _withVoidCallback((callback) => + get_animation_name_ffi(_sceneManager!, entity, outPtr, i, callback)); names.add(outPtr.cast().toDartString()); } + allocator.free(outPtr); return names; } @@ -896,7 +1019,8 @@ class FilamentControllerFFI extends FilamentController { throw Exception("No viewer available, ignoring"); } _entities.remove(entity); - remove_entity_ffi(_viewer!, entity); + await _withVoidCallback( + (callback) => remove_entity_ffi(_viewer!, entity, callback)); _onUnloadController.add(entity); } @@ -905,7 +1029,8 @@ class FilamentControllerFFI extends FilamentController { if (_viewer == null) { throw Exception("No viewer available, ignoring"); } - clear_entities_ffi(_viewer!); + await _withVoidCallback( + (callback) => clear_entities_ffi(_viewer!, callback)); for (final entity in _entities) { _onUnloadController.add(entity); @@ -1537,8 +1662,14 @@ class FilamentControllerFFI extends FilamentController { indicesPtr.elementAt(i).value = indices[i]; } - var entity = create_geometry_ffi(_viewer!, vertexPtr, vertices.length, - indicesPtr, indices.length, materialPathPtr.cast()); + var entity = await _withIntCallback((callback) => create_geometry_ffi( + _viewer!, + vertexPtr, + vertices.length, + indicesPtr, + indices.length, + materialPathPtr.cast(), + callback)); if (entity == _FILAMENT_ASSET_ERROR) { throw Exception("Failed to create geometry"); } diff --git a/lib/generated_bindings.dart b/lib/generated_bindings.dart index bdc9b2c0..d4d2529f 100644 --- a/lib/generated_bindings.dart +++ b/lib/generated_bindings.dart @@ -984,7 +984,10 @@ external void test_collisions( ffi.NativeFunction< ffi.Void Function( ffi.Pointer renderCallbackOwner)>>, - ffi.Pointer)>( + ffi.Pointer, + ffi.Pointer< + ffi.NativeFunction< + ffi.Void Function(ffi.Pointer viewer)>>)>( symbol: 'create_filament_viewer_ffi', assetId: 'flutter_filament_plugin') external ffi.Pointer create_filament_viewer_ffi( ffi.Pointer context, @@ -996,34 +999,46 @@ external ffi.Pointer create_filament_viewer_ffi( ffi.Void Function(ffi.Pointer renderCallbackOwner)>> renderCallback, ffi.Pointer renderCallbackOwner, + ffi.Pointer< + ffi.NativeFunction viewer)>> + callback, ); @ffi.Native< - ffi.Void Function(ffi.Pointer, ffi.Pointer, - ffi.Uint32, ffi.Uint32)>( + ffi.Void Function( + ffi.Pointer, + ffi.Pointer, + ffi.Uint32, + ffi.Uint32, + ffi.Pointer>)>( symbol: 'create_swap_chain_ffi', assetId: 'flutter_filament_plugin') external void create_swap_chain_ffi( ffi.Pointer viewer, ffi.Pointer surface, int width, int height, -); - -@ffi.Native)>( - symbol: 'destroy_swap_chain_ffi', assetId: 'flutter_filament_plugin') -external void destroy_swap_chain_ffi( - ffi.Pointer viewer, + ffi.Pointer> onComplete, ); @ffi.Native< - ffi.Void Function( - ffi.Pointer, ffi.IntPtr, ffi.Uint32, ffi.Uint32)>( + ffi.Void Function(ffi.Pointer, + ffi.Pointer>)>( + symbol: 'destroy_swap_chain_ffi', assetId: 'flutter_filament_plugin') +external void destroy_swap_chain_ffi( + ffi.Pointer viewer, + ffi.Pointer> onComplete, +); + +@ffi.Native< + ffi.Void Function(ffi.Pointer, ffi.IntPtr, ffi.Uint32, + ffi.Uint32, ffi.Pointer>)>( symbol: 'create_render_target_ffi', assetId: 'flutter_filament_plugin') external void create_render_target_ffi( ffi.Pointer viewer, int nativeTextureId, int width, int height, + ffi.Pointer> onComplete, ); @ffi.Native)>( @@ -1059,8 +1074,8 @@ external void set_frame_interval_ffi( ); @ffi.Native< - ffi.Void Function( - ffi.Pointer, ffi.Uint32, ffi.Uint32, ffi.Float)>( + ffi.Void Function(ffi.Pointer, ffi.Uint32, ffi.Uint32, + ffi.Float, ffi.Pointer>)>( symbol: 'update_viewport_and_camera_projection_ffi', assetId: 'flutter_filament_plugin') external void update_viewport_and_camera_projection_ffi( @@ -1068,6 +1083,7 @@ external void update_viewport_and_camera_projection_ffi( int width, int height, double scaleFactor, + ffi.Pointer> onComplete, ); @ffi.Native< @@ -1153,19 +1169,21 @@ external void remove_ibl_ffi( ); @ffi.Native< - EntityId Function( - ffi.Pointer, - ffi.Uint8, - ffi.Float, - ffi.Float, - ffi.Float, - ffi.Float, - ffi.Float, - ffi.Float, - ffi.Float, - ffi.Float, - ffi.Bool)>(symbol: 'add_light_ffi', assetId: 'flutter_filament_plugin') -external int add_light_ffi( + ffi.Void Function( + ffi.Pointer, + ffi.Uint8, + ffi.Float, + ffi.Float, + ffi.Float, + ffi.Float, + ffi.Float, + ffi.Float, + ffi.Float, + ffi.Float, + ffi.Bool, + ffi.Pointer>)>( + symbol: 'add_light_ffi', assetId: 'flutter_filament_plugin') +external void add_light_ffi( ffi.Pointer viewer, int type, double colour, @@ -1177,6 +1195,7 @@ external int add_light_ffi( double dirY, double dirZ, bool shadows, + ffi.Pointer> callback, ); @ffi.Native, EntityId)>( @@ -1193,63 +1212,87 @@ external void clear_lights_ffi( ); @ffi.Native< - EntityId Function(ffi.Pointer, ffi.Pointer, - ffi.Int)>(symbol: 'load_glb_ffi', assetId: 'flutter_filament_plugin') -external int load_glb_ffi( + ffi.Void Function(ffi.Pointer, ffi.Pointer, ffi.Int, + ffi.Pointer>)>( + symbol: 'load_glb_ffi', assetId: 'flutter_filament_plugin') +external void load_glb_ffi( ffi.Pointer sceneManager, ffi.Pointer assetPath, int numInstances, + ffi.Pointer> callback, ); @ffi.Native< - EntityId Function( - ffi.Pointer, ffi.Pointer, ffi.Size, ffi.Int)>( + ffi.Void Function( + ffi.Pointer, + ffi.Pointer, + ffi.Size, + ffi.Int, + ffi.Pointer>)>( symbol: 'load_glb_from_buffer_ffi', assetId: 'flutter_filament_plugin') -external int load_glb_from_buffer_ffi( +external void load_glb_from_buffer_ffi( ffi.Pointer sceneManager, ffi.Pointer data, int length, int numInstances, + ffi.Pointer> callback, ); @ffi.Native< - EntityId Function(ffi.Pointer, ffi.Pointer, - ffi.Pointer)>( + ffi.Void Function( + ffi.Pointer, + ffi.Pointer, + ffi.Pointer, + ffi.Pointer>)>( symbol: 'load_gltf_ffi', assetId: 'flutter_filament_plugin') -external int load_gltf_ffi( +external void load_gltf_ffi( ffi.Pointer sceneManager, ffi.Pointer assetPath, ffi.Pointer relativePath, + ffi.Pointer> callback, ); -@ffi.Native, EntityId)>( +@ffi.Native< + ffi.Void Function(ffi.Pointer, EntityId, + ffi.Pointer>)>( symbol: 'create_instance_ffi', assetId: 'flutter_filament_plugin') -external int create_instance_ffi( +external void create_instance_ffi( ffi.Pointer sceneManager, int entityId, + ffi.Pointer> callback, ); -@ffi.Native, EntityId)>( +@ffi.Native< + ffi.Void Function(ffi.Pointer, EntityId, + ffi.Pointer>)>( symbol: 'remove_entity_ffi', assetId: 'flutter_filament_plugin') external void remove_entity_ffi( ffi.Pointer viewer, int asset, + ffi.Pointer> callback, ); -@ffi.Native)>( +@ffi.Native< + ffi.Void Function(ffi.Pointer, + ffi.Pointer>)>( symbol: 'clear_entities_ffi', assetId: 'flutter_filament_plugin') external void clear_entities_ffi( ffi.Pointer viewer, + ffi.Pointer> callback, ); @ffi.Native< ffi.Bool Function( - ffi.Pointer, EntityId, ffi.Pointer)>( + ffi.Pointer, + EntityId, + ffi.Pointer, + ffi.Pointer>)>( symbol: 'set_camera_ffi', assetId: 'flutter_filament_plugin') external bool set_camera_ffi( ffi.Pointer viewer, int asset, ffi.Pointer nodeName, + ffi.Pointer> callback, ); @ffi.Native< @@ -1296,27 +1339,40 @@ external void stop_animation_ffi( int index, ); -@ffi.Native, EntityId)>( +@ffi.Native< + ffi.Void Function(ffi.Pointer, EntityId, + ffi.Pointer>)>( symbol: 'get_animation_count_ffi', assetId: 'flutter_filament_plugin') -external int get_animation_count_ffi( +external void get_animation_count_ffi( ffi.Pointer sceneManager, int asset, + ffi.Pointer> callback, ); @ffi.Native< ffi.Void Function( - ffi.Pointer, EntityId, ffi.Pointer, ffi.Int)>( + ffi.Pointer, + EntityId, + ffi.Pointer, + ffi.Int, + ffi.Pointer>)>( symbol: 'get_animation_name_ffi', assetId: 'flutter_filament_plugin') external void get_animation_name_ffi( ffi.Pointer sceneManager, int asset, ffi.Pointer outPtr, int index, + ffi.Pointer> callback, ); @ffi.Native< - ffi.Void Function(ffi.Pointer, EntityId, - ffi.Pointer, ffi.Pointer, ffi.Int)>( + ffi.Void Function( + ffi.Pointer, + EntityId, + ffi.Pointer, + ffi.Pointer, + ffi.Int, + ffi.Pointer>)>( symbol: 'get_morph_target_name_ffi', assetId: 'flutter_filament_plugin') external void get_morph_target_name_ffi( ffi.Pointer sceneManager, @@ -1324,17 +1380,22 @@ external void get_morph_target_name_ffi( ffi.Pointer meshName, ffi.Pointer outPtr, int index, + ffi.Pointer> callback, ); @ffi.Native< - ffi.Int Function( - ffi.Pointer, EntityId, ffi.Pointer)>( + ffi.Void Function( + ffi.Pointer, + EntityId, + ffi.Pointer, + ffi.Pointer>)>( symbol: 'get_morph_target_name_count_ffi', assetId: 'flutter_filament_plugin') -external int get_morph_target_name_count_ffi( +external void get_morph_target_name_count_ffi( ffi.Pointer sceneManager, int asset, ffi.Pointer meshName, + ffi.Pointer> callback, ); @ffi.Native< @@ -1350,7 +1411,7 @@ external void set_morph_target_weights_ffi( ); @ffi.Native< - ffi.Bool Function( + ffi.Void Function( ffi.Pointer, EntityId, ffi.Pointer, @@ -1358,9 +1419,10 @@ external void set_morph_target_weights_ffi( ffi.Pointer, ffi.Int, ffi.Int, - ffi.Float)>( + ffi.Float, + ffi.Pointer>)>( symbol: 'set_morph_animation_ffi', assetId: 'flutter_filament_plugin') -external bool set_morph_animation_ffi( +external void set_morph_animation_ffi( ffi.Pointer sceneManager, int asset, ffi.Pointer entityName, @@ -1369,22 +1431,25 @@ external bool set_morph_animation_ffi( int numMorphTargets, int numFrames, double frameLengthInMs, + ffi.Pointer> callback, ); @ffi.Native< - ffi.Bool Function( + ffi.Void Function( ffi.Pointer, EntityId, ffi.Pointer, ffi.Pointer, - ffi.Pointer)>( + ffi.Pointer, + ffi.Pointer>)>( symbol: 'set_bone_transform_ffi', assetId: 'flutter_filament_plugin') -external bool set_bone_transform_ffi( +external void set_bone_transform_ffi( ffi.Pointer sceneManager, int asset, ffi.Pointer entityName, ffi.Pointer transform, ffi.Pointer boneName, + ffi.Pointer> callback, ); @ffi.Native< @@ -1441,16 +1506,23 @@ external void reset_to_rest_pose_ffi( external void ios_dummy_ffi(); @ffi.Native< - EntityId Function(ffi.Pointer, ffi.Pointer, - ffi.Int, ffi.Pointer, ffi.Int, ffi.Pointer)>( + ffi.Void Function( + ffi.Pointer, + ffi.Pointer, + ffi.Int, + ffi.Pointer, + ffi.Int, + ffi.Pointer, + ffi.Pointer>)>( symbol: 'create_geometry_ffi', assetId: 'flutter_filament_plugin') -external int create_geometry_ffi( +external void create_geometry_ffi( ffi.Pointer viewer, ffi.Pointer vertices, int numVertices, ffi.Pointer indices, int numIndices, ffi.Pointer materialPath, + ffi.Pointer> callback, ); final class __mbstate_t extends ffi.Union { diff --git a/lib/widgets/filament_gesture_detector_mobile.dart b/lib/widgets/filament_gesture_detector_mobile.dart index 3645a082..8865cc69 100644 --- a/lib/widgets/filament_gesture_detector_mobile.dart +++ b/lib/widgets/filament_gesture_detector_mobile.dart @@ -124,15 +124,20 @@ class _FilamentGestureDetectorMobileState double _lastScale = 0; // pinch zoom on mobile - // couldn't find any equivalent for pointerCount in Listener so we use two widgets: - // - outer is a GestureDetector only for pinch zoom - // - inner is a Listener for all other gestures (including scroll zoom on desktop) + // couldn't find any equivalent for pointerCount in Listener (?) so we use a GestureDetector @override Widget build(BuildContext context) { return Stack(children: [ Positioned.fill( child: GestureDetector( - behavior: HitTestBehavior.opaque, + behavior: HitTestBehavior.translucent, + onTapDown: (d) { + if (widget.enablePicking) { + print("PICK"); + widget.controller.pick( + d.globalPosition.dx.toInt(), d.globalPosition.dy.toInt()); + } + }, onDoubleTap: () { setState(() { _rotateOnPointerMove = !_rotateOnPointerMove; diff --git a/macos/flutter_filament.podspec b/macos/flutter_filament.podspec index 7853340f..0bd55cb7 100644 --- a/macos/flutter_filament.podspec +++ b/macos/flutter_filament.podspec @@ -33,7 +33,7 @@ A new Flutter plugin project. s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', "CLANG_CXX_LANGUAGE_STANDARD" => "c++17", - 'OTHER_CXXFLAGS' => '"--std=c++17" "-fmodules" "-fcxx-modules" "-fvisibility=default" "$(inherited)"', + 'OTHER_CXXFLAGS' => '"--std=c++17" "-fmodules" "-fcxx-modules" "-fvisibility=default" "-Wno-documentation-deprecated-sync" "$(inherited)"', 'OTHER_CFLAGS' => '"-fvisibility=default" "$(inherited)"', 'USER_HEADER_SEARCH_PATHS' => '"${PODS_TARGET_SRCROOT}/include" "${PODS_TARGET_SRCROOT}/include/filament" "$(inherited)"', 'ALWAYS_SEARCH_USER_PATHS' => 'YES', From fda70fb6d1b59a6fb1a08cb0bd2da948225f7ea2 Mon Sep 17 00:00:00 2001 From: Nick Fisher Date: Wed, 6 Mar 2024 19:20:02 +1100 Subject: [PATCH 22/52] use std::vector instead of dynamic array to stop MSVC complaining --- ios/src/SceneManager.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ios/src/SceneManager.cpp b/ios/src/SceneManager.cpp index 01f312c7..74ac4ea8 100644 --- a/ios/src/SceneManager.cpp +++ b/ios/src/SceneManager.cpp @@ -206,8 +206,8 @@ namespace flutter_filament FilamentAsset *asset = nullptr; if(numInstances > 1) { - FilamentInstance* instances[numInstances]; - asset = _assetLoader->createInstancedAsset((const uint8_t *)data, length, instances, numInstances); + std::vector instances(numInstances); + asset = _assetLoader->createInstancedAsset((const uint8_t *)data, length, instances.data(), numInstances); } else { asset = _assetLoader->createAsset( (const uint8_t *)data, length); From 732a6d05272d381980e6287658a83e57e9d9d560 Mon Sep 17 00:00:00 2001 From: Nick Fisher Date: Wed, 6 Mar 2024 19:19:13 +1100 Subject: [PATCH 23/52] reduce default Windows color/depth buffer to 24/16 --- windows/wgl_context.cpp | 22 +++++++--------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/windows/wgl_context.cpp b/windows/wgl_context.cpp index bf377e8b..6c404ff5 100644 --- a/windows/wgl_context.cpp +++ b/windows/wgl_context.cpp @@ -25,9 +25,9 @@ WGLContext::WGLContext(flutter::PluginRegistrarWindows *pluginRegistrar, PIXELFORMATDESCRIPTOR pfd = { sizeof(PIXELFORMATDESCRIPTOR), 1, - PFD_DRAW_TO_BITMAP | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER, // Flags + PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER, // Flags PFD_TYPE_RGBA, // The kind of framebuffer. RGBA or palette. - 32, // Colordepth of the framebuffer. + 24, // Colordepth of the framebuffer. 0, 0, 0, @@ -41,7 +41,7 @@ WGLContext::WGLContext(flutter::PluginRegistrarWindows *pluginRegistrar, 0, 0, 0, - 32, // Number of bits for the depthbuffer + 16, // Number of bits for the depthbuffer 0, // Number of bits for the stencilbuffer 0, // Number of Aux buffers in the framebuffer. PFD_MAIN_PLANE, @@ -97,7 +97,9 @@ WGLContext::WGLContext(flutter::PluginRegistrarWindows *pluginRegistrar, } void WGLContext::ResizeRenderingSurface(uint32_t width, uint32_t height, uint32_t left, uint32_t top) { + #if WGL_USE_BACKING_WINDOW _backingWindow->Resize(width, height, left, top); + #endif } void WGLContext::CreateRenderingSurface( @@ -128,20 +130,10 @@ void WGLContext::CreateRenderingSurface( "attempting to create a new one."); } else { - _active = std::make_unique( + auto active = std::make_unique( _pluginRegistrar, _textureRegistrar, std::move(result), width, height, _context); - - if (_active->flutterTextureId != -1) { - std::vector resultList; - resultList.push_back(flutter::EncodableValue((int64_t) nullptr)); - resultList.push_back(flutter::EncodableValue((int64_t) nullptr)); - resultList.push_back(flutter::EncodableValue((int64_t) nullptr)); - resultList.push_back(flutter::EncodableValue((int64_t)sharedContext)); - result->Success(resultList); - } else { - result->Error("NO_FLUTTER_TEXTURE", "Unknown error registering texture with Flutter.", nullptr); - } + _active = std::move(active); } #endif } From 150f409ae1710d2b11cfc4f23a2e7742dd4a49df Mon Sep 17 00:00:00 2001 From: Nick Fisher Date: Wed, 6 Mar 2024 19:18:24 +1100 Subject: [PATCH 24/52] don't mark texture frame available when USE_BACKING_WINDOW is true --- windows/flutter_filament_plugin.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/windows/flutter_filament_plugin.cpp b/windows/flutter_filament_plugin.cpp index 7c3d4d96..e9b0112c 100644 --- a/windows/flutter_filament_plugin.cpp +++ b/windows/flutter_filament_plugin.cpp @@ -157,7 +157,9 @@ void FlutterFilamentPlugin::RenderCallback() { #ifdef USE_ANGLE _active->RenderCallback(); #endif +#if !WGL_USE_BACKING_WINDOW _textureRegistrar->MarkTextureFrameAvailable(flutterTextureId); +#endif } } From 6e7e918125a8c0550a582e53f668d72761159d30 Mon Sep 17 00:00:00 2001 From: Nick Fisher Date: Wed, 6 Mar 2024 19:17:27 +1100 Subject: [PATCH 25/52] only conditionally add WGL_USE_BACKING_WINDOW --- windows/CMakeLists.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/windows/CMakeLists.txt b/windows/CMakeLists.txt index 20bb2560..3180cb8c 100644 --- a/windows/CMakeLists.txt +++ b/windows/CMakeLists.txt @@ -29,7 +29,9 @@ if(USE_ANGLE) add_compile_definitions(USE_ANGLE) list(APPEND PLUGIN_SOURCES "flutter_angle_texture.cpp" "egl_context.cpp" ) else() - add_compile_definitions(WGL_USE_BACKING_WINDOW) + if(WGL_USE_BACKING_WINDOW) + add_compile_definitions(WGL_USE_BACKING_WINDOW) + endif() list(APPEND PLUGIN_SOURCES "wgl_context.cpp" "opengl_texture_buffer.cpp" "backing_window.cpp") # if(WGL_USE_BACKING_WINDOW) # list(APPEND PLUGIN_SOURCES ) From c797cfc3b705dbd00b5f1013a0a36d9970756632 Mon Sep 17 00:00:00 2001 From: Nick Fisher Date: Wed, 6 Mar 2024 19:17:03 +1100 Subject: [PATCH 26/52] don't unnecessarily repaint FilamentWidget --- lib/widgets/filament_widget.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/widgets/filament_widget.dart b/lib/widgets/filament_widget.dart index b6343df3..e40f498c 100644 --- a/lib/widgets/filament_widget.dart +++ b/lib/widgets/filament_widget.dart @@ -305,5 +305,5 @@ class TransparencyPainter extends CustomPainter { } @override - bool shouldRepaint(covariant CustomPainter oldDelegate) => true; + bool shouldRepaint(covariant CustomPainter oldDelegate) => false; } From 89c7f7ddc300e9b4d1d07828279b2f3f48b18530 Mon Sep 17 00:00:00 2001 From: Nick Fisher Date: Wed, 6 Mar 2024 20:06:05 +1100 Subject: [PATCH 27/52] hardcode frame interval to 1 --- ios/src/FilamentViewer.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ios/src/FilamentViewer.cpp b/ios/src/FilamentViewer.cpp index e1a7d358..08ec3d32 100644 --- a/ios/src/FilamentViewer.cpp +++ b/ios/src/FilamentViewer.cpp @@ -335,7 +335,7 @@ namespace flutter_filament { _frameInterval = frameInterval; Renderer::FrameRateOptions fro; - fro.interval = frameInterval; + fro.interval = 1; //frameInterval; _renderer->setFrameRateOptions(fro); Log("Set framerate interval to %f", frameInterval); } @@ -1045,7 +1045,7 @@ namespace flutter_filament if (secsSinceLastFpsCheck >= 1) { auto fps = _frameCount / secsSinceLastFpsCheck; - Log("%ffps", fps); + Log("%ffps (%d skipped)", fps, _skippedFrames); _frameCount = 0; _skippedFrames = 0; _fpsCounterStartTime = now; From 8ef166d9b8e7fcfab7d28f810281783ec09f9d4e Mon Sep 17 00:00:00 2001 From: Nick Fisher Date: Wed, 6 Mar 2024 19:19:37 +1100 Subject: [PATCH 28/52] move Flutter result checks to OpenGLTextureBuffer --- windows/opengl_texture_buffer.cpp | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/windows/opengl_texture_buffer.cpp b/windows/opengl_texture_buffer.cpp index f261abcb..4b501b21 100644 --- a/windows/opengl_texture_buffer.cpp +++ b/windows/opengl_texture_buffer.cpp @@ -117,11 +117,15 @@ OpenGLTextureBuffer::OpenGLTextureBuffer( flutterTextureId = textureRegistrar->RegisterTexture(texture.get()); std::cout << "Registered Flutter texture ID " << flutterTextureId << std::endl; - std::vector resultList; - resultList.push_back(flutter::EncodableValue(flutterTextureId)); - resultList.push_back(flutter::EncodableValue((int64_t) nullptr)); - resultList.push_back(flutter::EncodableValue(glTextureId)); - result->Success(resultList); + + if (flutterTextureId != -1) { + std::vector resultList; + resultList.push_back(flutter::EncodableValue(flutterTextureId)); + resultList.push_back(flutter::EncodableValue((int64_t) nullptr)); + resultList.push_back(flutter::EncodableValue(glTextureId)); + resultList.push_back(flutter::EncodableValue((int64_t)_context)); + result->Success(resultList); + } } OpenGLTextureBuffer::~OpenGLTextureBuffer() { From f0c48d7f0a1a41dbe5b02484993dd1d0d8ec4f3c Mon Sep 17 00:00:00 2001 From: Nick Fisher Date: Thu, 7 Mar 2024 12:19:01 +1100 Subject: [PATCH 29/52] cleanup EGL/D3D texture rendering --- lib/filament_controller_ffi.dart | 3 +- windows/backing_window.cpp | 1 - windows/egl_context.cpp | 61 +++++++++++++++++++---------- windows/egl_context.h | 23 ++++++++--- windows/flutter_angle_texture.cpp | 1 + windows/flutter_angle_texture.h | 3 +- windows/flutter_filament_plugin.cpp | 24 +++++------- windows/flutter_filament_plugin.h | 4 +- windows/flutter_render_context.h | 6 +++ windows/wgl_context.cpp | 2 +- windows/wgl_context.h | 3 -- 11 files changed, 81 insertions(+), 50 deletions(-) diff --git a/lib/filament_controller_ffi.dart b/lib/filament_controller_ffi.dart index 5a5facc8..51f633d8 100644 --- a/lib/filament_controller_ffi.dart +++ b/lib/filament_controller_ffi.dart @@ -297,6 +297,8 @@ class FilamentControllerFFI extends FilamentController { throw Exception("Failed to get resource loader"); } + var renderingSurface = await _createRenderingSurface(); + if (Platform.isWindows && requiresTextureWidget) { _driver = Pointer.fromAddress( await _channel.invokeMethod("getDriverPlatform")); @@ -309,7 +311,6 @@ class FilamentControllerFFI extends FilamentController { var renderCallbackOwner = Pointer.fromAddress(renderCallbackResult[1]); - var renderingSurface = await _createRenderingSurface(); print("Got rendering surface"); diff --git a/windows/backing_window.cpp b/windows/backing_window.cpp index bc314c26..e9668489 100644 --- a/windows/backing_window.cpp +++ b/windows/backing_window.cpp @@ -5,7 +5,6 @@ #include #include -#include #include #include #include diff --git a/windows/egl_context.cpp b/windows/egl_context.cpp index 1e7a87e6..cec41498 100644 --- a/windows/egl_context.cpp +++ b/windows/egl_context.cpp @@ -1,8 +1,17 @@ + #include "egl_context.h" +#define FILAMENT_USE_EXTERNAL_GLES3 +#include + +#pragma comment(lib, "dwmapi.lib") +#pragma comment(lib, "comctl32.lib") + namespace flutter_filament { -EGLContext::EGLContext(flutter::PluginRegistrarWindows* pluginRegistrar, flutter::TextureRegistrar* textureRegistrar); { +FlutterEGLContext::FlutterEGLContext( + flutter::PluginRegistrarWindows* pluginRegistrar, + flutter::TextureRegistrar* textureRegistrar) : FlutterRenderContext(pluginRegistrar, textureRegistrar) { _platform = new filament::backend::PlatformEGL(); @@ -34,7 +43,7 @@ EGLContext::EGLContext(flutter::PluginRegistrarWindows* pluginRegistrar, flutter dxgi->Release(); if (!adapter_) { std::cout << "Failed to locate default D3D adapter" << std::endl; - return false; + return; } DXGI_ADAPTER_DESC adapter_desc_; @@ -49,7 +58,7 @@ EGLContext::EGLContext(flutter::PluginRegistrarWindows* pluginRegistrar, flutter if (FAILED(hr)) { std::cout << "Failed to create D3D device" << std::endl; - return false; + return; } Microsoft::WRL::ComPtr dxgi_device = nullptr; @@ -74,7 +83,7 @@ EGLContext::EGLContext(flutter::PluginRegistrarWindows* pluginRegistrar, flutter EGLBoolean bindAPI = eglBindAPI(EGL_OPENGL_ES_API); if (UTILS_UNLIKELY(!bindAPI)) { std::cout << "eglBindAPI EGL_OPENGL_ES_API failed" << std::endl; - return false; + return; } _eglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY); @@ -106,10 +115,10 @@ EGLContext::EGLContext(flutter::PluginRegistrarWindows* pluginRegistrar, flutter if (UTILS_UNLIKELY(!initialized)) { std::cout << "eglInitialize failed" << std::endl; - return false; + return; } - importGLESExtensionsEntryPoints(); + glext::importGLESExtensionsEntryPoints(); EGLint configsCount; @@ -126,53 +135,65 @@ EGLContext::EGLContext(flutter::PluginRegistrarWindows* pluginRegistrar, flutter // find an opaque config if (!eglChooseConfig(_eglDisplay, configAttribs, &_eglConfig, 1, &configsCount)) { - return false; + return; } - _context = (void *)eglCreateContext(_eglDisplay, _eglConfig, EGL_NO_CONTEXT, - contextAttribs); + auto ctx = eglCreateContext(_eglDisplay, _eglConfig, EGL_NO_CONTEXT,contextAttribs); + _context = (void*)ctx; if (UTILS_UNLIKELY(_context == EGL_NO_CONTEXT)) { - return false; + return; } } -EGLContext::CreateRenderingSurface( +void FlutterEGLContext::CreateRenderingSurface( uint32_t width, uint32_t height, std::unique_ptr> result, uint32_t left, uint32_t top ) { - importGLESExtensionsEntryPoints(); + + glext::importGLESExtensionsEntryPoints(); if(left != 0 || top != 0) { result->Error("ERROR", "Rendering with EGL uses a Texture render target/Flutter widget and does not need a window offset."); - return false; + return; } if (_active.get()) { result->Error("ERROR", "Texture already exists. You must call destroyTexture before " "attempting to create a new one."); - return false; + return; } - _active = std::make_unique( + std::unique_ptr active = std::make_unique( _pluginRegistrar, _textureRegistrar, std::move(result), width, height, _D3D11Device, _D3D11DeviceContext, _eglConfig, _eglDisplay, _context, [=](size_t width, size_t height) { + std::cout << "RESIZE" << std::endl; std::vector list; list.push_back((int64_t)width); list.push_back((int64_t)height); - auto val = std::make_unique(list); - this->_channel->InvokeMethod("resize", std::move(val), nullptr); + // auto val = std::make_unique(list); + // this->_channel->InvokeMethod("resize", std::move(val), nullptr); }); + _active = std::move(active); - return _active->flutterTextureId != -1; } -EGLContext::GetSharedContext() { - return (void*)_eglContext; +void FlutterEGLContext::RenderCallback() { + if(_active.get()) { + ((FlutterAngleTexture*)_active.get())->RenderCallback(); + } +} + +void* FlutterEGLContext::GetSharedContext() { + return (void*)_context; +} + +void* FlutterEGLContext::GetPlatform() { + return (void*)_platform; } } \ No newline at end of file diff --git a/windows/egl_context.h b/windows/egl_context.h index 454cae0f..c77b7216 100644 --- a/windows/egl_context.h +++ b/windows/egl_context.h @@ -1,24 +1,35 @@ #ifndef _EGL_CONTEXT_H #define _EGL_CONTEXT_H +#include + +#include +#include +#include +#include + #include "flutter_angle_texture.h" #include "backend/platforms/PlatformEGL.h" +#include "flutter_render_context.h" namespace flutter_filament { -class EGLContext : public FlutterRenderingContext { +class FlutterEGLContext : public FlutterRenderContext { public: - EGLContext(flutter::PluginRegistrarWindows* pluginRegistrar, flutter::TextureRegistrar* textureRegistrar); + FlutterEGLContext(flutter::PluginRegistrarWindows* pluginRegistrar, flutter::TextureRegistrar* textureRegistrar); + void* GetSharedContext(); + void RenderCallback(); + void* GetPlatform(); + void CreateRenderingSurface(uint32_t width, uint32_t height, std::unique_ptr> result, uint32_t left, uint32_t top ); + private: - EGLContext _context = NULL; + void* _context = nullptr; EGLConfig _eglConfig = NULL; EGLDisplay _eglDisplay = NULL; - std::unique_ptr _active = nullptr; - std::unique_ptr _inactive = nullptr; ID3D11Device* _D3D11Device = nullptr; ID3D11DeviceContext* _D3D11DeviceContext = nullptr; filament::backend::Platform* _platform = nullptr; -} +}; } diff --git a/windows/flutter_angle_texture.cpp b/windows/flutter_angle_texture.cpp index 96e8d413..832e20e6 100644 --- a/windows/flutter_angle_texture.cpp +++ b/windows/flutter_angle_texture.cpp @@ -236,6 +236,7 @@ FlutterAngleTexture::FlutterAngleTexture( resultList.push_back(flutter::EncodableValue(flutterTextureId)); resultList.push_back(flutter::EncodableValue((int64_t) nullptr)); resultList.push_back(flutter::EncodableValue(glTextureId)); + resultList.push_back(flutter::EncodableValue((int64_t) eglContext)); result->Success(resultList); } diff --git a/windows/flutter_angle_texture.h b/windows/flutter_angle_texture.h index 479d0e1b..fd250887 100644 --- a/windows/flutter_angle_texture.h +++ b/windows/flutter_angle_texture.h @@ -28,7 +28,7 @@ typedef uint32_t GLuint; namespace flutter_filament { -class FlutterAngleTexture : FlutterTextureBuffer { +class FlutterAngleTexture : public FlutterTextureBuffer { public: FlutterAngleTexture( flutter::PluginRegistrarWindows* pluginRegistrar, @@ -49,6 +49,7 @@ class FlutterAngleTexture : FlutterTextureBuffer { GLuint glTextureId = 0; std::unique_ptr texture; + int64_t flutterTextureId = 0; private: flutter::PluginRegistrarWindows* _pluginRegistrar; diff --git a/windows/flutter_filament_plugin.cpp b/windows/flutter_filament_plugin.cpp index e9b0112c..3670da36 100644 --- a/windows/flutter_filament_plugin.cpp +++ b/windows/flutter_filament_plugin.cpp @@ -3,11 +3,7 @@ #include "flutter_filament_plugin.h" -// This must be included before many other Windows headers. -#include - -// For getPlatformVersion; remove unless needed for your plugin implementation. -#include +#include #include #include @@ -18,7 +14,7 @@ #include #include #include -#include + #include #include #include @@ -31,11 +27,6 @@ #include "FlutterFilamentApi.h" -#include -#include -#include -#include - #include "flutter_render_context.h" #if USE_ANGLE @@ -154,8 +145,12 @@ void render_callback(void *owner) { void FlutterFilamentPlugin::RenderCallback() { if (_context) { auto flutterTextureId = _context->GetFlutterTextureId(); + if(flutterTextureId == -1) { + std::cout << "Bad texture" << std::endl; + return; + } #ifdef USE_ANGLE - _active->RenderCallback(); + _context->RenderCallback(); #endif #if !WGL_USE_BACKING_WINDOW _textureRegistrar->MarkTextureFrameAvailable(flutterTextureId); @@ -185,7 +180,7 @@ void FlutterFilamentPlugin::CreateTexture( // this will be used to create a backing texture and passed to Filament if (!_context) { #ifdef USE_ANGLE - _context = std::make_unique(_pluginRegistrar); + _context = std::make_unique(_pluginRegistrar, _textureRegistrar); #else _context = std::make_unique(_pluginRegistrar, _textureRegistrar); #endif @@ -197,7 +192,6 @@ void FlutterFilamentPlugin::DestroyTexture( const flutter::MethodCall &methodCall, std::unique_ptr> result) { - const auto *flutterTextureId = std::get_if(methodCall.arguments()); if (!flutterTextureId) { @@ -263,7 +257,7 @@ void FlutterFilamentPlugin::HandleMethodCall( result->Success(resultList); } else if (methodCall.method_name() == "getDriverPlatform") { #ifdef USE_ANGLE - result->Success(flutter::EncodableValue((int64_t)_platform)); + result->Success(flutter::EncodableValue((int64_t)_context->GetPlatform())); #else result->Success(flutter::EncodableValue((int64_t) nullptr)); #endif diff --git a/windows/flutter_filament_plugin.h b/windows/flutter_filament_plugin.h index 95502014..3b918fc1 100644 --- a/windows/flutter_filament_plugin.h +++ b/windows/flutter_filament_plugin.h @@ -16,7 +16,7 @@ #include "FlutterFilamentApi.h" -#if ANGLE +#if USE_ANGLE #include "egl_context.h" #else #include "wgl_context.h" @@ -60,7 +60,7 @@ public: private: #ifdef USE_ANGLE - std::unique_ptr _context = nullptr; + std::unique_ptr _context = nullptr; #else std::unique_ptr _context = nullptr; #endif diff --git a/windows/flutter_render_context.h b/windows/flutter_render_context.h index f1ebd238..42e2fe70 100644 --- a/windows/flutter_render_context.h +++ b/windows/flutter_render_context.h @@ -12,6 +12,7 @@ namespace flutter_filament { class FlutterRenderContext { public: + void CreateRenderingSurface(uint32_t width, uint32_t height, std::unique_ptr> result, uint32_t left, uint32_t top ); void DestroyTexture(std::unique_ptr> result) { @@ -33,11 +34,16 @@ namespace flutter_filament { }); } int64_t GetFlutterTextureId() { + if(!_active) { + return -1; + } return _active->flutterTextureId; } void* sharedContext = nullptr; protected: + FlutterRenderContext( flutter::PluginRegistrarWindows* pluginRegistrar, flutter::TextureRegistrar* textureRegistrar) : _pluginRegistrar(pluginRegistrar), _textureRegistrar(textureRegistrar) {}; + flutter::PluginRegistrarWindows* _pluginRegistrar; flutter::TextureRegistrar* _textureRegistrar; std::unique_ptr _active = nullptr; diff --git a/windows/wgl_context.cpp b/windows/wgl_context.cpp index 6c404ff5..344f7ecd 100644 --- a/windows/wgl_context.cpp +++ b/windows/wgl_context.cpp @@ -10,7 +10,7 @@ namespace flutter_filament { WGLContext::WGLContext(flutter::PluginRegistrarWindows *pluginRegistrar, flutter::TextureRegistrar *textureRegistrar) - : _pluginRegistrar(pluginRegistrar), _textureRegistrar(textureRegistrar) { + : FlutterRenderContext(pluginRegistrar, textureRegistrar) { auto hwnd = pluginRegistrar->GetView()->GetNativeWindow(); diff --git a/windows/wgl_context.h b/windows/wgl_context.h index e08e5f3d..33406180 100644 --- a/windows/wgl_context.h +++ b/windows/wgl_context.h @@ -20,9 +20,6 @@ namespace flutter_filament { uint32_t width, uint32_t height, uint32_t left, uint32_t top ); private: - - flutter::PluginRegistrarWindows* _pluginRegistrar = nullptr; - flutter::TextureRegistrar* _textureRegistrar = nullptr; HGLRC _context = NULL; #if WGL_USE_BACKING_WINDOW std::unique_ptr _backingWindow = nullptr; From 34d5478beb4c96ae56f3692e51118f9dbe042c19 Mon Sep 17 00:00:00 2001 From: Nick Fisher Date: Thu, 7 Mar 2024 16:42:57 +1100 Subject: [PATCH 30/52] disable postprocessing by default --- ios/src/FilamentViewer.cpp | 30 +++--------------------------- 1 file changed, 3 insertions(+), 27 deletions(-) diff --git a/ios/src/FilamentViewer.cpp b/ios/src/FilamentViewer.cpp index 08ec3d32..988b61e1 100644 --- a/ios/src/FilamentViewer.cpp +++ b/ios/src/FilamentViewer.cpp @@ -150,40 +150,27 @@ namespace flutter_filament _scene = _engine->createScene(); - Log("Scene created"); - utils::Entity camera = EntityManager::get().create(); _mainCamera = _engine->createCamera(camera); - Log("Main camera created"); _view = _engine->createView(); - Log("View created"); - setToneMapping(ToneMapping::ACES); - Log("Set tone mapping"); -#ifdef __EMSCRIPTEN__ - Log("Bloom is disabled on WebGL builds as it causes instability with certain drivers"); decltype(_view->getBloomOptions()) opts; opts.enabled = false; _view->setBloomOptions(opts); _view->setAmbientOcclusionOptions({.enabled = false}); - _view->setDynamicResolutionOptions({.enabled = false}); _view->setDithering(filament::Dithering::NONE); - _view->setAntiAliasing(filament::AntiAliasing::NONE); + setAntiAliasing(false, false, false); _view->setShadowingEnabled(false); _view->setScreenSpaceRefractionEnabled(false); - -#else - setBloom(0.6f); - Log("Set bloom"); -#endif - + setPostProcessing(false); + _view->setScene(_scene); _view->setCamera(_mainCamera); @@ -197,17 +184,6 @@ namespace flutter_filament const float sens = _mainCamera->getSensitivity(); Log("Camera aperture %f shutter %f sensitivity %f", aperture, shutterSpeed, sens); - - View::DynamicResolutionOptions options; - options.enabled = false; - // options.homogeneousScaling = homogeneousScaling; - // options.minScale = filament::math::float2{ minScale }; - // options.maxScale = filament::math::float2{ maxScale }; - // options.sharpness = sharpness; - // options.quality = View::QualityLevel::ULTRA; - _view->setDynamicResolutionOptions(options); - - setAntiAliasing(false, true, false); EntityManager &em = EntityManager::get(); From c34a1b0370cb1cd73ff6ea9ce568fa53b9741146 Mon Sep 17 00:00:00 2001 From: Nick Fisher Date: Thu, 7 Mar 2024 16:43:29 +1100 Subject: [PATCH 31/52] remove duplicate sharedContext --- windows/flutter_render_context.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/windows/flutter_render_context.h b/windows/flutter_render_context.h index 42e2fe70..16092891 100644 --- a/windows/flutter_render_context.h +++ b/windows/flutter_render_context.h @@ -39,8 +39,7 @@ namespace flutter_filament { } return _active->flutterTextureId; } - void* sharedContext = nullptr; - + protected: FlutterRenderContext( flutter::PluginRegistrarWindows* pluginRegistrar, flutter::TextureRegistrar* textureRegistrar) : _pluginRegistrar(pluginRegistrar), _textureRegistrar(textureRegistrar) {}; From 1fccaf19906283fa3d1cc6cf52e2e4465127a619 Mon Sep 17 00:00:00 2001 From: Nick Fisher Date: Thu, 7 Mar 2024 16:44:03 +1100 Subject: [PATCH 32/52] EGL logging --- windows/egl_context.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/windows/egl_context.cpp b/windows/egl_context.cpp index cec41498..342a4d5f 100644 --- a/windows/egl_context.cpp +++ b/windows/egl_context.cpp @@ -135,6 +135,7 @@ FlutterEGLContext::FlutterEGLContext( // find an opaque config if (!eglChooseConfig(_eglDisplay, configAttribs, &_eglConfig, 1, &configsCount)) { + std::cout << "Failed to find EGL config" << std::endl; return; } From 59d6b17f630a4a6556cb39da9e0f30a37908f400 Mon Sep 17 00:00:00 2001 From: Nick Fisher Date: Thu, 7 Mar 2024 16:44:27 +1100 Subject: [PATCH 33/52] remove duplicate ANGLE flutterTextureId --- windows/flutter_angle_texture.h | 1 - 1 file changed, 1 deletion(-) diff --git a/windows/flutter_angle_texture.h b/windows/flutter_angle_texture.h index fd250887..c207b7bb 100644 --- a/windows/flutter_angle_texture.h +++ b/windows/flutter_angle_texture.h @@ -49,7 +49,6 @@ class FlutterAngleTexture : public FlutterTextureBuffer { GLuint glTextureId = 0; std::unique_ptr texture; - int64_t flutterTextureId = 0; private: flutter::PluginRegistrarWindows* _pluginRegistrar; From a872cef4364fba8b4066a63d208f74aa819132c9 Mon Sep 17 00:00:00 2001 From: Nick Fisher Date: Thu, 7 Mar 2024 16:44:56 +1100 Subject: [PATCH 34/52] initialize flutterTextureId to -1 --- windows/flutter_texture_buffer.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/windows/flutter_texture_buffer.h b/windows/flutter_texture_buffer.h index cde6a228..3bf0c342 100644 --- a/windows/flutter_texture_buffer.h +++ b/windows/flutter_texture_buffer.h @@ -11,7 +11,7 @@ namespace flutter_filament { class FlutterTextureBuffer { public: - int64_t flutterTextureId = 0; + int64_t flutterTextureId = -1; }; } From b8aab1d0300df4ce646ca576460ecd9e8ad84e75 Mon Sep 17 00:00:00 2001 From: Nick Fisher Date: Thu, 7 Mar 2024 17:16:03 +1100 Subject: [PATCH 35/52] turn PP off by default --- ios/src/FilamentViewer.cpp | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/ios/src/FilamentViewer.cpp b/ios/src/FilamentViewer.cpp index 988b61e1..6aeb9a5a 100644 --- a/ios/src/FilamentViewer.cpp +++ b/ios/src/FilamentViewer.cpp @@ -158,9 +158,9 @@ namespace flutter_filament setToneMapping(ToneMapping::ACES); - decltype(_view->getBloomOptions()) opts; - opts.enabled = false; - _view->setBloomOptions(opts); + // there's a glitch on certain iGPUs where nothing will render when postprocessing is enabled and bloom is disabled + // set bloom to a small value here + setBloom(0.01); _view->setAmbientOcclusionOptions({.enabled = false}); _view->setDynamicResolutionOptions({.enabled = false}); @@ -170,7 +170,7 @@ namespace flutter_filament _view->setShadowingEnabled(false); _view->setScreenSpaceRefractionEnabled(false); setPostProcessing(false); - + _view->setScene(_scene); _view->setCamera(_mainCamera); @@ -268,14 +268,15 @@ namespace flutter_filament void FilamentViewer::setBloom(float strength) { + decltype(_view->getBloomOptions()) opts; #ifdef __EMSCRIPTEN__ + opts.enabled = false; Log("Bloom is disabled on WebGL builds as it causes instability with certain drivers. setBloom will be ignored"); #else - decltype(_view->getBloomOptions()) opts; opts.enabled = true; opts.strength = strength; - _view->setBloomOptions(opts); #endif + _view->setBloomOptions(opts); } void FilamentViewer::setToneMapping(ToneMapping toneMapping) From 4377a557472f4d22d60e63dbc5ac43289a987332 Mon Sep 17 00:00:00 2001 From: Nick Fisher Date: Thu, 7 Mar 2024 17:16:48 +1100 Subject: [PATCH 36/52] no need to create WGL context explicitly - defer to Engine --- windows/wgl_context.cpp | 85 +---------------------------------------- 1 file changed, 1 insertion(+), 84 deletions(-) diff --git a/windows/wgl_context.cpp b/windows/wgl_context.cpp index 344f7ecd..44e7209e 100644 --- a/windows/wgl_context.cpp +++ b/windows/wgl_context.cpp @@ -11,89 +11,6 @@ namespace flutter_filament { WGLContext::WGLContext(flutter::PluginRegistrarWindows *pluginRegistrar, flutter::TextureRegistrar *textureRegistrar) : FlutterRenderContext(pluginRegistrar, textureRegistrar) { - - auto hwnd = pluginRegistrar->GetView()->GetNativeWindow(); - - HDC whdc = GetDC(hwnd); - if (whdc == NULL) { - std::cout << "No device context for temporary window" << std::endl; - return; - } - - std::cout << "No GL context exists, creating" << std::endl; - - PIXELFORMATDESCRIPTOR pfd = { - sizeof(PIXELFORMATDESCRIPTOR), - 1, - PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER, // Flags - PFD_TYPE_RGBA, // The kind of framebuffer. RGBA or palette. - 24, // Colordepth of the framebuffer. - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 16, // Number of bits for the depthbuffer - 0, // Number of bits for the stencilbuffer - 0, // Number of Aux buffers in the framebuffer. - PFD_MAIN_PLANE, - 0, - 0, - 0, - 0}; - - int pixelFormat = ChoosePixelFormat(whdc, &pfd); - SetPixelFormat(whdc, pixelFormat, &pfd); - - // We need a tmp context to retrieve and call wglCreateContextAttribsARB. - HGLRC tempContext = wglCreateContext(whdc); - if (!wglMakeCurrent(whdc, tempContext)) { - std::cout << "Failed to acquire temporary context" << std::endl; - return; - } - - GLenum err = glGetError(); - - if (err != GL_NO_ERROR) { - std::cout << "GL Error @ 455 %d" << std::endl; - return; - } - - PFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribs = nullptr; - - wglCreateContextAttribs = - (PFNWGLCREATECONTEXTATTRIBSARBPROC)wglGetProcAddress( - "wglCreateContextAttribsARB"); - - if (!wglCreateContextAttribs) { - std::cout << "Failed to resolve wglCreateContextAttribsARB" << std::endl; - return; - } - - for (int minor = 5; minor >= 1; minor--) { - std::vector mAttribs = {WGL_CONTEXT_MAJOR_VERSION_ARB, 4, - WGL_CONTEXT_MINOR_VERSION_ARB, minor, 0}; - _context = wglCreateContextAttribs(whdc, nullptr, mAttribs.data()); - if (_context) { - break; - } - } - - wglMakeCurrent(NULL, NULL); - wglDeleteContext(tempContext); - - if (!_context || !wglMakeCurrent(whdc, _context)) { - std::cout << "Failed to create OpenGL context." << std::endl; - return; - } } void WGLContext::ResizeRenderingSurface(uint32_t width, uint32_t height, uint32_t left, uint32_t top) { @@ -118,7 +35,7 @@ void WGLContext::CreateRenderingSurface( resultList.push_back( flutter::EncodableValue((int64_t)_backingWindow->GetHandle())); resultList.push_back(flutter::EncodableValue((int64_t) nullptr)); - resultList.push_back(flutter::EncodableValue((int64_t)sharedContext)); + resultList.push_back(flutter::EncodableValue((int64_t)_context)); result->Success(resultList); #else if(left != 0 || top != 0) { From deb3bac35d003a64a3e641a6b5fcf829732555ae Mon Sep 17 00:00:00 2001 From: Nick Fisher Date: Thu, 7 Mar 2024 18:27:09 +1100 Subject: [PATCH 37/52] create new WGL context when WGL_USE_BACKING_WINDOW is false --- windows/wgl_context.cpp | 87 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) diff --git a/windows/wgl_context.cpp b/windows/wgl_context.cpp index 44e7209e..b9d88737 100644 --- a/windows/wgl_context.cpp +++ b/windows/wgl_context.cpp @@ -11,6 +11,93 @@ namespace flutter_filament { WGLContext::WGLContext(flutter::PluginRegistrarWindows *pluginRegistrar, flutter::TextureRegistrar *textureRegistrar) : FlutterRenderContext(pluginRegistrar, textureRegistrar) { + + #if WGL_USE_BACKING_WINDOW + return; + #endif + + auto hwnd = pluginRegistrar->GetView()->GetNativeWindow(); + + HDC whdc = GetDC(hwnd); + if (whdc == NULL) { + std::cout << "No device context for temporary window" << std::endl; + return; + } + + std::cout << "No GL context exists, creating" << std::endl; + + PIXELFORMATDESCRIPTOR pfd = { + sizeof(PIXELFORMATDESCRIPTOR), + 1, + PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER, // Flags + PFD_TYPE_RGBA, // The kind of framebuffer. RGBA or palette. + 24, // Colordepth of the framebuffer. + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 16, // Number of bits for the depthbuffer + 0, // Number of bits for the stencilbuffer + 0, // Number of Aux buffers in the framebuffer. + PFD_MAIN_PLANE, + 0, + 0, + 0, + 0}; + + int pixelFormat = ChoosePixelFormat(whdc, &pfd); + SetPixelFormat(whdc, pixelFormat, &pfd); + + // We need a tmp context to retrieve and call wglCreateContextAttribsARB. + HGLRC tempContext = wglCreateContext(whdc); + if (!wglMakeCurrent(whdc, tempContext)) { + std::cout << "Failed to acquire temporary context" << std::endl; + return; + } + + GLenum err = glGetError(); + + if (err != GL_NO_ERROR) { + std::cout << "GL Error @ 455 %d" << std::endl; + return; + } + + PFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribs = nullptr; + + wglCreateContextAttribs = + (PFNWGLCREATECONTEXTATTRIBSARBPROC)wglGetProcAddress( + "wglCreateContextAttribsARB"); + + if (!wglCreateContextAttribs) { + std::cout << "Failed to resolve wglCreateContextAttribsARB" << std::endl; + return; + } + + for (int minor = 5; minor >= 1; minor--) { + std::vector mAttribs = {WGL_CONTEXT_MAJOR_VERSION_ARB, 4, + WGL_CONTEXT_MINOR_VERSION_ARB, minor, 0}; + _context = wglCreateContextAttribs(whdc, nullptr, mAttribs.data()); + if (_context) { + break; + } + } + + wglMakeCurrent(NULL, NULL); + wglDeleteContext(tempContext); + + if (!_context || !wglMakeCurrent(whdc, _context)) { + std::cout << "Failed to create OpenGL context." << std::endl; + return; + } } void WGLContext::ResizeRenderingSurface(uint32_t width, uint32_t height, uint32_t left, uint32_t top) { From 684d758bd939c37b162fd69d81a96db71127d4e4 Mon Sep 17 00:00:00 2001 From: Nick Fisher Date: Thu, 7 Mar 2024 18:30:09 +1100 Subject: [PATCH 38/52] set pixel ratio before creating rect --- lib/filament_controller_ffi.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/filament_controller_ffi.dart b/lib/filament_controller_ffi.dart index 51f633d8..a5b140ed 100644 --- a/lib/filament_controller_ffi.dart +++ b/lib/filament_controller_ffi.dart @@ -146,12 +146,13 @@ class FilamentControllerFFI extends FilamentController { @override Future setDimensions(Rect rect, double pixelRatio) async { + _pixelRatio = pixelRatio; this._rect.value = Rect.fromLTWH( (rect.left * _pixelRatio).floor().toDouble(), rect.top * _pixelRatio.floor().toDouble(), (rect.width * _pixelRatio).ceil().toDouble(), (rect.height * _pixelRatio).ceil().toDouble()); - _pixelRatio = pixelRatio; + if (!_rectCompleter.isCompleted) { _rectCompleter.complete(this._rect.value); } From 3cbf796248e9e3b38959beb69eab77729c0d8b13 Mon Sep 17 00:00:00 2001 From: Nick Fisher Date: Fri, 8 Mar 2024 18:00:30 +0800 Subject: [PATCH 39/52] pass through scaleStart/Update/End to mobile gesture detector --- lib/filament_controller_ffi.dart | 5 ++-- lib/widgets/filament_gesture_detector.dart | 23 ++++++++++----- .../filament_gesture_detector_mobile.dart | 29 ++++++++++++++++--- 3 files changed, 44 insertions(+), 13 deletions(-) diff --git a/lib/filament_controller_ffi.dart b/lib/filament_controller_ffi.dart index 5a5facc8..b7279374 100644 --- a/lib/filament_controller_ffi.dart +++ b/lib/filament_controller_ffi.dart @@ -146,12 +146,13 @@ class FilamentControllerFFI extends FilamentController { @override Future setDimensions(Rect rect, double pixelRatio) async { + _pixelRatio = pixelRatio; this._rect.value = Rect.fromLTWH( (rect.left * _pixelRatio).floor().toDouble(), rect.top * _pixelRatio.floor().toDouble(), (rect.width * _pixelRatio).ceil().toDouble(), (rect.height * _pixelRatio).ceil().toDouble()); - _pixelRatio = pixelRatio; + print("Using dimensions ${_rect.value} (pixel ratio : $_pixelRatio)"); if (!_rectCompleter.isCompleted) { _rectCompleter.complete(this._rect.value); } @@ -1404,7 +1405,7 @@ class FilamentControllerFFI extends FilamentController { textureDetails.value!.height - (y * _pixelRatio).toInt(), outPtr); int wait = 0; while (outPtr.value == 0) { - await Future.delayed(const Duration(milliseconds: 32)); + await Future.delayed(const Duration(milliseconds: 16)); wait++; if (wait > 10) { allocator.free(outPtr); diff --git a/lib/widgets/filament_gesture_detector.dart b/lib/widgets/filament_gesture_detector.dart index 978cd7dc..fa697e67 100644 --- a/lib/widgets/filament_gesture_detector.dart +++ b/lib/widgets/filament_gesture_detector.dart @@ -41,13 +41,20 @@ class FilamentGestureDetector extends StatelessWidget { /// final bool enablePicking; + final void Function(ScaleStartDetails)? onScaleStart; + final void Function(ScaleUpdateDetails)? onScaleUpdate; + final void Function(ScaleEndDetails)? onScaleEnd; + const FilamentGestureDetector( {Key? key, required this.controller, this.child, this.showControlOverlay = false, this.enableCamera = true, - this.enablePicking = true}) + this.enablePicking = true, + this.onScaleStart, + this.onScaleUpdate, + this.onScaleEnd}) : super(key: key); @override @@ -64,12 +71,14 @@ class FilamentGestureDetector extends StatelessWidget { ); } else { return FilamentGestureDetectorMobile( - controller: controller, - child: child, - showControlOverlay: showControlOverlay, - enableCamera: enableCamera, - enablePicking: enablePicking, - ); + controller: controller, + child: child, + showControlOverlay: showControlOverlay, + enableCamera: enableCamera, + enablePicking: enablePicking, + onScaleStart: onScaleStart, + onScaleUpdate: onScaleUpdate, + onScaleEnd: onScaleEnd); } } } diff --git a/lib/widgets/filament_gesture_detector_mobile.dart b/lib/widgets/filament_gesture_detector_mobile.dart index 8865cc69..03484c5c 100644 --- a/lib/widgets/filament_gesture_detector_mobile.dart +++ b/lib/widgets/filament_gesture_detector_mobile.dart @@ -39,6 +39,10 @@ class FilamentGestureDetectorMobile extends StatefulWidget { final double zoomDelta; + final void Function(ScaleStartDetails)? onScaleStart; + final void Function(ScaleUpdateDetails)? onScaleUpdate; + final void Function(ScaleEndDetails)? onScaleEnd; + const FilamentGestureDetectorMobile( {Key? key, required this.controller, @@ -46,6 +50,9 @@ class FilamentGestureDetectorMobile extends StatefulWidget { this.showControlOverlay = false, this.enableCamera = true, this.enablePicking = true, + this.onScaleStart, + this.onScaleUpdate, + this.onScaleEnd, this.zoomDelta = 1}) : super(key: key); @@ -132,11 +139,12 @@ class _FilamentGestureDetectorMobileState child: GestureDetector( behavior: HitTestBehavior.translucent, onTapDown: (d) { - if (widget.enablePicking) { - print("PICK"); - widget.controller.pick( - d.globalPosition.dx.toInt(), d.globalPosition.dy.toInt()); + if (!widget.enablePicking) { + return; } + + widget.controller.pick( + d.globalPosition.dx.toInt(), d.globalPosition.dy.toInt()); }, onDoubleTap: () { setState(() { @@ -144,6 +152,10 @@ class _FilamentGestureDetectorMobileState }); }, onScaleStart: (d) async { + if (widget.onScaleStart != null) { + widget.onScaleStart!.call(d); + return; + } if (d.pointerCount == 2 && widget.enableCamera) { _scaling = true; await widget.controller.zoomBegin(); @@ -158,6 +170,10 @@ class _FilamentGestureDetectorMobileState } }, onScaleUpdate: (ScaleUpdateDetails d) async { + if (widget.onScaleUpdate != null) { + widget.onScaleUpdate!.call(d); + return; + } if (d.pointerCount == 2 && widget.enableCamera) { if (d.horizontalScale != _lastScale) { widget.controller.zoomUpdate( @@ -177,6 +193,11 @@ class _FilamentGestureDetectorMobileState } }, onScaleEnd: (d) async { + if (widget.onScaleEnd != null) { + widget.onScaleEnd!.call(d); + return; + } + if (d.pointerCount == 2 && widget.enableCamera) { widget.controller.zoomEnd(); } else if (!_scaling && widget.enableCamera) { From 7d0ae605d6363217e0b76de20dda6ef01df5ccc8 Mon Sep 17 00:00:00 2001 From: Nick Fisher Date: Fri, 8 Mar 2024 18:00:49 +0800 Subject: [PATCH 40/52] update kotlin/gradle versions --- android/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/android/build.gradle b/android/build.gradle index 9dd73e3c..43e9f803 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -2,14 +2,14 @@ group 'app.polyvox.filament' version '1.0-SNAPSHOT' buildscript { - ext.kotlin_version = '1.6.20' + ext.kotlin_version = '1.7.10' repositories { google() mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:4.1.0' + classpath 'com.android.tools.build:gradle:7.3.0' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" } } From 2cbaeabe6c3c11117f691562a5bf01046d8864b8 Mon Sep 17 00:00:00 2001 From: Nick Fisher Date: Fri, 8 Mar 2024 18:01:04 +0800 Subject: [PATCH 41/52] update kotlin/gradle versions --- android/gradle/wrapper/gradle-wrapper.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties index 3c9d0852..8048ce64 100644 --- a/android/gradle/wrapper/gradle-wrapper.properties +++ b/android/gradle/wrapper/gradle-wrapper.properties @@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.3-all.zip \ No newline at end of file From d6cc6c13fef048cac6882fe14b12e6127f311b9f Mon Sep 17 00:00:00 2001 From: Nick Fisher Date: Fri, 8 Mar 2024 18:01:22 +0800 Subject: [PATCH 42/52] update example app gradle --- example/android/app/build.gradle | 2 +- example/android/gradle.properties | 8 +++++++- example/android/gradle/wrapper/gradle-wrapper.properties | 3 +-- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/example/android/app/build.gradle b/example/android/app/build.gradle index 60b5af61..f5e12798 100644 --- a/example/android/app/build.gradle +++ b/example/android/app/build.gradle @@ -28,7 +28,7 @@ apply plugin: 'kotlin-android-extensions' apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" android { - compileSdkVersion 33 + compileSdkVersion 34 ndkVersion "25.2.9519653" compileOptions { diff --git a/example/android/gradle.properties b/example/android/gradle.properties index 94adc3a3..0387ff25 100644 --- a/example/android/gradle.properties +++ b/example/android/gradle.properties @@ -1,3 +1,9 @@ -org.gradle.jvmargs=-Xmx1536M +org.gradle.jvmargs=-Xmx1536M \ +--add-exports=java.base/sun.nio.ch=ALL-UNNAMED \ +--add-opens=java.base/java.lang=ALL-UNNAMED \ +--add-opens=java.base/java.lang.reflect=ALL-UNNAMED \ +--add-opens=java.base/java.io=ALL-UNNAMED \ +--add-exports=jdk.unsupported/sun.misc=ALL-UNNAMED android.useAndroidX=true android.enableJetifier=true + diff --git a/example/android/gradle/wrapper/gradle-wrapper.properties b/example/android/gradle/wrapper/gradle-wrapper.properties index bc6a58af..8048ce64 100644 --- a/example/android/gradle/wrapper/gradle-wrapper.properties +++ b/example/android/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,5 @@ -#Fri Jun 23 08:50:38 CEST 2017 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.3-all.zip \ No newline at end of file From f72aeb486c65166724dacee883d994828b6ded62 Mon Sep 17 00:00:00 2001 From: Nick Fisher Date: Fri, 8 Mar 2024 18:01:38 +0800 Subject: [PATCH 43/52] add release instructions for Android --- README.md | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 79a6a47f..3b29ae9c 100644 --- a/README.md +++ b/README.md @@ -422,11 +422,27 @@ make generate-background-material # Known issues -On Windows, loading a glTF (but NOT a glb) may crash due to a race condition between uploading resource data to GPU memory and being freed on the host side. +## Windows -This has been fixed in recent versions of Filament, but other bugs on Windows prevent upgrading. +Loading a glTF (but NOT a glb) may crash due to a race condition between uploading resource data to GPU memory and being freed on the host side. This has been fixed in recent versions of Filament, but other bugs on Windows prevent upgrading. Only workaround is to load a .glb file. -Only workaround is to load a .glb file. +## Android +In release mode, you must add the following to your `app/build.gradle`: + +``` + buildTypes { + release { + ... + shrinkResources false + minifyEnabled false + } + } +... +dependencies { + .... + implementation 'net.java.dev.jna:jna:5.10.0@aar' +} +``` # Thanks From 950d00dc3df5b7272a7b14d7c014680194020ec8 Mon Sep 17 00:00:00 2001 From: Nick Fisher Date: Fri, 8 Mar 2024 18:02:06 +0800 Subject: [PATCH 44/52] update CHANGELOG --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 66543ed9..bfb16b6a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,9 @@ * added basic collision detection + callbacks * added keyboard/mouse widgets + controls * `createViewer` now `awaits` the insertion of `FilamentWidget` so you no longer need to manually defer calling until after FilamentWidget has been rendered +* `setCameraRotation` now accepts a quaternion instead of an axis/angle +* instancing is now supported. +* `setBoneTransform` has been removed. To set the transform for a bone, just `addBoneAnimation` with a single frame. ## 0.6.0 From 13fa60e8de08011ee961ae5f9768374ce2131bbd Mon Sep 17 00:00:00 2001 From: Nick Fisher Date: Sat, 9 Mar 2024 11:17:25 +0800 Subject: [PATCH 45/52] pass callback to native pick() and remove pick_ffi --- ios/include/FilamentViewer.hpp | 2 +- ios/include/FlutterFilamentApi.h | 2 +- ios/include/FlutterFilamentFFIApi.h | 1 - ios/src/FilamentViewer.cpp | 5 ++--- ios/src/FlutterFilamentApi.cpp | 4 ++-- 5 files changed, 6 insertions(+), 8 deletions(-) diff --git a/ios/include/FilamentViewer.hpp b/ios/include/FilamentViewer.hpp index ce908396..b3905d74 100644 --- a/ios/include/FilamentViewer.hpp +++ b/ios/include/FilamentViewer.hpp @@ -125,7 +125,7 @@ namespace flutter_filament void scrollBegin(); void scrollUpdate(float x, float y, float delta); void scrollEnd(); - void pick(uint32_t x, uint32_t y, EntityId *entityId); + void pick(uint32_t x, uint32_t y, void (*callback)(EntityId entityId, int x, int y)); EntityId addLight(LightManager::Type t, float colour, float intensity, float posX, float posY, float posZ, float dirX, float dirY, float dirZ, bool shadows); void removeLight(EntityId entityId); diff --git a/ios/include/FlutterFilamentApi.h b/ios/include/FlutterFilamentApi.h index 6c084a69..958401ab 100644 --- a/ios/include/FlutterFilamentApi.h +++ b/ios/include/FlutterFilamentApi.h @@ -184,7 +184,7 @@ extern "C" FLUTTER_PLUGIN_EXPORT int reveal_mesh(void *sceneManager, EntityId asset, const char *meshName); FLUTTER_PLUGIN_EXPORT void set_post_processing(void *const viewer, bool enabled); FLUTTER_PLUGIN_EXPORT void set_antialiasing(void *const viewer, bool msaa, bool fxaa, bool taa); - FLUTTER_PLUGIN_EXPORT void pick(void *const viewer, int x, int y, EntityId *entityId); + FLUTTER_PLUGIN_EXPORT void pick(void *const viewer, int x, int y, void (*callback)(EntityId entityId, int x, int y)); FLUTTER_PLUGIN_EXPORT const char *get_name_for_entity(void *const sceneManager, const EntityId entityId); FLUTTER_PLUGIN_EXPORT EntityId find_child_entity_by_name(void *const sceneManager, const EntityId parent, const char* name); FLUTTER_PLUGIN_EXPORT int get_entity_count(void *const sceneManager, const EntityId target, bool renderableOnly); diff --git a/ios/include/FlutterFilamentFFIApi.h b/ios/include/FlutterFilamentFFIApi.h index 22dbb4a7..78cedcf5 100644 --- a/ios/include/FlutterFilamentFFIApi.h +++ b/ios/include/FlutterFilamentFFIApi.h @@ -113,7 +113,6 @@ extern "C" float frameLengthInMs, bool isModelSpace); FLUTTER_PLUGIN_EXPORT void set_post_processing_ffi(void *const viewer, bool enabled); - FLUTTER_PLUGIN_EXPORT void pick_ffi(void *const viewer, int x, int y, EntityId *entityId); FLUTTER_PLUGIN_EXPORT void reset_to_rest_pose_ffi(void *const sceneManager, EntityId entityId); FLUTTER_PLUGIN_EXPORT void ios_dummy_ffi(); FLUTTER_PLUGIN_EXPORT void create_geometry_ffi(void *const viewer, float *vertices, int numVertices, uint16_t *indices, int numIndices, const char *materialPath, void (*callback)(EntityId)); diff --git a/ios/src/FilamentViewer.cpp b/ios/src/FilamentViewer.cpp index 6aeb9a5a..621a682d 100644 --- a/ios/src/FilamentViewer.cpp +++ b/ios/src/FilamentViewer.cpp @@ -1442,12 +1442,11 @@ namespace flutter_filament _manipulator = nullptr; } - void FilamentViewer::pick(uint32_t x, uint32_t y, EntityId *entityId) + void FilamentViewer::pick(uint32_t x, uint32_t y, void (*callback)(EntityId entityId, int x, int y)) { _view->pick(x, y, [=](filament::View::PickingQueryResult const &result) { - - *entityId = Entity::smuggle(result.renderable); + callback(Entity::smuggle(result.renderable), x, y); }); } diff --git a/ios/src/FlutterFilamentApi.cpp b/ios/src/FlutterFilamentApi.cpp index bf92d288..cd23252b 100644 --- a/ios/src/FlutterFilamentApi.cpp +++ b/ios/src/FlutterFilamentApi.cpp @@ -548,9 +548,9 @@ extern "C" return ((SceneManager *)sceneManager)->reveal(asset, meshName); } - FLUTTER_PLUGIN_EXPORT void pick(void *const viewer, int x, int y, EntityId *entityId) + FLUTTER_PLUGIN_EXPORT void pick(void *const viewer, int x, int y, void (*callback)(EntityId entityId, int x, int y)) { - ((FilamentViewer *)viewer)->pick(static_cast(x), static_cast(y), static_cast(entityId)); + ((FilamentViewer *)viewer)->pick(static_cast(x), static_cast(y), callback); } FLUTTER_PLUGIN_EXPORT const char *get_name_for_entity(void *const sceneManager, const EntityId entityId) From 00c0e2526e297750d5c5af68ee403a284bf8e63b Mon Sep 17 00:00:00 2001 From: Nick Fisher Date: Sat, 9 Mar 2024 11:17:54 +0800 Subject: [PATCH 46/52] pass callback to native pick(), dont use _ffi version for get_animation_names --- lib/filament_controller_ffi.dart | 45 +++++++++++++++++--------------- 1 file changed, 24 insertions(+), 21 deletions(-) diff --git a/lib/filament_controller_ffi.dart b/lib/filament_controller_ffi.dart index f62f273b..7ef5b074 100644 --- a/lib/filament_controller_ffi.dart +++ b/lib/filament_controller_ffi.dart @@ -14,6 +14,7 @@ import 'package:flutter_filament/filament_controller.dart'; import 'package:flutter_filament/animations/animation_data.dart'; import 'package:flutter_filament/generated_bindings.dart'; +import 'package:flutter_filament/generated_bindings.dart' as gb; import 'package:flutter_filament/hardware/hardware_keyboard_listener.dart'; import 'package:flutter_filament/rendering_surface.dart'; @@ -114,6 +115,9 @@ class FilamentControllerFFI extends FilamentController { _usesBackingWindow = result; }); } + _onPickResultCallable = + NativeCallable.listener( + _onPickResult); } bool _rendering = false; @@ -312,7 +316,6 @@ class FilamentControllerFFI extends FilamentController { var renderCallbackOwner = Pointer.fromAddress(renderCallbackResult[1]); - print("Got rendering surface"); final uberarchivePtr = @@ -883,8 +886,7 @@ class FilamentControllerFFI extends FilamentController { var names = []; var outPtr = allocator(255); for (int i = 0; i < animationCount; i++) { - await _withVoidCallback((callback) => - get_animation_name_ffi(_sceneManager!, entity, outPtr, i, callback)); + get_animation_name(_sceneManager!, entity, outPtr, i); names.add(outPtr.cast().toDartString()); } allocator.free(outPtr); @@ -1073,7 +1075,7 @@ class FilamentControllerFFI extends FilamentController { if (_viewer == null) { throw Exception("No viewer available, ignoring"); } - play_animation_ffi( + play_animation( _sceneManager!, entity, index, loop, reverse, replaceActive, crossfade); } @@ -1394,29 +1396,30 @@ class FilamentControllerFFI extends FilamentController { return result.cast().toDartString(); } + final _pick = >{}; + + void _onPickResult(FilamentEntity entityId, int x, int y) { + _pickResultController.add(( + entity: entityId, + x: (x / _pixelRatio).toDouble(), + y: (textureDetails.value!.height - y) / _pixelRatio + )); + } + + late NativeCallable + _onPickResultCallable; + @override void pick(int x, int y) async { if (_viewer == null) { throw Exception("No viewer available, ignoring"); } - final outPtr = allocator(1); - outPtr.value = 0; - pick_ffi(_viewer!, (x * _pixelRatio).toInt(), - textureDetails.value!.height - (y * _pixelRatio).toInt(), outPtr); - int wait = 0; - while (outPtr.value == 0) { - await Future.delayed(const Duration(milliseconds: 16)); - wait++; - if (wait > 10) { - allocator.free(outPtr); - throw Exception("Failed to get picking result"); - } - } - var entityId = outPtr.value; - _pickResultController - .add((entity: entityId, x: x.toDouble(), y: y.toDouble())); - allocator.free(outPtr); + gb.pick( + _viewer!, + (x * _pixelRatio).toInt(), + textureDetails.value!.height - (y * _pixelRatio).toInt(), + _onPickResultCallable.nativeFunction); } @override From ed8bcd5794d6e02f21bd5704bb3f657636b1c054 Mon Sep 17 00:00:00 2001 From: Nick Fisher Date: Sat, 9 Mar 2024 11:18:18 +0800 Subject: [PATCH 47/52] remove logging --- ios/src/SceneManager.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/ios/src/SceneManager.cpp b/ios/src/SceneManager.cpp index 74ac4ea8..cdd44d98 100644 --- a/ios/src/SceneManager.cpp +++ b/ios/src/SceneManager.cpp @@ -1181,7 +1181,6 @@ namespace flutter_filament } void SceneManager::setParent(EntityId childEntityId, EntityId parentEntityId) { - // Log("Parenting child %d to %d", childEntityId, parentEntityId); auto& tm = _engine->getTransformManager(); const auto child = Entity::import(childEntityId); const auto parent = Entity::import(parentEntityId); From f02cd5d61160cb133f188a0a449cb9e77e6b7e84 Mon Sep 17 00:00:00 2001 From: Nick Fisher Date: Sat, 9 Mar 2024 11:22:12 +0800 Subject: [PATCH 48/52] rejig frame timings --- ios/src/FlutterFilamentFFIApi.cpp | 61 ++++++++++--------------------- 1 file changed, 19 insertions(+), 42 deletions(-) diff --git a/ios/src/FlutterFilamentFFIApi.cpp b/ios/src/FlutterFilamentFFIApi.cpp index a380d348..cb21a94d 100644 --- a/ios/src/FlutterFilamentFFIApi.cpp +++ b/ios/src/FlutterFilamentFFIApi.cpp @@ -40,28 +40,7 @@ public: _t = new std::thread([this]() { auto last = std::chrono::high_resolution_clock::now(); while (!_stop) { - - auto now = std::chrono::high_resolution_clock::now(); - - float elapsed = float(std::chrono::duration_cast(now - last).count()); - - while(elapsed < _frameIntervalInMilliseconds - 5) { - - std::function task; - std::unique_lock lock(_access); - if (_tasks.empty()) { - _cond.wait_for(lock, std::chrono::duration(1)); - now = std::chrono::high_resolution_clock::now(); - elapsed = float(std::chrono::duration_cast(now - last).count()); - continue; - } - task = std::move(_tasks.front()); - _tasks.pop_front(); - task(); - - now = std::chrono::high_resolution_clock::now(); - elapsed = float(std::chrono::duration_cast(now - last).count()); - } + last = std::chrono::high_resolution_clock::now(); if (_rendering) { // auto frameStart = std::chrono::high_resolution_clock::now(); @@ -69,26 +48,30 @@ public: // auto frameEnd = std::chrono::high_resolution_clock::now(); } - elapsed = float(std::chrono::duration_cast(now - last).count()); + auto now = std::chrono::high_resolution_clock::now(); - while(elapsed < _frameIntervalInMilliseconds) { - std::function task; - std::unique_lock lock(_access); - if (_tasks.empty()) { - _cond.wait_for(lock, std::chrono::duration(1)); - now = std::chrono::high_resolution_clock::now(); - elapsed = float(std::chrono::duration_cast(now - last).count()); - continue; - } + float elapsed = float(std::chrono::duration_cast(now - last).count()); + + std::function task; + + std::unique_lock lock(_access); + + if(_tasks.empty()) { + _cond.wait_for(lock, std::chrono::duration(1)); + continue; + } + while(!_tasks.empty()) { task = std::move(_tasks.front()); _tasks.pop_front(); task(); - - now = std::chrono::high_resolution_clock::now(); - elapsed = float(std::chrono::duration_cast(now - last).count()); } - last = now; + now = std::chrono::high_resolution_clock::now(); + elapsed = float(std::chrono::duration_cast(now - last).count()); + if(elapsed < _frameIntervalInMilliseconds) { + auto sleepFor = std::chrono::microseconds(int(_frameIntervalInMilliseconds - elapsed) * 1000); + std::this_thread::sleep_for(sleepFor); + } } }); } @@ -503,12 +486,6 @@ FLUTTER_PLUGIN_EXPORT void set_post_processing_ffi(void *const viewer, auto fut = _rl->add_task(lambda); } -FLUTTER_PLUGIN_EXPORT void pick_ffi(void *const viewer, int x, int y, - EntityId *entityId) { - std::packaged_task lambda([=] { pick(viewer, x, y, entityId); }); - auto fut = _rl->add_task(lambda); -} - FLUTTER_PLUGIN_EXPORT void get_name_for_entity_ffi(void *const sceneManager, const EntityId entityId, void (*callback)(const char*)) { std::packaged_task lambda( From 2fc1e6a1275b9226726cec8a581d11f0efca034d Mon Sep 17 00:00:00 2001 From: Nick Fisher Date: Sat, 9 Mar 2024 11:22:24 +0800 Subject: [PATCH 49/52] update generated bindings --- lib/generated_bindings.dart | 36 +++++++++++++++++------------------- 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/lib/generated_bindings.dart b/lib/generated_bindings.dart index d4d2529f..9c996011 100644 --- a/lib/generated_bindings.dart +++ b/lib/generated_bindings.dart @@ -842,13 +842,22 @@ external void set_antialiasing( @ffi.Native< ffi.Void Function( - ffi.Pointer, ffi.Int, ffi.Int, ffi.Pointer)>( + ffi.Pointer, + ffi.Int, + ffi.Int, + ffi.Pointer< + ffi.NativeFunction< + ffi.Void Function( + EntityId entityId, ffi.Int x, ffi.Int y)>>)>( symbol: 'pick', assetId: 'flutter_filament_plugin') external void pick( ffi.Pointer viewer, int x, int y, - ffi.Pointer entityId, + ffi.Pointer< + ffi.NativeFunction< + ffi.Void Function(EntityId entityId, ffi.Int x, ffi.Int y)>> + callback, ); @ffi.Native Function(ffi.Pointer, EntityId)>( @@ -974,7 +983,7 @@ external void test_collisions( ); @ffi.Native< - ffi.Pointer Function( + ffi.Void Function( ffi.Pointer, ffi.Pointer, ffi.Pointer, @@ -989,7 +998,7 @@ external void test_collisions( ffi.NativeFunction< ffi.Void Function(ffi.Pointer viewer)>>)>( symbol: 'create_filament_viewer_ffi', assetId: 'flutter_filament_plugin') -external ffi.Pointer create_filament_viewer_ffi( +external void create_filament_viewer_ffi( ffi.Pointer context, ffi.Pointer platform, ffi.Pointer uberArchivePath, @@ -1282,17 +1291,17 @@ external void clear_entities_ffi( ); @ffi.Native< - ffi.Bool Function( + ffi.Void Function( ffi.Pointer, EntityId, ffi.Pointer, - ffi.Pointer>)>( + ffi.Pointer>)>( symbol: 'set_camera_ffi', assetId: 'flutter_filament_plugin') -external bool set_camera_ffi( +external void set_camera_ffi( ffi.Pointer viewer, int asset, ffi.Pointer nodeName, - ffi.Pointer> callback, + ffi.Pointer> callback, ); @ffi.Native< @@ -1483,17 +1492,6 @@ external void set_post_processing_ffi( bool enabled, ); -@ffi.Native< - ffi.Void Function( - ffi.Pointer, ffi.Int, ffi.Int, ffi.Pointer)>( - symbol: 'pick_ffi', assetId: 'flutter_filament_plugin') -external void pick_ffi( - ffi.Pointer viewer, - int x, - int y, - ffi.Pointer entityId, -); - @ffi.Native, EntityId)>( symbol: 'reset_to_rest_pose_ffi', assetId: 'flutter_filament_plugin') external void reset_to_rest_pose_ffi( From 5e0c4e1f7070354a35d60f34fe8c57444e668d5f Mon Sep 17 00:00:00 2001 From: Nick Fisher Date: Sat, 9 Mar 2024 11:22:33 +0800 Subject: [PATCH 50/52] update pubspec.lock --- pubspec.lock | 50 +++++++++++++++++++++++++++++++++++++------------- 1 file changed, 37 insertions(+), 13 deletions(-) diff --git a/pubspec.lock b/pubspec.lock index 3c741b10..7da92e41 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -120,6 +120,30 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.2" + leak_tracker: + dependency: transitive + description: + name: leak_tracker + sha256: "41b90ceaec6d79819f31e975e61d479516efe701dea35f891b2f986c1b031422" + url: "https://pub.dev" + source: hosted + version: "9.0.17" + leak_tracker_flutter_testing: + dependency: transitive + description: + name: leak_tracker_flutter_testing + sha256: "54808cfcfa87dbc0d74c61ac063d624adf1bd5c0407301f32b06c783c60dc4ca" + url: "https://pub.dev" + source: hosted + version: "2.0.0" + leak_tracker_testing: + dependency: transitive + description: + name: leak_tracker_testing + sha256: "7e71be3c161472f6c9158ac8875dd8de575060d60b5d159ebca3600ea32c9116" + url: "https://pub.dev" + source: hosted + version: "1.0.6" lints: dependency: transitive description: @@ -140,26 +164,26 @@ packages: dependency: transitive description: name: matcher - sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e" + sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb url: "https://pub.dev" source: hosted - version: "0.12.16" + version: "0.12.16+1" material_color_utilities: dependency: transitive description: name: material_color_utilities - sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41" + sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a" url: "https://pub.dev" source: hosted - version: "0.5.0" + version: "0.8.0" meta: dependency: transitive description: name: meta - sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e + sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04 url: "https://pub.dev" source: hosted - version: "1.10.0" + version: "1.11.0" package_config: dependency: transitive description: @@ -172,10 +196,10 @@ packages: dependency: transitive description: name: path - sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917" + sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" url: "https://pub.dev" source: hosted - version: "1.8.3" + version: "1.9.0" plugin_platform_interface: dependency: "direct main" description: @@ -261,14 +285,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.4" - web: + vm_service: dependency: transitive description: - name: web - sha256: "14f1f70c51119012600c5f1f60ca68efda5a9b6077748163c6af2893ec5df8fc" + name: vm_service + sha256: b3d56ff4341b8f182b96aceb2fa20e3dcb336b9f867bc0eafc0de10f1048e957 url: "https://pub.dev" source: hosted - version: "0.2.1-beta" + version: "13.0.0" yaml: dependency: transitive description: @@ -286,5 +310,5 @@ packages: source: hosted version: "2.1.1" sdks: - dart: ">=3.2.0-157.0.dev <4.0.0" + dart: ">=3.2.0-0 <4.0.0" flutter: ">=3.16.0-0.2.pre" From 357c585d44652e20d4580abd9357d588a18ed8a3 Mon Sep 17 00:00:00 2001 From: Nick Fisher Date: Mon, 25 Mar 2024 22:21:37 +0800 Subject: [PATCH 51/52] add (very rough) gizmo, restructure Dart package into library, add EntityListWidget --- CHANGELOG.md | 2 + example/lib/camera_matrix_overlay.dart | 4 +- example/lib/example_viewport.dart | 6 +- example/lib/main.dart | 152 +- example/lib/menus/asset_submenu.dart | 149 +- example/lib/menus/camera_submenu.dart | 17 +- example/lib/menus/controller_menu.dart | 17 +- example/lib/menus/rendering_submenu.dart | 9 +- example/lib/menus/scene_menu.dart | 2 +- example/lib/picker_result_widget.dart | 2 +- ios/include/FilamentViewer.hpp | 2 +- ios/include/FlutterFilamentApi.h | 4 +- ios/include/FlutterFilamentFFIApi.h | 2 +- ios/include/SceneManager.hpp | 23 + .../components/AnimationComponentManager.hpp | 2 + ios/include/material/FileMaterialProvider.hpp | 95 - ios/include/material/gizmo.S | 12 + ios/include/material/gizmo.apple.S | 12 + ios/include/material/gizmo.bin | 3 + ios/include/material/gizmo.c | 1847 +++++++++++++++++ ios/include/material/gizmo.h | 13 + ios/src/FilamentViewer.cpp | 12 +- ios/src/FlutterFilamentApi.cpp | 19 +- ios/src/FlutterFilamentFFIApi.cpp | 8 +- ios/src/SceneManager.cpp | 170 +- lib/FlutterFilamentPluginWeb.dart | 2 +- .../animations/animation_builder.dart | 2 +- .../animations/animation_data.dart | 0 lib/{ => filament}/animations/bvh.dart | 5 +- .../entities/entity_transform_controller.dart | 26 +- lib/filament/entities/gizmo.dart | 72 + lib/{ => filament}/filament_controller.dart | 109 +- .../filament_controller_ffi.dart | 212 +- lib/{ => filament}/generated_bindings.dart | 28 +- .../generated_bindings_web.dart | 0 lib/{ => filament}/rendering_surface.dart | 0 lib/filament/scene.dart | 118 ++ .../utils}/camera_orientation.dart | 0 .../utils}/hardware_keyboard_listener.dart | 3 +- .../utils}/hardware_keyboard_poll.dart | 4 +- .../utils}/light_options.dart | 0 lib/filament/utils/using_pointer.dart | 10 + .../widgets/camera_options_widget.dart | 5 +- .../widgets/debug/entity_list_widget.dart | 147 ++ .../entity_controller_mouse_widget.dart | 4 +- .../widgets/filament_gesture_detector.dart | 55 +- .../filament_gesture_detector_desktop.dart | 35 +- .../filament_gesture_detector_mobile.dart | 0 .../widgets/filament_widget.dart | 2 +- .../widgets/ibl_rotation_slider.dart | 2 +- lib/{ => filament}/widgets/light_slider.dart | 4 +- lib/flutter_filament.dart | 12 + materials/gizmo.mat | 40 + 53 files changed, 2913 insertions(+), 568 deletions(-) delete mode 100644 ios/include/material/FileMaterialProvider.hpp create mode 100644 ios/include/material/gizmo.S create mode 100644 ios/include/material/gizmo.apple.S create mode 100644 ios/include/material/gizmo.bin create mode 100644 ios/include/material/gizmo.c create mode 100644 ios/include/material/gizmo.h rename lib/{ => filament}/animations/animation_builder.dart (98%) rename lib/{ => filament}/animations/animation_data.dart (100%) rename lib/{ => filament}/animations/bvh.dart (97%) rename lib/{ => filament}/entities/entity_transform_controller.dart (81%) create mode 100644 lib/filament/entities/gizmo.dart rename lib/{ => filament}/filament_controller.dart (92%) rename lib/{ => filament}/filament_controller_ffi.dart (92%) rename lib/{ => filament}/generated_bindings.dart (98%) rename lib/{ => filament}/generated_bindings_web.dart (100%) rename lib/{ => filament}/rendering_surface.dart (100%) create mode 100644 lib/filament/scene.dart rename lib/{camera => filament/utils}/camera_orientation.dart (100%) rename lib/{hardware => filament/utils}/hardware_keyboard_listener.dart (93%) rename lib/{hardware => filament/utils}/hardware_keyboard_poll.dart (85%) rename lib/{lights => filament/utils}/light_options.dart (100%) create mode 100644 lib/filament/utils/using_pointer.dart rename lib/{ => filament}/widgets/camera_options_widget.dart (98%) create mode 100644 lib/filament/widgets/debug/entity_list_widget.dart rename lib/{ => filament}/widgets/entity_controller_mouse_widget.dart (94%) rename lib/{ => filament}/widgets/filament_gesture_detector.dart (59%) rename lib/{ => filament}/widgets/filament_gesture_detector_desktop.dart (86%) rename lib/{ => filament}/widgets/filament_gesture_detector_mobile.dart (100%) rename lib/{ => filament}/widgets/filament_widget.dart (99%) rename lib/{ => filament}/widgets/ibl_rotation_slider.dart (92%) rename lib/{ => filament}/widgets/light_slider.dart (98%) create mode 100644 lib/flutter_filament.dart create mode 100644 materials/gizmo.mat diff --git a/CHANGELOG.md b/CHANGELOG.md index bfb16b6a..8d16015f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ * `setCameraRotation` now accepts a quaternion instead of an axis/angle * instancing is now supported. * `setBoneTransform` has been removed. To set the transform for a bone, just `addBoneAnimation` with a single frame. +* the Dart library has been restructured to expose a cleaner API surface. Import `package:flutter_filament/flutter_filament.dart` +* created a separate `Scene` class to hold lights/entities. For now, this is simply a singleton that holds all `getScene` ## 0.6.0 diff --git a/example/lib/camera_matrix_overlay.dart b/example/lib/camera_matrix_overlay.dart index ae04c45f..1e51c8e0 100644 --- a/example/lib/camera_matrix_overlay.dart +++ b/example/lib/camera_matrix_overlay.dart @@ -2,7 +2,7 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter/widgets.dart'; -import 'package:flutter_filament/filament_controller.dart'; +import 'package:flutter_filament/flutter_filament.dart'; import 'package:vector_math/vector_math_64.dart' as v; class CameraMatrixOverlay extends StatefulWidget { @@ -96,7 +96,7 @@ class _CameraMatrixOverlayState extends State { crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ - Text("Camera position : $_cameraPosition $_cameraRotation", + Text("Camera : $_cameraPosition $_cameraRotation", style: const TextStyle(color: Colors.white, fontSize: 10)), // widget.showProjectionMatrices diff --git a/example/lib/example_viewport.dart b/example/lib/example_viewport.dart index 1e9e8bf2..932964cb 100644 --- a/example/lib/example_viewport.dart +++ b/example/lib/example_viewport.dart @@ -1,9 +1,5 @@ import 'package:flutter/widgets.dart'; -import 'package:flutter_filament/entities/entity_transform_controller.dart'; -import 'package:flutter_filament/filament_controller.dart'; -import 'package:flutter_filament/widgets/entity_controller_mouse_widget.dart'; -import 'package:flutter_filament/widgets/filament_gesture_detector.dart'; -import 'package:flutter_filament/widgets/filament_widget.dart'; +import 'package:flutter_filament/flutter_filament.dart'; class ExampleViewport extends StatelessWidget { final FilamentController? controller; diff --git a/example/lib/main.dart b/example/lib/main.dart index 2de07159..ec022a18 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -1,22 +1,19 @@ import 'dart:async'; import 'package:flutter/material.dart'; -import 'package:flutter_filament/entities/entity_transform_controller.dart'; -import 'package:flutter_filament/filament_controller_ffi.dart'; + import 'package:flutter_filament_example/camera_matrix_overlay.dart'; import 'package:flutter_filament_example/menus/controller_menu.dart'; import 'package:flutter_filament_example/example_viewport.dart'; import 'package:flutter_filament_example/picker_result_widget.dart'; import 'package:flutter_filament_example/menus/scene_menu.dart'; -import 'package:flutter_filament/filament_controller.dart'; +import 'package:flutter_filament/flutter_filament.dart'; const loadDefaultScene = bool.hasEnvironment('--load-default-scene'); -const foo = String.fromEnvironment('foo'); void main() async { print(loadDefaultScene); - print(foo); runApp(const MyApp()); } @@ -31,7 +28,15 @@ class _MyAppState extends State with SingleTickerProviderStateMixin { @override Widget build(BuildContext context) { return MaterialApp( - theme: ThemeData(useMaterial3: true), + theme: ThemeData( + useMaterial3: true, + textTheme: const TextTheme( + labelLarge: TextStyle(fontSize: 12), + displayMedium: TextStyle(fontSize: 12), + headlineMedium: const TextStyle(fontSize: 12), + titleMedium: TextStyle(fontSize: 12), + bodyLarge: TextStyle(fontSize: 14), + bodyMedium: TextStyle(fontSize: 12))), // showPerformanceOverlay: true, home: const Scaffold(body: ExampleWidget())); } @@ -69,12 +74,6 @@ class ExampleWidgetState extends State { static bool hasSkybox = false; static bool coneHidden = false; - static final assets = []; - static FilamentEntity? flightHelmet; - static FilamentEntity? buster; - - static FilamentEntity? directionalLight; - static bool loop = false; static final showProjectionMatrices = ValueNotifier(false); @@ -90,13 +89,13 @@ class ExampleWidgetState extends State { }); WidgetsBinding.instance.addPostFrameCallback((timeStamp) async { await _filamentController!.createViewer(); - _createEntityLoadListener(); + await _filamentController! .loadSkybox("assets/default_env/default_env_skybox.ktx"); await _filamentController!.setRendering(true); - assets.add( - await _filamentController!.loadGlb("assets/shapes/shapes.glb")); + + await _filamentController!.loadGlb("assets/shapes/shapes.glb"); await _filamentController! .setCameraManipulatorOptions(zoomSpeed: 1.0); @@ -114,82 +113,9 @@ class ExampleWidgetState extends State { _listener.cancel(); } - void _createEntityLoadListener() { - _listener = - _filamentController!.onLoad.listen((FilamentEntity entity) async { - assets.add(entity); - if (mounted) { - setState(() {}); - } - print(_filamentController!.getNameForEntity(entity) ?? "NAME NOT FOUND"); - }); - } - EntityTransformController? _transformController; - FilamentEntity? _controlled; - final _sharedFocusNode = FocusNode(); - Widget _assetEntry(FilamentEntity entity) { - return FutureBuilder( - future: _filamentController!.getAnimationNames(entity), - builder: (_, animations) { - if (animations.data == null) { - return Container(); - } - return Row(children: [ - Text("Asset ${entity}"), - IconButton( - iconSize: 14, - tooltip: "Transform to unit cube", - onPressed: () async { - await _filamentController!.transformToUnitCube(entity); - }, - icon: const Icon(Icons.settings_overscan_outlined)), - IconButton( - iconSize: 14, - tooltip: "Attach mouse control", - onPressed: () async { - _transformController?.dispose(); - if (_controlled == entity) { - _controlled = null; - } else { - _controlled = entity; - _transformController?.dispose(); - _transformController = - EntityTransformController(_filamentController!, entity); - } - setState(() {}); - }, - icon: Icon(Icons.control_camera, - color: - _controlled == entity ? Colors.green : Colors.black)), - IconButton( - iconSize: 14, - tooltip: "Remove", - onPressed: () async { - await _filamentController!.removeEntity(entity); - assets.remove(entity); - setState(() {}); - }, - icon: const Icon(Icons.cancel_sharp)), - if (animations.data!.isNotEmpty) - PopupMenuButton( - tooltip: "Animations", - itemBuilder: (_) => animations.data! - .map((a) => PopupMenuItem( - value: a, - child: Text(a), - )) - .toList(), - onSelected: (value) async { - print("Playing animation $value"); - await _filamentController! - .playAnimation(entity, animations.data!.indexOf(value!)); - }, - ) - ]); - }); - } + final _sharedFocusNode = FocusNode(); @override Widget build(BuildContext context) { @@ -201,26 +127,14 @@ class ExampleWidgetState extends State { padding: _viewportMargin, keyboardFocusNode: _sharedFocusNode), ), + EntityListWidget(controller: _filamentController), Positioned( - bottom: 80, - left: 10, - height: 200, - width: 300, - child: Container( - padding: const EdgeInsets.symmetric(horizontal: 30, vertical: 10), - height: 100, - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(30), - color: Colors.white.withOpacity(0.25), - ), - child: ListView(children: assets.map(_assetEntry).toList()))), - Positioned( - bottom: 10, - left: 10, + bottom: 0, + left: 0, right: 10, - height: 60, + height: 30, child: Container( - height: 60, + height: 30, decoration: BoxDecoration( borderRadius: BorderRadius.circular(30), color: Colors.white.withOpacity(0.25), @@ -231,11 +145,17 @@ class ExampleWidgetState extends State { ControllerMenu( sharedFocusNode: _sharedFocusNode, controller: _filamentController, + onToggleViewport: () { + setState(() { + _viewportMargin = (_viewportMargin == EdgeInsets.zero) + ? const EdgeInsets.all(30) + : EdgeInsets.zero; + }); + }, onControllerDestroyed: () {}, onControllerCreated: (controller) { setState(() { _filamentController = controller; - _createEntityLoadListener(); }); }), SceneMenu( @@ -251,21 +171,21 @@ class ExampleWidgetState extends State { child: Container( color: Colors.transparent, child: const Text("shapes.glb"))), - SizedBox(width: 5), + const SizedBox(width: 5), GestureDetector( onTap: () async { await _filamentController!.loadGlb('assets/1.glb'); }, child: Container( color: Colors.transparent, child: const Text("1.glb"))), - SizedBox(width: 5), + const SizedBox(width: 5), GestureDetector( onTap: () async { await _filamentController!.loadGlb('assets/2.glb'); }, child: Container( color: Colors.transparent, child: const Text("2.glb"))), - SizedBox(width: 5), + const SizedBox(width: 5), GestureDetector( onTap: () async { await _filamentController!.loadGlb('assets/3.glb'); @@ -273,21 +193,11 @@ class ExampleWidgetState extends State { child: Container( color: Colors.transparent, child: const Text("3.glb"))), Expanded(child: Container()), - TextButton( - child: const Text("Toggle viewport size"), - onPressed: () { - setState(() { - _viewportMargin = (_viewportMargin == EdgeInsets.zero) - ? const EdgeInsets.all(30) - : EdgeInsets.zero; - }); - }, - ) ]))), _filamentController == null ? Container() : Padding( - padding: const EdgeInsets.only(top: 40, left: 20, right: 20), + padding: const EdgeInsets.only(top: 10, left: 20, right: 20), child: ValueListenableBuilder( valueListenable: showProjectionMatrices, builder: (ctx, value, child) => CameraMatrixOverlay( diff --git a/example/lib/menus/asset_submenu.dart b/example/lib/menus/asset_submenu.dart index 358cfebb..9355d64c 100644 --- a/example/lib/menus/asset_submenu.dart +++ b/example/lib/menus/asset_submenu.dart @@ -1,11 +1,7 @@ -import 'dart:async'; import 'dart:math'; import 'package:flutter/material.dart'; -import 'package:flutter/widgets.dart'; -import 'package:flutter_filament/animations/animation_data.dart'; - -import 'package:flutter_filament/filament_controller.dart'; +import 'package:flutter_filament/flutter_filament.dart'; import 'package:flutter_filament_example/main.dart'; import 'package:permission_handler/permission_handler.dart'; @@ -30,8 +26,8 @@ class _AssetSubmenuState extends State { MenuItemButton( closeOnActivate: false, onPressed: () async { - var entity = await widget.controller - .getChildEntity(ExampleWidgetState.assets.last, "Cylinder"); + var entity = await widget.controller.getChildEntity( + widget.controller.scene.listEntities().last, "Cylinder"); await showDialog( context: context, builder: (BuildContext context) { @@ -44,7 +40,7 @@ class _AssetSubmenuState extends State { MenuItemButton( onPressed: () async { await widget.controller.addBoneAnimation( - ExampleWidgetState.assets.last, + widget.controller.scene.listEntities().last, BoneAnimationData([ "Bone" ], [ @@ -59,13 +55,14 @@ class _AssetSubmenuState extends State { const Text('Set bone transform for Cylinder (pi/2 rotation X)')), MenuItemButton( onPressed: () async { - await widget.controller.resetBones(ExampleWidgetState.assets.last); + await widget.controller + .resetBones(widget.controller.scene.listEntities().last); }, child: const Text('Reset bones for Cylinder')), MenuItemButton( onPressed: () async { await widget.controller.addBoneAnimation( - ExampleWidgetState.assets.last, + widget.controller.scene.listEntities().last, BoneAnimationData( ["Bone"], ["Cylinder"], @@ -84,7 +81,7 @@ class _AssetSubmenuState extends State { closeOnActivate: false, onPressed: () async { var names = await widget.controller.getMorphTargetNames( - ExampleWidgetState.assets.last, "Cylinder"); + widget.controller.scene.listEntities().last, "Cylinder"); print("NAMES : $names"); await showDialog( context: context, @@ -100,7 +97,7 @@ class _AssetSubmenuState extends State { MenuItemButton( onPressed: () async { widget.controller.setMorphTargetWeights( - ExampleWidgetState.assets.last, + widget.controller.scene.listEntities().last, "Cylinder", List.filled(4, 1.0)); }, @@ -108,24 +105,26 @@ class _AssetSubmenuState extends State { MenuItemButton( onPressed: () async { widget.controller.setMorphTargetWeights( - ExampleWidgetState.assets.last, + widget.controller.scene.listEntities().last, "Cylinder", List.filled(4, 0.0)); }, child: const Text("Set Cylinder morph weights to 0")), MenuItemButton( onPressed: () async { - widget.controller - .setPosition(ExampleWidgetState.assets.last, 1.0, 1.0, -1.0); + widget.controller.setPosition( + widget.controller.scene.listEntities().last, 1.0, 1.0, -1.0); }, child: const Text('Set position to 1, 1, -1'), ), MenuItemButton( onPressed: () async { if (ExampleWidgetState.coneHidden) { - widget.controller.reveal(ExampleWidgetState.assets.last, "Cone"); + widget.controller + .reveal(widget.controller.scene.listEntities().last, "Cone"); } else { - widget.controller.hide(ExampleWidgetState.assets.last, "Cone"); + widget.controller + .hide(widget.controller.scene.listEntities().last, "Cone"); } ExampleWidgetState.coneHidden = !ExampleWidgetState.coneHidden; @@ -135,7 +134,10 @@ class _AssetSubmenuState extends State { MenuItemButton( onPressed: () async { widget.controller.setMaterialColor( - ExampleWidgetState.assets.last, "Cone", 0, Colors.purple); + widget.controller.scene.listEntities().last, + "Cone", + 0, + Colors.purple); }, child: const Text("Set cone material color to purple")), MenuItemButton( @@ -150,31 +152,47 @@ class _AssetSubmenuState extends State { } Widget _geometrySubmenu() { - return MenuItemButton( - onPressed: () async { - await widget.controller.createGeometry([ - -1, - 0, - -1, - -1, - 0, - 1, - 1, - 0, - 1, - 1, - 0, - -1, - ], [ - 0, - 1, - 2, - 2, - 3, - 0 - ], "asset://assets/solidcolor.filamat"); - }, - child: const Text("Custom geometry")); + return SubmenuButton( + menuChildren: [ + MenuItemButton( + onPressed: () async { + var verts = [ + -1.0, + 0.0, + -1.0, + -1.0, + 0.0, + 1.0, + 1.0, + 0.0, + 1.0, + 1.0, + 0.0, + -1.0, + ]; + var indices = [0, 1, 2, 2, 3, 0]; + var geom = await widget.controller.createGeometry(verts, indices, + materialPath: "asset://assets/solidcolor.filamat"); + }, + child: const Text("Quad")), + MenuItemButton( + onPressed: () async { + await widget.controller.createGeometry([ + 0, + 0, + 0, + 1, + 0, + 0, + ], [ + 0, + 1 + ], primitiveType: PrimitiveType.LINES); + }, + child: const Text("Line")) + ], + child: const Text("Custom Geometry"), + ); } @override @@ -185,50 +203,24 @@ class _AssetSubmenuState extends State { _geometrySubmenu(), MenuItemButton( onPressed: () async { - ExampleWidgetState.directionalLight = await widget.controller + await widget.controller .addLight(1, 6500, 150000, 0, 1, 0, 0, -1, 0, true); }, child: const Text("Add directional light"), ), + MenuItemButton( + onPressed: () async { + await widget.controller + .addLight(2, 6500, 150000, 0, 1, 0, 0, -1, 0, true); + }, + child: const Text("Add point light"), + ), MenuItemButton( onPressed: () async { await widget.controller.clearLights(); }, child: const Text("Clear lights"), ), - MenuItemButton( - onPressed: () async { - if (ExampleWidgetState.buster == null) { - ExampleWidgetState.buster = await widget.controller.loadGltf( - "assets/BusterDrone/scene.gltf", "assets/BusterDrone", - force: true); - await widget.controller - .playAnimation(ExampleWidgetState.buster!, 0, loop: true); - } else { - await widget.controller - .removeEntity(ExampleWidgetState.buster!); - ExampleWidgetState.buster = null; - } - }, - child: Text(ExampleWidgetState.buster == null - ? 'Load buster' - : 'Remove buster')), - MenuItemButton( - onPressed: () async { - if (ExampleWidgetState.flightHelmet == null) { - ExampleWidgetState.flightHelmet ??= await widget.controller - .loadGltf('assets/FlightHelmet/FlightHelmet.gltf', - 'assets/FlightHelmet', - force: true); - } else { - await widget.controller - .removeEntity(ExampleWidgetState.flightHelmet!); - ExampleWidgetState.flightHelmet = null; - } - }, - child: Text(ExampleWidgetState.flightHelmet == null - ? 'Load flight helmet' - : 'Remove flight helmet')), MenuItemButton( onPressed: () { widget.controller.setBackgroundColor(const Color(0xAA73C9FA)); @@ -272,9 +264,6 @@ class _AssetSubmenuState extends State { MenuItemButton( onPressed: () async { await widget.controller.clearEntities(); - ExampleWidgetState.flightHelmet = null; - ExampleWidgetState.buster = null; - ExampleWidgetState.assets.clear(); }, child: const Text('Clear assets')), ], diff --git a/example/lib/menus/camera_submenu.dart b/example/lib/menus/camera_submenu.dart index ba424e6e..38751868 100644 --- a/example/lib/menus/camera_submenu.dart +++ b/example/lib/menus/camera_submenu.dart @@ -3,7 +3,8 @@ import 'dart:math'; import 'package:flutter/material.dart'; import 'package:vector_math/vector_math_64.dart' as v; -import 'package:flutter_filament/filament_controller.dart'; +import 'package:flutter_filament/flutter_filament.dart'; + import 'package:flutter_filament_example/main.dart'; class CameraSubmenu extends StatefulWidget { @@ -104,20 +105,6 @@ class _CameraSubmenuState extends State { }, child: const Text('Move to 0,0,0, facing towards 0,0,-1'), ), - MenuItemButton( - onPressed: () async { - await widget.controller - .setCamera(ExampleWidgetState.assets.last!, null); - }, - child: const Text('Set to first camera in last added asset'), - ), - MenuItemButton( - onPressed: () async { - await widget.controller - .moveCameraToAsset(ExampleWidgetState.assets.last!); - }, - child: const Text("Move to last added asset"), - ), MenuItemButton( onPressed: () { widget.controller.setCameraRotation( diff --git a/example/lib/menus/controller_menu.dart b/example/lib/menus/controller_menu.dart index 69ee210a..cd055eb3 100644 --- a/example/lib/menus/controller_menu.dart +++ b/example/lib/menus/controller_menu.dart @@ -2,11 +2,12 @@ import 'dart:io'; import 'package:flutter/material.dart'; import 'package:flutter/widgets.dart'; -import 'package:flutter_filament/filament_controller.dart'; -import 'package:flutter_filament/filament_controller_ffi.dart'; + +import 'package:flutter_filament/flutter_filament.dart'; class ControllerMenu extends StatefulWidget { final FilamentController? controller; + final void Function() onToggleViewport; final void Function(FilamentController controller) onControllerCreated; final void Function() onControllerDestroyed; final FocusNode sharedFocusNode; @@ -15,7 +16,8 @@ class ControllerMenu extends StatefulWidget { {this.controller, required this.onControllerCreated, required this.onControllerDestroyed, - required this.sharedFocusNode}); + required this.sharedFocusNode, + required this.onToggleViewport}); @override State createState() => _ControllerMenuState(); @@ -60,6 +62,7 @@ class _ControllerMenuState extends State { ? null : () async { await _filamentController!.createViewer(); + await _filamentController! .setCameraManipulatorOptions(zoomSpeed: 1.0); }, @@ -100,7 +103,13 @@ class _ControllerMenuState extends State { } return MenuAnchor( childFocusNode: widget.sharedFocusNode, - menuChildren: items, + menuChildren: items + + [ + TextButton( + child: const Text("Toggle viewport size"), + onPressed: widget.onToggleViewport, + ) + ], builder: (BuildContext context, MenuController controller, Widget? child) { return TextButton( diff --git a/example/lib/menus/rendering_submenu.dart b/example/lib/menus/rendering_submenu.dart index 2c9f5edf..3c2bbbf1 100644 --- a/example/lib/menus/rendering_submenu.dart +++ b/example/lib/menus/rendering_submenu.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter/widgets.dart'; -import 'package:flutter_filament/filament_controller.dart'; +import 'package:flutter_filament/flutter_filament.dart'; import 'package:flutter_filament_example/main.dart'; class RenderingSubmenu extends StatefulWidget { @@ -64,6 +64,13 @@ class _RenderingSubmenuState extends State { child: Text( "Turn recording ${ExampleWidgetState.recording ? "OFF" : "ON"}) "), ), + MenuItemButton( + onPressed: () async { + await widget.controller + .addLight(2, 6000, 100000, 0, 0, 0, 0, 1, 0, false); + }, + child: Text("Add light"), + ), ], child: const Text("Rendering"), ); diff --git a/example/lib/menus/scene_menu.dart b/example/lib/menus/scene_menu.dart index d2c95f1c..649e97d0 100644 --- a/example/lib/menus/scene_menu.dart +++ b/example/lib/menus/scene_menu.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; -import 'package:flutter_filament/filament_controller.dart'; +import 'package:flutter_filament/flutter_filament.dart'; import 'package:flutter_filament_example/menus/asset_submenu.dart'; import 'package:flutter_filament_example/menus/camera_submenu.dart'; import 'package:flutter_filament_example/menus/rendering_submenu.dart'; diff --git a/example/lib/picker_result_widget.dart b/example/lib/picker_result_widget.dart index dca58d90..2efce702 100644 --- a/example/lib/picker_result_widget.dart +++ b/example/lib/picker_result_widget.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:flutter_filament/filament_controller.dart'; +import 'package:flutter_filament/flutter_filament.dart'; class PickerResultWidget extends StatelessWidget { final FilamentController controller; diff --git a/ios/include/FilamentViewer.hpp b/ios/include/FilamentViewer.hpp index b3905d74..8aa757b5 100644 --- a/ios/include/FilamentViewer.hpp +++ b/ios/include/FilamentViewer.hpp @@ -138,7 +138,7 @@ namespace flutter_filament void setAntiAliasing(bool msaaEnabled, bool fxaaEnabled, bool taaEnabled); void setDepthOfField(); - EntityId createGeometry(float* vertices, uint32_t numVertices, uint16_t* indices, uint32_t numIndices, const char* materialPath); + EntityId createGeometry(float* vertices, uint32_t numVertices, uint16_t* indices, uint32_t numIndices, filament::RenderableManager::PrimitiveType primitiveType = RenderableManager::PrimitiveType::TRIANGLES, const char* materialPath = nullptr); SceneManager *const getSceneManager() { diff --git a/ios/include/FlutterFilamentApi.h b/ios/include/FlutterFilamentApi.h index 958401ab..1d27b29a 100644 --- a/ios/include/FlutterFilamentApi.h +++ b/ios/include/FlutterFilamentApi.h @@ -197,9 +197,11 @@ extern "C" FLUTTER_PLUGIN_EXPORT void remove_collision_component(void *const sceneManager, EntityId entityId); FLUTTER_PLUGIN_EXPORT void add_animation_component(void *const sceneManager, EntityId entityId); - FLUTTER_PLUGIN_EXPORT EntityId create_geometry(void *const viewer, float* vertices, int numVertices, uint16_t* indices, int numIndices, const char* materialPath); + FLUTTER_PLUGIN_EXPORT EntityId create_geometry(void *const viewer, float* vertices, int numVertices, uint16_t* indices, int numIndices, int primitiveType, const char* materialPath); FLUTTER_PLUGIN_EXPORT void set_parent(void *const sceneManager, EntityId child, EntityId parent); FLUTTER_PLUGIN_EXPORT void test_collisions(void *const sceneManager, EntityId entity); + FLUTTER_PLUGIN_EXPORT void set_priority(void *const sceneManager, EntityId entityId, int priority); + FLUTTER_PLUGIN_EXPORT void get_gizmo(void *const sceneManager, EntityId* out); #ifdef __cplusplus } diff --git a/ios/include/FlutterFilamentFFIApi.h b/ios/include/FlutterFilamentFFIApi.h index 78cedcf5..e4f50675 100644 --- a/ios/include/FlutterFilamentFFIApi.h +++ b/ios/include/FlutterFilamentFFIApi.h @@ -115,7 +115,7 @@ extern "C" FLUTTER_PLUGIN_EXPORT void set_post_processing_ffi(void *const viewer, bool enabled); FLUTTER_PLUGIN_EXPORT void reset_to_rest_pose_ffi(void *const sceneManager, EntityId entityId); FLUTTER_PLUGIN_EXPORT void ios_dummy_ffi(); - FLUTTER_PLUGIN_EXPORT void create_geometry_ffi(void *const viewer, float *vertices, int numVertices, uint16_t *indices, int numIndices, const char *materialPath, void (*callback)(EntityId)); + FLUTTER_PLUGIN_EXPORT void create_geometry_ffi(void *const viewer, float *vertices, int numVertices, uint16_t *indices, int numIndices, int primitiveType, const char *materialPath, void (*callback)(EntityId)); #ifdef __cplusplus } diff --git a/ios/include/SceneManager.hpp b/ios/include/SceneManager.hpp index 52465eb3..96b685e3 100644 --- a/ios/include/SceneManager.hpp +++ b/ios/include/SceneManager.hpp @@ -12,6 +12,10 @@ #include #include +#include +#include + +#include "material/gizmo.h" #include "utils/NameComponentManager.h" #include "ResourceBuffer.hpp" #include "components/CollisionComponentManager.hpp" @@ -141,6 +145,19 @@ namespace flutter_filament /// @param entityId void getInstances(EntityId entityId, EntityId* out); + /// + /// Sets the draw priority for the given entity. See RenderableManager.h for more details. + /// + void setPriority(EntityId entity, int priority); + + + /// @brief returns the gizmo entity, used to manipulate the global transform for entities + /// @param out a pointer to an array of three EntityId {x, y, z} + /// @return + /// + void getGizmo(EntityId* out); + + friend class FilamentViewer; @@ -154,6 +171,7 @@ namespace flutter_filament gltfio::TextureProvider *_stbDecoder = nullptr; gltfio::TextureProvider *_ktxDecoder = nullptr; std::mutex _mutex; + Material* _gizmoMaterial; utils::NameComponentManager* _ncm; @@ -173,5 +191,10 @@ namespace flutter_filament const gltfio::FilamentInstance* instance, const char *entityName); + EntityId addGizmo(); + utils::Entity _gizmoX; + utils::Entity _gizmoY; + utils::Entity _gizmoZ; + }; } diff --git a/ios/include/components/AnimationComponentManager.hpp b/ios/include/components/AnimationComponentManager.hpp index 98f1eb29..72083940 100644 --- a/ios/include/components/AnimationComponentManager.hpp +++ b/ios/include/components/AnimationComponentManager.hpp @@ -115,6 +115,8 @@ namespace flutter_filament { { auto now = high_resolution_clock::now(); + // Log("animation component count : %d", ) + for(auto it = begin(); it < end(); it++) { const auto& entity = getEntity(it); diff --git a/ios/include/material/FileMaterialProvider.hpp b/ios/include/material/FileMaterialProvider.hpp deleted file mode 100644 index dacf41ab..00000000 --- a/ios/include/material/FileMaterialProvider.hpp +++ /dev/null @@ -1,95 +0,0 @@ -#ifndef FILE_MATERIAL_PROVIDER -#define FILE_MATERIAL_PROVIDER - -#include -#include -#include -#include -#include -#include -#include -#include -#include "Log.hpp" - -namespace flutter_filament { - - - class FileMaterialProvider : public filament::gltfio::MaterialProvider { - - filament::Material* _m; - const filament::Material* _ms[1]; - filament::Texture* mDummyTexture = nullptr; - - public: - FileMaterialProvider(filament::Engine* engine, const void* const data, const size_t size) { - _m = filament::Material::Builder() - .package(data, size) - .build(*engine); - _ms[0] = _m; - unsigned char texels[4] = {}; - mDummyTexture = filament::Texture::Builder() - .width(1).height(1) - .format(filament::Texture::InternalFormat::RGBA8) - .build(*engine); - filament::Texture::PixelBufferDescriptor pbd(texels, sizeof(texels), filament::Texture::Format::RGBA, - filament::Texture::Type::UBYTE); - mDummyTexture->setImage(*engine, 0, std::move(pbd)); - } - - filament::MaterialInstance* createMaterialInstance(filament::gltfio::MaterialKey* config, filament::gltfio::UvMap* uvmap, - const char* label = "material", const char* extras = nullptr) { - - auto getUvIndex = [uvmap](uint8_t srcIndex, bool hasTexture) -> int { - return hasTexture ? int(uvmap->at(srcIndex)) - 1 : -1; - }; - - auto instance = _m->createInstance(); - filament::math::mat3f identity; - instance->setParameter("baseColorUvMatrix", identity); - instance->setParameter("normalUvMatrix", identity); - - instance->setParameter("baseColorIndex", getUvIndex(config->baseColorUV, config->hasBaseColorTexture)); - instance->setParameter("normalIndex", getUvIndex(config->normalUV, config->hasNormalTexture)); - if(config->hasNormalTexture) { - filament::TextureSampler sampler; - instance->setParameter("normalMap", mDummyTexture, sampler); - instance->setParameter("baseColorMap", mDummyTexture, sampler); - } else { - Log("No normal texture for specified material."); - } - - return instance; - } - - /** - * Creates or fetches a compiled Filament material corresponding to the given config. - */ - virtual filament::Material* getMaterial(filament::gltfio::MaterialKey* config, filament::gltfio::UvMap* uvmap, const char* label = "material") { - return _m; - } - - /** - * Gets a weak reference to the array of cached materials. - */ - const filament::Material* const* getMaterials() const noexcept { - return _ms; - } - - /** - * Gets the number of cached materials. - */ - size_t getMaterialsCount() const noexcept { - return (size_t)1; - } - - void destroyMaterials() { - - } - - bool needsDummyData(filament::VertexAttribute attrib) const noexcept { - return true; - } - }; -} - -#endif \ No newline at end of file diff --git a/ios/include/material/gizmo.S b/ios/include/material/gizmo.S new file mode 100644 index 00000000..1181b279 --- /dev/null +++ b/ios/include/material/gizmo.S @@ -0,0 +1,12 @@ + .global GIZMO_GIZMO_OFFSET; + .global GIZMO_GIZMO_SIZE; + + .global GIZMO_PACKAGE + .section .rodata +GIZMO_PACKAGE: + .incbin "gizmo.bin" +GIZMO_GIZMO_OFFSET: + .int 0 +GIZMO_GIZMO_SIZE: + .int 36751 + diff --git a/ios/include/material/gizmo.apple.S b/ios/include/material/gizmo.apple.S new file mode 100644 index 00000000..1cbfaa90 --- /dev/null +++ b/ios/include/material/gizmo.apple.S @@ -0,0 +1,12 @@ + .global _GIZMO_GIZMO_OFFSET; + .global _GIZMO_GIZMO_SIZE; + + .global _GIZMO_PACKAGE + .section __TEXT,__const +_GIZMO_PACKAGE: + .incbin "gizmo.bin" +_GIZMO_GIZMO_OFFSET: + .int 0 +_GIZMO_GIZMO_SIZE: + .int 36751 + diff --git a/ios/include/material/gizmo.bin b/ios/include/material/gizmo.bin new file mode 100644 index 00000000..119c5f89 --- /dev/null +++ b/ios/include/material/gizmo.bin @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:bbb413c69ac6f77243a02049d2ccf494a1f20ea88fb8ca159b22c5bc0a4fdd95 +size 36751 diff --git a/ios/include/material/gizmo.c b/ios/include/material/gizmo.c new file mode 100644 index 00000000..55f4d6c0 --- /dev/null +++ b/ios/include/material/gizmo.c @@ -0,0 +1,1847 @@ +#include + +const uint8_t GIZMO_PACKAGE[] = { +// GIZMO +0x53, 0x52, 0x45, 0x56, 0x5f, 0x54, 0x41, 0x4d, 0x04, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x00, 0x54, 0x41, 0x45, 0x46, +0x5f, 0x54, 0x41, 0x4d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x45, 0x4d, 0x41, 0x4e, 0x5f, 0x54, 0x41, 0x4d, 0x06, 0x00, 0x00, +0x00, 0x47, 0x69, 0x7a, 0x6d, 0x6f, 0x00, 0x4c, 0x44, 0x4d, 0x53, 0x5f, 0x54, 0x41, 0x4d, 0x04, 0x00, 0x00, 0x00, 0x06, +0x00, 0x00, 0x00, 0x4e, 0x4d, 0x4f, 0x44, 0x5f, 0x54, 0x41, 0x4d, 0x01, 0x00, 0x00, 0x00, 0x00, 0x46, 0x49, 0x4e, 0x55, +0x5f, 0x54, 0x41, 0x4d, 0x98, 0x00, 0x00, 0x00, 0x09, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x55, 0x6e, 0x69, 0x66, 0x6f, 0x72, +0x6d, 0x73, 0x00, 0x00, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x55, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x73, 0x00, 0x01, +0x4c, 0x69, 0x67, 0x68, 0x74, 0x73, 0x55, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x73, 0x00, 0x04, 0x53, 0x68, 0x61, 0x64, +0x6f, 0x77, 0x55, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x73, 0x00, 0x05, 0x46, 0x72, 0x6f, 0x78, 0x65, 0x6c, 0x52, 0x65, +0x63, 0x6f, 0x72, 0x64, 0x55, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x73, 0x00, 0x06, 0x46, 0x72, 0x6f, 0x78, 0x65, 0x6c, +0x73, 0x55, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x73, 0x00, 0x07, 0x42, 0x6f, 0x6e, 0x65, 0x73, 0x55, 0x6e, 0x69, 0x66, +0x6f, 0x72, 0x6d, 0x73, 0x00, 0x02, 0x4d, 0x6f, 0x72, 0x70, 0x68, 0x69, 0x6e, 0x67, 0x55, 0x6e, 0x69, 0x66, 0x6f, 0x72, +0x6d, 0x73, 0x00, 0x03, 0x4d, 0x61, 0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x00, 0x08, +0x50, 0x4d, 0x41, 0x53, 0x5f, 0x54, 0x41, 0x4d, 0xa9, 0x00, 0x00, 0x00, 0x03, 0x00, 0x02, 0x07, 0x07, 0x01, 0x02, 0x09, +0x07, 0x00, 0x09, 0x00, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x5f, 0x73, 0x68, 0x61, 0x64, 0x6f, 0x77, 0x4d, 0x61, 0x70, 0x00, +0x01, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x5f, 0x69, 0x62, 0x6c, 0x44, 0x46, 0x47, 0x00, 0x02, 0x6c, 0x69, 0x67, 0x68, 0x74, +0x5f, 0x69, 0x62, 0x6c, 0x53, 0x70, 0x65, 0x63, 0x75, 0x6c, 0x61, 0x72, 0x00, 0x03, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x5f, +0x73, 0x73, 0x61, 0x6f, 0x00, 0x04, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x5f, 0x73, 0x73, 0x72, 0x00, 0x05, 0x6c, 0x69, 0x67, +0x68, 0x74, 0x5f, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x75, 0x72, 0x65, 0x00, 0x06, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x5f, +0x66, 0x6f, 0x67, 0x00, 0x07, 0x6d, 0x6f, 0x72, 0x70, 0x68, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x42, 0x75, 0x66, 0x66, +0x65, 0x72, 0x5f, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x00, 0x08, 0x6d, 0x6f, 0x72, 0x70, 0x68, 0x54, +0x61, 0x72, 0x67, 0x65, 0x74, 0x42, 0x75, 0x66, 0x66, 0x65, 0x72, 0x5f, 0x74, 0x61, 0x6e, 0x67, 0x65, 0x6e, 0x74, 0x73, +0x00, 0x20, 0x42, 0x49, 0x55, 0x5f, 0x54, 0x41, 0x4d, 0x3b, 0x00, 0x00, 0x00, 0x4d, 0x61, 0x74, 0x65, 0x72, 0x69, 0x61, +0x6c, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x74, 0x72, 0x61, 0x6e, +0x73, 0x66, 0x6f, 0x72, 0x6d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x02, 0x63, 0x6f, 0x6c, 0x6f, +0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x20, 0x42, 0x49, 0x53, 0x5f, 0x54, 0x41, 0x4d, +0x17, 0x00, 0x00, 0x00, 0x4d, 0x61, 0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x53, 0x4e, 0x4f, 0x43, 0x5f, 0x54, 0x41, 0x4d, 0x08, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x42, 0x55, 0x53, 0x5f, 0x54, 0x41, 0x4d, 0x17, 0x00, 0x00, 0x00, 0x4d, +0x61, 0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x53, 0x53, 0x4f, 0x44, 0x5f, 0x54, 0x41, 0x4d, 0x01, 0x00, 0x00, 0x00, 0x00, 0x49, 0x53, 0x4f, 0x44, 0x5f, +0x54, 0x41, 0x4d, 0x01, 0x00, 0x00, 0x00, 0x00, 0x4e, 0x45, 0x4c, 0x42, 0x5f, 0x54, 0x41, 0x4d, 0x01, 0x00, 0x00, 0x00, +0x00, 0x44, 0x4d, 0x52, 0x54, 0x5f, 0x54, 0x41, 0x4d, 0x01, 0x00, 0x00, 0x00, 0x00, 0x4c, 0x46, 0x45, 0x52, 0x5f, 0x54, +0x41, 0x4d, 0x01, 0x00, 0x00, 0x00, 0x00, 0x49, 0x52, 0x57, 0x43, 0x5f, 0x54, 0x41, 0x4d, 0x01, 0x00, 0x00, 0x00, 0x01, +0x53, 0x57, 0x45, 0x44, 0x5f, 0x54, 0x41, 0x4d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x49, 0x52, 0x57, 0x44, 0x5f, 0x54, 0x41, +0x4d, 0x01, 0x00, 0x00, 0x00, 0x00, 0x53, 0x45, 0x54, 0x44, 0x5f, 0x54, 0x41, 0x4d, 0x01, 0x00, 0x00, 0x00, 0x00, 0x54, +0x53, 0x4e, 0x49, 0x5f, 0x54, 0x41, 0x4d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x53, 0x43, 0x32, 0x41, 0x5f, 0x54, 0x41, 0x4d, +0x01, 0x00, 0x00, 0x00, 0x00, 0x4f, 0x43, 0x32, 0x41, 0x5f, 0x54, 0x41, 0x4d, 0x01, 0x00, 0x00, 0x00, 0x00, 0x4f, 0x4d, +0x55, 0x43, 0x5f, 0x54, 0x41, 0x4d, 0x01, 0x00, 0x00, 0x00, 0x00, 0x50, 0x4f, 0x52, 0x50, 0x5f, 0x54, 0x41, 0x4d, 0x08, +0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x49, 0x55, 0x55, 0x5f, 0x54, 0x41, 0x4d, 0x08, +0x00, 0x00, 0x00, 0xe9, 0xdd, 0x71, 0x15, 0x03, 0x83, 0x54, 0xec, 0x44, 0x41, 0x48, 0x53, 0x5f, 0x54, 0x41, 0x4d, 0x01, +0x00, 0x00, 0x00, 0x00, 0x4c, 0x4d, 0x48, 0x53, 0x5f, 0x54, 0x41, 0x4d, 0x01, 0x00, 0x00, 0x00, 0x00, 0x4d, 0x46, 0x45, +0x52, 0x5f, 0x54, 0x41, 0x4d, 0x01, 0x00, 0x00, 0x00, 0x00, 0x54, 0x46, 0x45, 0x52, 0x5f, 0x54, 0x41, 0x4d, 0x01, 0x00, +0x00, 0x00, 0x00, 0x52, 0x4f, 0x49, 0x43, 0x5f, 0x54, 0x41, 0x4d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x41, 0x51, 0x45, 0x52, +0x5f, 0x54, 0x41, 0x4d, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x41, 0x41, 0x50, 0x53, 0x5f, 0x54, 0x41, 0x4d, +0x01, 0x00, 0x00, 0x00, 0x00, 0x52, 0x41, 0x56, 0x53, 0x5f, 0x54, 0x41, 0x4d, 0x04, 0x00, 0x00, 0x00, 0x9a, 0x99, 0x19, +0x3e, 0x52, 0x48, 0x54, 0x53, 0x5f, 0x54, 0x41, 0x4d, 0x04, 0x00, 0x00, 0x00, 0xcd, 0xcc, 0x4c, 0x3e, 0x4f, 0x44, 0x45, +0x56, 0x5f, 0x54, 0x41, 0x4d, 0x01, 0x00, 0x00, 0x00, 0x03, 0x52, 0x54, 0x4e, 0x49, 0x5f, 0x54, 0x41, 0x4d, 0x01, 0x00, +0x00, 0x00, 0x00, 0x50, 0x44, 0x53, 0x43, 0x5f, 0x54, 0x41, 0x4d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x54, 0x58, 0x45, 0x54, +0x5f, 0x43, 0x49, 0x44, 0x49, 0x4f, 0x00, 0x00, 0x67, 0x02, 0x00, 0x00, 0x23, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, +0x20, 0x33, 0x30, 0x30, 0x20, 0x65, 0x73, 0x00, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x20, 0x4d, 0x61, 0x74, 0x65, 0x72, +0x69, 0x61, 0x6c, 0x56, 0x65, 0x72, 0x74, 0x65, 0x78, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x73, 0x00, 0x7b, 0x00, 0x76, 0x65, +0x63, 0x34, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x3b, 0x00, 0x7d, 0x3b, +0x00, 0x23, 0x69, 0x66, 0x6e, 0x64, 0x65, 0x66, 0x20, 0x53, 0x50, 0x49, 0x52, 0x56, 0x5f, 0x43, 0x52, 0x4f, 0x53, 0x53, +0x5f, 0x43, 0x4f, 0x4e, 0x53, 0x54, 0x41, 0x4e, 0x54, 0x5f, 0x49, 0x44, 0x5f, 0x35, 0x00, 0x23, 0x64, 0x65, 0x66, 0x69, +0x6e, 0x65, 0x20, 0x53, 0x50, 0x49, 0x52, 0x56, 0x5f, 0x43, 0x52, 0x4f, 0x53, 0x53, 0x5f, 0x43, 0x4f, 0x4e, 0x53, 0x54, +0x41, 0x4e, 0x54, 0x5f, 0x49, 0x44, 0x5f, 0x35, 0x20, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x00, 0x23, 0x65, 0x6e, 0x64, 0x69, +0x66, 0x00, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, 0x62, 0x6f, 0x6f, 0x6c, 0x20, 0x43, 0x4f, 0x4e, 0x46, 0x49, 0x47, 0x5f, +0x50, 0x4f, 0x57, 0x45, 0x52, 0x5f, 0x56, 0x52, 0x5f, 0x53, 0x48, 0x41, 0x44, 0x45, 0x52, 0x5f, 0x57, 0x4f, 0x52, 0x4b, +0x41, 0x52, 0x4f, 0x55, 0x4e, 0x44, 0x53, 0x20, 0x3d, 0x20, 0x53, 0x50, 0x49, 0x52, 0x56, 0x5f, 0x43, 0x52, 0x4f, 0x53, +0x53, 0x5f, 0x43, 0x4f, 0x4e, 0x53, 0x54, 0x41, 0x4e, 0x54, 0x5f, 0x49, 0x44, 0x5f, 0x35, 0x3b, 0x00, 0x6c, 0x61, 0x79, +0x6f, 0x75, 0x74, 0x28, 0x73, 0x74, 0x64, 0x31, 0x34, 0x30, 0x29, 0x20, 0x75, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x20, +0x46, 0x72, 0x61, 0x6d, 0x65, 0x55, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x73, 0x00, 0x6d, 0x61, 0x74, 0x34, 0x20, 0x61, +0x3b, 0x00, 0x6d, 0x61, 0x74, 0x34, 0x20, 0x62, 0x3b, 0x00, 0x6d, 0x61, 0x74, 0x34, 0x20, 0x63, 0x3b, 0x00, 0x6d, 0x61, +0x74, 0x34, 0x20, 0x64, 0x3b, 0x00, 0x6d, 0x61, 0x74, 0x34, 0x20, 0x65, 0x5b, 0x32, 0x5d, 0x3b, 0x00, 0x6d, 0x61, 0x74, +0x34, 0x20, 0x66, 0x3b, 0x00, 0x6d, 0x61, 0x74, 0x34, 0x20, 0x67, 0x3b, 0x00, 0x76, 0x65, 0x63, 0x34, 0x20, 0x68, 0x3b, +0x00, 0x76, 0x65, 0x63, 0x32, 0x20, 0x69, 0x3b, 0x00, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x6a, 0x3b, 0x00, 0x66, 0x6c, +0x6f, 0x61, 0x74, 0x20, 0x6b, 0x3b, 0x00, 0x76, 0x65, 0x63, 0x34, 0x20, 0x6c, 0x3b, 0x00, 0x76, 0x65, 0x63, 0x34, 0x20, +0x6d, 0x3b, 0x00, 0x76, 0x65, 0x63, 0x32, 0x20, 0x6e, 0x3b, 0x00, 0x76, 0x65, 0x63, 0x32, 0x20, 0x6f, 0x3b, 0x00, 0x6d, +0x65, 0x64, 0x69, 0x75, 0x6d, 0x70, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x70, 0x3b, 0x00, 0x6d, 0x65, 0x64, 0x69, +0x75, 0x6d, 0x70, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x71, 0x3b, 0x00, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x72, +0x3b, 0x00, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x73, 0x3b, 0x00, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x74, 0x3b, 0x00, +0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x75, 0x3b, 0x00, 0x6d, 0x65, 0x64, 0x69, 0x75, 0x6d, 0x70, 0x20, 0x66, 0x6c, 0x6f, +0x61, 0x74, 0x20, 0x76, 0x3b, 0x00, 0x6d, 0x65, 0x64, 0x69, 0x75, 0x6d, 0x70, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, +0x77, 0x3b, 0x00, 0x6d, 0x65, 0x64, 0x69, 0x75, 0x6d, 0x70, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x78, 0x3b, 0x00, +0x6d, 0x65, 0x64, 0x69, 0x75, 0x6d, 0x70, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x79, 0x3b, 0x00, 0x6d, 0x65, 0x64, +0x69, 0x75, 0x6d, 0x70, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x7a, 0x3b, 0x00, 0x6d, 0x65, 0x64, 0x69, 0x75, 0x6d, +0x70, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x61, 0x7a, 0x3b, 0x00, 0x6d, 0x65, 0x64, 0x69, 0x75, 0x6d, 0x70, 0x20, +0x76, 0x65, 0x63, 0x34, 0x20, 0x62, 0x7a, 0x3b, 0x00, 0x6d, 0x65, 0x64, 0x69, 0x75, 0x6d, 0x70, 0x20, 0x75, 0x76, 0x65, +0x63, 0x33, 0x20, 0x63, 0x7a, 0x3b, 0x00, 0x6d, 0x65, 0x64, 0x69, 0x75, 0x6d, 0x70, 0x20, 0x69, 0x6e, 0x74, 0x20, 0x64, +0x7a, 0x3b, 0x00, 0x6d, 0x65, 0x64, 0x69, 0x75, 0x6d, 0x70, 0x20, 0x76, 0x65, 0x63, 0x32, 0x20, 0x65, 0x7a, 0x3b, 0x00, +0x6d, 0x65, 0x64, 0x69, 0x75, 0x6d, 0x70, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x66, 0x7a, 0x3b, 0x00, 0x6d, 0x65, +0x64, 0x69, 0x75, 0x6d, 0x70, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x67, 0x7a, 0x3b, 0x00, 0x6d, 0x65, 0x64, 0x69, +0x75, 0x6d, 0x70, 0x20, 0x76, 0x65, 0x63, 0x33, 0x20, 0x68, 0x7a, 0x5b, 0x39, 0x5d, 0x3b, 0x00, 0x76, 0x65, 0x63, 0x33, +0x20, 0x69, 0x7a, 0x3b, 0x00, 0x6d, 0x65, 0x64, 0x69, 0x75, 0x6d, 0x70, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x6a, +0x7a, 0x3b, 0x00, 0x6d, 0x65, 0x64, 0x69, 0x75, 0x6d, 0x70, 0x20, 0x76, 0x65, 0x63, 0x34, 0x20, 0x6b, 0x7a, 0x3b, 0x00, +0x6d, 0x65, 0x64, 0x69, 0x75, 0x6d, 0x70, 0x20, 0x76, 0x65, 0x63, 0x34, 0x20, 0x6c, 0x7a, 0x3b, 0x00, 0x6d, 0x65, 0x64, +0x69, 0x75, 0x6d, 0x70, 0x20, 0x76, 0x65, 0x63, 0x32, 0x20, 0x6d, 0x7a, 0x3b, 0x00, 0x6d, 0x65, 0x64, 0x69, 0x75, 0x6d, +0x70, 0x20, 0x69, 0x6e, 0x74, 0x20, 0x6e, 0x7a, 0x3b, 0x00, 0x6d, 0x65, 0x64, 0x69, 0x75, 0x6d, 0x70, 0x20, 0x66, 0x6c, +0x6f, 0x61, 0x74, 0x20, 0x6f, 0x7a, 0x3b, 0x00, 0x76, 0x65, 0x63, 0x34, 0x20, 0x70, 0x7a, 0x3b, 0x00, 0x6d, 0x65, 0x64, +0x69, 0x75, 0x6d, 0x70, 0x20, 0x69, 0x6e, 0x74, 0x20, 0x71, 0x7a, 0x3b, 0x00, 0x6d, 0x65, 0x64, 0x69, 0x75, 0x6d, 0x70, +0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x72, 0x7a, 0x3b, 0x00, 0x6d, 0x65, 0x64, 0x69, 0x75, 0x6d, 0x70, 0x20, 0x66, +0x6c, 0x6f, 0x61, 0x74, 0x20, 0x73, 0x7a, 0x3b, 0x00, 0x6d, 0x65, 0x64, 0x69, 0x75, 0x6d, 0x70, 0x20, 0x66, 0x6c, 0x6f, +0x61, 0x74, 0x20, 0x74, 0x7a, 0x3b, 0x00, 0x6d, 0x65, 0x64, 0x69, 0x75, 0x6d, 0x70, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, +0x20, 0x75, 0x7a, 0x3b, 0x00, 0x6d, 0x65, 0x64, 0x69, 0x75, 0x6d, 0x70, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x76, +0x7a, 0x3b, 0x00, 0x6d, 0x65, 0x64, 0x69, 0x75, 0x6d, 0x70, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x77, 0x7a, 0x3b, +0x00, 0x6d, 0x65, 0x64, 0x69, 0x75, 0x6d, 0x70, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x78, 0x7a, 0x3b, 0x00, 0x76, 0x65, +0x63, 0x33, 0x20, 0x79, 0x7a, 0x3b, 0x00, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x7a, 0x7a, 0x3b, 0x00, 0x6d, 0x65, 0x64, +0x69, 0x75, 0x6d, 0x70, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x61, 0x7a, 0x7a, 0x3b, 0x00, 0x75, 0x69, 0x6e, 0x74, +0x20, 0x62, 0x7a, 0x7a, 0x3b, 0x00, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x63, 0x7a, 0x7a, 0x3b, 0x00, 0x66, 0x6c, 0x6f, +0x61, 0x74, 0x20, 0x64, 0x7a, 0x7a, 0x3b, 0x00, 0x6d, 0x65, 0x64, 0x69, 0x75, 0x6d, 0x70, 0x20, 0x76, 0x65, 0x63, 0x33, +0x20, 0x65, 0x7a, 0x7a, 0x3b, 0x00, 0x6d, 0x65, 0x64, 0x69, 0x75, 0x6d, 0x70, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, +0x66, 0x7a, 0x7a, 0x3b, 0x00, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x67, 0x7a, 0x7a, 0x3b, 0x00, 0x6d, 0x65, 0x64, 0x69, +0x75, 0x6d, 0x70, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x68, 0x7a, 0x7a, 0x3b, 0x00, 0x66, 0x6c, 0x6f, 0x61, 0x74, +0x20, 0x69, 0x7a, 0x7a, 0x3b, 0x00, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x6a, 0x7a, 0x7a, 0x3b, 0x00, 0x6d, 0x61, 0x74, +0x33, 0x20, 0x6b, 0x7a, 0x7a, 0x3b, 0x00, 0x6d, 0x61, 0x74, 0x34, 0x20, 0x6c, 0x7a, 0x7a, 0x3b, 0x00, 0x6d, 0x61, 0x74, +0x34, 0x20, 0x6d, 0x7a, 0x7a, 0x3b, 0x00, 0x6d, 0x65, 0x64, 0x69, 0x75, 0x6d, 0x70, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, +0x20, 0x6e, 0x7a, 0x7a, 0x3b, 0x00, 0x6d, 0x65, 0x64, 0x69, 0x75, 0x6d, 0x70, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, +0x6f, 0x7a, 0x7a, 0x3b, 0x00, 0x6d, 0x65, 0x64, 0x69, 0x75, 0x6d, 0x70, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x70, +0x7a, 0x7a, 0x3b, 0x00, 0x6d, 0x65, 0x64, 0x69, 0x75, 0x6d, 0x70, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x71, 0x7a, +0x7a, 0x3b, 0x00, 0x76, 0x65, 0x63, 0x34, 0x20, 0x72, 0x7a, 0x7a, 0x5b, 0x34, 0x5d, 0x3b, 0x00, 0x6d, 0x65, 0x64, 0x69, +0x75, 0x6d, 0x70, 0x20, 0x69, 0x6e, 0x74, 0x20, 0x73, 0x7a, 0x7a, 0x3b, 0x00, 0x6d, 0x65, 0x64, 0x69, 0x75, 0x6d, 0x70, +0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x74, 0x7a, 0x7a, 0x3b, 0x00, 0x6d, 0x65, 0x64, 0x69, 0x75, 0x6d, 0x70, 0x20, +0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x75, 0x7a, 0x7a, 0x3b, 0x00, 0x6d, 0x65, 0x64, 0x69, 0x75, 0x6d, 0x70, 0x20, 0x66, +0x6c, 0x6f, 0x61, 0x74, 0x20, 0x76, 0x7a, 0x7a, 0x3b, 0x00, 0x6d, 0x65, 0x64, 0x69, 0x75, 0x6d, 0x70, 0x20, 0x76, 0x65, +0x63, 0x34, 0x20, 0x77, 0x7a, 0x7a, 0x5b, 0x34, 0x38, 0x5d, 0x3b, 0x00, 0x7d, 0x20, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x55, +0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x73, 0x3b, 0x00, 0x6c, 0x61, 0x79, 0x6f, 0x75, 0x74, 0x28, 0x6c, 0x6f, 0x63, 0x61, +0x74, 0x69, 0x6f, 0x6e, 0x20, 0x3d, 0x20, 0x30, 0x29, 0x20, 0x69, 0x6e, 0x20, 0x76, 0x65, 0x63, 0x34, 0x20, 0x6d, 0x65, +0x73, 0x68, 0x5f, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x3b, 0x00, 0x66, 0x6c, 0x61, 0x74, 0x20, 0x6f, 0x75, +0x74, 0x20, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, +0x3b, 0x00, 0x6f, 0x75, 0x74, 0x20, 0x76, 0x65, 0x63, 0x34, 0x20, 0x76, 0x65, 0x72, 0x74, 0x65, 0x78, 0x5f, 0x77, 0x6f, +0x72, 0x6c, 0x64, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x3b, 0x00, 0x6f, 0x75, 0x74, 0x20, 0x76, 0x65, 0x63, +0x34, 0x20, 0x76, 0x65, 0x72, 0x74, 0x65, 0x78, 0x5f, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x3b, 0x00, 0x76, +0x6f, 0x69, 0x64, 0x20, 0x6d, 0x61, 0x69, 0x6e, 0x28, 0x29, 0x00, 0x69, 0x66, 0x20, 0x28, 0x43, 0x4f, 0x4e, 0x46, 0x49, +0x47, 0x5f, 0x50, 0x4f, 0x57, 0x45, 0x52, 0x5f, 0x56, 0x52, 0x5f, 0x53, 0x48, 0x41, 0x44, 0x45, 0x52, 0x5f, 0x57, 0x4f, +0x52, 0x4b, 0x41, 0x52, 0x4f, 0x55, 0x4e, 0x44, 0x53, 0x29, 0x00, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, +0x69, 0x6e, 0x64, 0x65, 0x78, 0x20, 0x3d, 0x20, 0x28, 0x31, 0x20, 0x2b, 0x20, 0x67, 0x6c, 0x5f, 0x49, 0x6e, 0x73, 0x74, +0x61, 0x6e, 0x63, 0x65, 0x49, 0x44, 0x29, 0x20, 0x2d, 0x20, 0x31, 0x3b, 0x00, 0x7d, 0x00, 0x65, 0x6c, 0x73, 0x65, 0x00, +0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x20, 0x3d, 0x20, 0x67, 0x6c, 0x5f, +0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x49, 0x44, 0x3b, 0x00, 0x76, 0x65, 0x63, 0x34, 0x20, 0x5f, 0x32, 0x39, +0x37, 0x20, 0x3d, 0x20, 0x6d, 0x65, 0x73, 0x68, 0x5f, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x3b, 0x00, 0x5f, +0x32, 0x39, 0x37, 0x2e, 0x7a, 0x20, 0x3d, 0x20, 0x28, 0x5f, 0x32, 0x39, 0x37, 0x2e, 0x7a, 0x20, 0x2a, 0x20, 0x28, 0x2d, +0x30, 0x2e, 0x35, 0x29, 0x29, 0x20, 0x2b, 0x20, 0x30, 0x2e, 0x35, 0x3b, 0x00, 0x76, 0x65, 0x63, 0x34, 0x20, 0x5f, 0x32, +0x37, 0x33, 0x20, 0x3d, 0x20, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x55, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x73, 0x2e, 0x66, +0x20, 0x2a, 0x20, 0x5f, 0x32, 0x39, 0x37, 0x3b, 0x00, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x5f, 0x32, 0x37, 0x35, 0x20, +0x3d, 0x20, 0x5f, 0x32, 0x37, 0x33, 0x2e, 0x77, 0x3b, 0x00, 0x76, 0x65, 0x63, 0x34, 0x20, 0x5f, 0x33, 0x32, 0x35, 0x3b, +0x00, 0x69, 0x66, 0x20, 0x28, 0x61, 0x62, 0x73, 0x28, 0x5f, 0x32, 0x37, 0x35, 0x29, 0x20, 0x3c, 0x20, 0x31, 0x2e, 0x30, +0x38, 0x34, 0x32, 0x30, 0x32, 0x32, 0x65, 0x2d, 0x31, 0x39, 0x29, 0x00, 0x76, 0x65, 0x63, 0x34, 0x20, 0x5f, 0x33, 0x31, +0x36, 0x20, 0x3d, 0x20, 0x5f, 0x32, 0x37, 0x33, 0x3b, 0x00, 0x5f, 0x33, 0x31, 0x36, 0x2e, 0x77, 0x20, 0x3d, 0x20, 0x28, +0x5f, 0x32, 0x37, 0x35, 0x20, 0x3c, 0x20, 0x30, 0x2e, 0x30, 0x29, 0x20, 0x3f, 0x20, 0x28, 0x2d, 0x31, 0x2e, 0x30, 0x38, +0x34, 0x32, 0x30, 0x32, 0x32, 0x65, 0x2d, 0x31, 0x39, 0x29, 0x20, 0x3a, 0x20, 0x31, 0x2e, 0x30, 0x38, 0x34, 0x32, 0x30, +0x32, 0x32, 0x65, 0x2d, 0x31, 0x39, 0x3b, 0x00, 0x5f, 0x33, 0x32, 0x35, 0x20, 0x3d, 0x20, 0x5f, 0x33, 0x31, 0x36, 0x3b, +0x00, 0x5f, 0x33, 0x32, 0x35, 0x20, 0x3d, 0x20, 0x5f, 0x32, 0x37, 0x33, 0x3b, 0x00, 0x76, 0x65, 0x63, 0x33, 0x20, 0x5f, +0x31, 0x38, 0x34, 0x20, 0x3d, 0x20, 0x4d, 0x61, 0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x56, 0x65, 0x72, 0x74, 0x65, 0x78, +0x49, 0x6e, 0x70, 0x75, 0x74, 0x73, 0x28, 0x5f, 0x33, 0x32, 0x35, 0x20, 0x2a, 0x20, 0x28, 0x31, 0x2e, 0x30, 0x20, 0x2f, +0x20, 0x5f, 0x33, 0x32, 0x35, 0x2e, 0x77, 0x29, 0x29, 0x2e, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x50, 0x6f, 0x73, 0x69, 0x74, +0x69, 0x6f, 0x6e, 0x2e, 0x78, 0x79, 0x7a, 0x3b, 0x00, 0x76, 0x65, 0x72, 0x74, 0x65, 0x78, 0x5f, 0x77, 0x6f, 0x72, 0x6c, +0x64, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x78, 0x20, 0x3d, 0x20, 0x5f, 0x31, 0x38, 0x34, 0x2e, 0x78, +0x3b, 0x00, 0x76, 0x65, 0x72, 0x74, 0x65, 0x78, 0x5f, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, +0x6f, 0x6e, 0x2e, 0x79, 0x20, 0x3d, 0x20, 0x5f, 0x31, 0x38, 0x34, 0x2e, 0x79, 0x3b, 0x00, 0x76, 0x65, 0x72, 0x74, 0x65, +0x78, 0x5f, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x7a, 0x20, 0x3d, 0x20, +0x5f, 0x31, 0x38, 0x34, 0x2e, 0x7a, 0x3b, 0x00, 0x76, 0x65, 0x72, 0x74, 0x65, 0x78, 0x5f, 0x70, 0x6f, 0x73, 0x69, 0x74, +0x69, 0x6f, 0x6e, 0x20, 0x3d, 0x20, 0x5f, 0x32, 0x39, 0x37, 0x3b, 0x00, 0x5f, 0x32, 0x39, 0x37, 0x2e, 0x7a, 0x20, 0x3d, +0x20, 0x28, 0x5f, 0x32, 0x39, 0x37, 0x2e, 0x7a, 0x20, 0x2a, 0x20, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x55, 0x6e, 0x69, 0x66, +0x6f, 0x72, 0x6d, 0x73, 0x2e, 0x69, 0x2e, 0x78, 0x29, 0x20, 0x2b, 0x20, 0x28, 0x5f, 0x32, 0x39, 0x37, 0x2e, 0x77, 0x20, +0x2a, 0x20, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x55, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x73, 0x2e, 0x69, 0x2e, 0x79, 0x29, +0x3b, 0x00, 0x67, 0x6c, 0x5f, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x3d, 0x20, 0x5f, 0x32, 0x39, 0x37, +0x3b, 0x00, 0x70, 0x72, 0x65, 0x63, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x6d, 0x65, 0x64, 0x69, 0x75, 0x6d, 0x70, 0x20, +0x66, 0x6c, 0x6f, 0x61, 0x74, 0x3b, 0x00, 0x70, 0x72, 0x65, 0x63, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x6d, 0x65, 0x64, +0x69, 0x75, 0x6d, 0x70, 0x20, 0x69, 0x6e, 0x74, 0x3b, 0x00, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x20, 0x4d, 0x61, 0x74, +0x65, 0x72, 0x69, 0x61, 0x6c, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x73, 0x00, 0x76, 0x65, 0x63, 0x34, 0x20, 0x62, 0x61, 0x73, +0x65, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x3b, 0x00, 0x76, 0x65, 0x63, 0x34, 0x20, 0x65, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x76, +0x65, 0x3b, 0x00, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x63, 0x6c, 0x65, 0x61, 0x72, 0x43, 0x6f, 0x61, 0x74, 0x3b, 0x00, +0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x63, 0x6c, 0x65, 0x61, 0x72, 0x43, 0x6f, 0x61, 0x74, 0x52, 0x6f, 0x75, 0x67, 0x68, +0x6e, 0x65, 0x73, 0x73, 0x3b, 0x00, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x61, 0x6e, 0x69, 0x73, 0x6f, 0x74, 0x72, 0x6f, +0x70, 0x79, 0x3b, 0x00, 0x76, 0x65, 0x63, 0x33, 0x20, 0x61, 0x6e, 0x69, 0x73, 0x6f, 0x74, 0x72, 0x6f, 0x70, 0x79, 0x44, +0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x3b, 0x00, 0x76, 0x65, 0x63, 0x34, 0x20, 0x5f, 0x33, 0x33, 0x39, 0x3b, +0x00, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x5f, 0x33, 0x34, 0x30, 0x3b, 0x00, 0x76, 0x65, 0x63, 0x33, 0x20, 0x5f, 0x33, +0x34, 0x31, 0x3b, 0x00, 0x6c, 0x61, 0x79, 0x6f, 0x75, 0x74, 0x28, 0x73, 0x74, 0x64, 0x31, 0x34, 0x30, 0x29, 0x20, 0x75, +0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x20, 0x4d, 0x61, 0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x50, 0x61, 0x72, 0x61, 0x6d, +0x73, 0x00, 0x68, 0x69, 0x67, 0x68, 0x70, 0x20, 0x6d, 0x61, 0x74, 0x34, 0x20, 0x61, 0x3b, 0x00, 0x76, 0x65, 0x63, 0x33, +0x20, 0x62, 0x3b, 0x00, 0x7d, 0x20, 0x6d, 0x61, 0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, +0x3b, 0x00, 0x6c, 0x61, 0x79, 0x6f, 0x75, 0x74, 0x28, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x3d, 0x20, +0x30, 0x29, 0x20, 0x6f, 0x75, 0x74, 0x20, 0x76, 0x65, 0x63, 0x34, 0x20, 0x66, 0x72, 0x61, 0x67, 0x43, 0x6f, 0x6c, 0x6f, +0x72, 0x3b, 0x00, 0x66, 0x72, 0x61, 0x67, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x20, 0x3d, 0x20, 0x4d, 0x61, 0x74, 0x65, 0x72, +0x69, 0x61, 0x6c, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x73, 0x28, 0x76, 0x65, 0x63, 0x34, 0x28, 0x6d, 0x61, 0x74, 0x65, 0x72, +0x69, 0x61, 0x6c, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x2e, 0x62, 0x2c, 0x20, 0x31, 0x2e, 0x30, 0x29, 0x2c, 0x20, 0x5f, +0x33, 0x33, 0x39, 0x2c, 0x20, 0x5f, 0x33, 0x34, 0x30, 0x2c, 0x20, 0x5f, 0x33, 0x34, 0x30, 0x2c, 0x20, 0x5f, 0x33, 0x34, +0x30, 0x2c, 0x20, 0x5f, 0x33, 0x34, 0x31, 0x29, 0x2e, 0x62, 0x61, 0x73, 0x65, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x3b, 0x00, +0x76, 0x65, 0x63, 0x34, 0x20, 0x5f, 0x35, 0x37, 0x36, 0x3b, 0x00, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x5f, 0x35, 0x37, +0x37, 0x3b, 0x00, 0x76, 0x65, 0x63, 0x33, 0x20, 0x5f, 0x35, 0x37, 0x38, 0x3b, 0x00, 0x68, 0x69, 0x67, 0x68, 0x70, 0x20, +0x6d, 0x61, 0x74, 0x34, 0x20, 0x62, 0x3b, 0x00, 0x68, 0x69, 0x67, 0x68, 0x70, 0x20, 0x6d, 0x61, 0x74, 0x34, 0x20, 0x63, +0x3b, 0x00, 0x68, 0x69, 0x67, 0x68, 0x70, 0x20, 0x6d, 0x61, 0x74, 0x34, 0x20, 0x64, 0x3b, 0x00, 0x68, 0x69, 0x67, 0x68, +0x70, 0x20, 0x6d, 0x61, 0x74, 0x34, 0x20, 0x65, 0x5b, 0x32, 0x5d, 0x3b, 0x00, 0x68, 0x69, 0x67, 0x68, 0x70, 0x20, 0x6d, +0x61, 0x74, 0x34, 0x20, 0x66, 0x3b, 0x00, 0x68, 0x69, 0x67, 0x68, 0x70, 0x20, 0x6d, 0x61, 0x74, 0x34, 0x20, 0x67, 0x3b, +0x00, 0x68, 0x69, 0x67, 0x68, 0x70, 0x20, 0x76, 0x65, 0x63, 0x34, 0x20, 0x68, 0x3b, 0x00, 0x68, 0x69, 0x67, 0x68, 0x70, +0x20, 0x76, 0x65, 0x63, 0x32, 0x20, 0x69, 0x3b, 0x00, 0x68, 0x69, 0x67, 0x68, 0x70, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, +0x20, 0x6a, 0x3b, 0x00, 0x68, 0x69, 0x67, 0x68, 0x70, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x6b, 0x3b, 0x00, 0x68, +0x69, 0x67, 0x68, 0x70, 0x20, 0x76, 0x65, 0x63, 0x34, 0x20, 0x6c, 0x3b, 0x00, 0x68, 0x69, 0x67, 0x68, 0x70, 0x20, 0x76, +0x65, 0x63, 0x34, 0x20, 0x6d, 0x3b, 0x00, 0x68, 0x69, 0x67, 0x68, 0x70, 0x20, 0x76, 0x65, 0x63, 0x32, 0x20, 0x6e, 0x3b, +0x00, 0x68, 0x69, 0x67, 0x68, 0x70, 0x20, 0x76, 0x65, 0x63, 0x32, 0x20, 0x6f, 0x3b, 0x00, 0x66, 0x6c, 0x6f, 0x61, 0x74, +0x20, 0x70, 0x3b, 0x00, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x71, 0x3b, 0x00, 0x68, 0x69, 0x67, 0x68, 0x70, 0x20, 0x66, +0x6c, 0x6f, 0x61, 0x74, 0x20, 0x72, 0x3b, 0x00, 0x68, 0x69, 0x67, 0x68, 0x70, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, +0x73, 0x3b, 0x00, 0x68, 0x69, 0x67, 0x68, 0x70, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x74, 0x3b, 0x00, 0x68, 0x69, +0x67, 0x68, 0x70, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x75, 0x3b, 0x00, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x76, +0x3b, 0x00, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x77, 0x3b, 0x00, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x78, 0x3b, 0x00, +0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x79, 0x3b, 0x00, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x7a, 0x3b, 0x00, 0x66, 0x6c, +0x6f, 0x61, 0x74, 0x20, 0x61, 0x7a, 0x3b, 0x00, 0x76, 0x65, 0x63, 0x34, 0x20, 0x62, 0x7a, 0x3b, 0x00, 0x75, 0x76, 0x65, +0x63, 0x33, 0x20, 0x63, 0x7a, 0x3b, 0x00, 0x69, 0x6e, 0x74, 0x20, 0x64, 0x7a, 0x3b, 0x00, 0x76, 0x65, 0x63, 0x32, 0x20, +0x65, 0x7a, 0x3b, 0x00, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x66, 0x7a, 0x3b, 0x00, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, +0x67, 0x7a, 0x3b, 0x00, 0x76, 0x65, 0x63, 0x33, 0x20, 0x68, 0x7a, 0x5b, 0x39, 0x5d, 0x3b, 0x00, 0x68, 0x69, 0x67, 0x68, +0x70, 0x20, 0x76, 0x65, 0x63, 0x33, 0x20, 0x69, 0x7a, 0x3b, 0x00, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x6a, 0x7a, 0x3b, +0x00, 0x76, 0x65, 0x63, 0x34, 0x20, 0x6b, 0x7a, 0x3b, 0x00, 0x76, 0x65, 0x63, 0x34, 0x20, 0x6c, 0x7a, 0x3b, 0x00, 0x76, +0x65, 0x63, 0x32, 0x20, 0x6d, 0x7a, 0x3b, 0x00, 0x69, 0x6e, 0x74, 0x20, 0x6e, 0x7a, 0x3b, 0x00, 0x66, 0x6c, 0x6f, 0x61, +0x74, 0x20, 0x6f, 0x7a, 0x3b, 0x00, 0x68, 0x69, 0x67, 0x68, 0x70, 0x20, 0x76, 0x65, 0x63, 0x34, 0x20, 0x70, 0x7a, 0x3b, +0x00, 0x69, 0x6e, 0x74, 0x20, 0x71, 0x7a, 0x3b, 0x00, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x72, 0x7a, 0x3b, 0x00, 0x66, +0x6c, 0x6f, 0x61, 0x74, 0x20, 0x73, 0x7a, 0x3b, 0x00, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x74, 0x7a, 0x3b, 0x00, 0x66, +0x6c, 0x6f, 0x61, 0x74, 0x20, 0x75, 0x7a, 0x3b, 0x00, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x76, 0x7a, 0x3b, 0x00, 0x66, +0x6c, 0x6f, 0x61, 0x74, 0x20, 0x77, 0x7a, 0x3b, 0x00, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x78, 0x7a, 0x3b, 0x00, 0x68, 0x69, +0x67, 0x68, 0x70, 0x20, 0x76, 0x65, 0x63, 0x33, 0x20, 0x79, 0x7a, 0x3b, 0x00, 0x68, 0x69, 0x67, 0x68, 0x70, 0x20, 0x66, +0x6c, 0x6f, 0x61, 0x74, 0x20, 0x7a, 0x7a, 0x3b, 0x00, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x61, 0x7a, 0x7a, 0x3b, 0x00, +0x68, 0x69, 0x67, 0x68, 0x70, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x62, 0x7a, 0x7a, 0x3b, 0x00, 0x68, 0x69, 0x67, 0x68, +0x70, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x63, 0x7a, 0x7a, 0x3b, 0x00, 0x68, 0x69, 0x67, 0x68, 0x70, 0x20, 0x66, +0x6c, 0x6f, 0x61, 0x74, 0x20, 0x64, 0x7a, 0x7a, 0x3b, 0x00, 0x76, 0x65, 0x63, 0x33, 0x20, 0x65, 0x7a, 0x7a, 0x3b, 0x00, +0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x66, 0x7a, 0x7a, 0x3b, 0x00, 0x68, 0x69, 0x67, 0x68, 0x70, 0x20, 0x66, 0x6c, 0x6f, +0x61, 0x74, 0x20, 0x67, 0x7a, 0x7a, 0x3b, 0x00, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x68, 0x7a, 0x7a, 0x3b, 0x00, 0x68, +0x69, 0x67, 0x68, 0x70, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x69, 0x7a, 0x7a, 0x3b, 0x00, 0x68, 0x69, 0x67, 0x68, +0x70, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x6a, 0x7a, 0x7a, 0x3b, 0x00, 0x68, 0x69, 0x67, 0x68, 0x70, 0x20, 0x6d, +0x61, 0x74, 0x33, 0x20, 0x6b, 0x7a, 0x7a, 0x3b, 0x00, 0x68, 0x69, 0x67, 0x68, 0x70, 0x20, 0x6d, 0x61, 0x74, 0x34, 0x20, +0x6c, 0x7a, 0x7a, 0x3b, 0x00, 0x68, 0x69, 0x67, 0x68, 0x70, 0x20, 0x6d, 0x61, 0x74, 0x34, 0x20, 0x6d, 0x7a, 0x7a, 0x3b, +0x00, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x6e, 0x7a, 0x7a, 0x3b, 0x00, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x6f, 0x7a, +0x7a, 0x3b, 0x00, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x70, 0x7a, 0x7a, 0x3b, 0x00, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, +0x71, 0x7a, 0x7a, 0x3b, 0x00, 0x68, 0x69, 0x67, 0x68, 0x70, 0x20, 0x76, 0x65, 0x63, 0x34, 0x20, 0x72, 0x7a, 0x7a, 0x5b, +0x34, 0x5d, 0x3b, 0x00, 0x69, 0x6e, 0x74, 0x20, 0x73, 0x7a, 0x7a, 0x3b, 0x00, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x74, +0x7a, 0x7a, 0x3b, 0x00, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x75, 0x7a, 0x7a, 0x3b, 0x00, 0x66, 0x6c, 0x6f, 0x61, 0x74, +0x20, 0x76, 0x7a, 0x7a, 0x3b, 0x00, 0x76, 0x65, 0x63, 0x34, 0x20, 0x77, 0x7a, 0x7a, 0x5b, 0x34, 0x38, 0x5d, 0x3b, 0x00, +0x75, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x20, 0x6d, 0x65, 0x64, 0x69, 0x75, 0x6d, 0x70, 0x20, 0x73, 0x61, 0x6d, 0x70, +0x6c, 0x65, 0x72, 0x43, 0x75, 0x62, 0x65, 0x20, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x5f, 0x66, 0x6f, 0x67, 0x3b, 0x00, 0x69, +0x6e, 0x20, 0x68, 0x69, 0x67, 0x68, 0x70, 0x20, 0x76, 0x65, 0x63, 0x34, 0x20, 0x76, 0x65, 0x72, 0x74, 0x65, 0x78, 0x5f, +0x77, 0x6f, 0x72, 0x6c, 0x64, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x3b, 0x00, 0x76, 0x65, 0x63, 0x34, 0x20, +0x66, 0x6f, 0x67, 0x28, 0x69, 0x6e, 0x6f, 0x75, 0x74, 0x20, 0x76, 0x65, 0x63, 0x34, 0x20, 0x63, 0x6f, 0x6c, 0x6f, 0x72, +0x2c, 0x20, 0x68, 0x69, 0x67, 0x68, 0x70, 0x20, 0x76, 0x65, 0x63, 0x33, 0x20, 0x76, 0x69, 0x65, 0x77, 0x29, 0x00, 0x68, +0x69, 0x67, 0x68, 0x70, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x5f, 0x31, 0x38, 0x37, 0x20, 0x3d, 0x20, 0x6c, 0x65, +0x6e, 0x67, 0x74, 0x68, 0x28, 0x76, 0x69, 0x65, 0x77, 0x29, 0x3b, 0x00, 0x69, 0x66, 0x20, 0x28, 0x5f, 0x31, 0x38, 0x37, +0x20, 0x3e, 0x20, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x55, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x73, 0x2e, 0x64, 0x7a, 0x7a, +0x29, 0x00, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x3b, 0x00, 0x68, 0x69, 0x67, 0x68, +0x70, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x5f, 0x32, 0x31, 0x34, 0x20, 0x3d, 0x20, 0x66, 0x72, 0x61, 0x6d, 0x65, +0x55, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x73, 0x2e, 0x63, 0x7a, 0x7a, 0x20, 0x2a, 0x20, 0x76, 0x69, 0x65, 0x77, 0x2e, +0x79, 0x3b, 0x00, 0x68, 0x69, 0x67, 0x68, 0x70, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x5f, 0x35, 0x38, 0x38, 0x3b, +0x00, 0x69, 0x66, 0x20, 0x28, 0x61, 0x62, 0x73, 0x28, 0x5f, 0x32, 0x31, 0x34, 0x29, 0x20, 0x3e, 0x20, 0x30, 0x2e, 0x30, +0x30, 0x31, 0x32, 0x35, 0x29, 0x00, 0x5f, 0x35, 0x38, 0x38, 0x20, 0x3d, 0x20, 0x28, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x55, +0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x73, 0x2e, 0x79, 0x7a, 0x2e, 0x7a, 0x20, 0x2d, 0x20, 0x28, 0x66, 0x72, 0x61, 0x6d, +0x65, 0x55, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x73, 0x2e, 0x79, 0x7a, 0x2e, 0x78, 0x20, 0x2a, 0x20, 0x65, 0x78, 0x70, +0x28, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x55, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x73, 0x2e, 0x79, 0x7a, 0x2e, 0x79, 0x20, +0x2d, 0x20, 0x5f, 0x32, 0x31, 0x34, 0x29, 0x29, 0x29, 0x20, 0x2f, 0x20, 0x5f, 0x32, 0x31, 0x34, 0x3b, 0x00, 0x5f, 0x35, +0x38, 0x38, 0x20, 0x3d, 0x20, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x55, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x73, 0x2e, 0x79, +0x7a, 0x2e, 0x7a, 0x3b, 0x00, 0x68, 0x69, 0x67, 0x68, 0x70, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x5f, 0x32, 0x34, +0x37, 0x20, 0x3d, 0x20, 0x65, 0x78, 0x70, 0x28, 0x2d, 0x28, 0x5f, 0x35, 0x38, 0x38, 0x20, 0x2a, 0x20, 0x6d, 0x61, 0x78, +0x28, 0x5f, 0x31, 0x38, 0x37, 0x20, 0x2d, 0x20, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x55, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, +0x73, 0x2e, 0x7a, 0x7a, 0x2c, 0x20, 0x30, 0x2e, 0x30, 0x29, 0x29, 0x29, 0x3b, 0x00, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, +0x6d, 0x70, 0x5f, 0x63, 0x6f, 0x70, 0x79, 0x5f, 0x32, 0x34, 0x37, 0x20, 0x3d, 0x20, 0x5f, 0x32, 0x34, 0x37, 0x3b, 0x00, +0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x5f, 0x32, 0x35, 0x34, 0x20, 0x3d, 0x20, 0x6d, 0x69, 0x6e, 0x28, 0x31, 0x2e, 0x30, +0x20, 0x2d, 0x20, 0x6d, 0x70, 0x5f, 0x63, 0x6f, 0x70, 0x79, 0x5f, 0x32, 0x34, 0x37, 0x2c, 0x20, 0x66, 0x72, 0x61, 0x6d, +0x65, 0x55, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x73, 0x2e, 0x61, 0x7a, 0x7a, 0x29, 0x3b, 0x00, 0x76, 0x65, 0x63, 0x33, +0x20, 0x5f, 0x35, 0x38, 0x39, 0x3b, 0x00, 0x69, 0x66, 0x20, 0x28, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x55, 0x6e, 0x69, 0x66, +0x6f, 0x72, 0x6d, 0x73, 0x2e, 0x66, 0x7a, 0x7a, 0x20, 0x3e, 0x20, 0x30, 0x2e, 0x30, 0x29, 0x00, 0x68, 0x69, 0x67, 0x68, +0x70, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x5f, 0x32, 0x37, 0x34, 0x20, 0x3d, 0x20, 0x28, 0x5f, 0x31, 0x38, 0x37, +0x20, 0x2a, 0x20, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x55, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x73, 0x2e, 0x69, 0x7a, 0x7a, +0x29, 0x20, 0x2d, 0x20, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x55, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x73, 0x2e, 0x6a, 0x7a, +0x7a, 0x3b, 0x00, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x6d, 0x70, 0x5f, 0x63, 0x6f, 0x70, 0x79, 0x5f, 0x32, 0x37, 0x34, +0x20, 0x3d, 0x20, 0x5f, 0x32, 0x37, 0x34, 0x3b, 0x00, 0x68, 0x69, 0x67, 0x68, 0x70, 0x20, 0x76, 0x65, 0x63, 0x32, 0x20, +0x5f, 0x32, 0x38, 0x31, 0x20, 0x3d, 0x20, 0x75, 0x6e, 0x70, 0x61, 0x63, 0x6b, 0x48, 0x61, 0x6c, 0x66, 0x32, 0x78, 0x31, +0x36, 0x28, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x55, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x73, 0x2e, 0x62, 0x7a, 0x7a, 0x29, +0x3b, 0x00, 0x5f, 0x35, 0x38, 0x39, 0x20, 0x3d, 0x20, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x55, 0x6e, 0x69, 0x66, 0x6f, 0x72, +0x6d, 0x73, 0x2e, 0x65, 0x7a, 0x7a, 0x20, 0x2a, 0x20, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x4c, 0x6f, 0x64, 0x28, +0x6c, 0x69, 0x67, 0x68, 0x74, 0x5f, 0x66, 0x6f, 0x67, 0x2c, 0x20, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x73, 0x65, +0x28, 0x6d, 0x61, 0x74, 0x33, 0x28, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x55, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x73, 0x2e, +0x67, 0x5b, 0x30, 0x5d, 0x2e, 0x78, 0x79, 0x7a, 0x2c, 0x20, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x55, 0x6e, 0x69, 0x66, 0x6f, +0x72, 0x6d, 0x73, 0x2e, 0x67, 0x5b, 0x31, 0x5d, 0x2e, 0x78, 0x79, 0x7a, 0x2c, 0x20, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x55, +0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x73, 0x2e, 0x67, 0x5b, 0x32, 0x5d, 0x2e, 0x78, 0x79, 0x7a, 0x29, 0x29, 0x20, 0x2a, +0x20, 0x76, 0x69, 0x65, 0x77, 0x2c, 0x20, 0x6d, 0x69, 0x78, 0x28, 0x5f, 0x32, 0x38, 0x31, 0x2e, 0x79, 0x2c, 0x20, 0x5f, +0x32, 0x38, 0x31, 0x2e, 0x78, 0x2c, 0x20, 0x63, 0x6c, 0x61, 0x6d, 0x70, 0x28, 0x6d, 0x70, 0x5f, 0x63, 0x6f, 0x70, 0x79, +0x5f, 0x32, 0x37, 0x34, 0x2c, 0x20, 0x30, 0x2e, 0x30, 0x2c, 0x20, 0x31, 0x2e, 0x30, 0x29, 0x29, 0x29, 0x2e, 0x78, 0x79, +0x7a, 0x3b, 0x00, 0x5f, 0x35, 0x38, 0x39, 0x20, 0x3d, 0x20, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x55, 0x6e, 0x69, 0x66, 0x6f, +0x72, 0x6d, 0x73, 0x2e, 0x65, 0x7a, 0x7a, 0x3b, 0x00, 0x76, 0x65, 0x63, 0x33, 0x20, 0x5f, 0x33, 0x32, 0x32, 0x20, 0x3d, +0x20, 0x5f, 0x35, 0x38, 0x39, 0x20, 0x2a, 0x20, 0x28, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x55, 0x6e, 0x69, 0x66, 0x6f, 0x72, +0x6d, 0x73, 0x2e, 0x66, 0x7a, 0x20, 0x2a, 0x20, 0x5f, 0x32, 0x35, 0x34, 0x29, 0x3b, 0x00, 0x76, 0x65, 0x63, 0x33, 0x20, +0x5f, 0x35, 0x39, 0x31, 0x3b, 0x00, 0x69, 0x66, 0x20, 0x28, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x55, 0x6e, 0x69, 0x66, 0x6f, +0x72, 0x6d, 0x73, 0x2e, 0x68, 0x7a, 0x7a, 0x20, 0x3e, 0x20, 0x30, 0x2e, 0x30, 0x29, 0x00, 0x68, 0x69, 0x67, 0x68, 0x70, +0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x5f, 0x33, 0x34, 0x31, 0x20, 0x3d, 0x20, 0x65, 0x78, 0x70, 0x28, 0x2d, 0x28, +0x5f, 0x35, 0x38, 0x38, 0x20, 0x2a, 0x20, 0x6d, 0x61, 0x78, 0x28, 0x5f, 0x31, 0x38, 0x37, 0x20, 0x2d, 0x20, 0x66, 0x72, +0x61, 0x6d, 0x65, 0x55, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x73, 0x2e, 0x67, 0x7a, 0x7a, 0x2c, 0x20, 0x30, 0x2e, 0x30, +0x29, 0x29, 0x29, 0x3b, 0x00, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x6d, 0x70, 0x5f, 0x63, 0x6f, 0x70, 0x79, 0x5f, 0x33, +0x34, 0x31, 0x20, 0x3d, 0x20, 0x5f, 0x33, 0x34, 0x31, 0x3b, 0x00, 0x68, 0x69, 0x67, 0x68, 0x70, 0x20, 0x66, 0x6c, 0x6f, +0x61, 0x74, 0x20, 0x5f, 0x33, 0x35, 0x37, 0x20, 0x3d, 0x20, 0x6d, 0x61, 0x78, 0x28, 0x64, 0x6f, 0x74, 0x28, 0x6e, 0x6f, +0x72, 0x6d, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x28, 0x76, 0x69, 0x65, 0x77, 0x29, 0x2c, 0x20, 0x66, 0x72, 0x61, 0x6d, 0x65, +0x55, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x73, 0x2e, 0x69, 0x7a, 0x29, 0x2c, 0x20, 0x30, 0x2e, 0x30, 0x29, 0x3b, 0x00, +0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x6d, 0x70, 0x5f, 0x63, 0x6f, 0x70, 0x79, 0x5f, 0x33, 0x35, 0x37, 0x20, 0x3d, 0x20, +0x5f, 0x33, 0x35, 0x37, 0x3b, 0x00, 0x5f, 0x35, 0x39, 0x31, 0x20, 0x3d, 0x20, 0x5f, 0x33, 0x32, 0x32, 0x20, 0x2b, 0x20, +0x28, 0x28, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x55, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x73, 0x2e, 0x6b, 0x7a, 0x2e, 0x78, +0x79, 0x7a, 0x20, 0x2a, 0x20, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x55, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x73, 0x2e, 0x6b, +0x7a, 0x2e, 0x77, 0x29, 0x20, 0x2a, 0x20, 0x28, 0x70, 0x6f, 0x77, 0x28, 0x6d, 0x70, 0x5f, 0x63, 0x6f, 0x70, 0x79, 0x5f, +0x33, 0x35, 0x37, 0x2c, 0x20, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x55, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x73, 0x2e, 0x68, +0x7a, 0x7a, 0x29, 0x20, 0x2a, 0x20, 0x28, 0x31, 0x2e, 0x30, 0x20, 0x2d, 0x20, 0x6d, 0x70, 0x5f, 0x63, 0x6f, 0x70, 0x79, +0x5f, 0x33, 0x34, 0x31, 0x29, 0x29, 0x29, 0x3b, 0x00, 0x5f, 0x35, 0x39, 0x31, 0x20, 0x3d, 0x20, 0x5f, 0x33, 0x32, 0x32, +0x3b, 0x00, 0x76, 0x65, 0x63, 0x34, 0x20, 0x5f, 0x33, 0x37, 0x31, 0x20, 0x3d, 0x20, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x3b, +0x00, 0x76, 0x65, 0x63, 0x33, 0x20, 0x5f, 0x33, 0x37, 0x37, 0x20, 0x3d, 0x20, 0x28, 0x5f, 0x33, 0x37, 0x31, 0x2e, 0x78, +0x79, 0x7a, 0x20, 0x2a, 0x20, 0x28, 0x31, 0x2e, 0x30, 0x20, 0x2d, 0x20, 0x5f, 0x32, 0x35, 0x34, 0x29, 0x29, 0x20, 0x2b, +0x20, 0x5f, 0x35, 0x39, 0x31, 0x3b, 0x00, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x2e, 0x78, 0x20, 0x3d, 0x20, 0x5f, 0x33, 0x37, +0x37, 0x2e, 0x78, 0x3b, 0x00, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x2e, 0x79, 0x20, 0x3d, 0x20, 0x5f, 0x33, 0x37, 0x37, 0x2e, +0x79, 0x3b, 0x00, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x2e, 0x7a, 0x20, 0x3d, 0x20, 0x5f, 0x33, 0x37, 0x37, 0x2e, 0x7a, 0x3b, +0x00, 0x66, 0x72, 0x61, 0x67, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x20, 0x3d, 0x20, 0x4d, 0x61, 0x74, 0x65, 0x72, 0x69, 0x61, +0x6c, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x73, 0x28, 0x76, 0x65, 0x63, 0x34, 0x28, 0x6d, 0x61, 0x74, 0x65, 0x72, 0x69, 0x61, +0x6c, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x2e, 0x62, 0x2c, 0x20, 0x31, 0x2e, 0x30, 0x29, 0x2c, 0x20, 0x5f, 0x35, 0x37, +0x36, 0x2c, 0x20, 0x5f, 0x35, 0x37, 0x37, 0x2c, 0x20, 0x5f, 0x35, 0x37, 0x37, 0x2c, 0x20, 0x5f, 0x35, 0x37, 0x37, 0x2c, +0x20, 0x5f, 0x35, 0x37, 0x38, 0x29, 0x2e, 0x62, 0x61, 0x73, 0x65, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x3b, 0x00, 0x76, 0x65, +0x63, 0x34, 0x20, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x20, 0x3d, 0x20, 0x66, 0x72, 0x61, 0x67, 0x43, 0x6f, 0x6c, 0x6f, 0x72, +0x3b, 0x00, 0x68, 0x69, 0x67, 0x68, 0x70, 0x20, 0x76, 0x65, 0x63, 0x33, 0x20, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x5f, 0x31, +0x20, 0x3d, 0x20, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x55, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x73, 0x2e, 0x6b, 0x7a, 0x7a, +0x20, 0x2a, 0x20, 0x28, 0x76, 0x65, 0x72, 0x74, 0x65, 0x78, 0x5f, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x50, 0x6f, 0x73, 0x69, +0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x78, 0x79, 0x7a, 0x20, 0x2d, 0x20, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x55, 0x6e, 0x69, 0x66, +0x6f, 0x72, 0x6d, 0x73, 0x2e, 0x62, 0x5b, 0x33, 0x5d, 0x2e, 0x78, 0x79, 0x7a, 0x29, 0x3b, 0x00, 0x76, 0x65, 0x63, 0x34, +0x20, 0x5f, 0x34, 0x34, 0x32, 0x20, 0x3d, 0x20, 0x66, 0x6f, 0x67, 0x28, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x2c, 0x20, 0x70, +0x61, 0x72, 0x61, 0x6d, 0x5f, 0x31, 0x29, 0x3b, 0x00, 0x66, 0x72, 0x61, 0x67, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x20, 0x3d, +0x20, 0x5f, 0x34, 0x34, 0x32, 0x3b, 0x00, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x20, 0x50, 0x65, 0x72, 0x52, 0x65, 0x6e, +0x64, 0x65, 0x72, 0x61, 0x62, 0x6c, 0x65, 0x44, 0x61, 0x74, 0x61, 0x00, 0x68, 0x69, 0x67, 0x68, 0x70, 0x20, 0x6d, 0x61, +0x74, 0x34, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x46, 0x72, 0x6f, 0x6d, 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x4d, 0x61, 0x74, +0x72, 0x69, 0x78, 0x3b, 0x00, 0x68, 0x69, 0x67, 0x68, 0x70, 0x20, 0x6d, 0x61, 0x74, 0x33, 0x20, 0x77, 0x6f, 0x72, 0x6c, +0x64, 0x46, 0x72, 0x6f, 0x6d, 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x4e, 0x6f, 0x72, 0x6d, 0x61, 0x6c, 0x4d, 0x61, 0x74, 0x72, +0x69, 0x78, 0x3b, 0x00, 0x68, 0x69, 0x67, 0x68, 0x70, 0x20, 0x69, 0x6e, 0x74, 0x20, 0x6d, 0x6f, 0x72, 0x70, 0x68, 0x54, +0x61, 0x72, 0x67, 0x65, 0x74, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x3b, 0x00, 0x68, 0x69, 0x67, 0x68, 0x70, 0x20, 0x69, 0x6e, +0x74, 0x20, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x3b, 0x00, 0x68, 0x69, 0x67, +0x68, 0x70, 0x20, 0x69, 0x6e, 0x74, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x49, 0x64, 0x3b, 0x00, 0x68, 0x69, 0x67, +0x68, 0x70, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x75, 0x73, 0x65, 0x72, 0x44, 0x61, 0x74, 0x61, 0x3b, 0x00, 0x68, +0x69, 0x67, 0x68, 0x70, 0x20, 0x76, 0x65, 0x63, 0x34, 0x20, 0x72, 0x65, 0x73, 0x65, 0x72, 0x76, 0x65, 0x64, 0x5b, 0x38, +0x5d, 0x3b, 0x00, 0x23, 0x69, 0x66, 0x6e, 0x64, 0x65, 0x66, 0x20, 0x53, 0x50, 0x49, 0x52, 0x56, 0x5f, 0x43, 0x52, 0x4f, +0x53, 0x53, 0x5f, 0x43, 0x4f, 0x4e, 0x53, 0x54, 0x41, 0x4e, 0x54, 0x5f, 0x49, 0x44, 0x5f, 0x31, 0x00, 0x23, 0x64, 0x65, +0x66, 0x69, 0x6e, 0x65, 0x20, 0x53, 0x50, 0x49, 0x52, 0x56, 0x5f, 0x43, 0x52, 0x4f, 0x53, 0x53, 0x5f, 0x43, 0x4f, 0x4e, +0x53, 0x54, 0x41, 0x4e, 0x54, 0x5f, 0x49, 0x44, 0x5f, 0x31, 0x20, 0x36, 0x34, 0x00, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x20, +0x69, 0x6e, 0x74, 0x20, 0x43, 0x4f, 0x4e, 0x46, 0x49, 0x47, 0x5f, 0x4d, 0x41, 0x58, 0x5f, 0x49, 0x4e, 0x53, 0x54, 0x41, +0x4e, 0x43, 0x45, 0x53, 0x20, 0x3d, 0x20, 0x53, 0x50, 0x49, 0x52, 0x56, 0x5f, 0x43, 0x52, 0x4f, 0x53, 0x53, 0x5f, 0x43, +0x4f, 0x4e, 0x53, 0x54, 0x41, 0x4e, 0x54, 0x5f, 0x49, 0x44, 0x5f, 0x31, 0x3b, 0x00, 0x69, 0x6e, 0x74, 0x20, 0x5f, 0x31, +0x37, 0x37, 0x3b, 0x00, 0x6c, 0x61, 0x79, 0x6f, 0x75, 0x74, 0x28, 0x73, 0x74, 0x64, 0x31, 0x34, 0x30, 0x29, 0x20, 0x75, +0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x20, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x55, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, +0x73, 0x00, 0x50, 0x65, 0x72, 0x52, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x61, 0x62, 0x6c, 0x65, 0x44, 0x61, 0x74, 0x61, 0x20, +0x61, 0x5b, 0x43, 0x4f, 0x4e, 0x46, 0x49, 0x47, 0x5f, 0x4d, 0x41, 0x58, 0x5f, 0x49, 0x4e, 0x53, 0x54, 0x41, 0x4e, 0x43, +0x45, 0x53, 0x5d, 0x3b, 0x00, 0x7d, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x55, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, +0x73, 0x3b, 0x00, 0x6c, 0x61, 0x79, 0x6f, 0x75, 0x74, 0x28, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x3d, +0x20, 0x30, 0x29, 0x20, 0x6f, 0x75, 0x74, 0x20, 0x68, 0x69, 0x67, 0x68, 0x70, 0x20, 0x76, 0x65, 0x63, 0x32, 0x20, 0x6f, +0x75, 0x74, 0x50, 0x69, 0x63, 0x6b, 0x69, 0x6e, 0x67, 0x3b, 0x00, 0x69, 0x6e, 0x20, 0x68, 0x69, 0x67, 0x68, 0x70, 0x20, +0x76, 0x65, 0x63, 0x34, 0x20, 0x76, 0x65, 0x72, 0x74, 0x65, 0x78, 0x5f, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, +0x3b, 0x00, 0x6f, 0x75, 0x74, 0x50, 0x69, 0x63, 0x6b, 0x69, 0x6e, 0x67, 0x2e, 0x78, 0x20, 0x3d, 0x20, 0x69, 0x6e, 0x74, +0x42, 0x69, 0x74, 0x73, 0x54, 0x6f, 0x46, 0x6c, 0x6f, 0x61, 0x74, 0x28, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x55, 0x6e, +0x69, 0x66, 0x6f, 0x72, 0x6d, 0x73, 0x2e, 0x61, 0x5b, 0x28, 0x28, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x55, 0x6e, 0x69, +0x66, 0x6f, 0x72, 0x6d, 0x73, 0x2e, 0x61, 0x5b, 0x30, 0x5d, 0x2e, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x43, 0x68, 0x61, 0x6e, +0x6e, 0x65, 0x6c, 0x73, 0x20, 0x26, 0x20, 0x32, 0x30, 0x34, 0x38, 0x29, 0x20, 0x21, 0x3d, 0x20, 0x30, 0x29, 0x20, 0x3f, +0x20, 0x5f, 0x31, 0x37, 0x37, 0x20, 0x3a, 0x20, 0x30, 0x5d, 0x2e, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x49, 0x64, 0x29, +0x3b, 0x00, 0x6f, 0x75, 0x74, 0x50, 0x69, 0x63, 0x6b, 0x69, 0x6e, 0x67, 0x2e, 0x79, 0x20, 0x3d, 0x20, 0x76, 0x65, 0x72, +0x74, 0x65, 0x78, 0x5f, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x7a, 0x20, 0x2f, 0x20, 0x76, 0x65, 0x72, +0x74, 0x65, 0x78, 0x5f, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x77, 0x3b, 0x00, 0x66, 0x72, 0x61, 0x67, +0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x20, 0x3d, 0x20, 0x76, 0x65, 0x63, 0x34, 0x28, 0x30, 0x2e, 0x30, 0x29, 0x3b, 0x00, 0x23, +0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x47, 0x4c, 0x5f, 0x45, 0x58, 0x54, 0x5f, 0x63, 0x6c, 0x69, +0x70, 0x5f, 0x63, 0x75, 0x6c, 0x6c, 0x5f, 0x64, 0x69, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x20, 0x3a, 0x20, 0x72, 0x65, +0x71, 0x75, 0x69, 0x72, 0x65, 0x00, 0x76, 0x65, 0x63, 0x34, 0x20, 0x5f, 0x37, 0x32, 0x20, 0x3d, 0x20, 0x6d, 0x65, 0x73, +0x68, 0x5f, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x3b, 0x00, 0x5f, 0x37, 0x32, 0x2e, 0x7a, 0x20, 0x3d, 0x20, +0x28, 0x5f, 0x37, 0x32, 0x2e, 0x7a, 0x20, 0x2a, 0x20, 0x28, 0x2d, 0x30, 0x2e, 0x35, 0x29, 0x29, 0x20, 0x2b, 0x20, 0x30, +0x2e, 0x35, 0x3b, 0x00, 0x76, 0x65, 0x63, 0x34, 0x20, 0x5f, 0x37, 0x37, 0x20, 0x3d, 0x20, 0x66, 0x72, 0x61, 0x6d, 0x65, +0x55, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x73, 0x2e, 0x66, 0x20, 0x2a, 0x20, 0x5f, 0x37, 0x32, 0x3b, 0x00, 0x66, 0x6c, +0x6f, 0x61, 0x74, 0x20, 0x5f, 0x37, 0x38, 0x20, 0x3d, 0x20, 0x5f, 0x37, 0x37, 0x2e, 0x77, 0x3b, 0x00, 0x76, 0x65, 0x63, +0x34, 0x20, 0x5f, 0x38, 0x36, 0x3b, 0x00, 0x69, 0x66, 0x20, 0x28, 0x61, 0x62, 0x73, 0x28, 0x5f, 0x37, 0x38, 0x29, 0x20, +0x3c, 0x20, 0x31, 0x2e, 0x30, 0x38, 0x34, 0x32, 0x30, 0x32, 0x32, 0x65, 0x2d, 0x31, 0x39, 0x29, 0x00, 0x76, 0x65, 0x63, +0x34, 0x20, 0x5f, 0x38, 0x35, 0x20, 0x3d, 0x20, 0x5f, 0x37, 0x37, 0x3b, 0x00, 0x5f, 0x38, 0x35, 0x2e, 0x77, 0x20, 0x3d, +0x20, 0x28, 0x5f, 0x37, 0x38, 0x20, 0x3c, 0x20, 0x30, 0x2e, 0x30, 0x29, 0x20, 0x3f, 0x20, 0x28, 0x2d, 0x31, 0x2e, 0x30, +0x38, 0x34, 0x32, 0x30, 0x32, 0x32, 0x65, 0x2d, 0x31, 0x39, 0x29, 0x20, 0x3a, 0x20, 0x31, 0x2e, 0x30, 0x38, 0x34, 0x32, +0x30, 0x32, 0x32, 0x65, 0x2d, 0x31, 0x39, 0x3b, 0x00, 0x5f, 0x38, 0x36, 0x20, 0x3d, 0x20, 0x5f, 0x38, 0x35, 0x3b, 0x00, +0x5f, 0x38, 0x36, 0x20, 0x3d, 0x20, 0x5f, 0x37, 0x37, 0x3b, 0x00, 0x76, 0x65, 0x63, 0x33, 0x20, 0x5f, 0x39, 0x32, 0x20, +0x3d, 0x20, 0x4d, 0x61, 0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x56, 0x65, 0x72, 0x74, 0x65, 0x78, 0x49, 0x6e, 0x70, 0x75, +0x74, 0x73, 0x28, 0x5f, 0x38, 0x36, 0x20, 0x2a, 0x20, 0x28, 0x31, 0x2e, 0x30, 0x20, 0x2f, 0x20, 0x5f, 0x38, 0x36, 0x2e, +0x77, 0x29, 0x29, 0x2e, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x78, 0x79, +0x7a, 0x3b, 0x00, 0x76, 0x65, 0x72, 0x74, 0x65, 0x78, 0x5f, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x50, 0x6f, 0x73, 0x69, 0x74, +0x69, 0x6f, 0x6e, 0x2e, 0x78, 0x20, 0x3d, 0x20, 0x5f, 0x39, 0x32, 0x2e, 0x78, 0x3b, 0x00, 0x76, 0x65, 0x72, 0x74, 0x65, +0x78, 0x5f, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x79, 0x20, 0x3d, 0x20, +0x5f, 0x39, 0x32, 0x2e, 0x79, 0x3b, 0x00, 0x76, 0x65, 0x72, 0x74, 0x65, 0x78, 0x5f, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x50, +0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x7a, 0x20, 0x3d, 0x20, 0x5f, 0x39, 0x32, 0x2e, 0x7a, 0x3b, 0x00, 0x76, +0x65, 0x72, 0x74, 0x65, 0x78, 0x5f, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x3d, 0x20, 0x5f, 0x37, 0x32, +0x3b, 0x00, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x5f, 0x31, 0x30, 0x33, 0x20, 0x3d, 0x20, 0x28, 0x66, 0x6c, 0x6f, 0x61, +0x74, 0x28, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x20, 0x25, 0x20, 0x32, +0x29, 0x20, 0x2a, 0x20, 0x32, 0x2e, 0x30, 0x29, 0x20, 0x2d, 0x20, 0x31, 0x2e, 0x30, 0x3b, 0x00, 0x5f, 0x37, 0x32, 0x2e, +0x78, 0x20, 0x3d, 0x20, 0x28, 0x5f, 0x37, 0x32, 0x2e, 0x78, 0x20, 0x2a, 0x20, 0x30, 0x2e, 0x35, 0x29, 0x20, 0x2b, 0x20, +0x28, 0x28, 0x5f, 0x37, 0x32, 0x2e, 0x77, 0x20, 0x2a, 0x20, 0x30, 0x2e, 0x35, 0x29, 0x20, 0x2a, 0x20, 0x5f, 0x31, 0x30, +0x33, 0x29, 0x3b, 0x00, 0x67, 0x6c, 0x5f, 0x43, 0x6c, 0x69, 0x70, 0x44, 0x69, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5b, +0x30, 0x5d, 0x20, 0x3d, 0x20, 0x5f, 0x37, 0x32, 0x2e, 0x78, 0x20, 0x2a, 0x20, 0x5f, 0x31, 0x30, 0x33, 0x3b, 0x00, 0x5f, +0x37, 0x32, 0x2e, 0x7a, 0x20, 0x3d, 0x20, 0x28, 0x5f, 0x37, 0x32, 0x2e, 0x7a, 0x20, 0x2a, 0x20, 0x66, 0x72, 0x61, 0x6d, +0x65, 0x55, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x73, 0x2e, 0x69, 0x2e, 0x78, 0x29, 0x20, 0x2b, 0x20, 0x28, 0x5f, 0x37, +0x32, 0x2e, 0x77, 0x20, 0x2a, 0x20, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x55, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x73, 0x2e, +0x69, 0x2e, 0x79, 0x29, 0x3b, 0x00, 0x67, 0x6c, 0x5f, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x3d, 0x20, +0x5f, 0x37, 0x32, 0x3b, 0x00, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x20, +0x3d, 0x20, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x20, 0x3e, 0x3e, 0x20, +0x31, 0x3b, 0x00, 0x23, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x34, 0x31, 0x30, 0x00, 0x23, 0x65, 0x78, 0x74, +0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x47, 0x4c, 0x5f, 0x41, 0x52, 0x42, 0x5f, 0x73, 0x68, 0x61, 0x64, 0x69, 0x6e, +0x67, 0x5f, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x5f, 0x70, 0x61, 0x63, 0x6b, 0x69, 0x6e, 0x67, 0x20, 0x3a, +0x20, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x00, 0x6c, 0x61, 0x79, 0x6f, 0x75, 0x74, 0x28, 0x6c, 0x6f, 0x63, 0x61, 0x74, +0x69, 0x6f, 0x6e, 0x20, 0x3d, 0x20, 0x38, 0x29, 0x20, 0x66, 0x6c, 0x61, 0x74, 0x20, 0x6f, 0x75, 0x74, 0x20, 0x69, 0x6e, +0x74, 0x20, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x3b, 0x00, 0x6c, 0x61, +0x79, 0x6f, 0x75, 0x74, 0x28, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x3d, 0x20, 0x34, 0x29, 0x20, 0x6f, +0x75, 0x74, 0x20, 0x76, 0x65, 0x63, 0x34, 0x20, 0x76, 0x65, 0x72, 0x74, 0x65, 0x78, 0x5f, 0x77, 0x6f, 0x72, 0x6c, 0x64, +0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x3b, 0x00, 0x6c, 0x61, 0x79, 0x6f, 0x75, 0x74, 0x28, 0x6c, 0x6f, 0x63, +0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x3d, 0x20, 0x37, 0x29, 0x20, 0x6f, 0x75, 0x74, 0x20, 0x76, 0x65, 0x63, 0x34, 0x20, +0x76, 0x65, 0x72, 0x74, 0x65, 0x78, 0x5f, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x3b, 0x00, 0x76, 0x65, 0x63, +0x34, 0x20, 0x5f, 0x32, 0x39, 0x38, 0x20, 0x3d, 0x20, 0x6d, 0x65, 0x73, 0x68, 0x5f, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, +0x6f, 0x6e, 0x3b, 0x00, 0x5f, 0x32, 0x39, 0x38, 0x2e, 0x7a, 0x20, 0x3d, 0x20, 0x28, 0x5f, 0x32, 0x39, 0x38, 0x2e, 0x7a, +0x20, 0x2a, 0x20, 0x28, 0x2d, 0x30, 0x2e, 0x35, 0x29, 0x29, 0x20, 0x2b, 0x20, 0x30, 0x2e, 0x35, 0x3b, 0x00, 0x76, 0x65, +0x63, 0x34, 0x20, 0x5f, 0x32, 0x37, 0x34, 0x20, 0x3d, 0x20, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x55, 0x6e, 0x69, 0x66, 0x6f, +0x72, 0x6d, 0x73, 0x2e, 0x66, 0x20, 0x2a, 0x20, 0x5f, 0x32, 0x39, 0x38, 0x3b, 0x00, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, +0x5f, 0x32, 0x37, 0x36, 0x20, 0x3d, 0x20, 0x5f, 0x32, 0x37, 0x34, 0x2e, 0x77, 0x3b, 0x00, 0x76, 0x65, 0x63, 0x34, 0x20, +0x5f, 0x33, 0x32, 0x36, 0x3b, 0x00, 0x69, 0x66, 0x20, 0x28, 0x61, 0x62, 0x73, 0x28, 0x5f, 0x32, 0x37, 0x36, 0x29, 0x20, +0x3c, 0x20, 0x31, 0x2e, 0x30, 0x38, 0x34, 0x32, 0x30, 0x32, 0x32, 0x65, 0x2d, 0x31, 0x39, 0x29, 0x00, 0x76, 0x65, 0x63, +0x34, 0x20, 0x5f, 0x33, 0x31, 0x37, 0x20, 0x3d, 0x20, 0x5f, 0x32, 0x37, 0x34, 0x3b, 0x00, 0x5f, 0x33, 0x31, 0x37, 0x2e, +0x77, 0x20, 0x3d, 0x20, 0x28, 0x5f, 0x32, 0x37, 0x36, 0x20, 0x3c, 0x20, 0x30, 0x2e, 0x30, 0x29, 0x20, 0x3f, 0x20, 0x28, +0x2d, 0x31, 0x2e, 0x30, 0x38, 0x34, 0x32, 0x30, 0x32, 0x32, 0x65, 0x2d, 0x31, 0x39, 0x29, 0x20, 0x3a, 0x20, 0x31, 0x2e, +0x30, 0x38, 0x34, 0x32, 0x30, 0x32, 0x32, 0x65, 0x2d, 0x31, 0x39, 0x3b, 0x00, 0x5f, 0x33, 0x32, 0x36, 0x20, 0x3d, 0x20, +0x5f, 0x33, 0x31, 0x37, 0x3b, 0x00, 0x5f, 0x33, 0x32, 0x36, 0x20, 0x3d, 0x20, 0x5f, 0x32, 0x37, 0x34, 0x3b, 0x00, 0x76, +0x65, 0x63, 0x33, 0x20, 0x5f, 0x31, 0x38, 0x34, 0x20, 0x3d, 0x20, 0x4d, 0x61, 0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x56, +0x65, 0x72, 0x74, 0x65, 0x78, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x73, 0x28, 0x5f, 0x33, 0x32, 0x36, 0x20, 0x2a, 0x20, 0x28, +0x31, 0x2e, 0x30, 0x20, 0x2f, 0x20, 0x5f, 0x33, 0x32, 0x36, 0x2e, 0x77, 0x29, 0x29, 0x2e, 0x77, 0x6f, 0x72, 0x6c, 0x64, +0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x78, 0x79, 0x7a, 0x3b, 0x00, 0x76, 0x65, 0x72, 0x74, 0x65, 0x78, +0x5f, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x3d, 0x20, 0x5f, 0x32, 0x39, 0x38, 0x3b, 0x00, 0x5f, 0x32, +0x39, 0x38, 0x2e, 0x7a, 0x20, 0x3d, 0x20, 0x28, 0x5f, 0x32, 0x39, 0x38, 0x2e, 0x7a, 0x20, 0x2a, 0x20, 0x66, 0x72, 0x61, +0x6d, 0x65, 0x55, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x73, 0x2e, 0x69, 0x2e, 0x78, 0x29, 0x20, 0x2b, 0x20, 0x28, 0x5f, +0x32, 0x39, 0x38, 0x2e, 0x77, 0x20, 0x2a, 0x20, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x55, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, +0x73, 0x2e, 0x69, 0x2e, 0x79, 0x29, 0x3b, 0x00, 0x67, 0x6c, 0x5f, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x20, +0x3d, 0x20, 0x5f, 0x32, 0x39, 0x38, 0x3b, 0x00, 0x75, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x20, 0x73, 0x61, 0x6d, 0x70, +0x6c, 0x65, 0x72, 0x43, 0x75, 0x62, 0x65, 0x20, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x5f, 0x66, 0x6f, 0x67, 0x3b, 0x00, 0x6c, +0x61, 0x79, 0x6f, 0x75, 0x74, 0x28, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x3d, 0x20, 0x34, 0x29, 0x20, +0x69, 0x6e, 0x20, 0x76, 0x65, 0x63, 0x34, 0x20, 0x76, 0x65, 0x72, 0x74, 0x65, 0x78, 0x5f, 0x77, 0x6f, 0x72, 0x6c, 0x64, +0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x3b, 0x00, 0x76, 0x65, 0x63, 0x34, 0x20, 0x66, 0x6f, 0x67, 0x28, 0x69, +0x6e, 0x6f, 0x75, 0x74, 0x20, 0x76, 0x65, 0x63, 0x34, 0x20, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x2c, 0x20, 0x76, 0x65, 0x63, +0x33, 0x20, 0x76, 0x69, 0x65, 0x77, 0x29, 0x00, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x5f, 0x31, 0x38, 0x37, 0x20, 0x3d, +0x20, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x28, 0x76, 0x69, 0x65, 0x77, 0x29, 0x3b, 0x00, 0x66, 0x6c, 0x6f, 0x61, 0x74, +0x20, 0x5f, 0x32, 0x31, 0x34, 0x20, 0x3d, 0x20, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x55, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, +0x73, 0x2e, 0x63, 0x7a, 0x7a, 0x20, 0x2a, 0x20, 0x76, 0x69, 0x65, 0x77, 0x2e, 0x79, 0x3b, 0x00, 0x66, 0x6c, 0x6f, 0x61, +0x74, 0x20, 0x5f, 0x35, 0x38, 0x38, 0x3b, 0x00, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x5f, 0x32, 0x35, 0x34, 0x20, 0x3d, +0x20, 0x6d, 0x69, 0x6e, 0x28, 0x31, 0x2e, 0x30, 0x20, 0x2d, 0x20, 0x65, 0x78, 0x70, 0x28, 0x2d, 0x28, 0x5f, 0x35, 0x38, +0x38, 0x20, 0x2a, 0x20, 0x6d, 0x61, 0x78, 0x28, 0x5f, 0x31, 0x38, 0x37, 0x20, 0x2d, 0x20, 0x66, 0x72, 0x61, 0x6d, 0x65, +0x55, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x73, 0x2e, 0x7a, 0x7a, 0x2c, 0x20, 0x30, 0x2e, 0x30, 0x29, 0x29, 0x29, 0x2c, +0x20, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x55, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x73, 0x2e, 0x61, 0x7a, 0x7a, 0x29, 0x3b, +0x00, 0x76, 0x65, 0x63, 0x32, 0x20, 0x5f, 0x32, 0x38, 0x31, 0x20, 0x3d, 0x20, 0x75, 0x6e, 0x70, 0x61, 0x63, 0x6b, 0x48, +0x61, 0x6c, 0x66, 0x32, 0x78, 0x31, 0x36, 0x28, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x55, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, +0x73, 0x2e, 0x62, 0x7a, 0x7a, 0x29, 0x3b, 0x00, 0x5f, 0x35, 0x38, 0x39, 0x20, 0x3d, 0x20, 0x66, 0x72, 0x61, 0x6d, 0x65, +0x55, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x73, 0x2e, 0x65, 0x7a, 0x7a, 0x20, 0x2a, 0x20, 0x74, 0x65, 0x78, 0x74, 0x75, +0x72, 0x65, 0x4c, 0x6f, 0x64, 0x28, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x5f, 0x66, 0x6f, 0x67, 0x2c, 0x20, 0x74, 0x72, 0x61, +0x6e, 0x73, 0x70, 0x6f, 0x73, 0x65, 0x28, 0x6d, 0x61, 0x74, 0x33, 0x28, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x55, 0x6e, 0x69, +0x66, 0x6f, 0x72, 0x6d, 0x73, 0x2e, 0x67, 0x5b, 0x30, 0x5d, 0x2e, 0x78, 0x79, 0x7a, 0x2c, 0x20, 0x66, 0x72, 0x61, 0x6d, +0x65, 0x55, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x73, 0x2e, 0x67, 0x5b, 0x31, 0x5d, 0x2e, 0x78, 0x79, 0x7a, 0x2c, 0x20, +0x66, 0x72, 0x61, 0x6d, 0x65, 0x55, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x73, 0x2e, 0x67, 0x5b, 0x32, 0x5d, 0x2e, 0x78, +0x79, 0x7a, 0x29, 0x29, 0x20, 0x2a, 0x20, 0x76, 0x69, 0x65, 0x77, 0x2c, 0x20, 0x6d, 0x69, 0x78, 0x28, 0x5f, 0x32, 0x38, +0x31, 0x2e, 0x79, 0x2c, 0x20, 0x5f, 0x32, 0x38, 0x31, 0x2e, 0x78, 0x2c, 0x20, 0x63, 0x6c, 0x61, 0x6d, 0x70, 0x28, 0x28, +0x5f, 0x31, 0x38, 0x37, 0x20, 0x2a, 0x20, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x55, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x73, +0x2e, 0x69, 0x7a, 0x7a, 0x29, 0x20, 0x2d, 0x20, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x55, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, +0x73, 0x2e, 0x6a, 0x7a, 0x7a, 0x2c, 0x20, 0x30, 0x2e, 0x30, 0x2c, 0x20, 0x31, 0x2e, 0x30, 0x29, 0x29, 0x29, 0x2e, 0x78, +0x79, 0x7a, 0x3b, 0x00, 0x5f, 0x35, 0x39, 0x31, 0x20, 0x3d, 0x20, 0x5f, 0x33, 0x32, 0x32, 0x20, 0x2b, 0x20, 0x28, 0x28, +0x66, 0x72, 0x61, 0x6d, 0x65, 0x55, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x73, 0x2e, 0x6b, 0x7a, 0x2e, 0x78, 0x79, 0x7a, +0x20, 0x2a, 0x20, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x55, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x73, 0x2e, 0x6b, 0x7a, 0x2e, +0x77, 0x29, 0x20, 0x2a, 0x20, 0x28, 0x70, 0x6f, 0x77, 0x28, 0x6d, 0x61, 0x78, 0x28, 0x64, 0x6f, 0x74, 0x28, 0x6e, 0x6f, +0x72, 0x6d, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x28, 0x76, 0x69, 0x65, 0x77, 0x29, 0x2c, 0x20, 0x66, 0x72, 0x61, 0x6d, 0x65, +0x55, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x73, 0x2e, 0x69, 0x7a, 0x29, 0x2c, 0x20, 0x30, 0x2e, 0x30, 0x29, 0x2c, 0x20, +0x66, 0x72, 0x61, 0x6d, 0x65, 0x55, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x73, 0x2e, 0x68, 0x7a, 0x7a, 0x29, 0x20, 0x2a, +0x20, 0x28, 0x31, 0x2e, 0x30, 0x20, 0x2d, 0x20, 0x65, 0x78, 0x70, 0x28, 0x2d, 0x28, 0x5f, 0x35, 0x38, 0x38, 0x20, 0x2a, +0x20, 0x6d, 0x61, 0x78, 0x28, 0x5f, 0x31, 0x38, 0x37, 0x20, 0x2d, 0x20, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x55, 0x6e, 0x69, +0x66, 0x6f, 0x72, 0x6d, 0x73, 0x2e, 0x67, 0x7a, 0x7a, 0x2c, 0x20, 0x30, 0x2e, 0x30, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, +0x3b, 0x00, 0x76, 0x65, 0x63, 0x33, 0x20, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x5f, 0x31, 0x20, 0x3d, 0x20, 0x66, 0x72, 0x61, +0x6d, 0x65, 0x55, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x73, 0x2e, 0x6b, 0x7a, 0x7a, 0x20, 0x2a, 0x20, 0x28, 0x76, 0x65, +0x72, 0x74, 0x65, 0x78, 0x5f, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x78, +0x79, 0x7a, 0x20, 0x2d, 0x20, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x55, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x73, 0x2e, 0x62, +0x5b, 0x33, 0x5d, 0x2e, 0x78, 0x79, 0x7a, 0x29, 0x3b, 0x00, 0x6d, 0x61, 0x74, 0x34, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, +0x46, 0x72, 0x6f, 0x6d, 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x4d, 0x61, 0x74, 0x72, 0x69, 0x78, 0x3b, 0x00, 0x6d, 0x61, 0x74, +0x33, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x46, 0x72, 0x6f, 0x6d, 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x4e, 0x6f, 0x72, 0x6d, +0x61, 0x6c, 0x4d, 0x61, 0x74, 0x72, 0x69, 0x78, 0x3b, 0x00, 0x69, 0x6e, 0x74, 0x20, 0x6d, 0x6f, 0x72, 0x70, 0x68, 0x54, +0x61, 0x72, 0x67, 0x65, 0x74, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x3b, 0x00, 0x69, 0x6e, 0x74, 0x20, 0x66, 0x6c, 0x61, 0x67, +0x73, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x3b, 0x00, 0x69, 0x6e, 0x74, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, +0x74, 0x49, 0x64, 0x3b, 0x00, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x75, 0x73, 0x65, 0x72, 0x44, 0x61, 0x74, 0x61, 0x3b, +0x00, 0x76, 0x65, 0x63, 0x34, 0x20, 0x72, 0x65, 0x73, 0x65, 0x72, 0x76, 0x65, 0x64, 0x5b, 0x38, 0x5d, 0x3b, 0x00, 0x6c, +0x61, 0x79, 0x6f, 0x75, 0x74, 0x28, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x3d, 0x20, 0x30, 0x29, 0x20, +0x6f, 0x75, 0x74, 0x20, 0x76, 0x65, 0x63, 0x32, 0x20, 0x6f, 0x75, 0x74, 0x50, 0x69, 0x63, 0x6b, 0x69, 0x6e, 0x67, 0x3b, +0x00, 0x6c, 0x61, 0x79, 0x6f, 0x75, 0x74, 0x28, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x3d, 0x20, 0x37, +0x29, 0x20, 0x69, 0x6e, 0x20, 0x76, 0x65, 0x63, 0x34, 0x20, 0x76, 0x65, 0x72, 0x74, 0x65, 0x78, 0x5f, 0x70, 0x6f, 0x73, +0x69, 0x74, 0x69, 0x6f, 0x6e, 0x3b, 0x00, 0x76, 0x65, 0x63, 0x34, 0x20, 0x5f, 0x33, 0x32, 0x35, 0x20, 0x3d, 0x20, 0x6d, +0x65, 0x73, 0x68, 0x5f, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x3b, 0x00, 0x5f, 0x33, 0x32, 0x35, 0x2e, 0x7a, +0x20, 0x3d, 0x20, 0x28, 0x5f, 0x33, 0x32, 0x35, 0x2e, 0x7a, 0x20, 0x2a, 0x20, 0x28, 0x2d, 0x30, 0x2e, 0x35, 0x29, 0x29, +0x20, 0x2b, 0x20, 0x30, 0x2e, 0x35, 0x3b, 0x00, 0x76, 0x65, 0x63, 0x34, 0x20, 0x5f, 0x33, 0x30, 0x31, 0x20, 0x3d, 0x20, +0x66, 0x72, 0x61, 0x6d, 0x65, 0x55, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x73, 0x2e, 0x66, 0x20, 0x2a, 0x20, 0x5f, 0x33, +0x32, 0x35, 0x3b, 0x00, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x5f, 0x33, 0x30, 0x33, 0x20, 0x3d, 0x20, 0x5f, 0x33, 0x30, +0x31, 0x2e, 0x77, 0x3b, 0x00, 0x76, 0x65, 0x63, 0x34, 0x20, 0x5f, 0x33, 0x35, 0x38, 0x3b, 0x00, 0x69, 0x66, 0x20, 0x28, +0x61, 0x62, 0x73, 0x28, 0x5f, 0x33, 0x30, 0x33, 0x29, 0x20, 0x3c, 0x20, 0x31, 0x2e, 0x30, 0x38, 0x34, 0x32, 0x30, 0x32, +0x32, 0x65, 0x2d, 0x31, 0x39, 0x29, 0x00, 0x76, 0x65, 0x63, 0x34, 0x20, 0x5f, 0x33, 0x34, 0x34, 0x20, 0x3d, 0x20, 0x5f, +0x33, 0x30, 0x31, 0x3b, 0x00, 0x5f, 0x33, 0x34, 0x34, 0x2e, 0x77, 0x20, 0x3d, 0x20, 0x28, 0x5f, 0x33, 0x30, 0x33, 0x20, +0x3c, 0x20, 0x30, 0x2e, 0x30, 0x29, 0x20, 0x3f, 0x20, 0x28, 0x2d, 0x31, 0x2e, 0x30, 0x38, 0x34, 0x32, 0x30, 0x32, 0x32, +0x65, 0x2d, 0x31, 0x39, 0x29, 0x20, 0x3a, 0x20, 0x31, 0x2e, 0x30, 0x38, 0x34, 0x32, 0x30, 0x32, 0x32, 0x65, 0x2d, 0x31, +0x39, 0x3b, 0x00, 0x5f, 0x33, 0x35, 0x38, 0x20, 0x3d, 0x20, 0x5f, 0x33, 0x34, 0x34, 0x3b, 0x00, 0x5f, 0x33, 0x35, 0x38, +0x20, 0x3d, 0x20, 0x5f, 0x33, 0x30, 0x31, 0x3b, 0x00, 0x76, 0x65, 0x63, 0x33, 0x20, 0x5f, 0x31, 0x38, 0x36, 0x20, 0x3d, +0x20, 0x4d, 0x61, 0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x56, 0x65, 0x72, 0x74, 0x65, 0x78, 0x49, 0x6e, 0x70, 0x75, 0x74, +0x73, 0x28, 0x5f, 0x33, 0x35, 0x38, 0x20, 0x2a, 0x20, 0x28, 0x31, 0x2e, 0x30, 0x20, 0x2f, 0x20, 0x5f, 0x33, 0x35, 0x38, +0x2e, 0x77, 0x29, 0x29, 0x2e, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x78, +0x79, 0x7a, 0x3b, 0x00, 0x76, 0x65, 0x72, 0x74, 0x65, 0x78, 0x5f, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x50, 0x6f, 0x73, 0x69, +0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x78, 0x20, 0x3d, 0x20, 0x5f, 0x31, 0x38, 0x36, 0x2e, 0x78, 0x3b, 0x00, 0x76, 0x65, 0x72, +0x74, 0x65, 0x78, 0x5f, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x79, 0x20, +0x3d, 0x20, 0x5f, 0x31, 0x38, 0x36, 0x2e, 0x79, 0x3b, 0x00, 0x76, 0x65, 0x72, 0x74, 0x65, 0x78, 0x5f, 0x77, 0x6f, 0x72, +0x6c, 0x64, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x7a, 0x20, 0x3d, 0x20, 0x5f, 0x31, 0x38, 0x36, 0x2e, +0x7a, 0x3b, 0x00, 0x76, 0x65, 0x72, 0x74, 0x65, 0x78, 0x5f, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x3d, +0x20, 0x5f, 0x33, 0x32, 0x35, 0x3b, 0x00, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x5f, 0x32, 0x31, 0x33, 0x20, 0x3d, 0x20, +0x28, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x28, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x69, 0x6e, 0x64, 0x65, +0x78, 0x20, 0x25, 0x20, 0x32, 0x29, 0x20, 0x2a, 0x20, 0x32, 0x2e, 0x30, 0x29, 0x20, 0x2d, 0x20, 0x31, 0x2e, 0x30, 0x3b, +0x00, 0x5f, 0x33, 0x32, 0x35, 0x2e, 0x78, 0x20, 0x3d, 0x20, 0x28, 0x5f, 0x33, 0x32, 0x35, 0x2e, 0x78, 0x20, 0x2a, 0x20, +0x30, 0x2e, 0x35, 0x29, 0x20, 0x2b, 0x20, 0x28, 0x28, 0x5f, 0x33, 0x32, 0x35, 0x2e, 0x77, 0x20, 0x2a, 0x20, 0x30, 0x2e, +0x35, 0x29, 0x20, 0x2a, 0x20, 0x5f, 0x32, 0x31, 0x33, 0x29, 0x3b, 0x00, 0x67, 0x6c, 0x5f, 0x43, 0x6c, 0x69, 0x70, 0x44, +0x69, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5b, 0x30, 0x5d, 0x20, 0x3d, 0x20, 0x5f, 0x33, 0x32, 0x35, 0x2e, 0x78, 0x20, +0x2a, 0x20, 0x5f, 0x32, 0x31, 0x33, 0x3b, 0x00, 0x5f, 0x33, 0x32, 0x35, 0x2e, 0x7a, 0x20, 0x3d, 0x20, 0x28, 0x5f, 0x33, +0x32, 0x35, 0x2e, 0x7a, 0x20, 0x2a, 0x20, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x55, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x73, +0x2e, 0x69, 0x2e, 0x78, 0x29, 0x20, 0x2b, 0x20, 0x28, 0x5f, 0x33, 0x32, 0x35, 0x2e, 0x77, 0x20, 0x2a, 0x20, 0x66, 0x72, +0x61, 0x6d, 0x65, 0x55, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x73, 0x2e, 0x69, 0x2e, 0x79, 0x29, 0x3b, 0x00, 0x67, 0x6c, +0x5f, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x3d, 0x20, 0x5f, 0x33, 0x32, 0x35, 0x3b, 0x00, 0x23, 0x69, +0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x20, 0x3c, 0x6d, 0x65, 0x74, 0x61, 0x6c, 0x5f, 0x73, 0x74, 0x64, 0x6c, 0x69, 0x62, +0x3e, 0x00, 0x23, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x20, 0x3c, 0x73, 0x69, 0x6d, 0x64, 0x2f, 0x73, 0x69, 0x6d, +0x64, 0x2e, 0x68, 0x3e, 0x00, 0x00, 0x75, 0x73, 0x69, 0x6e, 0x67, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, +0x65, 0x20, 0x6d, 0x65, 0x74, 0x61, 0x6c, 0x3b, 0x00, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x20, 0x46, 0x72, 0x61, 0x6d, +0x65, 0x55, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x73, 0x00, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x34, +0x78, 0x34, 0x20, 0x76, 0x69, 0x65, 0x77, 0x46, 0x72, 0x6f, 0x6d, 0x57, 0x6f, 0x72, 0x6c, 0x64, 0x4d, 0x61, 0x74, 0x72, +0x69, 0x78, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x34, 0x78, 0x34, 0x20, 0x77, 0x6f, 0x72, +0x6c, 0x64, 0x46, 0x72, 0x6f, 0x6d, 0x56, 0x69, 0x65, 0x77, 0x4d, 0x61, 0x74, 0x72, 0x69, 0x78, 0x3b, 0x00, 0x20, 0x20, +0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x34, 0x78, 0x34, 0x20, 0x63, 0x6c, 0x69, 0x70, 0x46, 0x72, 0x6f, 0x6d, 0x56, +0x69, 0x65, 0x77, 0x4d, 0x61, 0x74, 0x72, 0x69, 0x78, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, +0x34, 0x78, 0x34, 0x20, 0x76, 0x69, 0x65, 0x77, 0x46, 0x72, 0x6f, 0x6d, 0x43, 0x6c, 0x69, 0x70, 0x4d, 0x61, 0x74, 0x72, +0x69, 0x78, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x34, 0x78, 0x34, 0x20, 0x63, 0x6c, 0x69, +0x70, 0x46, 0x72, 0x6f, 0x6d, 0x57, 0x6f, 0x72, 0x6c, 0x64, 0x4d, 0x61, 0x74, 0x72, 0x69, 0x78, 0x5b, 0x32, 0x5d, 0x3b, +0x00, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x34, 0x78, 0x34, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x46, +0x72, 0x6f, 0x6d, 0x43, 0x6c, 0x69, 0x70, 0x4d, 0x61, 0x74, 0x72, 0x69, 0x78, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x66, +0x6c, 0x6f, 0x61, 0x74, 0x34, 0x78, 0x34, 0x20, 0x75, 0x73, 0x65, 0x72, 0x57, 0x6f, 0x72, 0x6c, 0x64, 0x46, 0x72, 0x6f, +0x6d, 0x57, 0x6f, 0x72, 0x6c, 0x64, 0x4d, 0x61, 0x74, 0x72, 0x69, 0x78, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, +0x6f, 0x61, 0x74, 0x34, 0x20, 0x63, 0x6c, 0x69, 0x70, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x6f, 0x72, 0x6d, 0x3b, 0x00, +0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x32, 0x20, 0x63, 0x6c, 0x69, 0x70, 0x43, 0x6f, 0x6e, 0x74, 0x72, +0x6f, 0x6c, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x74, 0x69, 0x6d, 0x65, 0x3b, 0x00, +0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6f, 0x72, 0x61, 0x6c, 0x4e, 0x6f, +0x69, 0x73, 0x65, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x34, 0x20, 0x75, 0x73, 0x65, 0x72, +0x54, 0x69, 0x6d, 0x65, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x34, 0x20, 0x72, 0x65, 0x73, +0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x32, 0x20, +0x6c, 0x6f, 0x67, 0x69, 0x63, 0x61, 0x6c, 0x56, 0x69, 0x65, 0x77, 0x70, 0x6f, 0x72, 0x74, 0x53, 0x63, 0x61, 0x6c, 0x65, +0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x32, 0x20, 0x6c, 0x6f, 0x67, 0x69, 0x63, 0x61, 0x6c, +0x56, 0x69, 0x65, 0x77, 0x70, 0x6f, 0x72, 0x74, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, +0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x6c, 0x6f, 0x64, 0x42, 0x69, 0x61, 0x73, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x66, +0x6c, 0x6f, 0x61, 0x74, 0x20, 0x72, 0x65, 0x66, 0x72, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x4c, 0x6f, 0x64, 0x4f, 0x66, +0x66, 0x73, 0x65, 0x74, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x6f, 0x6e, 0x65, 0x4f, +0x76, 0x65, 0x72, 0x46, 0x61, 0x72, 0x4d, 0x69, 0x6e, 0x75, 0x73, 0x4e, 0x65, 0x61, 0x72, 0x3b, 0x00, 0x20, 0x20, 0x20, +0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x6e, 0x65, 0x61, 0x72, 0x4f, 0x76, 0x65, 0x72, 0x46, 0x61, 0x72, 0x4d, 0x69, +0x6e, 0x75, 0x73, 0x4e, 0x65, 0x61, 0x72, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x63, +0x61, 0x6d, 0x65, 0x72, 0x61, 0x46, 0x61, 0x72, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, +0x65, 0x78, 0x70, 0x6f, 0x73, 0x75, 0x72, 0x65, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, +0x65, 0x76, 0x31, 0x30, 0x30, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x6e, 0x65, 0x65, +0x64, 0x73, 0x41, 0x6c, 0x70, 0x68, 0x61, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, +0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x61, 0x6f, 0x53, 0x61, 0x6d, 0x70, 0x6c, 0x69, 0x6e, 0x67, 0x51, 0x75, 0x61, 0x6c, +0x69, 0x74, 0x79, 0x41, 0x6e, 0x64, 0x45, 0x64, 0x67, 0x65, 0x44, 0x69, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x3b, 0x00, +0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x61, 0x6f, 0x42, 0x65, 0x6e, 0x74, 0x4e, 0x6f, 0x72, 0x6d, +0x61, 0x6c, 0x73, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x61, 0x6f, 0x52, 0x65, 0x73, +0x65, 0x72, 0x76, 0x65, 0x64, 0x30, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x61, 0x6f, +0x52, 0x65, 0x73, 0x65, 0x72, 0x76, 0x65, 0x64, 0x31, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, +0x34, 0x20, 0x7a, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x70, 0x61, 0x63, 0x6b, 0x65, +0x64, 0x5f, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x20, 0x66, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x3b, 0x00, 0x20, 0x20, 0x20, +0x20, 0x69, 0x6e, 0x74, 0x20, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x3b, 0x00, +0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x32, 0x20, 0x66, 0x72, 0x6f, 0x78, 0x65, 0x6c, 0x43, 0x6f, 0x75, +0x6e, 0x74, 0x58, 0x59, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x69, 0x62, 0x6c, 0x4c, +0x75, 0x6d, 0x69, 0x6e, 0x61, 0x6e, 0x63, 0x65, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, +0x69, 0x62, 0x6c, 0x52, 0x6f, 0x75, 0x67, 0x68, 0x6e, 0x65, 0x73, 0x73, 0x4f, 0x6e, 0x65, 0x4c, 0x65, 0x76, 0x65, 0x6c, +0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x33, 0x20, 0x69, 0x62, 0x6c, 0x53, 0x48, 0x5b, 0x39, +0x5d, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x64, 0x5f, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x33, +0x20, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x3b, 0x00, 0x20, 0x20, 0x20, +0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x70, 0x61, 0x64, 0x64, 0x69, 0x6e, 0x67, 0x30, 0x3b, 0x00, 0x20, 0x20, 0x20, +0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x34, 0x20, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x49, 0x6e, +0x74, 0x65, 0x6e, 0x73, 0x69, 0x74, 0x79, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x34, 0x20, +0x73, 0x75, 0x6e, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x32, 0x20, 0x6c, 0x69, 0x67, 0x68, +0x74, 0x46, 0x61, 0x72, 0x41, 0x74, 0x74, 0x65, 0x6e, 0x75, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x72, 0x61, 0x6d, +0x73, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x69, 0x6e, 0x74, 0x20, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, +0x61, 0x6c, 0x53, 0x68, 0x61, 0x64, 0x6f, 0x77, 0x73, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, +0x20, 0x73, 0x73, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x63, 0x74, 0x53, 0x68, 0x61, 0x64, 0x6f, 0x77, 0x44, 0x69, 0x73, 0x74, +0x61, 0x6e, 0x63, 0x65, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x34, 0x20, 0x63, 0x61, 0x73, +0x63, 0x61, 0x64, 0x65, 0x53, 0x70, 0x6c, 0x69, 0x74, 0x73, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x69, 0x6e, 0x74, 0x20, +0x63, 0x61, 0x73, 0x63, 0x61, 0x64, 0x65, 0x73, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, +0x72, 0x65, 0x73, 0x65, 0x72, 0x76, 0x65, 0x64, 0x30, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, +0x20, 0x72, 0x65, 0x73, 0x65, 0x72, 0x76, 0x65, 0x64, 0x31, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, +0x74, 0x20, 0x73, 0x68, 0x61, 0x64, 0x6f, 0x77, 0x50, 0x65, 0x6e, 0x75, 0x6d, 0x62, 0x72, 0x61, 0x52, 0x61, 0x74, 0x69, +0x6f, 0x53, 0x63, 0x61, 0x6c, 0x65, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x76, 0x73, +0x6d, 0x45, 0x78, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, +0x20, 0x76, 0x73, 0x6d, 0x44, 0x65, 0x70, 0x74, 0x68, 0x53, 0x63, 0x61, 0x6c, 0x65, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, +0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x76, 0x73, 0x6d, 0x4c, 0x69, 0x67, 0x68, 0x74, 0x42, 0x6c, 0x65, 0x65, 0x64, 0x52, +0x65, 0x64, 0x75, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x73, +0x68, 0x61, 0x64, 0x6f, 0x77, 0x53, 0x61, 0x6d, 0x70, 0x6c, 0x69, 0x6e, 0x67, 0x54, 0x79, 0x70, 0x65, 0x3b, 0x00, 0x20, +0x20, 0x20, 0x20, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x64, 0x5f, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x33, 0x20, 0x66, 0x6f, 0x67, +0x44, 0x65, 0x6e, 0x73, 0x69, 0x74, 0x79, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x66, +0x6f, 0x67, 0x53, 0x74, 0x61, 0x72, 0x74, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x66, +0x6f, 0x67, 0x4d, 0x61, 0x78, 0x4f, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x75, 0x69, +0x6e, 0x74, 0x20, 0x66, 0x6f, 0x67, 0x4d, 0x69, 0x6e, 0x4d, 0x61, 0x78, 0x4d, 0x69, 0x70, 0x3b, 0x00, 0x20, 0x20, 0x20, +0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x66, 0x6f, 0x67, 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, 0x46, 0x61, 0x6c, 0x6c, +0x6f, 0x66, 0x66, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x66, 0x6f, 0x67, 0x43, 0x75, +0x74, 0x4f, 0x66, 0x66, 0x44, 0x69, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x70, 0x61, +0x63, 0x6b, 0x65, 0x64, 0x5f, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x33, 0x20, 0x66, 0x6f, 0x67, 0x43, 0x6f, 0x6c, 0x6f, 0x72, +0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x66, 0x6f, 0x67, 0x43, 0x6f, 0x6c, 0x6f, 0x72, +0x46, 0x72, 0x6f, 0x6d, 0x49, 0x62, 0x6c, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x66, +0x6f, 0x67, 0x49, 0x6e, 0x73, 0x63, 0x61, 0x74, 0x74, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x53, 0x74, 0x61, 0x72, 0x74, 0x3b, +0x00, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x66, 0x6f, 0x67, 0x49, 0x6e, 0x73, 0x63, 0x61, 0x74, +0x74, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x53, 0x69, 0x7a, 0x65, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, +0x74, 0x20, 0x66, 0x6f, 0x67, 0x4f, 0x6e, 0x65, 0x4f, 0x76, 0x65, 0x72, 0x46, 0x61, 0x72, 0x4d, 0x69, 0x6e, 0x75, 0x73, +0x4e, 0x65, 0x61, 0x72, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x66, 0x6f, 0x67, 0x4e, +0x65, 0x61, 0x72, 0x4f, 0x76, 0x65, 0x72, 0x46, 0x61, 0x72, 0x4d, 0x69, 0x6e, 0x75, 0x73, 0x4e, 0x65, 0x61, 0x72, 0x3b, +0x00, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x33, 0x78, 0x33, 0x20, 0x66, 0x6f, 0x67, 0x46, 0x72, 0x6f, +0x6d, 0x57, 0x6f, 0x72, 0x6c, 0x64, 0x4d, 0x61, 0x74, 0x72, 0x69, 0x78, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, +0x6f, 0x61, 0x74, 0x34, 0x78, 0x34, 0x20, 0x73, 0x73, 0x72, 0x52, 0x65, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x69, +0x6f, 0x6e, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x34, 0x78, 0x34, 0x20, 0x73, 0x73, 0x72, +0x55, 0x76, 0x46, 0x72, 0x6f, 0x6d, 0x56, 0x69, 0x65, 0x77, 0x4d, 0x61, 0x74, 0x72, 0x69, 0x78, 0x3b, 0x00, 0x20, 0x20, +0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x73, 0x73, 0x72, 0x54, 0x68, 0x69, 0x63, 0x6b, 0x6e, 0x65, 0x73, 0x73, +0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x73, 0x73, 0x72, 0x42, 0x69, 0x61, 0x73, 0x3b, +0x00, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x73, 0x73, 0x72, 0x44, 0x69, 0x73, 0x74, 0x61, 0x6e, +0x63, 0x65, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x73, 0x73, 0x72, 0x53, 0x74, 0x72, +0x69, 0x64, 0x65, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x34, 0x20, 0x63, 0x75, 0x73, 0x74, +0x6f, 0x6d, 0x5b, 0x34, 0x5d, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x69, 0x6e, 0x74, 0x20, 0x72, 0x65, 0x63, 0x37, 0x30, +0x39, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x65, 0x73, 0x32, 0x52, 0x65, 0x73, 0x65, +0x72, 0x76, 0x65, 0x64, 0x30, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x65, 0x73, 0x32, +0x52, 0x65, 0x73, 0x65, 0x72, 0x76, 0x65, 0x64, 0x31, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, +0x20, 0x65, 0x73, 0x32, 0x52, 0x65, 0x73, 0x65, 0x72, 0x76, 0x65, 0x64, 0x32, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x66, +0x6c, 0x6f, 0x61, 0x74, 0x34, 0x20, 0x72, 0x65, 0x73, 0x65, 0x72, 0x76, 0x65, 0x64, 0x5b, 0x34, 0x38, 0x5d, 0x3b, 0x00, +0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x20, 0x6d, 0x61, 0x69, 0x6e, 0x30, 0x5f, 0x6f, 0x75, 0x74, 0x00, 0x20, 0x20, 0x20, +0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x34, 0x20, 0x76, 0x65, 0x72, 0x74, 0x65, 0x78, 0x5f, 0x77, 0x6f, 0x72, 0x6c, 0x64, +0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x5b, 0x5b, 0x75, 0x73, 0x65, 0x72, 0x28, 0x6c, 0x6f, 0x63, 0x6e, +0x34, 0x29, 0x5d, 0x5d, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x34, 0x20, 0x76, 0x65, 0x72, +0x74, 0x65, 0x78, 0x5f, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x5b, 0x5b, 0x75, 0x73, 0x65, 0x72, 0x28, +0x6c, 0x6f, 0x63, 0x6e, 0x37, 0x29, 0x5d, 0x5d, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x69, 0x6e, 0x74, 0x20, 0x69, 0x6e, +0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x20, 0x5b, 0x5b, 0x75, 0x73, 0x65, 0x72, 0x28, +0x6c, 0x6f, 0x63, 0x6e, 0x38, 0x29, 0x5d, 0x5d, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x34, +0x20, 0x67, 0x6c, 0x5f, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x5b, 0x5b, 0x70, 0x6f, 0x73, 0x69, 0x74, +0x69, 0x6f, 0x6e, 0x5d, 0x5d, 0x3b, 0x00, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x20, 0x6d, 0x61, 0x69, 0x6e, 0x30, 0x5f, +0x69, 0x6e, 0x00, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x34, 0x20, 0x6d, 0x65, 0x73, 0x68, 0x5f, 0x70, +0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x5b, 0x5b, 0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x28, +0x30, 0x29, 0x5d, 0x5d, 0x3b, 0x00, 0x76, 0x65, 0x72, 0x74, 0x65, 0x78, 0x20, 0x6d, 0x61, 0x69, 0x6e, 0x30, 0x5f, 0x6f, +0x75, 0x74, 0x20, 0x6d, 0x61, 0x69, 0x6e, 0x30, 0x28, 0x6d, 0x61, 0x69, 0x6e, 0x30, 0x5f, 0x69, 0x6e, 0x20, 0x69, 0x6e, +0x20, 0x5b, 0x5b, 0x73, 0x74, 0x61, 0x67, 0x65, 0x5f, 0x69, 0x6e, 0x5d, 0x5d, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, +0x61, 0x6e, 0x74, 0x20, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x55, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x73, 0x26, 0x20, 0x66, +0x72, 0x61, 0x6d, 0x65, 0x55, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x73, 0x20, 0x5b, 0x5b, 0x62, 0x75, 0x66, 0x66, 0x65, +0x72, 0x28, 0x31, 0x37, 0x29, 0x5d, 0x5d, 0x2c, 0x20, 0x75, 0x69, 0x6e, 0x74, 0x20, 0x67, 0x6c, 0x5f, 0x49, 0x6e, 0x73, +0x74, 0x61, 0x6e, 0x63, 0x65, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x20, 0x5b, 0x5b, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, +0x65, 0x5f, 0x69, 0x64, 0x5d, 0x5d, 0x29, 0x00, 0x20, 0x20, 0x20, 0x20, 0x6d, 0x61, 0x69, 0x6e, 0x30, 0x5f, 0x6f, 0x75, +0x74, 0x20, 0x6f, 0x75, 0x74, 0x20, 0x3d, 0x20, 0x7b, 0x7d, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x6f, 0x75, 0x74, 0x2e, +0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x20, 0x3d, 0x20, 0x69, 0x6e, 0x74, +0x28, 0x67, 0x6c, 0x5f, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x29, 0x3b, 0x00, +0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x34, 0x20, 0x5f, 0x32, 0x37, 0x37, 0x20, 0x3d, 0x20, 0x69, 0x6e, +0x2e, 0x6d, 0x65, 0x73, 0x68, 0x5f, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, +0x5f, 0x32, 0x37, 0x37, 0x2e, 0x7a, 0x20, 0x3d, 0x20, 0x66, 0x6d, 0x61, 0x28, 0x5f, 0x32, 0x37, 0x37, 0x2e, 0x7a, 0x2c, +0x20, 0x2d, 0x30, 0x2e, 0x35, 0x2c, 0x20, 0x30, 0x2e, 0x35, 0x29, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, +0x61, 0x74, 0x34, 0x20, 0x5f, 0x32, 0x35, 0x33, 0x20, 0x3d, 0x20, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x55, 0x6e, 0x69, 0x66, +0x6f, 0x72, 0x6d, 0x73, 0x2e, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x46, 0x72, 0x6f, 0x6d, 0x43, 0x6c, 0x69, 0x70, 0x4d, 0x61, +0x74, 0x72, 0x69, 0x78, 0x20, 0x2a, 0x20, 0x5f, 0x32, 0x37, 0x37, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, +0x61, 0x74, 0x20, 0x5f, 0x32, 0x35, 0x35, 0x20, 0x3d, 0x20, 0x5f, 0x32, 0x35, 0x33, 0x2e, 0x77, 0x3b, 0x00, 0x20, 0x20, +0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x34, 0x20, 0x5f, 0x33, 0x30, 0x31, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x69, +0x66, 0x20, 0x28, 0x61, 0x62, 0x73, 0x28, 0x5f, 0x32, 0x35, 0x35, 0x29, 0x20, 0x3c, 0x20, 0x31, 0x2e, 0x30, 0x38, 0x34, +0x32, 0x30, 0x32, 0x32, 0x65, 0x2d, 0x31, 0x39, 0x29, 0x00, 0x20, 0x20, 0x20, 0x20, 0x7b, 0x00, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x34, 0x20, 0x5f, 0x32, 0x39, 0x36, 0x20, 0x3d, 0x20, 0x5f, 0x32, +0x35, 0x33, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x32, 0x39, 0x36, 0x2e, 0x77, 0x20, 0x3d, +0x20, 0x28, 0x5f, 0x32, 0x35, 0x35, 0x20, 0x3c, 0x20, 0x30, 0x2e, 0x30, 0x29, 0x20, 0x3f, 0x20, 0x28, 0x2d, 0x31, 0x2e, +0x30, 0x38, 0x34, 0x32, 0x30, 0x32, 0x32, 0x65, 0x2d, 0x31, 0x39, 0x29, 0x20, 0x3a, 0x20, 0x31, 0x2e, 0x30, 0x38, 0x34, +0x32, 0x30, 0x32, 0x32, 0x65, 0x2d, 0x31, 0x39, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x33, +0x30, 0x31, 0x20, 0x3d, 0x20, 0x5f, 0x32, 0x39, 0x36, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x00, 0x20, 0x20, 0x20, +0x20, 0x65, 0x6c, 0x73, 0x65, 0x00, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x33, 0x30, 0x31, 0x20, 0x3d, +0x20, 0x5f, 0x32, 0x35, 0x33, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x34, 0x20, 0x5f, 0x32, +0x36, 0x39, 0x20, 0x3d, 0x20, 0x5f, 0x33, 0x30, 0x31, 0x20, 0x2a, 0x20, 0x28, 0x31, 0x2e, 0x30, 0x20, 0x2f, 0x20, 0x5f, +0x33, 0x30, 0x31, 0x2e, 0x77, 0x29, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x6f, 0x75, 0x74, 0x2e, 0x76, 0x65, 0x72, 0x74, +0x65, 0x78, 0x5f, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x78, 0x20, 0x3d, +0x20, 0x5f, 0x32, 0x36, 0x39, 0x2e, 0x78, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x6f, 0x75, 0x74, 0x2e, 0x76, 0x65, 0x72, +0x74, 0x65, 0x78, 0x5f, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x79, 0x20, +0x3d, 0x20, 0x5f, 0x32, 0x36, 0x39, 0x2e, 0x79, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x6f, 0x75, 0x74, 0x2e, 0x76, 0x65, +0x72, 0x74, 0x65, 0x78, 0x5f, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x7a, +0x20, 0x3d, 0x20, 0x5f, 0x32, 0x36, 0x39, 0x2e, 0x7a, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x6f, 0x75, 0x74, 0x2e, 0x76, +0x65, 0x72, 0x74, 0x65, 0x78, 0x5f, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x3d, 0x20, 0x5f, 0x32, 0x37, +0x37, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x6f, 0x75, 0x74, 0x2e, 0x67, 0x6c, 0x5f, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, +0x6f, 0x6e, 0x20, 0x3d, 0x20, 0x5f, 0x32, 0x37, 0x37, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, +0x6e, 0x20, 0x6f, 0x75, 0x74, 0x3b, 0x00, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x20, 0x4d, 0x61, 0x74, 0x65, 0x72, 0x69, +0x61, 0x6c, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x00, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x34, 0x78, +0x34, 0x20, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x6f, 0x72, 0x6d, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, +0x61, 0x74, 0x33, 0x20, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, +0x34, 0x20, 0x66, 0x72, 0x61, 0x67, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x20, 0x5b, 0x5b, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x28, +0x30, 0x29, 0x5d, 0x5d, 0x3b, 0x00, 0x66, 0x72, 0x61, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x6d, 0x61, 0x69, 0x6e, 0x30, +0x5f, 0x6f, 0x75, 0x74, 0x20, 0x6d, 0x61, 0x69, 0x6e, 0x30, 0x28, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, +0x4d, 0x61, 0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x26, 0x20, 0x6d, 0x61, 0x74, 0x65, +0x72, 0x69, 0x61, 0x6c, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x20, 0x5b, 0x5b, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x28, +0x32, 0x35, 0x29, 0x5d, 0x5d, 0x29, 0x00, 0x20, 0x20, 0x20, 0x20, 0x6f, 0x75, 0x74, 0x2e, 0x66, 0x72, 0x61, 0x67, 0x43, +0x6f, 0x6c, 0x6f, 0x72, 0x20, 0x3d, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x34, 0x28, 0x68, 0x61, 0x6c, 0x66, 0x34, 0x28, +0x68, 0x61, 0x6c, 0x66, 0x33, 0x28, 0x6d, 0x61, 0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, +0x2e, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x29, 0x2c, 0x20, 0x68, 0x61, 0x6c, 0x66, 0x28, 0x31, 0x2e, 0x30, 0x29, 0x29, 0x29, +0x3b, 0x00, 0x66, 0x72, 0x61, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x76, 0x6f, 0x69, 0x64, 0x20, 0x6d, 0x61, 0x69, 0x6e, +0x30, 0x28, 0x29, 0x00, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x20, 0x73, 0x70, 0x76, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, +0x70, 0x74, 0x6f, 0x72, 0x53, 0x65, 0x74, 0x42, 0x75, 0x66, 0x66, 0x65, 0x72, 0x31, 0x20, 0x7b, 0x00, 0x64, 0x65, 0x70, +0x74, 0x68, 0x32, 0x64, 0x5f, 0x61, 0x72, 0x72, 0x61, 0x79, 0x3c, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x3e, 0x20, 0x6c, 0x69, +0x67, 0x68, 0x74, 0x5f, 0x73, 0x68, 0x61, 0x64, 0x6f, 0x77, 0x4d, 0x61, 0x70, 0x20, 0x5b, 0x5b, 0x69, 0x64, 0x28, 0x30, +0x29, 0x5d, 0x5d, 0x3b, 0x00, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x72, 0x20, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x5f, 0x73, +0x68, 0x61, 0x64, 0x6f, 0x77, 0x4d, 0x61, 0x70, 0x53, 0x6d, 0x70, 0x6c, 0x72, 0x20, 0x5b, 0x5b, 0x69, 0x64, 0x28, 0x31, +0x29, 0x5d, 0x5d, 0x3b, 0x00, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x32, 0x64, 0x3c, 0x66, 0x6c, 0x6f, 0x61, 0x74, +0x3e, 0x20, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x5f, 0x69, 0x62, 0x6c, 0x44, 0x46, 0x47, 0x20, 0x5b, 0x5b, 0x69, 0x64, 0x28, +0x32, 0x29, 0x5d, 0x5d, 0x3b, 0x00, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x72, 0x20, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x5f, +0x69, 0x62, 0x6c, 0x44, 0x46, 0x47, 0x53, 0x6d, 0x70, 0x6c, 0x72, 0x20, 0x5b, 0x5b, 0x69, 0x64, 0x28, 0x33, 0x29, 0x5d, +0x5d, 0x3b, 0x00, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x63, 0x75, 0x62, 0x65, 0x3c, 0x66, 0x6c, 0x6f, 0x61, 0x74, +0x3e, 0x20, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x5f, 0x69, 0x62, 0x6c, 0x53, 0x70, 0x65, 0x63, 0x75, 0x6c, 0x61, 0x72, 0x20, +0x5b, 0x5b, 0x69, 0x64, 0x28, 0x34, 0x29, 0x5d, 0x5d, 0x3b, 0x00, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x72, 0x20, 0x6c, +0x69, 0x67, 0x68, 0x74, 0x5f, 0x69, 0x62, 0x6c, 0x53, 0x70, 0x65, 0x63, 0x75, 0x6c, 0x61, 0x72, 0x53, 0x6d, 0x70, 0x6c, +0x72, 0x20, 0x5b, 0x5b, 0x69, 0x64, 0x28, 0x35, 0x29, 0x5d, 0x5d, 0x3b, 0x00, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, +0x32, 0x64, 0x5f, 0x61, 0x72, 0x72, 0x61, 0x79, 0x3c, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x3e, 0x20, 0x6c, 0x69, 0x67, 0x68, +0x74, 0x5f, 0x73, 0x73, 0x61, 0x6f, 0x20, 0x5b, 0x5b, 0x69, 0x64, 0x28, 0x36, 0x29, 0x5d, 0x5d, 0x3b, 0x00, 0x73, 0x61, +0x6d, 0x70, 0x6c, 0x65, 0x72, 0x20, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x5f, 0x73, 0x73, 0x61, 0x6f, 0x53, 0x6d, 0x70, 0x6c, +0x72, 0x20, 0x5b, 0x5b, 0x69, 0x64, 0x28, 0x37, 0x29, 0x5d, 0x5d, 0x3b, 0x00, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, +0x32, 0x64, 0x5f, 0x61, 0x72, 0x72, 0x61, 0x79, 0x3c, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x3e, 0x20, 0x6c, 0x69, 0x67, 0x68, +0x74, 0x5f, 0x73, 0x73, 0x72, 0x20, 0x5b, 0x5b, 0x69, 0x64, 0x28, 0x38, 0x29, 0x5d, 0x5d, 0x3b, 0x00, 0x73, 0x61, 0x6d, +0x70, 0x6c, 0x65, 0x72, 0x20, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x5f, 0x73, 0x73, 0x72, 0x53, 0x6d, 0x70, 0x6c, 0x72, 0x20, +0x5b, 0x5b, 0x69, 0x64, 0x28, 0x39, 0x29, 0x5d, 0x5d, 0x3b, 0x00, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x32, 0x64, +0x3c, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x3e, 0x20, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x5f, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, +0x75, 0x72, 0x65, 0x20, 0x5b, 0x5b, 0x69, 0x64, 0x28, 0x31, 0x30, 0x29, 0x5d, 0x5d, 0x3b, 0x00, 0x73, 0x61, 0x6d, 0x70, +0x6c, 0x65, 0x72, 0x20, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x5f, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x75, 0x72, 0x65, 0x53, +0x6d, 0x70, 0x6c, 0x72, 0x20, 0x5b, 0x5b, 0x69, 0x64, 0x28, 0x31, 0x31, 0x29, 0x5d, 0x5d, 0x3b, 0x00, 0x74, 0x65, 0x78, +0x74, 0x75, 0x72, 0x65, 0x63, 0x75, 0x62, 0x65, 0x3c, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x3e, 0x20, 0x6c, 0x69, 0x67, 0x68, +0x74, 0x5f, 0x66, 0x6f, 0x67, 0x20, 0x5b, 0x5b, 0x69, 0x64, 0x28, 0x31, 0x32, 0x29, 0x5d, 0x5d, 0x3b, 0x00, 0x73, 0x61, +0x6d, 0x70, 0x6c, 0x65, 0x72, 0x20, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x5f, 0x66, 0x6f, 0x67, 0x53, 0x6d, 0x70, 0x6c, 0x72, +0x20, 0x5b, 0x5b, 0x69, 0x64, 0x28, 0x31, 0x33, 0x29, 0x5d, 0x5d, 0x3b, 0x00, 0x66, 0x72, 0x61, 0x67, 0x6d, 0x65, 0x6e, +0x74, 0x20, 0x6d, 0x61, 0x69, 0x6e, 0x30, 0x5f, 0x6f, 0x75, 0x74, 0x20, 0x6d, 0x61, 0x69, 0x6e, 0x30, 0x28, 0x6d, 0x61, +0x69, 0x6e, 0x30, 0x5f, 0x69, 0x6e, 0x20, 0x69, 0x6e, 0x20, 0x5b, 0x5b, 0x73, 0x74, 0x61, 0x67, 0x65, 0x5f, 0x69, 0x6e, +0x5d, 0x5d, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, 0x73, 0x70, 0x76, 0x44, 0x65, 0x73, 0x63, +0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x53, 0x65, 0x74, 0x42, 0x75, 0x66, 0x66, 0x65, 0x72, 0x31, 0x26, 0x20, 0x73, 0x70, +0x76, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x53, 0x65, 0x74, 0x31, 0x20, 0x5b, 0x5b, 0x62, 0x75, +0x66, 0x66, 0x65, 0x72, 0x28, 0x32, 0x37, 0x29, 0x5d, 0x5d, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, +0x20, 0x46, 0x72, 0x61, 0x6d, 0x65, 0x55, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x73, 0x26, 0x20, 0x66, 0x72, 0x61, 0x6d, +0x65, 0x55, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x73, 0x20, 0x5b, 0x5b, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x28, 0x31, +0x37, 0x29, 0x5d, 0x5d, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, 0x4d, 0x61, 0x74, 0x65, 0x72, +0x69, 0x61, 0x6c, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x26, 0x20, 0x6d, 0x61, 0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x50, +0x61, 0x72, 0x61, 0x6d, 0x73, 0x20, 0x5b, 0x5b, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x28, 0x32, 0x35, 0x29, 0x5d, 0x5d, +0x29, 0x00, 0x20, 0x20, 0x20, 0x20, 0x68, 0x61, 0x6c, 0x66, 0x20, 0x5f, 0x37, 0x39, 0x36, 0x20, 0x3d, 0x20, 0x68, 0x61, +0x6c, 0x66, 0x28, 0x31, 0x2e, 0x30, 0x29, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x6f, 0x75, 0x74, 0x2e, 0x66, 0x72, 0x61, +0x67, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x20, 0x3d, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x34, 0x28, 0x68, 0x61, 0x6c, 0x66, +0x34, 0x28, 0x68, 0x61, 0x6c, 0x66, 0x33, 0x28, 0x6d, 0x61, 0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x50, 0x61, 0x72, 0x61, +0x6d, 0x73, 0x2e, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x29, 0x2c, 0x20, 0x5f, 0x37, 0x39, 0x36, 0x29, 0x29, 0x3b, 0x00, 0x20, +0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x33, 0x20, 0x5f, 0x34, 0x33, 0x37, 0x20, 0x3d, 0x20, 0x66, 0x72, 0x61, +0x6d, 0x65, 0x55, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x73, 0x2e, 0x66, 0x6f, 0x67, 0x46, 0x72, 0x6f, 0x6d, 0x57, 0x6f, +0x72, 0x6c, 0x64, 0x4d, 0x61, 0x74, 0x72, 0x69, 0x78, 0x20, 0x2a, 0x20, 0x28, 0x69, 0x6e, 0x2e, 0x76, 0x65, 0x72, 0x74, +0x65, 0x78, 0x5f, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x78, 0x79, 0x7a, +0x20, 0x2d, 0x20, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x55, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x73, 0x2e, 0x77, 0x6f, 0x72, +0x6c, 0x64, 0x46, 0x72, 0x6f, 0x6d, 0x56, 0x69, 0x65, 0x77, 0x4d, 0x61, 0x74, 0x72, 0x69, 0x78, 0x5b, 0x33, 0x5d, 0x2e, +0x78, 0x79, 0x7a, 0x29, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x68, 0x61, 0x6c, 0x66, 0x34, 0x20, 0x5f, 0x37, 0x38, 0x37, +0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x64, 0x6f, 0x00, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, +0x61, 0x74, 0x20, 0x5f, 0x35, 0x39, 0x32, 0x20, 0x3d, 0x20, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x28, 0x5f, 0x34, 0x33, +0x37, 0x29, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x5f, 0x35, 0x39, 0x32, +0x20, 0x3e, 0x20, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x55, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x73, 0x2e, 0x66, 0x6f, 0x67, +0x43, 0x75, 0x74, 0x4f, 0x66, 0x66, 0x44, 0x69, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x29, 0x00, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x7b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x37, +0x38, 0x37, 0x20, 0x3d, 0x20, 0x68, 0x61, 0x6c, 0x66, 0x34, 0x28, 0x6f, 0x75, 0x74, 0x2e, 0x66, 0x72, 0x61, 0x67, 0x43, +0x6f, 0x6c, 0x6f, 0x72, 0x29, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x62, +0x72, 0x65, 0x61, 0x6b, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x00, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x5f, 0x36, 0x30, 0x38, 0x20, 0x3d, 0x20, 0x5f, 0x34, 0x33, +0x37, 0x2e, 0x79, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x5f, +0x36, 0x30, 0x39, 0x20, 0x3d, 0x20, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x55, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x73, 0x2e, +0x66, 0x6f, 0x67, 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, 0x46, 0x61, 0x6c, 0x6c, 0x6f, 0x66, 0x66, 0x20, 0x2a, 0x20, 0x5f, +0x36, 0x30, 0x38, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x5f, +0x37, 0x38, 0x30, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x61, 0x62, 0x73, +0x28, 0x5f, 0x36, 0x30, 0x39, 0x29, 0x20, 0x3e, 0x20, 0x30, 0x2e, 0x30, 0x30, 0x31, 0x32, 0x35, 0x29, 0x00, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x37, 0x38, 0x30, 0x20, 0x3d, 0x20, 0x66, 0x6d, 0x61, +0x28, 0x2d, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x55, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x73, 0x2e, 0x66, 0x6f, 0x67, 0x44, +0x65, 0x6e, 0x73, 0x69, 0x74, 0x79, 0x5b, 0x30, 0x5d, 0x2c, 0x20, 0x65, 0x78, 0x70, 0x28, 0x66, 0x6d, 0x61, 0x28, 0x2d, +0x66, 0x72, 0x61, 0x6d, 0x65, 0x55, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x73, 0x2e, 0x66, 0x6f, 0x67, 0x48, 0x65, 0x69, +0x67, 0x68, 0x74, 0x46, 0x61, 0x6c, 0x6c, 0x6f, 0x66, 0x66, 0x2c, 0x20, 0x5f, 0x36, 0x30, 0x38, 0x2c, 0x20, 0x66, 0x72, +0x61, 0x6d, 0x65, 0x55, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x73, 0x2e, 0x66, 0x6f, 0x67, 0x44, 0x65, 0x6e, 0x73, 0x69, +0x74, 0x79, 0x5b, 0x31, 0x5d, 0x29, 0x29, 0x2c, 0x20, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x55, 0x6e, 0x69, 0x66, 0x6f, 0x72, +0x6d, 0x73, 0x2e, 0x66, 0x6f, 0x67, 0x44, 0x65, 0x6e, 0x73, 0x69, 0x74, 0x79, 0x5b, 0x32, 0x5d, 0x29, 0x20, 0x2f, 0x20, +0x5f, 0x36, 0x30, 0x39, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6c, 0x73, 0x65, 0x00, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x37, 0x38, 0x30, 0x20, 0x3d, 0x20, 0x66, 0x72, +0x61, 0x6d, 0x65, 0x55, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x73, 0x2e, 0x66, 0x6f, 0x67, 0x44, 0x65, 0x6e, 0x73, 0x69, +0x74, 0x79, 0x5b, 0x32, 0x5d, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x68, 0x61, 0x6c, 0x66, 0x20, +0x5f, 0x36, 0x34, 0x32, 0x20, 0x3d, 0x20, 0x6d, 0x69, 0x6e, 0x28, 0x5f, 0x37, 0x39, 0x36, 0x20, 0x2d, 0x20, 0x68, 0x61, +0x6c, 0x66, 0x28, 0x65, 0x78, 0x70, 0x28, 0x2d, 0x28, 0x5f, 0x37, 0x38, 0x30, 0x20, 0x2a, 0x20, 0x66, 0x61, 0x73, 0x74, +0x3a, 0x3a, 0x6d, 0x61, 0x78, 0x28, 0x5f, 0x35, 0x39, 0x32, 0x20, 0x2d, 0x20, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x55, 0x6e, +0x69, 0x66, 0x6f, 0x72, 0x6d, 0x73, 0x2e, 0x66, 0x6f, 0x67, 0x53, 0x74, 0x61, 0x72, 0x74, 0x2c, 0x20, 0x30, 0x2e, 0x30, +0x29, 0x29, 0x29, 0x29, 0x2c, 0x20, 0x68, 0x61, 0x6c, 0x66, 0x28, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x55, 0x6e, 0x69, 0x66, +0x6f, 0x72, 0x6d, 0x73, 0x2e, 0x66, 0x6f, 0x67, 0x4d, 0x61, 0x78, 0x4f, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x29, 0x29, +0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x68, 0x61, 0x6c, 0x66, 0x33, 0x20, 0x5f, 0x38, 0x31, 0x31, +0x20, 0x3d, 0x20, 0x68, 0x61, 0x6c, 0x66, 0x33, 0x28, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x33, 0x28, 0x66, 0x72, 0x61, 0x6d, +0x65, 0x55, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x73, 0x2e, 0x66, 0x6f, 0x67, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x29, 0x29, +0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x68, 0x61, 0x6c, 0x66, 0x33, 0x20, 0x5f, 0x37, 0x38, 0x31, +0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x55, +0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x73, 0x2e, 0x66, 0x6f, 0x67, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x46, 0x72, 0x6f, 0x6d, +0x49, 0x62, 0x6c, 0x20, 0x3e, 0x20, 0x30, 0x2e, 0x30, 0x29, 0x00, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x68, 0x61, 0x6c, 0x66, 0x32, 0x20, 0x5f, 0x38, 0x30, 0x33, 0x20, 0x3d, 0x20, 0x68, 0x61, 0x6c, 0x66, +0x32, 0x28, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x32, 0x28, 0x61, 0x73, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x3c, 0x68, 0x61, 0x6c, +0x66, 0x32, 0x3e, 0x28, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x55, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x73, 0x2e, 0x66, 0x6f, +0x67, 0x4d, 0x69, 0x6e, 0x4d, 0x61, 0x78, 0x4d, 0x69, 0x70, 0x29, 0x29, 0x29, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x37, 0x38, 0x31, 0x20, 0x3d, 0x20, 0x5f, 0x38, 0x31, 0x31, 0x20, 0x2a, +0x20, 0x68, 0x61, 0x6c, 0x66, 0x34, 0x28, 0x73, 0x70, 0x76, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, +0x53, 0x65, 0x74, 0x31, 0x2e, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x5f, 0x66, 0x6f, 0x67, 0x2e, 0x73, 0x61, 0x6d, 0x70, 0x6c, +0x65, 0x28, 0x73, 0x70, 0x76, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x53, 0x65, 0x74, 0x31, 0x2e, +0x6c, 0x69, 0x67, 0x68, 0x74, 0x5f, 0x66, 0x6f, 0x67, 0x53, 0x6d, 0x70, 0x6c, 0x72, 0x2c, 0x20, 0x28, 0x74, 0x72, 0x61, +0x6e, 0x73, 0x70, 0x6f, 0x73, 0x65, 0x28, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x33, 0x78, 0x33, 0x28, 0x66, 0x72, 0x61, 0x6d, +0x65, 0x55, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x73, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x57, 0x6f, 0x72, 0x6c, 0x64, 0x46, +0x72, 0x6f, 0x6d, 0x57, 0x6f, 0x72, 0x6c, 0x64, 0x4d, 0x61, 0x74, 0x72, 0x69, 0x78, 0x5b, 0x30, 0x5d, 0x2e, 0x78, 0x79, +0x7a, 0x2c, 0x20, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x55, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x73, 0x2e, 0x75, 0x73, 0x65, +0x72, 0x57, 0x6f, 0x72, 0x6c, 0x64, 0x46, 0x72, 0x6f, 0x6d, 0x57, 0x6f, 0x72, 0x6c, 0x64, 0x4d, 0x61, 0x74, 0x72, 0x69, +0x78, 0x5b, 0x31, 0x5d, 0x2e, 0x78, 0x79, 0x7a, 0x2c, 0x20, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x55, 0x6e, 0x69, 0x66, 0x6f, +0x72, 0x6d, 0x73, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x57, 0x6f, 0x72, 0x6c, 0x64, 0x46, 0x72, 0x6f, 0x6d, 0x57, 0x6f, 0x72, +0x6c, 0x64, 0x4d, 0x61, 0x74, 0x72, 0x69, 0x78, 0x5b, 0x32, 0x5d, 0x2e, 0x78, 0x79, 0x7a, 0x29, 0x29, 0x20, 0x2a, 0x20, +0x5f, 0x34, 0x33, 0x37, 0x29, 0x2c, 0x20, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x28, 0x6d, 0x69, 0x78, 0x28, 0x5f, 0x38, 0x30, +0x33, 0x2e, 0x79, 0x2c, 0x20, 0x5f, 0x38, 0x30, 0x33, 0x2e, 0x78, 0x2c, 0x20, 0x63, 0x6c, 0x61, 0x6d, 0x70, 0x28, 0x68, +0x61, 0x6c, 0x66, 0x28, 0x66, 0x6d, 0x61, 0x28, 0x5f, 0x35, 0x39, 0x32, 0x2c, 0x20, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x55, +0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x73, 0x2e, 0x66, 0x6f, 0x67, 0x4f, 0x6e, 0x65, 0x4f, 0x76, 0x65, 0x72, 0x46, 0x61, +0x72, 0x4d, 0x69, 0x6e, 0x75, 0x73, 0x4e, 0x65, 0x61, 0x72, 0x2c, 0x20, 0x2d, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x55, 0x6e, +0x69, 0x66, 0x6f, 0x72, 0x6d, 0x73, 0x2e, 0x66, 0x6f, 0x67, 0x4e, 0x65, 0x61, 0x72, 0x4f, 0x76, 0x65, 0x72, 0x46, 0x61, +0x72, 0x4d, 0x69, 0x6e, 0x75, 0x73, 0x4e, 0x65, 0x61, 0x72, 0x29, 0x29, 0x2c, 0x20, 0x68, 0x61, 0x6c, 0x66, 0x28, 0x30, +0x2e, 0x30, 0x29, 0x2c, 0x20, 0x5f, 0x37, 0x39, 0x36, 0x29, 0x29, 0x29, 0x29, 0x29, 0x2e, 0x78, 0x79, 0x7a, 0x3b, 0x00, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x37, 0x38, 0x31, 0x20, 0x3d, 0x20, 0x5f, +0x38, 0x31, 0x31, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x68, 0x61, 0x6c, 0x66, 0x33, 0x20, 0x5f, +0x36, 0x39, 0x31, 0x20, 0x3d, 0x20, 0x5f, 0x37, 0x38, 0x31, 0x20, 0x2a, 0x20, 0x28, 0x68, 0x61, 0x6c, 0x66, 0x28, 0x66, +0x72, 0x61, 0x6d, 0x65, 0x55, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x73, 0x2e, 0x69, 0x62, 0x6c, 0x4c, 0x75, 0x6d, 0x69, +0x6e, 0x61, 0x6e, 0x63, 0x65, 0x29, 0x20, 0x2a, 0x20, 0x5f, 0x36, 0x34, 0x32, 0x29, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x68, 0x61, 0x6c, 0x66, 0x33, 0x20, 0x5f, 0x37, 0x38, 0x36, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x55, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, +0x73, 0x2e, 0x66, 0x6f, 0x67, 0x49, 0x6e, 0x73, 0x63, 0x61, 0x74, 0x74, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x53, 0x69, 0x7a, +0x65, 0x20, 0x3e, 0x20, 0x30, 0x2e, 0x30, 0x29, 0x00, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x5f, 0x37, 0x38, 0x36, 0x20, 0x3d, 0x20, 0x5f, 0x36, 0x39, 0x31, 0x20, 0x2b, 0x20, 0x28, 0x28, 0x68, 0x61, 0x6c, +0x66, 0x34, 0x28, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x55, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x73, 0x2e, 0x6c, 0x69, 0x67, +0x68, 0x74, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x49, 0x6e, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x74, 0x79, 0x29, 0x2e, 0x78, 0x79, +0x7a, 0x20, 0x2a, 0x20, 0x68, 0x61, 0x6c, 0x66, 0x28, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x55, 0x6e, 0x69, 0x66, 0x6f, 0x72, +0x6d, 0x73, 0x2e, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x49, 0x6e, 0x74, 0x65, 0x6e, 0x73, 0x69, +0x74, 0x79, 0x2e, 0x77, 0x29, 0x29, 0x20, 0x2a, 0x20, 0x28, 0x70, 0x6f, 0x77, 0x28, 0x68, 0x61, 0x6c, 0x66, 0x28, 0x66, +0x61, 0x73, 0x74, 0x3a, 0x3a, 0x6d, 0x61, 0x78, 0x28, 0x64, 0x6f, 0x74, 0x28, 0x66, 0x61, 0x73, 0x74, 0x3a, 0x3a, 0x6e, +0x6f, 0x72, 0x6d, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x28, 0x5f, 0x34, 0x33, 0x37, 0x29, 0x2c, 0x20, 0x66, 0x6c, 0x6f, 0x61, +0x74, 0x33, 0x28, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x55, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x73, 0x2e, 0x6c, 0x69, 0x67, +0x68, 0x74, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x29, 0x29, 0x2c, 0x20, 0x30, 0x2e, 0x30, 0x29, 0x29, +0x2c, 0x20, 0x68, 0x61, 0x6c, 0x66, 0x28, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x55, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x73, +0x2e, 0x66, 0x6f, 0x67, 0x49, 0x6e, 0x73, 0x63, 0x61, 0x74, 0x74, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x53, 0x69, 0x7a, 0x65, +0x29, 0x29, 0x20, 0x2a, 0x20, 0x28, 0x5f, 0x37, 0x39, 0x36, 0x20, 0x2d, 0x20, 0x68, 0x61, 0x6c, 0x66, 0x28, 0x65, 0x78, +0x70, 0x28, 0x2d, 0x28, 0x5f, 0x37, 0x38, 0x30, 0x20, 0x2a, 0x20, 0x66, 0x61, 0x73, 0x74, 0x3a, 0x3a, 0x6d, 0x61, 0x78, +0x28, 0x5f, 0x35, 0x39, 0x32, 0x20, 0x2d, 0x20, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x55, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, +0x73, 0x2e, 0x66, 0x6f, 0x67, 0x49, 0x6e, 0x73, 0x63, 0x61, 0x74, 0x74, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x53, 0x74, 0x61, +0x72, 0x74, 0x2c, 0x20, 0x30, 0x2e, 0x30, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x37, 0x38, 0x36, 0x20, 0x3d, 0x20, 0x5f, 0x36, 0x39, 0x31, 0x3b, +0x00, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x68, 0x61, 0x6c, 0x66, 0x34, 0x20, 0x5f, 0x38, 0x32, 0x30, 0x20, +0x3d, 0x20, 0x68, 0x61, 0x6c, 0x66, 0x34, 0x28, 0x6f, 0x75, 0x74, 0x2e, 0x66, 0x72, 0x61, 0x67, 0x43, 0x6f, 0x6c, 0x6f, +0x72, 0x29, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x68, 0x61, 0x6c, 0x66, 0x33, 0x20, 0x5f, 0x37, +0x33, 0x37, 0x20, 0x3d, 0x20, 0x28, 0x5f, 0x38, 0x32, 0x30, 0x2e, 0x78, 0x79, 0x7a, 0x20, 0x2a, 0x20, 0x28, 0x5f, 0x37, +0x39, 0x36, 0x20, 0x2d, 0x20, 0x5f, 0x36, 0x34, 0x32, 0x29, 0x29, 0x20, 0x2b, 0x20, 0x5f, 0x37, 0x38, 0x36, 0x3b, 0x00, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x68, 0x61, 0x6c, 0x66, 0x34, 0x20, 0x5f, 0x37, 0x37, 0x35, 0x20, 0x3d, +0x20, 0x5f, 0x38, 0x32, 0x30, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x37, 0x37, 0x35, 0x2e, +0x78, 0x20, 0x3d, 0x20, 0x5f, 0x37, 0x33, 0x37, 0x2e, 0x78, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +0x5f, 0x37, 0x37, 0x35, 0x2e, 0x79, 0x20, 0x3d, 0x20, 0x5f, 0x37, 0x33, 0x37, 0x2e, 0x79, 0x3b, 0x00, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x37, 0x37, 0x35, 0x2e, 0x7a, 0x20, 0x3d, 0x20, 0x5f, 0x37, 0x33, 0x37, 0x2e, 0x7a, +0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x37, 0x38, 0x37, 0x20, 0x3d, 0x20, 0x5f, 0x37, 0x37, +0x35, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x62, 0x72, 0x65, 0x61, 0x6b, 0x3b, 0x00, 0x20, 0x20, +0x20, 0x20, 0x7d, 0x20, 0x77, 0x68, 0x69, 0x6c, 0x65, 0x28, 0x66, 0x61, 0x6c, 0x73, 0x65, 0x29, 0x3b, 0x00, 0x20, 0x20, +0x20, 0x20, 0x6f, 0x75, 0x74, 0x2e, 0x66, 0x72, 0x61, 0x67, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x20, 0x3d, 0x20, 0x66, 0x6c, +0x6f, 0x61, 0x74, 0x34, 0x28, 0x5f, 0x37, 0x38, 0x37, 0x29, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, +0x74, 0x34, 0x78, 0x34, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x46, 0x72, 0x6f, 0x6d, 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x4d, +0x61, 0x74, 0x72, 0x69, 0x78, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x33, 0x78, 0x33, 0x20, +0x77, 0x6f, 0x72, 0x6c, 0x64, 0x46, 0x72, 0x6f, 0x6d, 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x4e, 0x6f, 0x72, 0x6d, 0x61, 0x6c, +0x4d, 0x61, 0x74, 0x72, 0x69, 0x78, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x69, 0x6e, 0x74, 0x20, 0x6d, 0x6f, 0x72, 0x70, +0x68, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x69, 0x6e, +0x74, 0x20, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x3b, 0x00, 0x20, 0x20, 0x20, +0x20, 0x69, 0x6e, 0x74, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x49, 0x64, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x66, +0x6c, 0x6f, 0x61, 0x74, 0x20, 0x75, 0x73, 0x65, 0x72, 0x44, 0x61, 0x74, 0x61, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x66, +0x6c, 0x6f, 0x61, 0x74, 0x34, 0x20, 0x72, 0x65, 0x73, 0x65, 0x72, 0x76, 0x65, 0x64, 0x5b, 0x38, 0x5d, 0x3b, 0x00, 0x63, +0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, 0x69, 0x6e, 0x74, 0x20, 0x43, 0x4f, 0x4e, 0x46, 0x49, 0x47, 0x5f, 0x4d, +0x41, 0x58, 0x5f, 0x49, 0x4e, 0x53, 0x54, 0x41, 0x4e, 0x43, 0x45, 0x53, 0x20, 0x3d, 0x20, 0x53, 0x50, 0x49, 0x52, 0x56, +0x5f, 0x43, 0x52, 0x4f, 0x53, 0x53, 0x5f, 0x43, 0x4f, 0x4e, 0x53, 0x54, 0x41, 0x4e, 0x54, 0x5f, 0x49, 0x44, 0x5f, 0x31, +0x3b, 0x00, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x20, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x55, 0x6e, 0x69, 0x66, 0x6f, +0x72, 0x6d, 0x73, 0x00, 0x20, 0x20, 0x20, 0x20, 0x50, 0x65, 0x72, 0x52, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x61, 0x62, 0x6c, +0x65, 0x44, 0x61, 0x74, 0x61, 0x20, 0x64, 0x61, 0x74, 0x61, 0x5b, 0x43, 0x4f, 0x4e, 0x46, 0x49, 0x47, 0x5f, 0x4d, 0x41, +0x58, 0x5f, 0x49, 0x4e, 0x53, 0x54, 0x41, 0x4e, 0x43, 0x45, 0x53, 0x5d, 0x3b, 0x00, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, +0x6e, 0x74, 0x20, 0x69, 0x6e, 0x74, 0x20, 0x5f, 0x31, 0x37, 0x37, 0x20, 0x3d, 0x20, 0x7b, 0x7d, 0x3b, 0x00, 0x20, 0x20, +0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x32, 0x20, 0x6f, 0x75, 0x74, 0x50, 0x69, 0x63, 0x6b, 0x69, 0x6e, 0x67, 0x20, +0x5b, 0x5b, 0x63, 0x6f, 0x6c, 0x6f, 0x72, 0x28, 0x30, 0x29, 0x5d, 0x5d, 0x3b, 0x00, 0x66, 0x72, 0x61, 0x67, 0x6d, 0x65, +0x6e, 0x74, 0x20, 0x6d, 0x61, 0x69, 0x6e, 0x30, 0x5f, 0x6f, 0x75, 0x74, 0x20, 0x6d, 0x61, 0x69, 0x6e, 0x30, 0x28, 0x6d, +0x61, 0x69, 0x6e, 0x30, 0x5f, 0x69, 0x6e, 0x20, 0x69, 0x6e, 0x20, 0x5b, 0x5b, 0x73, 0x74, 0x61, 0x67, 0x65, 0x5f, 0x69, +0x6e, 0x5d, 0x5d, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x20, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, +0x55, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x73, 0x26, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x55, 0x6e, 0x69, 0x66, +0x6f, 0x72, 0x6d, 0x73, 0x20, 0x5b, 0x5b, 0x62, 0x75, 0x66, 0x66, 0x65, 0x72, 0x28, 0x31, 0x38, 0x29, 0x5d, 0x5d, 0x29, +0x00, 0x20, 0x20, 0x20, 0x20, 0x6f, 0x75, 0x74, 0x2e, 0x6f, 0x75, 0x74, 0x50, 0x69, 0x63, 0x6b, 0x69, 0x6e, 0x67, 0x2e, +0x78, 0x20, 0x3d, 0x20, 0x61, 0x73, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x3c, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x3e, 0x28, 0x6f, +0x62, 0x6a, 0x65, 0x63, 0x74, 0x55, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x73, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x5b, 0x28, +0x28, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x55, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x73, 0x2e, 0x64, 0x61, 0x74, 0x61, +0x5b, 0x30, 0x5d, 0x2e, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x73, 0x20, 0x26, 0x20, +0x32, 0x30, 0x34, 0x38, 0x29, 0x20, 0x21, 0x3d, 0x20, 0x30, 0x29, 0x20, 0x3f, 0x20, 0x5f, 0x31, 0x37, 0x37, 0x20, 0x3a, +0x20, 0x30, 0x5d, 0x2e, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x49, 0x64, 0x29, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x6f, +0x75, 0x74, 0x2e, 0x6f, 0x75, 0x74, 0x50, 0x69, 0x63, 0x6b, 0x69, 0x6e, 0x67, 0x2e, 0x79, 0x20, 0x3d, 0x20, 0x69, 0x6e, +0x2e, 0x76, 0x65, 0x72, 0x74, 0x65, 0x78, 0x5f, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x7a, 0x20, 0x2f, +0x20, 0x69, 0x6e, 0x2e, 0x76, 0x65, 0x72, 0x74, 0x65, 0x78, 0x5f, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x2e, +0x77, 0x3b, 0x00, 0x66, 0x72, 0x61, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x6d, 0x61, 0x69, 0x6e, 0x30, 0x5f, 0x6f, 0x75, +0x74, 0x20, 0x6d, 0x61, 0x69, 0x6e, 0x30, 0x28, 0x29, 0x00, 0x20, 0x20, 0x20, 0x20, 0x6f, 0x75, 0x74, 0x2e, 0x66, 0x72, +0x61, 0x67, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x20, 0x3d, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x34, 0x28, 0x30, 0x2e, 0x30, +0x29, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x67, 0x6c, 0x5f, 0x43, 0x6c, 0x69, 0x70, +0x44, 0x69, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x20, 0x5b, 0x5b, 0x63, 0x6c, 0x69, 0x70, 0x5f, 0x64, 0x69, 0x73, 0x74, +0x61, 0x6e, 0x63, 0x65, 0x5d, 0x5d, 0x20, 0x5b, 0x31, 0x5d, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, +0x74, 0x20, 0x67, 0x6c, 0x5f, 0x43, 0x6c, 0x69, 0x70, 0x44, 0x69, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x30, 0x20, +0x5b, 0x5b, 0x75, 0x73, 0x65, 0x72, 0x28, 0x63, 0x6c, 0x69, 0x70, 0x30, 0x29, 0x5d, 0x5d, 0x3b, 0x00, 0x20, 0x20, 0x20, +0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x34, 0x20, 0x5f, 0x36, 0x33, 0x20, 0x3d, 0x20, 0x69, 0x6e, 0x2e, 0x6d, 0x65, 0x73, +0x68, 0x5f, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, +0x74, 0x34, 0x20, 0x5f, 0x36, 0x36, 0x20, 0x3d, 0x20, 0x5f, 0x36, 0x33, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x36, +0x36, 0x2e, 0x7a, 0x20, 0x3d, 0x20, 0x66, 0x6d, 0x61, 0x28, 0x5f, 0x36, 0x33, 0x2e, 0x7a, 0x2c, 0x20, 0x2d, 0x30, 0x2e, +0x35, 0x2c, 0x20, 0x30, 0x2e, 0x35, 0x29, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x34, 0x20, +0x5f, 0x36, 0x37, 0x20, 0x3d, 0x20, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x55, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x73, 0x2e, +0x77, 0x6f, 0x72, 0x6c, 0x64, 0x46, 0x72, 0x6f, 0x6d, 0x43, 0x6c, 0x69, 0x70, 0x4d, 0x61, 0x74, 0x72, 0x69, 0x78, 0x20, +0x2a, 0x20, 0x5f, 0x36, 0x36, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x5f, 0x36, 0x38, +0x20, 0x3d, 0x20, 0x5f, 0x36, 0x37, 0x2e, 0x77, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x34, +0x20, 0x5f, 0x37, 0x36, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x61, 0x62, 0x73, 0x28, 0x5f, 0x36, +0x38, 0x29, 0x20, 0x3c, 0x20, 0x31, 0x2e, 0x30, 0x38, 0x34, 0x32, 0x30, 0x32, 0x32, 0x65, 0x2d, 0x31, 0x39, 0x29, 0x00, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x34, 0x20, 0x5f, 0x37, 0x35, 0x20, 0x3d, +0x20, 0x5f, 0x36, 0x37, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x37, 0x35, 0x2e, 0x77, 0x20, +0x3d, 0x20, 0x28, 0x5f, 0x36, 0x38, 0x20, 0x3c, 0x20, 0x30, 0x2e, 0x30, 0x29, 0x20, 0x3f, 0x20, 0x28, 0x2d, 0x31, 0x2e, +0x30, 0x38, 0x34, 0x32, 0x30, 0x32, 0x32, 0x65, 0x2d, 0x31, 0x39, 0x29, 0x20, 0x3a, 0x20, 0x31, 0x2e, 0x30, 0x38, 0x34, +0x32, 0x30, 0x32, 0x32, 0x65, 0x2d, 0x31, 0x39, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x37, +0x36, 0x20, 0x3d, 0x20, 0x5f, 0x37, 0x35, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x37, 0x36, +0x20, 0x3d, 0x20, 0x5f, 0x36, 0x37, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x34, 0x20, 0x5f, +0x37, 0x39, 0x20, 0x3d, 0x20, 0x5f, 0x37, 0x36, 0x20, 0x2a, 0x20, 0x28, 0x31, 0x2e, 0x30, 0x20, 0x2f, 0x20, 0x5f, 0x37, +0x36, 0x2e, 0x77, 0x29, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x6f, 0x75, 0x74, 0x2e, 0x76, 0x65, 0x72, 0x74, 0x65, 0x78, +0x5f, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x78, 0x20, 0x3d, 0x20, 0x5f, +0x37, 0x39, 0x2e, 0x78, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x6f, 0x75, 0x74, 0x2e, 0x76, 0x65, 0x72, 0x74, 0x65, 0x78, +0x5f, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x79, 0x20, 0x3d, 0x20, 0x5f, +0x37, 0x39, 0x2e, 0x79, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x6f, 0x75, 0x74, 0x2e, 0x76, 0x65, 0x72, 0x74, 0x65, 0x78, +0x5f, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x7a, 0x20, 0x3d, 0x20, 0x5f, +0x37, 0x39, 0x2e, 0x7a, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x6f, 0x75, 0x74, 0x2e, 0x76, 0x65, 0x72, 0x74, 0x65, 0x78, +0x5f, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x3d, 0x20, 0x5f, 0x36, 0x36, 0x3b, 0x00, 0x20, 0x20, 0x20, +0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x5f, 0x38, 0x39, 0x20, 0x3d, 0x20, 0x66, 0x6d, 0x61, 0x28, 0x66, 0x6c, 0x6f, +0x61, 0x74, 0x28, 0x6f, 0x75, 0x74, 0x2e, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x69, 0x6e, 0x64, 0x65, +0x78, 0x20, 0x25, 0x20, 0x32, 0x29, 0x2c, 0x20, 0x32, 0x2e, 0x30, 0x2c, 0x20, 0x2d, 0x31, 0x2e, 0x30, 0x29, 0x3b, 0x00, +0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x5f, 0x39, 0x34, 0x20, 0x3d, 0x20, 0x66, 0x6d, 0x61, 0x28, +0x5f, 0x36, 0x33, 0x2e, 0x78, 0x2c, 0x20, 0x30, 0x2e, 0x35, 0x2c, 0x20, 0x28, 0x5f, 0x36, 0x33, 0x2e, 0x77, 0x20, 0x2a, +0x20, 0x30, 0x2e, 0x35, 0x29, 0x20, 0x2a, 0x20, 0x5f, 0x38, 0x39, 0x29, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x36, +0x36, 0x2e, 0x78, 0x20, 0x3d, 0x20, 0x5f, 0x39, 0x34, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x6f, 0x75, 0x74, 0x2e, 0x67, +0x6c, 0x5f, 0x43, 0x6c, 0x69, 0x70, 0x44, 0x69, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5b, 0x30, 0x5d, 0x20, 0x3d, 0x20, +0x5f, 0x39, 0x34, 0x20, 0x2a, 0x20, 0x5f, 0x38, 0x39, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x6f, 0x75, 0x74, 0x2e, 0x67, +0x6c, 0x5f, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x3d, 0x20, 0x5f, 0x36, 0x36, 0x3b, 0x00, 0x20, 0x20, +0x20, 0x20, 0x6f, 0x75, 0x74, 0x2e, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, +0x20, 0x3d, 0x20, 0x6f, 0x75, 0x74, 0x2e, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x69, 0x6e, 0x64, 0x65, +0x78, 0x20, 0x3e, 0x3e, 0x20, 0x31, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x6f, 0x75, 0x74, 0x2e, 0x67, 0x6c, 0x5f, 0x43, +0x6c, 0x69, 0x70, 0x44, 0x69, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x30, 0x20, 0x3d, 0x20, 0x6f, 0x75, 0x74, 0x2e, +0x67, 0x6c, 0x5f, 0x43, 0x6c, 0x69, 0x70, 0x44, 0x69, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5b, 0x30, 0x5d, 0x3b, 0x00, +0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x34, 0x20, 0x5f, 0x32, 0x37, 0x38, 0x20, 0x3d, 0x20, 0x69, 0x6e, +0x2e, 0x6d, 0x65, 0x73, 0x68, 0x5f, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, +0x5f, 0x32, 0x37, 0x38, 0x2e, 0x7a, 0x20, 0x3d, 0x20, 0x66, 0x6d, 0x61, 0x28, 0x5f, 0x32, 0x37, 0x38, 0x2e, 0x7a, 0x2c, +0x20, 0x2d, 0x30, 0x2e, 0x35, 0x2c, 0x20, 0x30, 0x2e, 0x35, 0x29, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, +0x61, 0x74, 0x34, 0x20, 0x5f, 0x32, 0x35, 0x34, 0x20, 0x3d, 0x20, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x55, 0x6e, 0x69, 0x66, +0x6f, 0x72, 0x6d, 0x73, 0x2e, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x46, 0x72, 0x6f, 0x6d, 0x43, 0x6c, 0x69, 0x70, 0x4d, 0x61, +0x74, 0x72, 0x69, 0x78, 0x20, 0x2a, 0x20, 0x5f, 0x32, 0x37, 0x38, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, +0x61, 0x74, 0x20, 0x5f, 0x32, 0x35, 0x36, 0x20, 0x3d, 0x20, 0x5f, 0x32, 0x35, 0x34, 0x2e, 0x77, 0x3b, 0x00, 0x20, 0x20, +0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x34, 0x20, 0x5f, 0x33, 0x30, 0x32, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x69, +0x66, 0x20, 0x28, 0x61, 0x62, 0x73, 0x28, 0x5f, 0x32, 0x35, 0x36, 0x29, 0x20, 0x3c, 0x20, 0x31, 0x2e, 0x30, 0x38, 0x34, +0x32, 0x30, 0x32, 0x32, 0x65, 0x2d, 0x31, 0x39, 0x29, 0x00, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, +0x6f, 0x61, 0x74, 0x34, 0x20, 0x5f, 0x32, 0x39, 0x37, 0x20, 0x3d, 0x20, 0x5f, 0x32, 0x35, 0x34, 0x3b, 0x00, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x32, 0x39, 0x37, 0x2e, 0x77, 0x20, 0x3d, 0x20, 0x28, 0x5f, 0x32, 0x35, 0x36, +0x20, 0x3c, 0x20, 0x30, 0x2e, 0x30, 0x29, 0x20, 0x3f, 0x20, 0x28, 0x2d, 0x31, 0x2e, 0x30, 0x38, 0x34, 0x32, 0x30, 0x32, +0x32, 0x65, 0x2d, 0x31, 0x39, 0x29, 0x20, 0x3a, 0x20, 0x31, 0x2e, 0x30, 0x38, 0x34, 0x32, 0x30, 0x32, 0x32, 0x65, 0x2d, +0x31, 0x39, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x33, 0x30, 0x32, 0x20, 0x3d, 0x20, 0x5f, +0x32, 0x39, 0x37, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x33, 0x30, 0x32, 0x20, 0x3d, 0x20, +0x5f, 0x32, 0x35, 0x34, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x34, 0x20, 0x5f, 0x32, 0x37, +0x30, 0x20, 0x3d, 0x20, 0x5f, 0x33, 0x30, 0x32, 0x20, 0x2a, 0x20, 0x28, 0x31, 0x2e, 0x30, 0x20, 0x2f, 0x20, 0x5f, 0x33, +0x30, 0x32, 0x2e, 0x77, 0x29, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x6f, 0x75, 0x74, 0x2e, 0x76, 0x65, 0x72, 0x74, 0x65, +0x78, 0x5f, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x78, 0x20, 0x3d, 0x20, +0x5f, 0x32, 0x37, 0x30, 0x2e, 0x78, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x6f, 0x75, 0x74, 0x2e, 0x76, 0x65, 0x72, 0x74, +0x65, 0x78, 0x5f, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x79, 0x20, 0x3d, +0x20, 0x5f, 0x32, 0x37, 0x30, 0x2e, 0x79, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x6f, 0x75, 0x74, 0x2e, 0x76, 0x65, 0x72, +0x74, 0x65, 0x78, 0x5f, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x7a, 0x20, +0x3d, 0x20, 0x5f, 0x32, 0x37, 0x30, 0x2e, 0x7a, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x6f, 0x75, 0x74, 0x2e, 0x76, 0x65, +0x72, 0x74, 0x65, 0x78, 0x5f, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x3d, 0x20, 0x5f, 0x32, 0x37, 0x38, +0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x6f, 0x75, 0x74, 0x2e, 0x67, 0x6c, 0x5f, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, +0x6e, 0x20, 0x3d, 0x20, 0x5f, 0x32, 0x37, 0x38, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x34, +0x20, 0x5f, 0x34, 0x33, 0x39, 0x20, 0x3d, 0x20, 0x6f, 0x75, 0x74, 0x2e, 0x66, 0x72, 0x61, 0x67, 0x43, 0x6f, 0x6c, 0x6f, +0x72, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x34, 0x20, 0x5f, 0x37, 0x38, 0x37, 0x3b, 0x00, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x37, 0x38, 0x37, 0x20, 0x3d, 0x20, 0x5f, +0x34, 0x33, 0x39, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x5f, +0x36, 0x34, 0x32, 0x20, 0x3d, 0x20, 0x66, 0x61, 0x73, 0x74, 0x3a, 0x3a, 0x6d, 0x69, 0x6e, 0x28, 0x31, 0x2e, 0x30, 0x20, +0x2d, 0x20, 0x65, 0x78, 0x70, 0x28, 0x2d, 0x28, 0x5f, 0x37, 0x38, 0x30, 0x20, 0x2a, 0x20, 0x66, 0x61, 0x73, 0x74, 0x3a, +0x3a, 0x6d, 0x61, 0x78, 0x28, 0x5f, 0x35, 0x39, 0x32, 0x20, 0x2d, 0x20, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x55, 0x6e, 0x69, +0x66, 0x6f, 0x72, 0x6d, 0x73, 0x2e, 0x66, 0x6f, 0x67, 0x53, 0x74, 0x61, 0x72, 0x74, 0x2c, 0x20, 0x30, 0x2e, 0x30, 0x29, +0x29, 0x29, 0x2c, 0x20, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x55, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x73, 0x2e, 0x66, 0x6f, +0x67, 0x4d, 0x61, 0x78, 0x4f, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x29, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x33, 0x20, 0x5f, 0x37, 0x38, 0x31, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x68, 0x61, 0x6c, 0x66, 0x32, 0x20, 0x5f, 0x38, 0x30, 0x30, 0x20, 0x3d, 0x20, +0x68, 0x61, 0x6c, 0x66, 0x32, 0x28, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x32, 0x28, 0x61, 0x73, 0x5f, 0x74, 0x79, 0x70, 0x65, +0x3c, 0x68, 0x61, 0x6c, 0x66, 0x32, 0x3e, 0x28, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x55, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, +0x73, 0x2e, 0x66, 0x6f, 0x67, 0x4d, 0x69, 0x6e, 0x4d, 0x61, 0x78, 0x4d, 0x69, 0x70, 0x29, 0x29, 0x29, 0x3b, 0x00, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x37, 0x38, 0x31, 0x20, 0x3d, 0x20, 0x66, 0x6c, +0x6f, 0x61, 0x74, 0x33, 0x28, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x55, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x73, 0x2e, 0x66, +0x6f, 0x67, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x29, 0x20, 0x2a, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x33, 0x28, 0x68, 0x61, +0x6c, 0x66, 0x34, 0x28, 0x73, 0x70, 0x76, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x53, 0x65, 0x74, +0x31, 0x2e, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x5f, 0x66, 0x6f, 0x67, 0x2e, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x28, 0x73, +0x70, 0x76, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x53, 0x65, 0x74, 0x31, 0x2e, 0x6c, 0x69, 0x67, +0x68, 0x74, 0x5f, 0x66, 0x6f, 0x67, 0x53, 0x6d, 0x70, 0x6c, 0x72, 0x2c, 0x20, 0x28, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, +0x6f, 0x73, 0x65, 0x28, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x33, 0x78, 0x33, 0x28, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x55, 0x6e, +0x69, 0x66, 0x6f, 0x72, 0x6d, 0x73, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x57, 0x6f, 0x72, 0x6c, 0x64, 0x46, 0x72, 0x6f, 0x6d, +0x57, 0x6f, 0x72, 0x6c, 0x64, 0x4d, 0x61, 0x74, 0x72, 0x69, 0x78, 0x5b, 0x30, 0x5d, 0x2e, 0x78, 0x79, 0x7a, 0x2c, 0x20, +0x66, 0x72, 0x61, 0x6d, 0x65, 0x55, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x73, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x57, 0x6f, +0x72, 0x6c, 0x64, 0x46, 0x72, 0x6f, 0x6d, 0x57, 0x6f, 0x72, 0x6c, 0x64, 0x4d, 0x61, 0x74, 0x72, 0x69, 0x78, 0x5b, 0x31, +0x5d, 0x2e, 0x78, 0x79, 0x7a, 0x2c, 0x20, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x55, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x73, +0x2e, 0x75, 0x73, 0x65, 0x72, 0x57, 0x6f, 0x72, 0x6c, 0x64, 0x46, 0x72, 0x6f, 0x6d, 0x57, 0x6f, 0x72, 0x6c, 0x64, 0x4d, +0x61, 0x74, 0x72, 0x69, 0x78, 0x5b, 0x32, 0x5d, 0x2e, 0x78, 0x79, 0x7a, 0x29, 0x29, 0x20, 0x2a, 0x20, 0x5f, 0x34, 0x33, +0x37, 0x29, 0x2c, 0x20, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x28, 0x6d, 0x69, 0x78, 0x28, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x28, +0x5f, 0x38, 0x30, 0x30, 0x2e, 0x79, 0x29, 0x2c, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x28, 0x5f, 0x38, 0x30, 0x30, 0x2e, +0x78, 0x29, 0x2c, 0x20, 0x66, 0x61, 0x73, 0x74, 0x3a, 0x3a, 0x63, 0x6c, 0x61, 0x6d, 0x70, 0x28, 0x66, 0x6d, 0x61, 0x28, +0x5f, 0x35, 0x39, 0x32, 0x2c, 0x20, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x55, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x73, 0x2e, +0x66, 0x6f, 0x67, 0x4f, 0x6e, 0x65, 0x4f, 0x76, 0x65, 0x72, 0x46, 0x61, 0x72, 0x4d, 0x69, 0x6e, 0x75, 0x73, 0x4e, 0x65, +0x61, 0x72, 0x2c, 0x20, 0x2d, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x55, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x73, 0x2e, 0x66, +0x6f, 0x67, 0x4e, 0x65, 0x61, 0x72, 0x4f, 0x76, 0x65, 0x72, 0x46, 0x61, 0x72, 0x4d, 0x69, 0x6e, 0x75, 0x73, 0x4e, 0x65, +0x61, 0x72, 0x29, 0x2c, 0x20, 0x30, 0x2e, 0x30, 0x2c, 0x20, 0x31, 0x2e, 0x30, 0x29, 0x29, 0x29, 0x29, 0x29, 0x2e, 0x78, +0x79, 0x7a, 0x29, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x37, 0x38, +0x31, 0x20, 0x3d, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x33, 0x28, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x55, 0x6e, 0x69, 0x66, +0x6f, 0x72, 0x6d, 0x73, 0x2e, 0x66, 0x6f, 0x67, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x29, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x33, 0x20, 0x5f, 0x36, 0x39, 0x31, 0x20, 0x3d, 0x20, 0x5f, 0x37, +0x38, 0x31, 0x20, 0x2a, 0x20, 0x28, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x55, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x73, 0x2e, +0x69, 0x62, 0x6c, 0x4c, 0x75, 0x6d, 0x69, 0x6e, 0x61, 0x6e, 0x63, 0x65, 0x20, 0x2a, 0x20, 0x5f, 0x36, 0x34, 0x32, 0x29, +0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x33, 0x20, 0x5f, 0x37, 0x38, +0x36, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x37, 0x38, 0x36, 0x20, +0x3d, 0x20, 0x5f, 0x36, 0x39, 0x31, 0x20, 0x2b, 0x20, 0x28, 0x28, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x55, 0x6e, 0x69, 0x66, +0x6f, 0x72, 0x6d, 0x73, 0x2e, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x49, 0x6e, 0x74, 0x65, 0x6e, +0x73, 0x69, 0x74, 0x79, 0x2e, 0x78, 0x79, 0x7a, 0x20, 0x2a, 0x20, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x55, 0x6e, 0x69, 0x66, +0x6f, 0x72, 0x6d, 0x73, 0x2e, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x49, 0x6e, 0x74, 0x65, 0x6e, +0x73, 0x69, 0x74, 0x79, 0x2e, 0x77, 0x29, 0x20, 0x2a, 0x20, 0x28, 0x70, 0x6f, 0x77, 0x28, 0x66, 0x61, 0x73, 0x74, 0x3a, +0x3a, 0x6d, 0x61, 0x78, 0x28, 0x64, 0x6f, 0x74, 0x28, 0x66, 0x61, 0x73, 0x74, 0x3a, 0x3a, 0x6e, 0x6f, 0x72, 0x6d, 0x61, +0x6c, 0x69, 0x7a, 0x65, 0x28, 0x5f, 0x34, 0x33, 0x37, 0x29, 0x2c, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x33, 0x28, 0x66, +0x72, 0x61, 0x6d, 0x65, 0x55, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x73, 0x2e, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x44, 0x69, +0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x29, 0x29, 0x2c, 0x20, 0x30, 0x2e, 0x30, 0x29, 0x2c, 0x20, 0x66, 0x72, 0x61, +0x6d, 0x65, 0x55, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x73, 0x2e, 0x66, 0x6f, 0x67, 0x49, 0x6e, 0x73, 0x63, 0x61, 0x74, +0x74, 0x65, 0x72, 0x69, 0x6e, 0x67, 0x53, 0x69, 0x7a, 0x65, 0x29, 0x20, 0x2a, 0x20, 0x28, 0x31, 0x2e, 0x30, 0x20, 0x2d, +0x20, 0x65, 0x78, 0x70, 0x28, 0x2d, 0x28, 0x5f, 0x37, 0x38, 0x30, 0x20, 0x2a, 0x20, 0x66, 0x61, 0x73, 0x74, 0x3a, 0x3a, +0x6d, 0x61, 0x78, 0x28, 0x5f, 0x35, 0x39, 0x32, 0x20, 0x2d, 0x20, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x55, 0x6e, 0x69, 0x66, +0x6f, 0x72, 0x6d, 0x73, 0x2e, 0x66, 0x6f, 0x67, 0x49, 0x6e, 0x73, 0x63, 0x61, 0x74, 0x74, 0x65, 0x72, 0x69, 0x6e, 0x67, +0x53, 0x74, 0x61, 0x72, 0x74, 0x2c, 0x20, 0x30, 0x2e, 0x30, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x3b, 0x00, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x33, 0x20, 0x5f, 0x37, 0x33, 0x37, 0x20, 0x3d, 0x20, +0x28, 0x5f, 0x34, 0x33, 0x39, 0x2e, 0x78, 0x79, 0x7a, 0x20, 0x2a, 0x20, 0x28, 0x31, 0x2e, 0x30, 0x20, 0x2d, 0x20, 0x5f, +0x36, 0x34, 0x32, 0x29, 0x29, 0x20, 0x2b, 0x20, 0x5f, 0x37, 0x38, 0x36, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x34, 0x20, 0x5f, 0x37, 0x37, 0x35, 0x20, 0x3d, 0x20, 0x5f, 0x34, 0x33, 0x39, +0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x6f, 0x75, 0x74, 0x2e, 0x66, 0x72, 0x61, 0x67, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x20, +0x3d, 0x20, 0x5f, 0x37, 0x38, 0x37, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x34, 0x20, 0x5f, +0x33, 0x30, 0x35, 0x20, 0x3d, 0x20, 0x69, 0x6e, 0x2e, 0x6d, 0x65, 0x73, 0x68, 0x5f, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, +0x6f, 0x6e, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x34, 0x20, 0x5f, 0x33, 0x32, 0x30, 0x20, +0x3d, 0x20, 0x5f, 0x33, 0x30, 0x35, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x33, 0x32, 0x30, 0x2e, 0x7a, 0x20, 0x3d, +0x20, 0x66, 0x6d, 0x61, 0x28, 0x5f, 0x33, 0x30, 0x35, 0x2e, 0x7a, 0x2c, 0x20, 0x2d, 0x30, 0x2e, 0x35, 0x2c, 0x20, 0x30, +0x2e, 0x35, 0x29, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x34, 0x20, 0x5f, 0x32, 0x38, 0x31, +0x20, 0x3d, 0x20, 0x66, 0x72, 0x61, 0x6d, 0x65, 0x55, 0x6e, 0x69, 0x66, 0x6f, 0x72, 0x6d, 0x73, 0x2e, 0x77, 0x6f, 0x72, +0x6c, 0x64, 0x46, 0x72, 0x6f, 0x6d, 0x43, 0x6c, 0x69, 0x70, 0x4d, 0x61, 0x74, 0x72, 0x69, 0x78, 0x20, 0x2a, 0x20, 0x5f, +0x33, 0x32, 0x30, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x5f, 0x32, 0x38, 0x33, 0x20, +0x3d, 0x20, 0x5f, 0x32, 0x38, 0x31, 0x2e, 0x77, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x34, +0x20, 0x5f, 0x33, 0x33, 0x34, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x28, 0x61, 0x62, 0x73, 0x28, 0x5f, +0x32, 0x38, 0x33, 0x29, 0x20, 0x3c, 0x20, 0x31, 0x2e, 0x30, 0x38, 0x34, 0x32, 0x30, 0x32, 0x32, 0x65, 0x2d, 0x31, 0x39, +0x29, 0x00, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x34, 0x20, 0x5f, 0x33, 0x32, +0x34, 0x20, 0x3d, 0x20, 0x5f, 0x32, 0x38, 0x31, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x33, +0x32, 0x34, 0x2e, 0x77, 0x20, 0x3d, 0x20, 0x28, 0x5f, 0x32, 0x38, 0x33, 0x20, 0x3c, 0x20, 0x30, 0x2e, 0x30, 0x29, 0x20, +0x3f, 0x20, 0x28, 0x2d, 0x31, 0x2e, 0x30, 0x38, 0x34, 0x32, 0x30, 0x32, 0x32, 0x65, 0x2d, 0x31, 0x39, 0x29, 0x20, 0x3a, +0x20, 0x31, 0x2e, 0x30, 0x38, 0x34, 0x32, 0x30, 0x32, 0x32, 0x65, 0x2d, 0x31, 0x39, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x5f, 0x33, 0x33, 0x34, 0x20, 0x3d, 0x20, 0x5f, 0x33, 0x32, 0x34, 0x3b, 0x00, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x5f, 0x33, 0x33, 0x34, 0x20, 0x3d, 0x20, 0x5f, 0x32, 0x38, 0x31, 0x3b, 0x00, 0x20, 0x20, +0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x34, 0x20, 0x5f, 0x32, 0x39, 0x37, 0x20, 0x3d, 0x20, 0x5f, 0x33, 0x33, 0x34, +0x20, 0x2a, 0x20, 0x28, 0x31, 0x2e, 0x30, 0x20, 0x2f, 0x20, 0x5f, 0x33, 0x33, 0x34, 0x2e, 0x77, 0x29, 0x3b, 0x00, 0x20, +0x20, 0x20, 0x20, 0x6f, 0x75, 0x74, 0x2e, 0x76, 0x65, 0x72, 0x74, 0x65, 0x78, 0x5f, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x50, +0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x78, 0x20, 0x3d, 0x20, 0x5f, 0x32, 0x39, 0x37, 0x2e, 0x78, 0x3b, 0x00, +0x20, 0x20, 0x20, 0x20, 0x6f, 0x75, 0x74, 0x2e, 0x76, 0x65, 0x72, 0x74, 0x65, 0x78, 0x5f, 0x77, 0x6f, 0x72, 0x6c, 0x64, +0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x79, 0x20, 0x3d, 0x20, 0x5f, 0x32, 0x39, 0x37, 0x2e, 0x79, 0x3b, +0x00, 0x20, 0x20, 0x20, 0x20, 0x6f, 0x75, 0x74, 0x2e, 0x76, 0x65, 0x72, 0x74, 0x65, 0x78, 0x5f, 0x77, 0x6f, 0x72, 0x6c, +0x64, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x7a, 0x20, 0x3d, 0x20, 0x5f, 0x32, 0x39, 0x37, 0x2e, 0x7a, +0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x6f, 0x75, 0x74, 0x2e, 0x76, 0x65, 0x72, 0x74, 0x65, 0x78, 0x5f, 0x70, 0x6f, 0x73, +0x69, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x3d, 0x20, 0x5f, 0x33, 0x32, 0x30, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6c, +0x6f, 0x61, 0x74, 0x20, 0x5f, 0x32, 0x30, 0x36, 0x20, 0x3d, 0x20, 0x66, 0x6d, 0x61, 0x28, 0x66, 0x6c, 0x6f, 0x61, 0x74, +0x28, 0x6f, 0x75, 0x74, 0x2e, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x20, +0x25, 0x20, 0x32, 0x29, 0x2c, 0x20, 0x32, 0x2e, 0x30, 0x2c, 0x20, 0x2d, 0x31, 0x2e, 0x30, 0x29, 0x3b, 0x00, 0x20, 0x20, +0x20, 0x20, 0x66, 0x6c, 0x6f, 0x61, 0x74, 0x20, 0x5f, 0x32, 0x31, 0x35, 0x20, 0x3d, 0x20, 0x66, 0x6d, 0x61, 0x28, 0x5f, +0x33, 0x30, 0x35, 0x2e, 0x78, 0x2c, 0x20, 0x30, 0x2e, 0x35, 0x2c, 0x20, 0x28, 0x5f, 0x33, 0x30, 0x35, 0x2e, 0x77, 0x20, +0x2a, 0x20, 0x30, 0x2e, 0x35, 0x29, 0x20, 0x2a, 0x20, 0x5f, 0x32, 0x30, 0x36, 0x29, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, +0x5f, 0x33, 0x32, 0x30, 0x2e, 0x78, 0x20, 0x3d, 0x20, 0x5f, 0x32, 0x31, 0x35, 0x3b, 0x00, 0x20, 0x20, 0x20, 0x20, 0x6f, +0x75, 0x74, 0x2e, 0x67, 0x6c, 0x5f, 0x43, 0x6c, 0x69, 0x70, 0x44, 0x69, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x5b, 0x30, +0x5d, 0x20, 0x3d, 0x20, 0x5f, 0x32, 0x31, 0x35, 0x20, 0x2a, 0x20, 0x5f, 0x32, 0x30, 0x36, 0x3b, 0x00, 0x20, 0x20, 0x20, +0x20, 0x6f, 0x75, 0x74, 0x2e, 0x67, 0x6c, 0x5f, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x3d, 0x20, 0x5f, +0x33, 0x32, 0x30, 0x3b, 0x00, 0x4c, 0x53, 0x4c, 0x47, 0x5f, 0x54, 0x41, 0x4d, 0x6a, 0x08, 0x00, 0x00, 0x12, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x86, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x86, 0x01, 0x00, 0x00, 0x01, +0x10, 0x00, 0x86, 0x00, 0x00, 0x00, 0x01, 0x10, 0x01, 0xc0, 0x01, 0x00, 0x00, 0x01, 0x20, 0x01, 0xd4, 0x01, 0x00, 0x00, +0x01, 0x30, 0x01, 0x20, 0x03, 0x00, 0x00, 0x01, 0x44, 0x01, 0x62, 0x03, 0x00, 0x00, 0x01, 0x80, 0x00, 0x7a, 0x03, 0x00, +0x00, 0x01, 0x90, 0x00, 0x7a, 0x03, 0x00, 0x00, 0x02, 0x00, 0x00, 0x84, 0x04, 0x00, 0x00, 0x02, 0x00, 0x01, 0x86, 0x05, +0x00, 0x00, 0x02, 0x10, 0x00, 0x84, 0x04, 0x00, 0x00, 0x02, 0x10, 0x01, 0xbe, 0x05, 0x00, 0x00, 0x02, 0x20, 0x01, 0xd0, +0x05, 0x00, 0x00, 0x02, 0x30, 0x01, 0x0a, 0x07, 0x00, 0x00, 0x02, 0x44, 0x01, 0x4a, 0x07, 0x00, 0x00, 0x02, 0x80, 0x00, +0x60, 0x07, 0x00, 0x00, 0x02, 0x90, 0x00, 0x60, 0x07, 0x00, 0x00, 0x80, 0x08, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x00, +0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x00, 0x04, 0x00, 0x05, 0x00, 0x06, 0x00, 0x07, 0x00, 0x08, 0x00, 0x09, 0x00, 0x02, +0x00, 0x0a, 0x00, 0x0b, 0x00, 0x0c, 0x00, 0x0d, 0x00, 0x0e, 0x00, 0x0f, 0x00, 0x10, 0x00, 0x11, 0x00, 0x12, 0x00, 0x13, +0x00, 0x14, 0x00, 0x15, 0x00, 0x16, 0x00, 0x17, 0x00, 0x18, 0x00, 0x19, 0x00, 0x1a, 0x00, 0x1b, 0x00, 0x1c, 0x00, 0x1d, +0x00, 0x1e, 0x00, 0x1f, 0x00, 0x20, 0x00, 0x21, 0x00, 0x22, 0x00, 0x23, 0x00, 0x24, 0x00, 0x25, 0x00, 0x26, 0x00, 0x27, +0x00, 0x28, 0x00, 0x29, 0x00, 0x2a, 0x00, 0x2b, 0x00, 0x2c, 0x00, 0x2d, 0x00, 0x2e, 0x00, 0x2f, 0x00, 0x30, 0x00, 0x31, +0x00, 0x32, 0x00, 0x33, 0x00, 0x34, 0x00, 0x35, 0x00, 0x36, 0x00, 0x37, 0x00, 0x38, 0x00, 0x39, 0x00, 0x3a, 0x00, 0x3b, +0x00, 0x3c, 0x00, 0x3d, 0x00, 0x3e, 0x00, 0x3f, 0x00, 0x40, 0x00, 0x41, 0x00, 0x42, 0x00, 0x43, 0x00, 0x44, 0x00, 0x45, +0x00, 0x46, 0x00, 0x47, 0x00, 0x48, 0x00, 0x49, 0x00, 0x4a, 0x00, 0x4b, 0x00, 0x4c, 0x00, 0x4d, 0x00, 0x4e, 0x00, 0x4f, +0x00, 0x50, 0x00, 0x51, 0x00, 0x52, 0x00, 0x53, 0x00, 0x54, 0x00, 0x55, 0x00, 0x56, 0x00, 0x57, 0x00, 0x58, 0x00, 0x59, +0x00, 0x5a, 0x00, 0x02, 0x00, 0x5b, 0x00, 0x02, 0x00, 0x5c, 0x00, 0x5d, 0x00, 0x5e, 0x00, 0x02, 0x00, 0x5f, 0x00, 0x5d, +0x00, 0x60, 0x00, 0x61, 0x00, 0x62, 0x00, 0x63, 0x00, 0x64, 0x00, 0x65, 0x00, 0x02, 0x00, 0x66, 0x00, 0x67, 0x00, 0x68, +0x00, 0x5d, 0x00, 0x5e, 0x00, 0x02, 0x00, 0x69, 0x00, 0x5d, 0x00, 0x6a, 0x00, 0x6b, 0x00, 0x6c, 0x00, 0x6d, 0x00, 0x6e, +0x00, 0x6f, 0x00, 0x70, 0x00, 0x5d, 0x00, 0xde, 0x01, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x71, 0x00, 0x72, +0x00, 0x73, 0x00, 0x02, 0x00, 0x74, 0x00, 0x75, 0x00, 0x76, 0x00, 0x77, 0x00, 0x78, 0x00, 0x79, 0x00, 0x04, 0x00, 0x7a, +0x00, 0x7b, 0x00, 0x7c, 0x00, 0x7d, 0x00, 0x02, 0x00, 0x7e, 0x00, 0x7f, 0x00, 0x80, 0x00, 0x81, 0x00, 0x5a, 0x00, 0x02, +0x00, 0x82, 0x00, 0x5d, 0x00, 0x51, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x71, 0x00, 0x72, 0x00, 0x5a, +0x00, 0x02, 0x00, 0x5d, 0x00, 0x71, 0x0c, 0x00, 0x00, 0xa2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x71, 0x00, 0x72, 0x00, 0x73, +0x00, 0x02, 0x00, 0x74, 0x00, 0x75, 0x00, 0x76, 0x00, 0x77, 0x00, 0x78, 0x00, 0x79, 0x00, 0x04, 0x00, 0x83, 0x00, 0x84, +0x00, 0x85, 0x00, 0x09, 0x00, 0x02, 0x00, 0x7e, 0x00, 0x86, 0x00, 0x87, 0x00, 0x88, 0x00, 0x89, 0x00, 0x8a, 0x00, 0x8b, +0x00, 0x8c, 0x00, 0x8d, 0x00, 0x8e, 0x00, 0x8f, 0x00, 0x90, 0x00, 0x91, 0x00, 0x92, 0x00, 0x93, 0x00, 0x94, 0x00, 0x95, +0x00, 0x96, 0x00, 0x97, 0x00, 0x98, 0x00, 0x99, 0x00, 0x9a, 0x00, 0x9b, 0x00, 0x9c, 0x00, 0x9d, 0x00, 0x9e, 0x00, 0x9f, +0x00, 0xa0, 0x00, 0xa1, 0x00, 0xa2, 0x00, 0xa3, 0x00, 0xa4, 0x00, 0xa5, 0x00, 0xa6, 0x00, 0xa7, 0x00, 0xa8, 0x00, 0xa9, +0x00, 0xaa, 0x00, 0xab, 0x00, 0xac, 0x00, 0xad, 0x00, 0xae, 0x00, 0xaf, 0x00, 0xb0, 0x00, 0xb1, 0x00, 0xb2, 0x00, 0xb3, +0x00, 0xb4, 0x00, 0xb5, 0x00, 0xb6, 0x00, 0xb7, 0x00, 0xb8, 0x00, 0xb9, 0x00, 0xba, 0x00, 0xbb, 0x00, 0xbc, 0x00, 0xbd, +0x00, 0xbe, 0x00, 0xbf, 0x00, 0xc0, 0x00, 0xc1, 0x00, 0xc2, 0x00, 0xc3, 0x00, 0xc4, 0x00, 0xc5, 0x00, 0xc6, 0x00, 0xc7, +0x00, 0xc8, 0x00, 0xc9, 0x00, 0xca, 0x00, 0xcb, 0x00, 0xcc, 0x00, 0xcd, 0x00, 0xce, 0x00, 0xcf, 0x00, 0x55, 0x00, 0x7d, +0x00, 0x02, 0x00, 0x7e, 0x00, 0x7f, 0x00, 0x80, 0x00, 0xd0, 0x00, 0xd1, 0x00, 0x81, 0x00, 0xd2, 0x00, 0x02, 0x00, 0xd3, +0x00, 0xd4, 0x00, 0x02, 0x00, 0xd5, 0x00, 0x5d, 0x00, 0xd6, 0x00, 0xd7, 0x00, 0xd8, 0x00, 0x02, 0x00, 0xd9, 0x00, 0x5d, +0x00, 0x5e, 0x00, 0x02, 0x00, 0xda, 0x00, 0x5d, 0x00, 0xdb, 0x00, 0xdc, 0x00, 0xdd, 0x00, 0xde, 0x00, 0xdf, 0x00, 0x02, +0x00, 0xe0, 0x00, 0xe1, 0x00, 0xe2, 0x00, 0xe3, 0x00, 0x5d, 0x00, 0x5e, 0x00, 0x02, 0x00, 0xe4, 0x00, 0x5d, 0x00, 0xe5, +0x00, 0xe6, 0x00, 0xe7, 0x00, 0x02, 0x00, 0xe8, 0x00, 0xe9, 0x00, 0xea, 0x00, 0xeb, 0x00, 0xec, 0x00, 0x5d, 0x00, 0x5e, +0x00, 0x02, 0x00, 0xed, 0x00, 0x5d, 0x00, 0xee, 0x00, 0xef, 0x00, 0xf0, 0x00, 0xf1, 0x00, 0xf2, 0x00, 0xd5, 0x00, 0x5d, +0x00, 0x5a, 0x00, 0x02, 0x00, 0xf3, 0x00, 0xf4, 0x00, 0xf5, 0x00, 0xf6, 0x00, 0xf7, 0x00, 0x5d, 0x00, 0x24, 0x03, 0x00, +0x00, 0x1d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x71, 0x00, 0x72, 0x00, 0xf8, 0x00, 0x02, 0x00, 0xf9, 0x00, 0xfa, 0x00, 0xfb, +0x00, 0xfc, 0x00, 0xfd, 0x00, 0xfe, 0x00, 0xff, 0x00, 0x04, 0x00, 0x00, 0x01, 0x01, 0x01, 0x07, 0x00, 0x02, 0x01, 0x03, +0x01, 0x04, 0x01, 0x02, 0x00, 0x05, 0x01, 0x06, 0x01, 0x07, 0x01, 0x08, 0x01, 0x5a, 0x00, 0x02, 0x00, 0x09, 0x01, 0x0a, +0x01, 0x5d, 0x00, 0x91, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x71, 0x00, 0x72, 0x00, 0x81, 0x00, 0x5a, +0x00, 0x02, 0x00, 0x0b, 0x01, 0x5d, 0x00, 0x42, 0x09, 0x00, 0x00, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x01, 0x01, +0x00, 0x02, 0x00, 0x03, 0x00, 0x04, 0x00, 0x05, 0x00, 0x06, 0x00, 0x07, 0x00, 0x08, 0x00, 0x09, 0x00, 0x02, 0x00, 0x0a, +0x00, 0x0b, 0x00, 0x0c, 0x00, 0x0d, 0x00, 0x0e, 0x00, 0x0f, 0x00, 0x10, 0x00, 0x11, 0x00, 0x12, 0x00, 0x13, 0x00, 0x14, +0x00, 0x15, 0x00, 0x16, 0x00, 0x17, 0x00, 0x18, 0x00, 0x19, 0x00, 0x1a, 0x00, 0x1b, 0x00, 0x1c, 0x00, 0x1d, 0x00, 0x1e, +0x00, 0x1f, 0x00, 0x20, 0x00, 0x21, 0x00, 0x22, 0x00, 0x23, 0x00, 0x24, 0x00, 0x25, 0x00, 0x26, 0x00, 0x27, 0x00, 0x28, +0x00, 0x29, 0x00, 0x2a, 0x00, 0x2b, 0x00, 0x2c, 0x00, 0x2d, 0x00, 0x2e, 0x00, 0x2f, 0x00, 0x30, 0x00, 0x31, 0x00, 0x32, +0x00, 0x33, 0x00, 0x34, 0x00, 0x35, 0x00, 0x36, 0x00, 0x37, 0x00, 0x38, 0x00, 0x39, 0x00, 0x3a, 0x00, 0x3b, 0x00, 0x3c, +0x00, 0x3d, 0x00, 0x3e, 0x00, 0x3f, 0x00, 0x40, 0x00, 0x41, 0x00, 0x42, 0x00, 0x43, 0x00, 0x44, 0x00, 0x45, 0x00, 0x46, +0x00, 0x47, 0x00, 0x48, 0x00, 0x49, 0x00, 0x4a, 0x00, 0x4b, 0x00, 0x4c, 0x00, 0x4d, 0x00, 0x4e, 0x00, 0x4f, 0x00, 0x50, +0x00, 0x51, 0x00, 0x52, 0x00, 0x53, 0x00, 0x54, 0x00, 0x55, 0x00, 0x56, 0x00, 0x57, 0x00, 0x58, 0x00, 0x59, 0x00, 0x5a, +0x00, 0x02, 0x00, 0x5b, 0x00, 0x02, 0x00, 0x5c, 0x00, 0x5d, 0x00, 0x5e, 0x00, 0x02, 0x00, 0x5f, 0x00, 0x5d, 0x00, 0x0d, +0x01, 0x0e, 0x01, 0x0f, 0x01, 0x10, 0x01, 0x11, 0x01, 0x12, 0x01, 0x02, 0x00, 0x13, 0x01, 0x14, 0x01, 0x15, 0x01, 0x5d, +0x00, 0x5e, 0x00, 0x02, 0x00, 0x16, 0x01, 0x5d, 0x00, 0x17, 0x01, 0x18, 0x01, 0x19, 0x01, 0x1a, 0x01, 0x1b, 0x01, 0x1c, +0x01, 0x1d, 0x01, 0x1e, 0x01, 0x1f, 0x01, 0x20, 0x01, 0x21, 0x01, 0x5d, 0x00, 0xa0, 0x07, 0x00, 0x00, 0x7d, 0x00, 0x00, +0x00, 0x22, 0x01, 0x23, 0x01, 0x01, 0x00, 0x02, 0x00, 0x03, 0x00, 0x04, 0x00, 0x05, 0x00, 0x06, 0x00, 0x07, 0x00, 0x08, +0x00, 0x09, 0x00, 0x02, 0x00, 0x0a, 0x00, 0x0b, 0x00, 0x0c, 0x00, 0x0d, 0x00, 0x0e, 0x00, 0x0f, 0x00, 0x10, 0x00, 0x11, +0x00, 0x12, 0x00, 0x13, 0x00, 0x14, 0x00, 0x15, 0x00, 0x16, 0x00, 0x17, 0x00, 0x18, 0x00, 0x94, 0x00, 0x95, 0x00, 0x1b, +0x00, 0x1c, 0x00, 0x1d, 0x00, 0x1e, 0x00, 0x9a, 0x00, 0x9b, 0x00, 0x9c, 0x00, 0x9d, 0x00, 0x9e, 0x00, 0x9f, 0x00, 0xa0, +0x00, 0xa1, 0x00, 0xa2, 0x00, 0xa3, 0x00, 0xa4, 0x00, 0xa5, 0x00, 0xa6, 0x00, 0x2c, 0x00, 0xa8, 0x00, 0xa9, 0x00, 0xaa, +0x00, 0xab, 0x00, 0xac, 0x00, 0xad, 0x00, 0x33, 0x00, 0xaf, 0x00, 0xb0, 0x00, 0xb1, 0x00, 0xb2, 0x00, 0xb3, 0x00, 0xb4, +0x00, 0xb5, 0x00, 0xb6, 0x00, 0x3c, 0x00, 0x3d, 0x00, 0xb9, 0x00, 0x3f, 0x00, 0x40, 0x00, 0x41, 0x00, 0xbd, 0x00, 0xbe, +0x00, 0x44, 0x00, 0xc0, 0x00, 0x46, 0x00, 0x47, 0x00, 0x48, 0x00, 0x49, 0x00, 0x4a, 0x00, 0xc6, 0x00, 0xc7, 0x00, 0xc8, +0x00, 0xc9, 0x00, 0x4f, 0x00, 0xcb, 0x00, 0xcc, 0x00, 0xcd, 0x00, 0xce, 0x00, 0xcf, 0x00, 0x55, 0x00, 0x56, 0x00, 0x24, +0x01, 0x25, 0x01, 0x26, 0x01, 0x5a, 0x00, 0x02, 0x00, 0x5b, 0x00, 0x02, 0x00, 0x5c, 0x00, 0x5d, 0x00, 0x5e, 0x00, 0x02, +0x00, 0x5f, 0x00, 0x5d, 0x00, 0x27, 0x01, 0x28, 0x01, 0x29, 0x01, 0x2a, 0x01, 0x2b, 0x01, 0x2c, 0x01, 0x02, 0x00, 0x2d, +0x01, 0x2e, 0x01, 0x2f, 0x01, 0x5d, 0x00, 0x5e, 0x00, 0x02, 0x00, 0x30, 0x01, 0x5d, 0x00, 0x31, 0x01, 0x6b, 0x00, 0x6c, +0x00, 0x6d, 0x00, 0x32, 0x01, 0x33, 0x01, 0x34, 0x01, 0x5d, 0x00, 0xd9, 0x01, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x22, +0x01, 0x23, 0x01, 0x73, 0x00, 0x02, 0x00, 0x74, 0x00, 0x75, 0x00, 0x76, 0x00, 0x77, 0x00, 0x78, 0x00, 0x79, 0x00, 0x04, +0x00, 0x7a, 0x00, 0x7b, 0x00, 0x7c, 0x00, 0x7d, 0x00, 0x02, 0x00, 0x0a, 0x00, 0x7f, 0x00, 0x80, 0x00, 0x81, 0x00, 0x5a, +0x00, 0x02, 0x00, 0x82, 0x00, 0x5d, 0x00, 0x52, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x22, 0x01, 0x23, 0x01, 0x5a, +0x00, 0x02, 0x00, 0x5d, 0x00, 0xa1, 0x0a, 0x00, 0x00, 0x99, 0x00, 0x00, 0x00, 0x22, 0x01, 0x23, 0x01, 0x73, 0x00, 0x02, +0x00, 0x74, 0x00, 0x75, 0x00, 0x76, 0x00, 0x77, 0x00, 0x78, 0x00, 0x79, 0x00, 0x04, 0x00, 0x83, 0x00, 0x84, 0x00, 0x85, +0x00, 0x09, 0x00, 0x02, 0x00, 0x0a, 0x00, 0x0b, 0x00, 0x0c, 0x00, 0x0d, 0x00, 0x0e, 0x00, 0x0f, 0x00, 0x10, 0x00, 0x11, +0x00, 0x12, 0x00, 0x13, 0x00, 0x14, 0x00, 0x15, 0x00, 0x16, 0x00, 0x17, 0x00, 0x18, 0x00, 0x94, 0x00, 0x95, 0x00, 0x1b, +0x00, 0x1c, 0x00, 0x1d, 0x00, 0x1e, 0x00, 0x9a, 0x00, 0x9b, 0x00, 0x9c, 0x00, 0x9d, 0x00, 0x9e, 0x00, 0x9f, 0x00, 0xa0, +0x00, 0xa1, 0x00, 0xa2, 0x00, 0xa3, 0x00, 0xa4, 0x00, 0xa5, 0x00, 0xa6, 0x00, 0x2c, 0x00, 0xa8, 0x00, 0xa9, 0x00, 0xaa, +0x00, 0xab, 0x00, 0xac, 0x00, 0xad, 0x00, 0x33, 0x00, 0xaf, 0x00, 0xb0, 0x00, 0xb1, 0x00, 0xb2, 0x00, 0xb3, 0x00, 0xb4, +0x00, 0xb5, 0x00, 0xb6, 0x00, 0x3c, 0x00, 0x3d, 0x00, 0xb9, 0x00, 0x3f, 0x00, 0x40, 0x00, 0x41, 0x00, 0xbd, 0x00, 0xbe, +0x00, 0x44, 0x00, 0xc0, 0x00, 0x46, 0x00, 0x47, 0x00, 0x48, 0x00, 0x49, 0x00, 0x4a, 0x00, 0xc6, 0x00, 0xc7, 0x00, 0xc8, +0x00, 0xc9, 0x00, 0x4f, 0x00, 0xcb, 0x00, 0xcc, 0x00, 0xcd, 0x00, 0xce, 0x00, 0xcf, 0x00, 0x55, 0x00, 0x7d, 0x00, 0x02, +0x00, 0x0a, 0x00, 0x7f, 0x00, 0x80, 0x00, 0x35, 0x01, 0x36, 0x01, 0x81, 0x00, 0x37, 0x01, 0x02, 0x00, 0x38, 0x01, 0xd4, +0x00, 0x02, 0x00, 0xd5, 0x00, 0x5d, 0x00, 0x39, 0x01, 0x3a, 0x01, 0xd8, 0x00, 0x02, 0x00, 0xd9, 0x00, 0x5d, 0x00, 0x5e, +0x00, 0x02, 0x00, 0xda, 0x00, 0x5d, 0x00, 0x3b, 0x01, 0xde, 0x00, 0xdf, 0x00, 0x02, 0x00, 0x3c, 0x01, 0x3d, 0x01, 0x5d, +0x00, 0x5e, 0x00, 0x02, 0x00, 0xe4, 0x00, 0x5d, 0x00, 0xe5, 0x00, 0xe6, 0x00, 0xe7, 0x00, 0x02, 0x00, 0x3e, 0x01, 0x5d, +0x00, 0x5e, 0x00, 0x02, 0x00, 0xed, 0x00, 0x5d, 0x00, 0xee, 0x00, 0xef, 0x00, 0xf0, 0x00, 0xf1, 0x00, 0xf2, 0x00, 0xd5, +0x00, 0x5d, 0x00, 0x5a, 0x00, 0x02, 0x00, 0xf3, 0x00, 0xf4, 0x00, 0x3f, 0x01, 0xf6, 0x00, 0xf7, 0x00, 0x5d, 0x00, 0x04, +0x03, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x22, 0x01, 0x23, 0x01, 0xf8, 0x00, 0x02, 0x00, 0x40, 0x01, 0x41, 0x01, 0x42, +0x01, 0x43, 0x01, 0x44, 0x01, 0x45, 0x01, 0x46, 0x01, 0x04, 0x00, 0x00, 0x01, 0x01, 0x01, 0x07, 0x00, 0x02, 0x01, 0x03, +0x01, 0x04, 0x01, 0x02, 0x00, 0x05, 0x01, 0x06, 0x01, 0x47, 0x01, 0x48, 0x01, 0x5a, 0x00, 0x02, 0x00, 0x09, 0x01, 0x0a, +0x01, 0x5d, 0x00, 0x92, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x22, 0x01, 0x23, 0x01, 0x81, 0x00, 0x5a, 0x00, 0x02, +0x00, 0x0b, 0x01, 0x5d, 0x00, 0x53, 0x08, 0x00, 0x00, 0x81, 0x00, 0x00, 0x00, 0x22, 0x01, 0x23, 0x01, 0x01, 0x00, 0x02, +0x00, 0x03, 0x00, 0x04, 0x00, 0x05, 0x00, 0x06, 0x00, 0x07, 0x00, 0x08, 0x00, 0x09, 0x00, 0x02, 0x00, 0x0a, 0x00, 0x0b, +0x00, 0x0c, 0x00, 0x0d, 0x00, 0x0e, 0x00, 0x0f, 0x00, 0x10, 0x00, 0x11, 0x00, 0x12, 0x00, 0x13, 0x00, 0x14, 0x00, 0x15, +0x00, 0x16, 0x00, 0x17, 0x00, 0x18, 0x00, 0x94, 0x00, 0x95, 0x00, 0x1b, 0x00, 0x1c, 0x00, 0x1d, 0x00, 0x1e, 0x00, 0x9a, +0x00, 0x9b, 0x00, 0x9c, 0x00, 0x9d, 0x00, 0x9e, 0x00, 0x9f, 0x00, 0xa0, 0x00, 0xa1, 0x00, 0xa2, 0x00, 0xa3, 0x00, 0xa4, +0x00, 0xa5, 0x00, 0xa6, 0x00, 0x2c, 0x00, 0xa8, 0x00, 0xa9, 0x00, 0xaa, 0x00, 0xab, 0x00, 0xac, 0x00, 0xad, 0x00, 0x33, +0x00, 0xaf, 0x00, 0xb0, 0x00, 0xb1, 0x00, 0xb2, 0x00, 0xb3, 0x00, 0xb4, 0x00, 0xb5, 0x00, 0xb6, 0x00, 0x3c, 0x00, 0x3d, +0x00, 0xb9, 0x00, 0x3f, 0x00, 0x40, 0x00, 0x41, 0x00, 0xbd, 0x00, 0xbe, 0x00, 0x44, 0x00, 0xc0, 0x00, 0x46, 0x00, 0x47, +0x00, 0x48, 0x00, 0x49, 0x00, 0x4a, 0x00, 0xc6, 0x00, 0xc7, 0x00, 0xc8, 0x00, 0xc9, 0x00, 0x4f, 0x00, 0xcb, 0x00, 0xcc, +0x00, 0xcd, 0x00, 0xce, 0x00, 0xcf, 0x00, 0x55, 0x00, 0x56, 0x00, 0x24, 0x01, 0x25, 0x01, 0x26, 0x01, 0x5a, 0x00, 0x02, +0x00, 0x5b, 0x00, 0x02, 0x00, 0x5c, 0x00, 0x5d, 0x00, 0x5e, 0x00, 0x02, 0x00, 0x5f, 0x00, 0x5d, 0x00, 0x49, 0x01, 0x4a, +0x01, 0x4b, 0x01, 0x4c, 0x01, 0x4d, 0x01, 0x4e, 0x01, 0x02, 0x00, 0x4f, 0x01, 0x50, 0x01, 0x51, 0x01, 0x5d, 0x00, 0x5e, +0x00, 0x02, 0x00, 0x52, 0x01, 0x5d, 0x00, 0x53, 0x01, 0x54, 0x01, 0x55, 0x01, 0x56, 0x01, 0x57, 0x01, 0x58, 0x01, 0x59, +0x01, 0x5a, 0x01, 0x5b, 0x01, 0x5c, 0x01, 0x21, 0x01, 0x5d, 0x00, 0x52, 0x49, 0x50, 0x53, 0x5f, 0x43, 0x49, 0x44, 0xfa, +0x2a, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x62, 0x04, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x4c, 0x4f, 0x4d, 0x53, 0x00, 0x03, 0x01, 0x00, 0x0a, 0x00, 0x08, 0x00, 0x35, 0x01, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x88, 0x12, 0x00, 0x00, 0x91, 0x02, 0x01, 0xd1, 0x10, 0x02, 0x47, 0x4c, 0x53, 0x4c, 0x2e, 0x73, +0x74, 0x64, 0x2e, 0x34, 0x35, 0x30, 0x00, 0x00, 0x00, 0x00, 0xa0, 0x04, 0x00, 0x01, 0xaf, 0x0f, 0x00, 0x04, 0xed, 0xc2, +0xa5, 0xf3, 0x06, 0x00, 0x6f, 0x9f, 0x01, 0xa1, 0x01, 0xae, 0x01, 0xc2, 0x01, 0xca, 0x01, 0x10, 0xb8, 0x01, 0x06, 0x40, +0x10, 0x08, 0x06, 0x10, 0x10, 0x04, 0x06, 0x10, 0x10, 0x04, 0x06, 0x10, 0x37, 0x02, 0x89, 0x00, 0x05, 0x00, 0x23, 0x00, +0x00, 0x07, 0x01, 0x10, 0x01, 0x05, 0x00, 0x23, 0x40, 0x00, 0x07, 0x01, 0x10, 0x01, 0x05, 0x00, 0x23, 0x40, 0x00, 0x07, +0x01, 0x10, 0x01, 0x05, 0x00, 0x23, 0x40, 0x00, 0x07, 0x01, 0x10, 0x01, 0x05, 0x00, 0x23, 0x40, 0x00, 0x07, 0x01, 0x10, +0x01, 0x05, 0x00, 0x23, 0x80, 0x01, 0x00, 0x07, 0x01, 0x10, 0x01, 0x05, 0x00, 0x23, 0x40, 0x00, 0x07, 0x01, 0x10, 0x01, +0x23, 0x40, 0x01, 0x23, 0x10, 0x01, 0x23, 0x08, 0x01, 0x23, 0x04, 0x01, 0x23, 0x04, 0x01, 0x23, 0x10, 0x01, 0x23, 0x10, +0x01, 0x23, 0x08, 0x01, 0x00, 0x00, 0x23, 0x08, 0x01, 0x00, 0x00, 0x23, 0x04, 0x01, 0x23, 0x04, 0x01, 0x23, 0x04, 0x01, +0x23, 0x04, 0x01, 0x23, 0x04, 0x01, 0x00, 0x00, 0x23, 0x04, 0x01, 0x00, 0x00, 0x23, 0x04, 0x01, 0x00, 0x00, 0x23, 0x04, +0x01, 0x00, 0x00, 0x23, 0x04, 0x01, 0x00, 0x00, 0x23, 0x04, 0x01, 0x00, 0x00, 0x23, 0x04, 0x01, 0x00, 0x00, 0x23, 0x04, +0x01, 0x00, 0x00, 0x23, 0x10, 0x01, 0x00, 0x00, 0x23, 0x0c, 0x01, 0x00, 0x00, 0x23, 0x04, 0x01, 0x00, 0x00, 0x23, 0x08, +0x01, 0x00, 0x00, 0x23, 0x04, 0x01, 0x00, 0x00, 0x23, 0x04, 0x01, 0x23, 0x90, 0x01, 0x01, 0x00, 0x00, 0x23, 0x0c, 0x01, +0x00, 0x00, 0x23, 0x04, 0x01, 0x00, 0x00, 0x23, 0x10, 0x01, 0x00, 0x00, 0x23, 0x10, 0x01, 0x00, 0x00, 0x23, 0x08, 0x01, +0x00, 0x00, 0x23, 0x04, 0x01, 0x23, 0x04, 0x01, 0x00, 0x00, 0x23, 0x10, 0x01, 0x00, 0x00, 0x23, 0x04, 0x01, 0x00, 0x00, +0x23, 0x04, 0x01, 0x00, 0x00, 0x23, 0x04, 0x01, 0x00, 0x00, 0x23, 0x04, 0x01, 0x00, 0x00, 0x23, 0x04, 0x01, 0x00, 0x00, +0x23, 0x04, 0x01, 0x00, 0x00, 0x23, 0x04, 0x01, 0x23, 0x04, 0x01, 0x23, 0x0c, 0x01, 0x00, 0x00, 0x23, 0x04, 0x01, 0x23, +0x04, 0x01, 0x23, 0x04, 0x01, 0x23, 0x04, 0x01, 0x00, 0x00, 0x23, 0x04, 0x01, 0x00, 0x00, 0x23, 0x0c, 0x01, 0x23, 0x04, +0x01, 0x00, 0x00, 0x23, 0x04, 0x01, 0x23, 0x04, 0x01, 0x23, 0x04, 0x01, 0x05, 0x00, 0x23, 0x04, 0x00, 0x07, 0x01, 0x10, +0x01, 0x05, 0x00, 0x23, 0x30, 0x00, 0x07, 0x01, 0x10, 0x01, 0x05, 0x00, 0x23, 0x40, 0x00, 0x07, 0x01, 0x10, 0x01, 0x00, +0x00, 0x23, 0x40, 0x01, 0x00, 0x00, 0x23, 0x04, 0x01, 0x00, 0x00, 0x23, 0x04, 0x01, 0x00, 0x00, 0x23, 0x04, 0x01, 0x23, +0x04, 0x01, 0x00, 0x00, 0x23, 0x40, 0x01, 0x00, 0x00, 0x23, 0x04, 0x01, 0x00, 0x00, 0x23, 0x04, 0x01, 0x00, 0x00, 0x23, +0x04, 0x01, 0x00, 0x00, 0x23, 0x04, 0x00, 0x00, 0x02, 0x10, 0x04, 0x22, 0x00, 0x10, 0x00, 0x21, 0x00, 0x10, 0x10, 0x1e, +0x00, 0x00, 0x60, 0x0e, 0x10, 0x00, 0x1e, 0x08, 0x10, 0x04, 0x0b, 0x2b, 0x10, 0x1a, 0x1e, 0x04, 0x10, 0x28, 0x1e, 0x07, +0x47, 0x0c, 0x02, 0x00, 0x0b, 0x01, 0x00, 0x01, 0x0b, 0x01, 0x01, 0x00, 0x00, 0x02, 0x93, 0x02, 0x02, 0xa1, 0x04, 0x02, +0x02, 0xa6, 0x02, 0x0a, 0x20, 0xb7, 0x02, 0x02, 0x08, 0x04, 0xb8, 0x02, 0x02, 0x09, 0x04, 0xae, 0x02, 0x12, 0x09, 0xb7, +0x02, 0x12, 0x08, 0x03, 0xb8, 0x02, 0x02, 0x1c, 0x03, 0xb5, 0x02, 0x02, 0x20, 0x01, 0xb5, 0x02, 0x02, 0x20, 0x00, 0xbb, +0x04, 0x1e, 0x12, 0x00, 0x00, 0x00, 0x00, 0x94, 0x02, 0x0e, 0x3e, 0x1a, 0x02, 0x0a, 0xbb, 0x04, 0x1e, 0x36, 0x05, 0x00, +0x00, 0x00, 0xbb, 0x04, 0x1f, 0x08, 0x02, 0x00, 0x00, 0x00, 0xbc, 0x02, 0x02, 0x0a, 0x5b, 0xb7, 0x02, 0x02, 0x08, 0x02, +0xb7, 0x02, 0x02, 0x1f, 0x03, 0xbb, 0x04, 0x1f, 0x02, 0x09, 0x00, 0x00, 0x00, 0xbc, 0x02, 0x02, 0x1c, 0x5f, 0xbb, 0x04, +0x1f, 0x02, 0x04, 0x00, 0x00, 0x00, 0xbc, 0x02, 0x02, 0x09, 0x61, 0xbb, 0x04, 0x1f, 0x02, 0x30, 0x00, 0x00, 0x00, 0xbc, +0x02, 0x02, 0x09, 0x63, 0xce, 0x83, 0x80, 0x02, 0x02, 0x0a, 0x0a, 0x0a, 0x0a, 0x5c, 0x0a, 0x0a, 0x09, 0x5d, 0x08, 0x08, +0x09, 0x09, 0x5d, 0x5d, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x09, 0x5e, 0x1e, 0x5d, +0x08, 0x08, 0x60, 0x1c, 0x08, 0x09, 0x09, 0x5d, 0x1e, 0x08, 0x09, 0x1e, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x1f, 0x1c, +0x08, 0x08, 0x1f, 0x08, 0x08, 0x1c, 0x08, 0x08, 0x08, 0x08, 0x08, 0x1d, 0x0a, 0x0a, 0x08, 0x08, 0x08, 0x08, 0x62, 0x1e, +0x08, 0x08, 0x08, 0x64, 0x3e, 0x02, 0x02, 0x65, 0x39, 0x66, 0x02, 0x02, 0x3e, 0x0e, 0x01, 0x09, 0x39, 0x6e, 0x02, 0x01, +0xbb, 0x04, 0x08, 0x1a, 0x00, 0x00, 0x00, 0xbf, 0xbb, 0x04, 0x08, 0x04, 0x00, 0x00, 0x00, 0x3f, 0xbb, 0x04, 0x08, 0x16, +0x00, 0x00, 0x00, 0x20, 0xbb, 0x04, 0x08, 0x0c, 0x00, 0x00, 0x00, 0x00, 0xbb, 0x04, 0x08, 0x04, 0x00, 0x00, 0x00, 0xa0, +0xbb, 0x04, 0x08, 0x08, 0x00, 0x00, 0x80, 0x3f, 0x3e, 0x12, 0x03, 0x1e, 0x39, 0x9e, 0x01, 0x02, 0x03, 0x3e, 0x02, 0x01, +0x1e, 0x39, 0xa0, 0x01, 0x02, 0x01, 0x3e, 0x18, 0x03, 0x09, 0x39, 0xad, 0x01, 0x02, 0x03, 0xbb, 0x04, 0x1f, 0x08, 0x00, +0x00, 0x00, 0x00, 0x3e, 0x02, 0x03, 0x08, 0xbb, 0x04, 0x1f, 0x06, 0x01, 0x00, 0x00, 0x00, 0x39, 0xad, 0x01, 0x18, 0x03, +0xbe, 0x02, 0x0c, 0x09, 0x08, 0x3e, 0x02, 0x03, 0xc8, 0x01, 0x39, 0xc9, 0x01, 0x02, 0x03, 0xc6, 0x06, 0x02, 0x8b, 0x03, +0x00, 0x03, 0x18, 0x02, 0x01, 0x1e, 0xba, 0x02, 0x02, 0x22, 0x06, 0x00, 0x13, 0x3c, 0xe4, 0x01, 0x67, 0x57, 0x01, 0x0a, +0x02, 0x02, 0x01, 0x09, 0x08, 0xd4, 0x02, 0xc1, 0x0a, 0x08, 0x3b, 0x3b, 0x02, 0x4a, 0x08, 0x02, 0x02, 0x80, 0x02, 0x4b, +0x08, 0x02, 0x02, 0xfe, 0x01, 0xd2, 0x0a, 0x09, 0x56, 0x56, 0x1e, 0x02, 0xc1, 0x12, 0x09, 0x4d, 0x27, 0x4d, 0xc1, 0x0a, +0x08, 0x04, 0x04, 0x03, 0x5c, 0x08, 0x02, 0x01, 0x04, 0x83, 0x02, 0xc8, 0x16, 0x2f, 0x02, 0x02, 0xf8, 0x01, 0xa7, 0x1e, +0x0d, 0x00, 0xba, 0x1e, 0x00, 0x01, 0x0d, 0x18, 0x02, 0xc8, 0x16, 0x2f, 0x06, 0x0c, 0xf4, 0x01, 0xd9, 0x14, 0x08, 0x02, +0x02, 0xf2, 0x01, 0x82, 0x02, 0xd2, 0x0a, 0x09, 0x44, 0x44, 0x56, 0x03, 0x99, 0x1e, 0x40, 0x18, 0x3f, 0xe5, 0x1e, 0x09, +0x50, 0x01, 0x01, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x2c, 0x01, 0x00, 0x00, 0x06, 0x01, 0x00, 0x00, 0xc1, 0x0a, 0x08, +0x49, 0x49, 0x03, 0xc8, 0x10, 0x08, 0x02, 0xf6, 0x01, 0x02, 0xce, 0x10, 0x09, 0x02, 0x45, 0x02, 0xb0, 0x0a, 0x13, 0x28, +0x28, 0xc1, 0x0a, 0x09, 0x05, 0x05, 0x00, 0x3d, 0x1c, 0xe1, 0x01, 0xe1, 0x01, 0xe1, 0x01, 0x18, 0x13, 0xb3, 0x01, 0x06, +0xae, 0x01, 0xb2, 0x01, 0xc1, 0x0a, 0x08, 0x02, 0x08, 0x00, 0x22, 0x02, 0x00, 0x13, 0xb3, 0x01, 0x04, 0xae, 0x01, 0xb6, +0x01, 0xc1, 0x0a, 0x08, 0x02, 0x0e, 0x01, 0x22, 0x02, 0x00, 0x13, 0xb3, 0x01, 0x02, 0xae, 0x01, 0x5b, 0xc1, 0x0a, 0x08, +0x02, 0x12, 0x02, 0x22, 0x02, 0x00, 0x22, 0x0f, 0xdb, 0x01, 0xc1, 0x0a, 0x08, 0x16, 0xc5, 0x01, 0x01, 0x3f, 0x08, 0x02, +0x02, 0xd2, 0x0a, 0x09, 0xda, 0x01, 0xda, 0x01, 0x16, 0x01, 0x13, 0xad, 0x01, 0xcd, 0x01, 0xca, 0x01, 0x28, 0x22, 0x00, +0xcd, 0x01, 0x8d, 0x1e, 0x88, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x31, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x4c, 0x4f, 0x4d, 0x53, 0x00, 0x03, 0x01, 0x00, 0x0a, 0x00, 0x08, 0x00, 0x59, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0xb8, 0x03, 0x00, 0x00, 0x91, 0x02, 0x01, 0xd1, 0x10, 0x02, 0x47, 0x4c, 0x53, 0x4c, 0x2e, 0x73, 0x74, 0x64, 0x2e, 0x34, +0x35, 0x30, 0x00, 0x00, 0x00, 0x00, 0xa0, 0x04, 0x00, 0x01, 0xdf, 0x0e, 0x04, 0x04, 0xed, 0xc2, 0xa5, 0xf3, 0x06, 0x00, +0xce, 0x01, 0xa0, 0x02, 0x04, 0x07, 0x37, 0x1e, 0x06, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, +0x00, 0x37, 0xb2, 0x02, 0x05, 0x00, 0x05, 0x00, 0x23, 0x00, 0x00, 0x07, 0x01, 0x10, 0x01, 0x00, 0x00, 0x23, 0x40, 0x00, +0x00, 0x02, 0x10, 0x04, 0x22, 0x00, 0x10, 0x00, 0x21, 0x08, 0x00, 0x48, 0x00, 0x10, 0x00, 0x1e, 0x00, 0x00, 0xc8, 0x01, +0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x0e, 0x00, 0x93, 0x02, 0x02, 0xa1, +0x04, 0x02, 0x02, 0xa6, 0x02, 0x12, 0x20, 0xb7, 0x02, 0x02, 0x0c, 0x04, 0xb7, 0x02, 0x02, 0x0c, 0x03, 0xfe, 0x02, 0x02, +0x0d, 0x0d, 0x0c, 0x0c, 0x0c, 0x0e, 0xb8, 0x02, 0x32, 0x0d, 0x04, 0xb5, 0x02, 0x04, 0x20, 0x01, 0xbb, 0x04, 0x2a, 0x46, +0x01, 0x00, 0x00, 0x00, 0xbb, 0x04, 0x0c, 0x5a, 0x00, 0x00, 0x80, 0x3f, 0xbe, 0x02, 0x5c, 0x28, 0x0e, 0x3e, 0x02, 0x02, +0xa8, 0x01, 0x39, 0xa9, 0x01, 0x02, 0x02, 0x3e, 0x02, 0x02, 0x0e, 0x3e, 0x44, 0x03, 0x0d, 0x39, 0xcd, 0x01, 0x02, 0x03, +0xad, 0x06, 0x0d, 0x8a, 0x02, 0xad, 0x06, 0x0c, 0x02, 0xad, 0x06, 0x0e, 0x02, 0xc6, 0x06, 0x02, 0xa1, 0x05, 0x00, 0x03, +0x18, 0x02, 0x13, 0xab, 0x01, 0xd8, 0x04, 0xaa, 0x01, 0x4d, 0x01, 0x0e, 0x02, 0x02, 0xc1, 0x0a, 0x0c, 0x02, 0x02, 0x00, +0xc1, 0x0a, 0x0c, 0x02, 0x04, 0x01, 0xc1, 0x0a, 0x0c, 0x02, 0x06, 0x02, 0xe0, 0x0a, 0x0d, 0x02, 0x06, 0x04, 0x02, 0xf8, +0x02, 0x80, 0x0b, 0x0f, 0x44, 0x44, 0x0a, 0x08, 0x08, 0x08, 0x06, 0xc1, 0x0a, 0x0d, 0x35, 0x35, 0x00, 0x22, 0xde, 0x01, +0x00, 0x8d, 0x1e, 0x88, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x53, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x4c, 0x4f, 0x4d, 0x53, 0x00, 0x03, 0x01, 0x00, 0x0a, 0x00, 0x08, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x98, 0x00, 0x00, 0x00, 0x91, 0x02, 0x01, 0xd1, 0x10, 0x02, 0x47, 0x4c, 0x53, 0x4c, 0x2e, 0x73, 0x74, 0x64, 0x2e, 0x34, +0x35, 0x30, 0x00, 0x00, 0x00, 0x00, 0xa0, 0x04, 0x00, 0x01, 0xcf, 0x0e, 0x04, 0x04, 0xed, 0xc2, 0xa5, 0xf3, 0x06, 0x00, +0xa0, 0x02, 0x04, 0x07, 0x93, 0x02, 0x02, 0xa1, 0x04, 0x02, 0x02, 0xc6, 0x06, 0x02, 0x02, 0x00, 0x03, 0x18, 0x02, 0x8d, +0x1e, 0x88, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x84, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4c, 0x4f, 0x4d, 0x53, +0x00, 0x03, 0x01, 0x00, 0x0a, 0x00, 0x08, 0x00, 0x50, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x20, 0x00, 0x00, +0x91, 0x02, 0x01, 0xd1, 0x10, 0x02, 0x47, 0x4c, 0x53, 0x4c, 0x2e, 0x73, 0x74, 0x64, 0x2e, 0x34, 0x35, 0x30, 0x00, 0x00, +0x00, 0x00, 0xa0, 0x04, 0x00, 0x01, 0xef, 0x0e, 0x04, 0x04, 0xed, 0xc2, 0xa5, 0xf3, 0x06, 0x00, 0x96, 0x01, 0xaa, 0x03, +0xa0, 0x02, 0x04, 0x07, 0x37, 0x28, 0x06, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, +0x22, 0x00, 0x00, 0x03, 0x00, 0x10, 0x9e, 0x01, 0x06, 0x40, 0x10, 0x08, 0x06, 0x10, 0x10, 0x04, 0x06, 0x10, 0x10, 0x04, +0x06, 0x10, 0x37, 0x02, 0x89, 0x00, 0x05, 0x00, 0x23, 0x00, 0x00, 0x07, 0x01, 0x10, 0x01, 0x05, 0x00, 0x23, 0x40, 0x00, +0x07, 0x01, 0x10, 0x01, 0x05, 0x00, 0x23, 0x40, 0x00, 0x07, 0x01, 0x10, 0x01, 0x05, 0x00, 0x23, 0x40, 0x00, 0x07, 0x01, +0x10, 0x01, 0x05, 0x00, 0x23, 0x40, 0x00, 0x07, 0x01, 0x10, 0x01, 0x05, 0x00, 0x23, 0x80, 0x01, 0x00, 0x07, 0x01, 0x10, +0x01, 0x05, 0x00, 0x23, 0x40, 0x00, 0x07, 0x01, 0x10, 0x01, 0x23, 0x40, 0x01, 0x23, 0x10, 0x01, 0x23, 0x08, 0x01, 0x23, +0x04, 0x01, 0x23, 0x04, 0x01, 0x23, 0x10, 0x01, 0x23, 0x10, 0x01, 0x23, 0x08, 0x01, 0x00, 0x00, 0x23, 0x08, 0x01, 0x00, +0x00, 0x23, 0x04, 0x01, 0x23, 0x04, 0x01, 0x23, 0x04, 0x01, 0x23, 0x04, 0x01, 0x23, 0x04, 0x01, 0x00, 0x00, 0x23, 0x04, +0x01, 0x00, 0x00, 0x23, 0x04, 0x01, 0x00, 0x00, 0x23, 0x04, 0x01, 0x00, 0x00, 0x23, 0x04, 0x01, 0x00, 0x00, 0x23, 0x04, +0x01, 0x00, 0x00, 0x23, 0x04, 0x01, 0x00, 0x00, 0x23, 0x04, 0x01, 0x00, 0x00, 0x23, 0x10, 0x01, 0x00, 0x00, 0x23, 0x0c, +0x01, 0x00, 0x00, 0x23, 0x04, 0x01, 0x00, 0x00, 0x23, 0x08, 0x01, 0x00, 0x00, 0x23, 0x04, 0x01, 0x00, 0x00, 0x23, 0x04, +0x01, 0x23, 0x90, 0x01, 0x01, 0x00, 0x00, 0x23, 0x0c, 0x01, 0x00, 0x00, 0x23, 0x04, 0x01, 0x00, 0x00, 0x23, 0x10, 0x01, +0x00, 0x00, 0x23, 0x10, 0x01, 0x00, 0x00, 0x23, 0x08, 0x01, 0x00, 0x00, 0x23, 0x04, 0x01, 0x23, 0x04, 0x01, 0x00, 0x00, +0x23, 0x10, 0x01, 0x00, 0x00, 0x23, 0x04, 0x01, 0x00, 0x00, 0x23, 0x04, 0x01, 0x00, 0x00, 0x23, 0x04, 0x01, 0x00, 0x00, +0x23, 0x04, 0x01, 0x00, 0x00, 0x23, 0x04, 0x01, 0x00, 0x00, 0x23, 0x04, 0x01, 0x00, 0x00, 0x23, 0x04, 0x01, 0x23, 0x04, +0x01, 0x23, 0x0c, 0x01, 0x00, 0x00, 0x23, 0x04, 0x01, 0x23, 0x04, 0x01, 0x23, 0x04, 0x01, 0x23, 0x04, 0x01, 0x00, 0x00, +0x23, 0x04, 0x01, 0x00, 0x00, 0x23, 0x0c, 0x01, 0x23, 0x04, 0x01, 0x00, 0x00, 0x23, 0x04, 0x01, 0x23, 0x04, 0x01, 0x23, +0x04, 0x01, 0x05, 0x00, 0x23, 0x04, 0x00, 0x07, 0x01, 0x10, 0x01, 0x05, 0x00, 0x23, 0x30, 0x00, 0x07, 0x01, 0x10, 0x01, +0x05, 0x00, 0x23, 0x40, 0x00, 0x07, 0x01, 0x10, 0x01, 0x00, 0x00, 0x23, 0x40, 0x01, 0x00, 0x00, 0x23, 0x04, 0x01, 0x00, +0x00, 0x23, 0x04, 0x01, 0x00, 0x00, 0x23, 0x04, 0x01, 0x23, 0x04, 0x01, 0x00, 0x00, 0x23, 0x40, 0x01, 0x00, 0x00, 0x23, +0x04, 0x01, 0x00, 0x00, 0x23, 0x04, 0x01, 0x00, 0x00, 0x23, 0x04, 0x01, 0x00, 0x00, 0x23, 0x04, 0x00, 0x00, 0x02, 0x10, +0x04, 0x22, 0x00, 0x10, 0x00, 0x21, 0x00, 0x10, 0x32, 0x1e, 0x04, 0x00, 0x5a, 0x00, 0x00, 0x6e, 0x00, 0x00, 0x06, 0x00, +0x00, 0x02, 0x00, 0x00, 0x08, 0x00, 0x00, 0x06, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x02, +0x00, 0x00, 0x22, 0x00, 0x10, 0x00, 0x22, 0x01, 0x10, 0x00, 0x21, 0x06, 0x00, 0x02, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x02, +0x00, 0x00, 0x04, 0x00, 0x00, 0x06, 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x06, 0x00, 0x00, 0x28, 0x00, 0x00, +0x02, 0x00, 0x00, 0x04, 0x00, 0x00, 0x02, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x08, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, +0x00, 0x04, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x04, 0x00, 0x00, 0x02, 0x00, 0x00, 0x04, 0x00, 0x00, 0x0e, +0x00, 0x37, 0x0a, 0x05, 0x00, 0x05, 0x00, 0x23, 0x00, 0x00, 0x07, 0x01, 0x10, 0x01, 0x00, 0x00, 0x23, 0x40, 0x00, 0x00, +0x02, 0x10, 0x04, 0x22, 0x00, 0x10, 0x00, 0x21, 0x08, 0x00, 0x46, 0x00, 0x10, 0x00, 0x1e, 0x00, 0x00, 0x18, 0x00, 0x00, +0x02, 0x00, 0x00, 0x06, 0x00, 0x00, 0xbc, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, +0x00, 0x00, 0x0e, 0x00, 0x00, 0x54, 0x00, 0x00, 0x04, 0x00, 0x93, 0x02, 0x02, 0xa1, 0x04, 0x02, 0x02, 0xa6, 0x02, 0x0a, +0x20, 0xb7, 0x02, 0x02, 0x08, 0x03, 0x94, 0x02, 0x0c, 0xb7, 0x02, 0x08, 0x08, 0x04, 0xfe, 0x02, 0x02, 0x13, 0x13, 0x08, +0x08, 0x08, 0x09, 0x3e, 0x18, 0x07, 0x13, 0x3e, 0x02, 0x07, 0x09, 0xc1, 0x04, 0x02, 0x13, 0x20, 0x21, 0xb8, 0x02, 0x22, +0x13, 0x04, 0xb8, 0x02, 0x02, 0x09, 0x03, 0xb5, 0x02, 0x02, 0x20, 0x01, 0xb5, 0x02, 0x02, 0x20, 0x00, 0xbb, 0x04, 0x35, +0x14, 0x03, 0x00, 0x00, 0x00, 0x3e, 0x24, 0x02, 0x33, 0xbb, 0x04, 0x35, 0x0c, 0x01, 0x00, 0x00, 0x00, 0x3e, 0x02, 0x02, +0x34, 0x3e, 0x2a, 0x02, 0x08, 0xbb, 0x04, 0x36, 0x06, 0x02, 0x00, 0x00, 0x00, 0xbc, 0x02, 0x02, 0x33, 0x71, 0xb7, 0x02, +0x02, 0x08, 0x02, 0xb7, 0x02, 0x02, 0x36, 0x03, 0xbb, 0x04, 0x36, 0x02, 0x09, 0x00, 0x00, 0x00, 0xbc, 0x02, 0x02, 0x09, +0x75, 0xbb, 0x04, 0x36, 0x02, 0x04, 0x00, 0x00, 0x00, 0xbc, 0x02, 0x02, 0x13, 0x77, 0xbb, 0x04, 0x36, 0x02, 0x30, 0x00, +0x00, 0x00, 0xbc, 0x02, 0x02, 0x13, 0x79, 0xce, 0x83, 0x80, 0x02, 0x02, 0x33, 0x33, 0x33, 0x33, 0x72, 0x33, 0x33, 0x13, +0x73, 0x08, 0x08, 0x13, 0x13, 0x73, 0x73, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x13, +0x74, 0x35, 0x73, 0x08, 0x08, 0x76, 0x09, 0x08, 0x13, 0x13, 0x73, 0x35, 0x08, 0x13, 0x35, 0x08, 0x08, 0x08, 0x08, 0x08, +0x08, 0x36, 0x09, 0x08, 0x08, 0x36, 0x08, 0x08, 0x09, 0x08, 0x08, 0x08, 0x08, 0x08, 0x34, 0x33, 0x33, 0x08, 0x08, 0x08, +0x08, 0x78, 0x35, 0x08, 0x08, 0x08, 0x7a, 0x3e, 0x02, 0x02, 0x7b, 0x39, 0x7c, 0x02, 0x02, 0x3e, 0x02, 0x02, 0x13, 0xbb, +0x04, 0x36, 0x16, 0x03, 0x00, 0x00, 0x00, 0xbb, 0x04, 0x08, 0x06, 0x00, 0x00, 0x00, 0x00, 0xbb, 0x04, 0x08, 0x08, 0x00, +0x00, 0x80, 0x3f, 0x3e, 0x0a, 0x01, 0x13, 0x39, 0x95, 0x01, 0x02, 0x01, 0x3e, 0x44, 0x07, 0x08, 0xbb, 0x04, 0x35, 0x0a, +0x37, 0x00, 0x00, 0x00, 0xbb, 0x04, 0x35, 0x12, 0x32, 0x00, 0x00, 0x00, 0x3e, 0x02, 0x02, 0x09, 0xbb, 0x04, 0x35, 0x08, +0x36, 0x00, 0x00, 0x00, 0xbb, 0x04, 0x36, 0x10, 0x01, 0x00, 0x00, 0x00, 0xbb, 0x04, 0x08, 0x0c, 0x0a, 0xd7, 0xa3, 0x3a, +0xbb, 0x04, 0x36, 0x0c, 0x00, 0x00, 0x00, 0x00, 0xbb, 0x04, 0x35, 0x1e, 0x33, 0x00, 0x00, 0x00, 0xbb, 0x04, 0x35, 0x1a, +0x34, 0x00, 0x00, 0x00, 0xbb, 0x04, 0x35, 0x0a, 0x38, 0x00, 0x00, 0x00, 0xbb, 0x04, 0x35, 0x06, 0x39, 0x00, 0x00, 0x00, +0xbb, 0x04, 0x35, 0x10, 0x3c, 0x00, 0x00, 0x00, 0xbb, 0x04, 0x35, 0x08, 0x3d, 0x00, 0x00, 0x00, 0xbb, 0x04, 0x35, 0x0c, +0x35, 0x00, 0x00, 0x00, 0x3e, 0x02, 0x02, 0x36, 0xbb, 0x04, 0x35, 0x1c, 0x06, 0x00, 0x00, 0x00, 0x89, 0x03, 0x16, 0x08, +0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0xab, 0x02, 0x02, 0xaf, 0x02, 0x3e, 0x02, 0x00, 0xb0, 0x02, 0x39, 0xb1, 0x02, 0x02, +0x00, 0xbb, 0x04, 0x35, 0x14, 0x1f, 0x00, 0x00, 0x00, 0xbb, 0x04, 0x35, 0x0e, 0x3b, 0x00, 0x00, 0x00, 0xbb, 0x04, 0x35, +0x12, 0x3a, 0x00, 0x00, 0x00, 0xbb, 0x04, 0x35, 0x16, 0x24, 0x00, 0x00, 0x00, 0xbb, 0x04, 0x35, 0x14, 0x22, 0x00, 0x00, +0x00, 0xbe, 0x02, 0x48, 0x33, 0x09, 0x3e, 0x02, 0x02, 0x85, 0x03, 0x39, 0x86, 0x03, 0x02, 0x02, 0x3e, 0x44, 0x03, 0x13, +0x39, 0xa9, 0x03, 0x02, 0x03, 0xbb, 0x04, 0x35, 0x0e, 0x3e, 0x00, 0x00, 0x00, 0xad, 0x06, 0x13, 0x9e, 0x02, 0xad, 0x06, +0x08, 0x02, 0xad, 0x06, 0x09, 0x02, 0xc6, 0x06, 0x02, 0xfb, 0x08, 0x00, 0x03, 0x18, 0x02, 0x39, 0x20, 0xe2, 0x06, 0x07, +0x39, 0x21, 0x04, 0x07, 0x01, 0x13, 0x74, 0xb8, 0x05, 0x3d, 0x09, 0x02, 0x02, 0x02, 0x18, 0x13, 0xc7, 0x01, 0x48, 0x87, +0x03, 0x58, 0x01, 0x09, 0x02, 0x02, 0xc1, 0x0a, 0x08, 0x02, 0x02, 0x00, 0xc1, 0x0a, 0x08, 0x02, 0x04, 0x01, 0xc1, 0x0a, +0x08, 0x02, 0x06, 0x02, 0xe0, 0x0a, 0x13, 0x02, 0x06, 0x04, 0x02, 0x98, 0x06, 0x80, 0x0b, 0x14, 0x52, 0x52, 0x0a, 0x08, +0x08, 0x08, 0x06, 0xc1, 0x0a, 0x13, 0x43, 0x43, 0x00, 0x22, 0xf2, 0x01, 0x00, 0x23, 0x7e, 0x16, 0x7d, 0x58, 0x40, 0x01, +0x13, 0x02, 0x02, 0x3d, 0x09, 0x02, 0x02, 0x02, 0x18, 0xc3, 0x10, 0x09, 0xff, 0x01, 0x85, 0x01, 0xff, 0x01, 0x13, 0x59, +0x04, 0x7d, 0xb1, 0x03, 0x01, 0x34, 0x02, 0x02, 0xc1, 0x12, 0x09, 0x04, 0x04, 0x0a, 0x01, 0x13, 0x04, 0x1a, 0x22, 0x02, +0x00, 0x22, 0x01, 0x04, 0xd9, 0x06, 0x13, 0x06, 0xaa, 0x06, 0x08, 0x04, 0x22, 0x20, 0x00, 0x8d, 0x1e, 0x88, 0x06, 0xc6, +0x06, 0x13, 0xa9, 0x06, 0x00, 0x22, 0xa7, 0x06, 0x20, 0x03, 0xa7, 0x06, 0x21, 0x02, 0x18, 0x04, 0x01, 0x09, 0xa8, 0x02, +0xac, 0x02, 0x5c, 0x08, 0x02, 0x01, 0x42, 0xba, 0x01, 0x13, 0x6e, 0x06, 0x7d, 0xbd, 0x01, 0x01, 0x08, 0x02, 0x02, 0xca, +0x16, 0x0f, 0x02, 0x0a, 0x02, 0xa7, 0x1e, 0x03, 0x00, 0xba, 0x1e, 0x00, 0x01, 0x03, 0x18, 0x02, 0x01, 0x13, 0x04, 0xc0, +0x02, 0x9e, 0x1e, 0xc3, 0x00, 0x00, 0x00, 0x18, 0x01, 0x13, 0xc7, 0x01, 0x0c, 0x7d, 0xc6, 0x01, 0x01, 0x09, 0x02, 0x02, +0x13, 0x6e, 0x06, 0x7d, 0xcb, 0x01, 0x01, 0x08, 0x02, 0x02, 0xc1, 0x0a, 0x08, 0x06, 0x0e, 0x02, 0x13, 0xb8, 0x01, 0x08, +0x24, 0xd3, 0x01, 0x01, 0x08, 0x02, 0x02, 0x4a, 0x08, 0x02, 0x12, 0x02, 0x5c, 0x08, 0x04, 0x01, 0x04, 0xd6, 0x01, 0xca, +0x16, 0x0f, 0x04, 0x04, 0x02, 0xa7, 0x1e, 0x03, 0x00, 0xba, 0x1e, 0x00, 0x01, 0x03, 0x18, 0x02, 0xc1, 0x0a, 0x08, 0x0c, +0x30, 0x00, 0xc1, 0x0a, 0x08, 0x04, 0x34, 0x01, 0xc3, 0x10, 0x08, 0x04, 0x04, 0x1e, 0x5c, 0x08, 0x02, 0x01, 0x1b, 0xe5, +0x01, 0x4a, 0x08, 0x02, 0x0c, 0x02, 0xc3, 0x10, 0x08, 0x02, 0x30, 0x02, 0xc8, 0x10, 0x08, 0x04, 0x04, 0x28, 0x99, 0x1e, +0x1c, 0x18, 0x1b, 0xe5, 0x1e, 0x08, 0xe0, 0x05, 0xd0, 0x00, 0x00, 0x00, 0xc2, 0x00, 0x00, 0x00, 0xea, 0x00, 0x00, 0x00, +0xdb, 0x00, 0x00, 0x00, 0x13, 0x6e, 0xb9, 0x05, 0x7d, 0xee, 0x01, 0x01, 0x08, 0x02, 0x02, 0xc3, 0x10, 0x08, 0x02, 0x6c, +0x02, 0x6c, 0x08, 0x02, 0x01, 0x28, 0xf1, 0x01, 0x8c, 0x01, 0x4a, 0x08, 0x02, 0xb1, 0x05, 0x02, 0x3f, 0x08, 0x06, 0x06, +0x5c, 0x08, 0x02, 0x01, 0x1b, 0xf6, 0x01, 0xc3, 0x10, 0x08, 0x06, 0xd4, 0x01, 0x06, 0x13, 0x6e, 0x04, 0x7d, 0xfb, 0x01, +0x01, 0x08, 0x02, 0x02, 0x6c, 0x08, 0x02, 0x01, 0x25, 0xfa, 0x01, 0xfd, 0x01, 0x13, 0xc7, 0x01, 0x06, 0x7d, 0x80, 0x02, +0x01, 0x09, 0x02, 0x02, 0x13, 0x6e, 0x04, 0x7d, 0x83, 0x02, 0x01, 0x08, 0x02, 0x02, 0xca, 0x16, 0x0f, 0x02, 0x02, 0xf4, +0x01, 0xa7, 0x1e, 0x03, 0x00, 0xba, 0x1e, 0x00, 0x01, 0x03, 0x18, 0x02, 0x13, 0x6e, 0x0a, 0x7d, 0x8b, 0x02, 0x01, 0x08, +0x02, 0x02, 0x4a, 0x08, 0x02, 0xa6, 0x01, 0x02, 0x13, 0x6e, 0x04, 0x7d, 0x8f, 0x02, 0x01, 0x08, 0x02, 0x02, 0xc3, 0x10, +0x08, 0x02, 0x08, 0x02, 0x13, 0x96, 0x02, 0x0a, 0x7d, 0x95, 0x02, 0x01, 0x36, 0x02, 0x02, 0x5c, 0x73, 0x02, 0x01, 0x3e, +0x98, 0x02, 0xc1, 0x0a, 0x08, 0x06, 0x06, 0x01, 0xc1, 0x0a, 0x08, 0x04, 0x0a, 0x00, 0x7c, 0x08, 0x04, 0x01, 0x2b, 0x92, +0x02, 0x8c, 0x01, 0x90, 0x01, 0x7c, 0x08, 0x02, 0x01, 0x2e, 0x9c, 0x02, 0x9e, 0x02, 0xa0, 0x02, 0x13, 0x52, 0x08, 0x7d, +0xa4, 0x02, 0x01, 0x33, 0x02, 0x02, 0xc1, 0x0a, 0x13, 0x02, 0x02, 0x00, 0x3d, 0x09, 0x02, 0x02, 0x02, 0x18, 0xc1, 0x0a, +0x13, 0x02, 0x06, 0x01, 0x3d, 0x09, 0x02, 0x02, 0x02, 0x18, 0xc1, 0x0a, 0x13, 0x02, 0x0a, 0x02, 0x3d, 0x09, 0x02, 0x02, +0x02, 0x18, 0xd0, 0x0a, 0x34, 0x02, 0x0a, 0x06, 0x02, 0xb4, 0x0a, 0x34, 0x02, 0x2d, 0x01, 0x00, 0x00, 0x01, 0xb0, 0x02, +0x0a, 0x02, 0x01, 0x09, 0x04, 0xa2, 0x04, 0xc1, 0x12, 0x09, 0x02, 0x10, 0x02, 0xe8, 0x0a, 0x13, 0x04, 0x0a, 0x04, 0x02, +0xa1, 0x02, 0x3d, 0x09, 0x02, 0x02, 0x02, 0x18, 0x4a, 0x09, 0x04, 0x72, 0x04, 0x99, 0x1e, 0x66, 0x18, 0x65, 0xe5, 0x1e, +0x09, 0x8a, 0x05, 0x02, 0x01, 0x00, 0x00, 0xdc, 0x00, 0x00, 0x00, 0x3b, 0x01, 0x00, 0x00, 0x07, 0x01, 0x00, 0x00, 0x13, +0x6e, 0x9f, 0x04, 0x7d, 0xbc, 0x02, 0x01, 0x08, 0x02, 0x02, 0x4a, 0x08, 0x04, 0x04, 0x84, 0x01, 0xce, 0x10, 0x09, 0x04, +0x95, 0x04, 0x04, 0x13, 0x6e, 0x04, 0x7d, 0xc3, 0x02, 0x01, 0x08, 0x02, 0x02, 0xca, 0x16, 0x0f, 0x02, 0x02, 0xf4, 0x02, +0xa7, 0x1e, 0x03, 0x00, 0xba, 0x1e, 0x00, 0x01, 0x03, 0x18, 0x02, 0x13, 0x6e, 0x0c, 0x7d, 0xcc, 0x02, 0x01, 0x08, 0x02, +0x02, 0xc3, 0x10, 0x08, 0x02, 0xa8, 0x02, 0x02, 0x6c, 0x08, 0x02, 0x01, 0x28, 0xcf, 0x02, 0x8c, 0x01, 0x4a, 0x08, 0x02, +0xf5, 0x03, 0x02, 0x3f, 0x08, 0x06, 0x06, 0x5c, 0x08, 0x02, 0x01, 0x1b, 0xd4, 0x02, 0x13, 0x7e, 0x06, 0x7d, 0xd7, 0x02, +0x01, 0x13, 0x02, 0x02, 0x3d, 0x09, 0x02, 0x02, 0x02, 0x18, 0x23, 0x6e, 0x02, 0x7d, 0xd7, 0x02, 0x89, 0x01, 0x01, 0x08, +0x02, 0x02, 0xce, 0x10, 0x09, 0x02, 0x06, 0x02, 0x01, 0x09, 0x04, 0xf6, 0x04, 0x5c, 0x09, 0x02, 0x01, 0x45, 0xdf, 0x02, +0x13, 0xc7, 0x01, 0x04, 0x7d, 0xe1, 0x02, 0x01, 0x09, 0x02, 0x02, 0xc4, 0x12, 0x08, 0x02, 0x08, 0x02, 0x6c, 0x08, 0x02, +0x01, 0x28, 0xe4, 0x02, 0x8c, 0x01, 0x6c, 0x08, 0x0a, 0x01, 0x1a, 0xe5, 0x02, 0xc5, 0x02, 0xc3, 0x10, 0x08, 0x08, 0xbc, +0x03, 0x32, 0x4a, 0x08, 0x02, 0x0a, 0x02, 0xce, 0x10, 0x09, 0x02, 0x26, 0x02, 0x4b, 0x09, 0x04, 0x60, 0x04, 0x99, 0x1e, +0x54, 0x18, 0x53, 0xe5, 0x1e, 0x09, 0x8e, 0x04, 0x42, 0x01, 0x00, 0x00, 0x08, 0x01, 0x00, 0x00, 0x72, 0x01, 0x00, 0x00, +0x47, 0x01, 0x00, 0x00, 0x01, 0x13, 0xb7, 0x03, 0xa0, 0x05, 0x3d, 0x09, 0x02, 0x02, 0x02, 0x18, 0xc3, 0x10, 0x08, 0x04, +0xcc, 0x03, 0xf0, 0x01, 0xce, 0x10, 0x09, 0x02, 0x06, 0x02, 0x4b, 0x09, 0x04, 0x04, 0xab, 0x03, 0x13, 0xb8, 0x01, 0x02, +0x23, 0xdf, 0x01, 0xc1, 0x0a, 0x08, 0x02, 0x04, 0x00, 0x22, 0x02, 0x00, 0x13, 0xb8, 0x01, 0x02, 0x23, 0xd3, 0x01, 0xc1, +0x0a, 0x08, 0x02, 0x08, 0x01, 0x22, 0x02, 0x00, 0x13, 0xb8, 0x01, 0x02, 0x23, 0x71, 0xc1, 0x0a, 0x08, 0x02, 0x0c, 0x02, +0x22, 0x02, 0x00, 0x01, 0x13, 0x02, 0xba, 0x05, 0x9e, 0x1e, 0x80, 0x01, 0x00, 0x00, 0x88, 0x06, 0x00, 0x00, 0x00, 0x00, +0xb9, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4c, 0x4f, 0x4d, 0x53, 0x00, 0x03, 0x01, 0x00, 0x0a, 0x00, 0x08, 0x00, +0xb4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0x05, 0x00, 0x00, 0x91, 0x02, 0x01, 0xd1, 0x10, 0x02, 0x47, 0x4c, +0x53, 0x4c, 0x2e, 0x73, 0x74, 0x64, 0x2e, 0x34, 0x35, 0x30, 0x00, 0x00, 0x00, 0x00, 0xa0, 0x04, 0x00, 0x01, 0xef, 0x0e, +0x04, 0x04, 0xed, 0xc2, 0xa5, 0xf3, 0x06, 0x00, 0x5d, 0x64, 0xa0, 0x02, 0x04, 0x07, 0x10, 0x20, 0x06, 0x10, 0x37, 0x02, +0x0b, 0x00, 0x05, 0x00, 0x23, 0x00, 0x00, 0x07, 0x01, 0x10, 0x01, 0x05, 0x00, 0x23, 0x40, 0x00, 0x07, 0x01, 0x10, 0x01, +0x23, 0x30, 0x01, 0x23, 0x04, 0x01, 0x23, 0x04, 0x01, 0x23, 0x04, 0x01, 0x23, 0x04, 0x10, 0x04, 0x06, 0x80, 0x02, 0x47, +0x02, 0x01, 0x00, 0x23, 0x00, 0x00, 0x00, 0x02, 0x10, 0x04, 0x22, 0x00, 0x10, 0x00, 0x21, 0x01, 0x10, 0x8e, 0x01, 0x1e, +0x00, 0x10, 0x0e, 0x1e, 0x07, 0x00, 0x8f, 0x01, 0x00, 0x93, 0x02, 0x02, 0xa1, 0x04, 0x02, 0x02, 0xa6, 0x02, 0x0a, 0x20, +0xb7, 0x02, 0x02, 0x08, 0x04, 0xb8, 0x02, 0x02, 0x09, 0x04, 0xb7, 0x02, 0x02, 0x08, 0x03, 0xb8, 0x02, 0x02, 0x0b, 0x03, +0xb5, 0x02, 0x02, 0x20, 0x01, 0xb5, 0x02, 0x02, 0x20, 0x00, 0xbb, 0x04, 0x0e, 0x02, 0x08, 0x00, 0x00, 0x00, 0xbc, 0x02, +0x02, 0x09, 0x0f, 0x8e, 0x03, 0x02, 0x0a, 0x0c, 0x0d, 0x0d, 0x0d, 0x08, 0x10, 0xbb, 0x04, 0x0e, 0x02, 0x40, 0x00, 0x00, +0x00, 0xbc, 0x02, 0x02, 0x11, 0x12, 0xae, 0x02, 0x02, 0x13, 0x3e, 0x02, 0x02, 0x14, 0x39, 0x15, 0x02, 0x02, 0xbb, 0x04, +0x0d, 0x02, 0x00, 0x00, 0x00, 0x00, 0xbb, 0x04, 0x0d, 0x02, 0x03, 0x00, 0x00, 0x00, 0x3e, 0x02, 0x02, 0x0d, 0xbb, 0x04, +0x0d, 0x06, 0x00, 0x08, 0x00, 0x00, 0x94, 0x02, 0x04, 0xbb, 0x04, 0x0d, 0x44, 0x04, 0x00, 0x00, 0x00, 0xbb, 0x04, 0x0e, +0x16, 0x02, 0x00, 0x00, 0x00, 0xb7, 0x02, 0x04, 0x08, 0x02, 0x3e, 0x1e, 0x03, 0x4d, 0x39, 0x5c, 0x02, 0x03, 0xbb, 0x04, +0x0e, 0x06, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x02, 0x03, 0x08, 0x3e, 0x04, 0x01, 0x09, 0x39, 0x63, 0x02, 0x01, 0x3e, 0x02, +0x01, 0x08, 0xbb, 0x04, 0x0e, 0x06, 0x03, 0x00, 0x00, 0x00, 0xbb, 0x04, 0x0e, 0x08, 0x01, 0x00, 0x00, 0x00, 0xad, 0x06, +0x0d, 0x8a, 0x01, 0xc6, 0x06, 0x02, 0xd9, 0x02, 0x00, 0x03, 0x18, 0x02, 0x33, 0x19, 0xa4, 0x02, 0x16, 0x17, 0x17, 0x18, +0x01, 0x0d, 0x02, 0x02, 0xc7, 0x18, 0x0d, 0x02, 0x02, 0xfa, 0x01, 0xcb, 0x14, 0x1e, 0x02, 0x02, 0x86, 0x02, 0xd9, 0x14, +0x0d, 0x32, 0x32, 0x04, 0xb8, 0x02, 0x33, 0x19, 0x0d, 0x16, 0x17, 0xb3, 0x01, 0x40, 0x01, 0x0d, 0x02, 0x02, 0xbc, 0x0e, +0x08, 0x9b, 0x01, 0x9b, 0x01, 0x13, 0x61, 0x06, 0x5d, 0x60, 0x22, 0x00, 0x06, 0x13, 0x65, 0x08, 0x64, 0x4b, 0x01, 0x08, +0x02, 0x02, 0x13, 0x65, 0x04, 0x64, 0x68, 0x01, 0x08, 0x02, 0x02, 0xc8, 0x10, 0x08, 0x02, 0x08, 0x02, 0x13, 0x61, 0x04, +0x5d, 0x6c, 0x22, 0x00, 0x04, 0x8d, 0x1e, 0x88, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x86, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x4c, 0x4f, 0x4d, 0x53, 0x00, 0x03, 0x01, 0x00, 0x0a, 0x00, 0x08, 0x00, 0xc4, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x2c, 0x01, 0x00, 0x00, 0x91, 0x02, 0x01, 0xd1, 0x10, 0x02, 0x47, 0x4c, 0x53, 0x4c, 0x2e, 0x73, +0x74, 0x64, 0x2e, 0x34, 0x35, 0x30, 0x00, 0x00, 0x00, 0x00, 0xa0, 0x04, 0x00, 0x01, 0xdf, 0x0e, 0x04, 0x04, 0xed, 0xc2, +0xa5, 0xf3, 0x06, 0x00, 0xc3, 0x01, 0xa0, 0x02, 0x04, 0x07, 0x00, 0x86, 0x03, 0x00, 0x10, 0x00, 0x1e, 0x00, 0x93, 0x02, +0x02, 0xa1, 0x04, 0x02, 0x02, 0xa6, 0x02, 0x12, 0x20, 0xb7, 0x02, 0x02, 0x0c, 0x04, 0xbb, 0x04, 0x0c, 0xc6, 0x01, 0x00, +0x00, 0x00, 0x00, 0xec, 0x04, 0x0d, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x3e, 0x28, 0x03, 0x0d, 0x39, 0xc2, 0x01, 0x02, 0x03, +0xc6, 0x06, 0x02, 0xfd, 0x02, 0x00, 0x03, 0x18, 0x02, 0x22, 0xfb, 0x02, 0xd1, 0x02, 0x8d, 0x1e, 0x88, 0x06, 0x00, 0x00, +0xc5, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4c, 0x4f, 0x4d, 0x53, 0x00, 0x03, 0x01, 0x00, 0x00, 0x00, 0x07, 0x00, +0x6d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x14, 0x00, 0x00, 0x91, 0x02, 0x01, 0xd1, 0x10, 0x02, 0x47, 0x4c, +0x53, 0x4c, 0x2e, 0x73, 0x74, 0x64, 0x2e, 0x34, 0x35, 0x30, 0x00, 0x00, 0x00, 0x00, 0xa0, 0x04, 0x00, 0x01, 0xbf, 0x0f, +0x00, 0x02, 0xed, 0xc2, 0xa5, 0xf3, 0x06, 0x00, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10, 0x1c, 0x06, 0x40, 0x10, +0x02, 0x06, 0x10, 0x10, 0x02, 0x06, 0x10, 0x10, 0x02, 0x06, 0x10, 0x37, 0x0b, 0x89, 0x00, 0x05, 0x00, 0x23, 0x00, 0x00, +0x07, 0x01, 0x10, 0x01, 0x05, 0x00, 0x23, 0x40, 0x00, 0x07, 0x01, 0x10, 0x01, 0x05, 0x00, 0x23, 0x40, 0x00, 0x07, 0x01, +0x10, 0x01, 0x05, 0x00, 0x23, 0x40, 0x00, 0x07, 0x01, 0x10, 0x01, 0x05, 0x00, 0x23, 0x40, 0x00, 0x07, 0x01, 0x10, 0x01, +0x05, 0x00, 0x23, 0x80, 0x01, 0x00, 0x07, 0x01, 0x10, 0x01, 0x05, 0x00, 0x23, 0x40, 0x00, 0x07, 0x01, 0x10, 0x01, 0x23, +0x40, 0x01, 0x23, 0x10, 0x01, 0x23, 0x08, 0x01, 0x23, 0x04, 0x01, 0x23, 0x04, 0x01, 0x23, 0x10, 0x01, 0x23, 0x10, 0x01, +0x23, 0x08, 0x01, 0x00, 0x00, 0x23, 0x08, 0x01, 0x00, 0x00, 0x23, 0x04, 0x01, 0x23, 0x04, 0x01, 0x23, 0x04, 0x01, 0x23, +0x04, 0x01, 0x23, 0x04, 0x01, 0x00, 0x00, 0x23, 0x04, 0x01, 0x00, 0x00, 0x23, 0x04, 0x01, 0x00, 0x00, 0x23, 0x04, 0x01, +0x00, 0x00, 0x23, 0x04, 0x01, 0x00, 0x00, 0x23, 0x04, 0x01, 0x00, 0x00, 0x23, 0x04, 0x01, 0x00, 0x00, 0x23, 0x04, 0x01, +0x00, 0x00, 0x23, 0x10, 0x01, 0x00, 0x00, 0x23, 0x0c, 0x01, 0x00, 0x00, 0x23, 0x04, 0x01, 0x00, 0x00, 0x23, 0x08, 0x01, +0x00, 0x00, 0x23, 0x04, 0x01, 0x00, 0x00, 0x23, 0x04, 0x01, 0x23, 0x90, 0x01, 0x01, 0x00, 0x00, 0x23, 0x0c, 0x01, 0x00, +0x00, 0x23, 0x04, 0x01, 0x00, 0x00, 0x23, 0x10, 0x01, 0x00, 0x00, 0x23, 0x10, 0x01, 0x00, 0x00, 0x23, 0x08, 0x01, 0x00, +0x00, 0x23, 0x04, 0x01, 0x23, 0x04, 0x01, 0x00, 0x00, 0x23, 0x10, 0x01, 0x00, 0x00, 0x23, 0x04, 0x01, 0x00, 0x00, 0x23, +0x04, 0x01, 0x00, 0x00, 0x23, 0x04, 0x01, 0x00, 0x00, 0x23, 0x04, 0x01, 0x00, 0x00, 0x23, 0x04, 0x01, 0x00, 0x00, 0x23, +0x04, 0x01, 0x00, 0x00, 0x23, 0x04, 0x01, 0x23, 0x04, 0x01, 0x23, 0x0c, 0x01, 0x00, 0x00, 0x23, 0x04, 0x01, 0x23, 0x04, +0x01, 0x23, 0x04, 0x01, 0x23, 0x04, 0x01, 0x00, 0x00, 0x23, 0x04, 0x01, 0x00, 0x00, 0x23, 0x0c, 0x01, 0x23, 0x04, 0x01, +0x00, 0x00, 0x23, 0x04, 0x01, 0x23, 0x04, 0x01, 0x23, 0x04, 0x01, 0x05, 0x00, 0x23, 0x04, 0x00, 0x07, 0x01, 0x10, 0x01, +0x05, 0x00, 0x23, 0x30, 0x00, 0x07, 0x01, 0x10, 0x01, 0x05, 0x00, 0x23, 0x40, 0x00, 0x07, 0x01, 0x10, 0x01, 0x00, 0x00, +0x23, 0x40, 0x01, 0x00, 0x00, 0x23, 0x04, 0x01, 0x00, 0x00, 0x23, 0x04, 0x01, 0x00, 0x00, 0x23, 0x04, 0x01, 0x23, 0x04, +0x01, 0x00, 0x00, 0x23, 0x40, 0x01, 0x00, 0x00, 0x23, 0x04, 0x01, 0x00, 0x00, 0x23, 0x04, 0x01, 0x00, 0x00, 0x23, 0x04, +0x01, 0x00, 0x00, 0x23, 0x04, 0x00, 0x00, 0x02, 0x10, 0x02, 0x22, 0x00, 0x10, 0x00, 0x21, 0x00, 0x10, 0x11, 0x1e, 0x00, +0x00, 0x02, 0x0e, 0x10, 0x00, 0x1e, 0x08, 0x10, 0x02, 0x0b, 0x2b, 0x10, 0x02, 0x1e, 0x04, 0x10, 0x02, 0x1e, 0x07, 0x10, +0x02, 0x0b, 0x03, 0x47, 0x0a, 0x02, 0x00, 0x0b, 0x01, 0x00, 0x01, 0x0b, 0x01, 0x01, 0x00, 0x00, 0x02, 0x93, 0x02, 0x22, +0xa1, 0x04, 0x02, 0x12, 0xa6, 0x02, 0x02, 0x20, 0xb7, 0x02, 0x02, 0x14, 0x04, 0xb8, 0x02, 0x02, 0x15, 0x04, 0xae, 0x02, +0x17, 0x15, 0xb7, 0x02, 0x1a, 0x14, 0x03, 0xb8, 0x02, 0x02, 0x17, 0x03, 0xb5, 0x02, 0x02, 0x20, 0x01, 0xb5, 0x02, 0x02, +0x20, 0x00, 0xbb, 0x04, 0x19, 0x02, 0x00, 0x00, 0x00, 0x00, 0x94, 0x02, 0x02, 0x3e, 0x02, 0x02, 0x16, 0xbb, 0x04, 0x19, +0x02, 0x01, 0x00, 0x00, 0x00, 0xbb, 0x04, 0x19, 0x02, 0x02, 0x00, 0x00, 0x00, 0xbb, 0x04, 0x19, 0x02, 0x05, 0x00, 0x00, +0x00, 0xbb, 0x04, 0x1a, 0x02, 0x02, 0x00, 0x00, 0x00, 0xbc, 0x02, 0x25, 0x16, 0x21, 0xb7, 0x02, 0x28, 0x14, 0x02, 0xb7, +0x02, 0x02, 0x1a, 0x03, 0xbb, 0x04, 0x1a, 0x02, 0x09, 0x00, 0x00, 0x00, 0xbc, 0x02, 0x29, 0x17, 0x24, 0xbb, 0x04, 0x1a, +0x2c, 0x04, 0x00, 0x00, 0x00, 0xbc, 0x02, 0x29, 0x15, 0x25, 0xbb, 0x04, 0x1a, 0x2c, 0x30, 0x00, 0x00, 0x00, 0xbc, 0x02, +0x29, 0x15, 0x26, 0xce, 0x83, 0x80, 0x02, 0x0b, 0x16, 0x16, 0x16, 0x16, 0x0e, 0x16, 0x16, 0x15, 0x22, 0x14, 0x14, 0x15, +0x15, 0x22, 0x22, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x15, 0x23, 0x19, 0x22, 0x14, +0x14, 0x0f, 0x17, 0x14, 0x15, 0x15, 0x22, 0x19, 0x14, 0x15, 0x19, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x1a, 0x17, 0x14, +0x14, 0x1a, 0x14, 0x14, 0x17, 0x14, 0x14, 0x14, 0x14, 0x14, 0x18, 0x16, 0x16, 0x14, 0x14, 0x14, 0x14, 0x10, 0x19, 0x14, +0x14, 0x14, 0x11, 0x3e, 0x38, 0x02, 0x0b, 0x39, 0x27, 0x35, 0x02, 0x3e, 0x38, 0x01, 0x15, 0x39, 0x28, 0x49, 0x01, 0xbb, +0x04, 0x14, 0x4c, 0x00, 0x00, 0x00, 0xbf, 0xbb, 0x04, 0x14, 0x02, 0x00, 0x00, 0x00, 0x3f, 0xbb, 0x04, 0x14, 0x02, 0x00, +0x00, 0x00, 0x20, 0xbb, 0x04, 0x14, 0x02, 0x00, 0x00, 0x00, 0x00, 0xbb, 0x04, 0x14, 0x02, 0x00, 0x00, 0x00, 0xa0, 0xbb, +0x04, 0x14, 0x02, 0x00, 0x00, 0x80, 0x3f, 0x3e, 0x02, 0x03, 0x19, 0x39, 0x2f, 0x55, 0x03, 0x3e, 0x58, 0x01, 0x19, 0x39, +0x30, 0x55, 0x01, 0x3e, 0x58, 0x03, 0x15, 0x39, 0x31, 0x55, 0x03, 0xbb, 0x04, 0x1a, 0x58, 0x00, 0x00, 0x00, 0x00, 0x3e, +0x02, 0x03, 0x14, 0xbb, 0x04, 0x1a, 0x02, 0x01, 0x00, 0x00, 0x00, 0x39, 0x31, 0x59, 0x03, 0xbb, 0x04, 0x14, 0x5c, 0x00, +0x00, 0x00, 0x40, 0xbc, 0x02, 0x02, 0x14, 0x34, 0x3e, 0x02, 0x03, 0x36, 0x39, 0x37, 0x5d, 0x03, 0xbe, 0x02, 0x0a, 0x15, +0x14, 0x3e, 0x56, 0x03, 0x0d, 0x39, 0x38, 0x5d, 0x03, 0xc6, 0x06, 0x12, 0x0d, 0x00, 0x13, 0x18, 0x6e, 0x01, 0x19, 0x02, +0x6a, 0x22, 0x6c, 0x00, 0x01, 0x19, 0x02, 0x6e, 0xc3, 0x18, 0x19, 0x02, 0x02, 0x3c, 0x13, 0x1d, 0x02, 0x0c, 0x20, 0x01, +0x16, 0x02, 0x02, 0x01, 0x15, 0x02, 0x78, 0xc1, 0x0a, 0x14, 0x02, 0x02, 0x02, 0x4a, 0x14, 0x02, 0x02, 0x30, 0x4b, 0x14, +0x02, 0x02, 0x30, 0xd2, 0x0a, 0x15, 0x02, 0x02, 0x08, 0x02, 0xc1, 0x12, 0x15, 0x02, 0x0c, 0x02, 0xc1, 0x0a, 0x14, 0x02, +0x02, 0x03, 0x5c, 0x14, 0x02, 0x01, 0x04, 0x45, 0xc8, 0x16, 0x1c, 0x02, 0x02, 0x38, 0xa7, 0x1e, 0x01, 0x00, 0xba, 0x1e, +0x00, 0x03, 0x01, 0x18, 0x04, 0xc8, 0x16, 0x1c, 0x02, 0x0a, 0x3c, 0xd9, 0x14, 0x14, 0x02, 0x02, 0x3c, 0x40, 0xd2, 0x0a, +0x15, 0x02, 0x02, 0x10, 0x03, 0x99, 0x1e, 0x08, 0x18, 0x07, 0xe5, 0x1e, 0x15, 0x0a, 0x44, 0x00, 0x00, 0x00, 0x39, 0x00, +0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x49, 0x00, 0x00, 0x00, 0xc1, 0x0a, 0x14, 0x02, 0x02, 0x03, 0xc8, 0x10, 0x14, 0x02, +0x42, 0x02, 0xce, 0x10, 0x15, 0x02, 0x06, 0x02, 0xb0, 0x0a, 0x0a, 0x02, 0x02, 0xc1, 0x0a, 0x15, 0x02, 0x02, 0x00, 0x3d, +0x17, 0x02, 0x02, 0x02, 0x18, 0x13, 0x33, 0x02, 0x06, 0x32, 0xc1, 0x0a, 0x14, 0x02, 0x04, 0x00, 0x22, 0x02, 0x00, 0x13, +0x33, 0x02, 0x06, 0x34, 0xc1, 0x0a, 0x14, 0x02, 0x08, 0x01, 0x22, 0x02, 0x00, 0x13, 0x33, 0x02, 0x06, 0x21, 0xc1, 0x0a, +0x14, 0x02, 0x0c, 0x02, 0x22, 0x02, 0x00, 0x22, 0xa4, 0x01, 0x2c, 0x01, 0x19, 0x02, 0xac, 0x01, 0xcb, 0x10, 0x19, 0x02, +0x02, 0x78, 0xbf, 0x0c, 0x14, 0x02, 0x02, 0x4a, 0x14, 0x02, 0x02, 0x50, 0xc3, 0x10, 0x14, 0x02, 0x02, 0x60, 0xc1, 0x0a, +0x14, 0x02, 0x38, 0x00, 0x4a, 0x14, 0x02, 0x02, 0x6c, 0xc1, 0x0a, 0x14, 0x02, 0x3c, 0x03, 0x4a, 0x14, 0x02, 0x02, 0x70, +0x4a, 0x14, 0x02, 0x02, 0x0a, 0x4b, 0x14, 0x02, 0x08, 0x02, 0xd2, 0x0a, 0x15, 0x02, 0x02, 0x44, 0x00, 0xc1, 0x0a, 0x14, +0x02, 0x02, 0x00, 0x4a, 0x14, 0x02, 0x02, 0x12, 0x13, 0x33, 0x02, 0x08, 0x1b, 0x22, 0x00, 0x02, 0xc1, 0x0a, 0x14, 0x02, +0x08, 0x01, 0x3f, 0x14, 0x02, 0x02, 0xd2, 0x0a, 0x15, 0x02, 0x02, 0x0c, 0x01, 0x13, 0x31, 0x02, 0x09, 0x1b, 0x22, 0x00, +0x02, 0x22, 0xd0, 0x01, 0x60, 0x8d, 0x1e, 0x88, 0x06, 0x00, 0x00, 0x00, 0x5a, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x4c, 0x4f, 0x4d, 0x53, 0x00, 0x03, 0x01, 0x00, 0x0a, 0x00, 0x08, 0x00, 0x36, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0xf8, 0x11, 0x00, 0x00, 0x91, 0x02, 0x01, 0xd1, 0x10, 0x02, 0x47, 0x4c, 0x53, 0x4c, 0x2e, 0x73, 0x74, 0x64, 0x2e, 0x34, +0x35, 0x30, 0x00, 0x00, 0x00, 0x00, 0xa0, 0x04, 0x00, 0x01, 0xaf, 0x0f, 0x00, 0x04, 0xed, 0xc2, 0xa5, 0xf3, 0x06, 0x00, +0x6f, 0x9f, 0x01, 0xa1, 0x01, 0xae, 0x01, 0xc2, 0x01, 0xcb, 0x01, 0x10, 0xb8, 0x01, 0x06, 0x40, 0x10, 0x08, 0x06, 0x10, +0x10, 0x04, 0x06, 0x10, 0x10, 0x04, 0x06, 0x10, 0x37, 0x02, 0x7c, 0x00, 0x05, 0x00, 0x23, 0x00, 0x00, 0x07, 0x01, 0x10, +0x01, 0x05, 0x00, 0x23, 0x40, 0x00, 0x07, 0x01, 0x10, 0x01, 0x05, 0x00, 0x23, 0x40, 0x00, 0x07, 0x01, 0x10, 0x01, 0x05, +0x00, 0x23, 0x40, 0x00, 0x07, 0x01, 0x10, 0x01, 0x05, 0x00, 0x23, 0x40, 0x00, 0x07, 0x01, 0x10, 0x01, 0x05, 0x00, 0x23, +0x80, 0x01, 0x00, 0x07, 0x01, 0x10, 0x01, 0x05, 0x00, 0x23, 0x40, 0x00, 0x07, 0x01, 0x10, 0x01, 0x23, 0x40, 0x01, 0x23, +0x10, 0x01, 0x23, 0x08, 0x01, 0x23, 0x04, 0x01, 0x23, 0x04, 0x01, 0x23, 0x10, 0x01, 0x23, 0x10, 0x01, 0x23, 0x08, 0x01, +0x23, 0x08, 0x01, 0x23, 0x04, 0x01, 0x23, 0x04, 0x01, 0x23, 0x04, 0x01, 0x23, 0x04, 0x01, 0x23, 0x04, 0x01, 0x23, 0x04, +0x01, 0x23, 0x04, 0x01, 0x00, 0x00, 0x23, 0x04, 0x01, 0x00, 0x00, 0x23, 0x04, 0x01, 0x00, 0x00, 0x23, 0x04, 0x01, 0x00, +0x00, 0x23, 0x04, 0x01, 0x00, 0x00, 0x23, 0x04, 0x01, 0x00, 0x00, 0x23, 0x10, 0x01, 0x00, 0x00, 0x23, 0x0c, 0x01, 0x00, +0x00, 0x23, 0x04, 0x01, 0x23, 0x08, 0x01, 0x23, 0x04, 0x01, 0x00, 0x00, 0x23, 0x04, 0x01, 0x23, 0x90, 0x01, 0x01, 0x00, +0x00, 0x23, 0x0c, 0x01, 0x23, 0x04, 0x01, 0x23, 0x10, 0x01, 0x00, 0x00, 0x23, 0x10, 0x01, 0x00, 0x00, 0x23, 0x08, 0x01, +0x00, 0x00, 0x23, 0x04, 0x01, 0x23, 0x04, 0x01, 0x00, 0x00, 0x23, 0x10, 0x01, 0x00, 0x00, 0x23, 0x04, 0x01, 0x00, 0x00, +0x23, 0x04, 0x01, 0x00, 0x00, 0x23, 0x04, 0x01, 0x00, 0x00, 0x23, 0x04, 0x01, 0x00, 0x00, 0x23, 0x04, 0x01, 0x00, 0x00, +0x23, 0x04, 0x01, 0x00, 0x00, 0x23, 0x04, 0x01, 0x23, 0x04, 0x01, 0x23, 0x0c, 0x01, 0x23, 0x04, 0x01, 0x23, 0x04, 0x01, +0x23, 0x04, 0x01, 0x23, 0x04, 0x01, 0x23, 0x04, 0x01, 0x23, 0x0c, 0x01, 0x23, 0x04, 0x01, 0x23, 0x04, 0x01, 0x23, 0x04, +0x01, 0x23, 0x04, 0x01, 0x05, 0x00, 0x23, 0x04, 0x00, 0x07, 0x01, 0x10, 0x01, 0x05, 0x00, 0x23, 0x30, 0x00, 0x07, 0x01, +0x10, 0x01, 0x05, 0x00, 0x23, 0x40, 0x00, 0x07, 0x01, 0x10, 0x01, 0x00, 0x00, 0x23, 0x40, 0x01, 0x00, 0x00, 0x23, 0x04, +0x01, 0x00, 0x00, 0x23, 0x04, 0x01, 0x00, 0x00, 0x23, 0x04, 0x01, 0x23, 0x04, 0x01, 0x23, 0x40, 0x01, 0x00, 0x00, 0x23, +0x04, 0x01, 0x00, 0x00, 0x23, 0x04, 0x01, 0x00, 0x00, 0x23, 0x04, 0x01, 0x00, 0x00, 0x23, 0x04, 0x00, 0x00, 0x02, 0x10, +0x04, 0x22, 0x00, 0x10, 0x00, 0x21, 0x00, 0x10, 0x10, 0x1e, 0x00, 0x00, 0x60, 0x0e, 0x10, 0x00, 0x1e, 0x08, 0x10, 0x04, +0x0b, 0x2b, 0x10, 0x1a, 0x1e, 0x04, 0x10, 0x28, 0x1e, 0x07, 0x47, 0x0e, 0x04, 0x00, 0x0b, 0x01, 0x00, 0x01, 0x0b, 0x01, +0x01, 0x01, 0x0b, 0x01, 0x03, 0x01, 0x0b, 0x01, 0x04, 0x00, 0x00, 0x02, 0x93, 0x02, 0x02, 0xa1, 0x04, 0x02, 0x02, 0xa6, +0x02, 0x0a, 0x20, 0xb7, 0x02, 0x02, 0x08, 0x04, 0xb8, 0x02, 0x02, 0x09, 0x04, 0xae, 0x02, 0x12, 0x09, 0xb7, 0x02, 0x12, +0x08, 0x03, 0xb8, 0x02, 0x02, 0x1c, 0x03, 0xb5, 0x02, 0x02, 0x20, 0x01, 0xb5, 0x02, 0x02, 0x20, 0x00, 0xbb, 0x04, 0x1e, +0x12, 0x00, 0x00, 0x00, 0x00, 0x94, 0x02, 0x0e, 0x3e, 0x1a, 0x02, 0x0a, 0xbb, 0x04, 0x1e, 0x36, 0x05, 0x00, 0x00, 0x00, +0xbb, 0x04, 0x1f, 0x08, 0x02, 0x00, 0x00, 0x00, 0xbc, 0x02, 0x02, 0x0a, 0x5b, 0xb7, 0x02, 0x02, 0x08, 0x02, 0xb7, 0x02, +0x02, 0x1f, 0x03, 0xbb, 0x04, 0x1f, 0x02, 0x09, 0x00, 0x00, 0x00, 0xbc, 0x02, 0x02, 0x1c, 0x5f, 0xbb, 0x04, 0x1f, 0x02, +0x04, 0x00, 0x00, 0x00, 0xbc, 0x02, 0x02, 0x09, 0x61, 0xbb, 0x04, 0x1f, 0x02, 0x30, 0x00, 0x00, 0x00, 0xbc, 0x02, 0x02, +0x09, 0x63, 0xce, 0x83, 0x80, 0x02, 0x02, 0x0a, 0x0a, 0x0a, 0x0a, 0x5c, 0x0a, 0x0a, 0x09, 0x5d, 0x08, 0x08, 0x09, 0x09, +0x5d, 0x5d, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x09, 0x5e, 0x1e, 0x5d, 0x08, 0x08, +0x60, 0x1c, 0x08, 0x09, 0x09, 0x5d, 0x1e, 0x08, 0x09, 0x1e, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x1f, 0x1c, 0x08, 0x08, +0x1f, 0x08, 0x08, 0x1c, 0x08, 0x08, 0x08, 0x08, 0x08, 0x1d, 0x0a, 0x0a, 0x08, 0x08, 0x08, 0x08, 0x62, 0x1e, 0x08, 0x08, +0x08, 0x64, 0x3e, 0x02, 0x02, 0x65, 0x39, 0x66, 0x02, 0x02, 0x3e, 0x0e, 0x01, 0x09, 0x39, 0x6e, 0x02, 0x01, 0xbb, 0x04, +0x08, 0x1a, 0x00, 0x00, 0x00, 0xbf, 0xbb, 0x04, 0x08, 0x04, 0x00, 0x00, 0x00, 0x3f, 0xbb, 0x04, 0x08, 0x16, 0x00, 0x00, +0x00, 0x20, 0xbb, 0x04, 0x08, 0x0c, 0x00, 0x00, 0x00, 0x00, 0xbb, 0x04, 0x08, 0x04, 0x00, 0x00, 0x00, 0xa0, 0xbb, 0x04, +0x08, 0x08, 0x00, 0x00, 0x80, 0x3f, 0x3e, 0x12, 0x03, 0x1e, 0x39, 0x9e, 0x01, 0x02, 0x03, 0x3e, 0x02, 0x01, 0x1e, 0x39, +0xa0, 0x01, 0x02, 0x01, 0x3e, 0x18, 0x03, 0x09, 0x39, 0xad, 0x01, 0x02, 0x03, 0xbb, 0x04, 0x1f, 0x08, 0x00, 0x00, 0x00, +0x00, 0x3e, 0x02, 0x03, 0x08, 0xbb, 0x04, 0x1f, 0x06, 0x01, 0x00, 0x00, 0x00, 0x39, 0xad, 0x01, 0x18, 0x03, 0xbc, 0x02, +0x0c, 0x08, 0xb6, 0x01, 0xde, 0x02, 0x02, 0x09, 0x08, 0xc8, 0x01, 0xc8, 0x01, 0x3e, 0x02, 0x03, 0xc9, 0x01, 0x39, 0xca, +0x01, 0x02, 0x03, 0xc6, 0x06, 0x02, 0x8d, 0x03, 0x00, 0x03, 0x18, 0x02, 0x01, 0x1e, 0xba, 0x02, 0x02, 0x22, 0x06, 0x00, +0x13, 0x3c, 0xe6, 0x01, 0x67, 0x57, 0x01, 0x0a, 0x02, 0x02, 0x01, 0x09, 0x08, 0xd6, 0x02, 0xc1, 0x0a, 0x08, 0x3b, 0x3b, +0x02, 0x4a, 0x08, 0x02, 0x02, 0x82, 0x02, 0x4b, 0x08, 0x02, 0x02, 0x80, 0x02, 0xd2, 0x0a, 0x09, 0x56, 0x56, 0x1e, 0x02, +0xc1, 0x12, 0x09, 0x4d, 0x27, 0x4d, 0xc1, 0x0a, 0x08, 0x04, 0x04, 0x03, 0x5c, 0x08, 0x02, 0x01, 0x04, 0x84, 0x02, 0xc8, +0x16, 0x2f, 0x02, 0x02, 0xfa, 0x01, 0xa7, 0x1e, 0x0d, 0x00, 0xba, 0x1e, 0x00, 0x01, 0x0d, 0x18, 0x02, 0xc8, 0x16, 0x2f, +0x06, 0x0c, 0xf6, 0x01, 0xd9, 0x14, 0x08, 0x02, 0x02, 0xf4, 0x01, 0x84, 0x02, 0xd2, 0x0a, 0x09, 0x44, 0x44, 0x56, 0x03, +0x99, 0x1e, 0x40, 0x18, 0x3f, 0xe5, 0x1e, 0x09, 0x50, 0x02, 0x01, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x2d, 0x01, 0x00, +0x00, 0x07, 0x01, 0x00, 0x00, 0xc1, 0x0a, 0x08, 0x49, 0x49, 0x03, 0xc8, 0x10, 0x08, 0x02, 0xf8, 0x01, 0x02, 0xce, 0x10, +0x09, 0x02, 0x45, 0x02, 0xb0, 0x0a, 0x13, 0x28, 0x28, 0xc1, 0x0a, 0x09, 0x05, 0x05, 0x00, 0x3d, 0x1c, 0xe3, 0x01, 0xe3, +0x01, 0xe3, 0x01, 0x18, 0x13, 0xb3, 0x01, 0x06, 0xae, 0x01, 0xb2, 0x01, 0xc1, 0x0a, 0x08, 0x02, 0x08, 0x00, 0x22, 0x02, +0x00, 0x13, 0xb3, 0x01, 0x04, 0xae, 0x01, 0xb6, 0x01, 0xc1, 0x0a, 0x08, 0x02, 0x0e, 0x01, 0x22, 0x02, 0x00, 0x13, 0xb3, +0x01, 0x02, 0xae, 0x01, 0x5b, 0xc1, 0x0a, 0x08, 0x02, 0x12, 0x02, 0x22, 0x02, 0x00, 0x22, 0x0f, 0xdd, 0x01, 0xc1, 0x0a, +0x08, 0x16, 0xc7, 0x01, 0x01, 0x3f, 0x08, 0x02, 0x02, 0xd2, 0x0a, 0x09, 0xdc, 0x01, 0xdc, 0x01, 0x16, 0x01, 0x13, 0xad, +0x01, 0xcd, 0x01, 0xcb, 0x01, 0x28, 0x22, 0x00, 0xcd, 0x01, 0x8d, 0x1e, 0x88, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x1c, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4c, 0x4f, 0x4d, 0x53, 0x00, 0x03, 0x01, 0x00, 0x0a, 0x00, 0x08, 0x00, +0x59, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x03, 0x00, 0x00, 0x91, 0x02, 0x01, 0xd1, 0x10, 0x02, 0x47, 0x4c, +0x53, 0x4c, 0x2e, 0x73, 0x74, 0x64, 0x2e, 0x34, 0x35, 0x30, 0x00, 0x00, 0x00, 0x00, 0xa0, 0x04, 0x00, 0x01, 0xdf, 0x0e, +0x04, 0x04, 0xed, 0xc2, 0xa5, 0xf3, 0x06, 0x00, 0xce, 0x01, 0xa0, 0x02, 0x04, 0x07, 0x37, 0xd0, 0x02, 0x05, 0x00, 0x05, +0x00, 0x23, 0x00, 0x00, 0x07, 0x01, 0x10, 0x01, 0x00, 0x00, 0x23, 0x40, 0x00, 0x00, 0x02, 0x10, 0x04, 0x22, 0x00, 0x10, +0x00, 0x21, 0x08, 0x10, 0x48, 0x1e, 0x00, 0x00, 0xc8, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, +0x00, 0x02, 0x00, 0x93, 0x02, 0x02, 0xa1, 0x04, 0x02, 0x02, 0xa6, 0x02, 0x12, 0x20, 0xb7, 0x02, 0x02, 0x0c, 0x04, 0xb7, +0x02, 0x02, 0x0c, 0x03, 0xfe, 0x02, 0x02, 0x0d, 0x0d, 0x0c, 0x0c, 0x0c, 0x0e, 0xb8, 0x02, 0x32, 0x0d, 0x04, 0xb5, 0x02, +0x04, 0x20, 0x01, 0xbb, 0x04, 0x2a, 0x46, 0x01, 0x00, 0x00, 0x00, 0xbb, 0x04, 0x0c, 0x5a, 0x00, 0x00, 0x80, 0x3f, 0xbe, +0x02, 0x5c, 0x28, 0x0e, 0x3e, 0x02, 0x02, 0xa8, 0x01, 0x39, 0xa9, 0x01, 0x02, 0x02, 0x3e, 0x02, 0x02, 0x0e, 0x3e, 0x44, +0x03, 0x0d, 0x39, 0xcd, 0x01, 0x02, 0x03, 0xad, 0x06, 0x0d, 0x8a, 0x02, 0xad, 0x06, 0x0c, 0x02, 0xad, 0x06, 0x0e, 0x02, +0xc6, 0x06, 0x02, 0xa1, 0x05, 0x00, 0x03, 0x18, 0x02, 0x13, 0xab, 0x01, 0xd8, 0x04, 0xaa, 0x01, 0x4d, 0x01, 0x0e, 0x02, +0x02, 0xc1, 0x0a, 0x0c, 0x02, 0x02, 0x00, 0xc1, 0x0a, 0x0c, 0x02, 0x04, 0x01, 0xc1, 0x0a, 0x0c, 0x02, 0x06, 0x02, 0xe0, +0x0a, 0x0d, 0x02, 0x06, 0x04, 0x02, 0xf8, 0x02, 0x80, 0x0b, 0x0f, 0x44, 0x44, 0x0a, 0x08, 0x08, 0x08, 0x06, 0xc1, 0x0a, +0x0d, 0x35, 0x35, 0x00, 0x22, 0xde, 0x01, 0x00, 0x8d, 0x1e, 0x88, 0x06, 0x00, 0x00, 0x00, 0x00, 0x53, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x4c, 0x4f, 0x4d, 0x53, 0x00, 0x03, 0x01, 0x00, 0x0a, 0x00, 0x08, 0x00, 0x06, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x98, 0x00, 0x00, 0x00, 0x91, 0x02, 0x01, 0xd1, 0x10, 0x02, 0x47, 0x4c, 0x53, 0x4c, 0x2e, 0x73, +0x74, 0x64, 0x2e, 0x34, 0x35, 0x30, 0x00, 0x00, 0x00, 0x00, 0xa0, 0x04, 0x00, 0x01, 0xcf, 0x0e, 0x04, 0x04, 0xed, 0xc2, +0xa5, 0xf3, 0x06, 0x00, 0xa0, 0x02, 0x04, 0x07, 0x93, 0x02, 0x02, 0xa1, 0x04, 0x02, 0x02, 0xc6, 0x06, 0x02, 0x02, 0x00, +0x03, 0x18, 0x02, 0x8d, 0x1e, 0x88, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0xee, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x4c, 0x4f, 0x4d, 0x53, 0x00, 0x03, 0x01, 0x00, 0x0a, 0x00, 0x08, 0x00, 0x50, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x74, 0x1d, 0x00, 0x00, 0x91, 0x02, 0x01, 0xd1, 0x10, 0x02, 0x47, 0x4c, 0x53, 0x4c, 0x2e, 0x73, 0x74, 0x64, 0x2e, 0x34, +0x35, 0x30, 0x00, 0x00, 0x00, 0x00, 0xa0, 0x04, 0x00, 0x01, 0xef, 0x0e, 0x04, 0x04, 0xed, 0xc2, 0xa5, 0xf3, 0x06, 0x00, +0x96, 0x01, 0xaa, 0x03, 0xa0, 0x02, 0x04, 0x07, 0x10, 0xe4, 0x01, 0x06, 0x40, 0x10, 0x08, 0x06, 0x10, 0x10, 0x04, 0x06, +0x10, 0x10, 0x04, 0x06, 0x10, 0x37, 0x02, 0x7c, 0x00, 0x05, 0x00, 0x23, 0x00, 0x00, 0x07, 0x01, 0x10, 0x01, 0x05, 0x00, +0x23, 0x40, 0x00, 0x07, 0x01, 0x10, 0x01, 0x05, 0x00, 0x23, 0x40, 0x00, 0x07, 0x01, 0x10, 0x01, 0x05, 0x00, 0x23, 0x40, +0x00, 0x07, 0x01, 0x10, 0x01, 0x05, 0x00, 0x23, 0x40, 0x00, 0x07, 0x01, 0x10, 0x01, 0x05, 0x00, 0x23, 0x80, 0x01, 0x00, +0x07, 0x01, 0x10, 0x01, 0x05, 0x00, 0x23, 0x40, 0x00, 0x07, 0x01, 0x10, 0x01, 0x23, 0x40, 0x01, 0x23, 0x10, 0x01, 0x23, +0x08, 0x01, 0x23, 0x04, 0x01, 0x23, 0x04, 0x01, 0x23, 0x10, 0x01, 0x23, 0x10, 0x01, 0x23, 0x08, 0x01, 0x23, 0x08, 0x01, +0x23, 0x04, 0x01, 0x23, 0x04, 0x01, 0x23, 0x04, 0x01, 0x23, 0x04, 0x01, 0x23, 0x04, 0x01, 0x23, 0x04, 0x01, 0x23, 0x04, +0x01, 0x00, 0x00, 0x23, 0x04, 0x01, 0x00, 0x00, 0x23, 0x04, 0x01, 0x00, 0x00, 0x23, 0x04, 0x01, 0x00, 0x00, 0x23, 0x04, +0x01, 0x00, 0x00, 0x23, 0x04, 0x01, 0x00, 0x00, 0x23, 0x10, 0x01, 0x00, 0x00, 0x23, 0x0c, 0x01, 0x00, 0x00, 0x23, 0x04, +0x01, 0x23, 0x08, 0x01, 0x23, 0x04, 0x01, 0x00, 0x00, 0x23, 0x04, 0x01, 0x23, 0x90, 0x01, 0x01, 0x00, 0x00, 0x23, 0x0c, +0x01, 0x23, 0x04, 0x01, 0x23, 0x10, 0x01, 0x00, 0x00, 0x23, 0x10, 0x01, 0x00, 0x00, 0x23, 0x08, 0x01, 0x00, 0x00, 0x23, +0x04, 0x01, 0x23, 0x04, 0x01, 0x00, 0x00, 0x23, 0x10, 0x01, 0x00, 0x00, 0x23, 0x04, 0x01, 0x00, 0x00, 0x23, 0x04, 0x01, +0x00, 0x00, 0x23, 0x04, 0x01, 0x00, 0x00, 0x23, 0x04, 0x01, 0x00, 0x00, 0x23, 0x04, 0x01, 0x00, 0x00, 0x23, 0x04, 0x01, +0x00, 0x00, 0x23, 0x04, 0x01, 0x23, 0x04, 0x01, 0x23, 0x0c, 0x01, 0x23, 0x04, 0x01, 0x23, 0x04, 0x01, 0x23, 0x04, 0x01, +0x23, 0x04, 0x01, 0x23, 0x04, 0x01, 0x23, 0x0c, 0x01, 0x23, 0x04, 0x01, 0x23, 0x04, 0x01, 0x23, 0x04, 0x01, 0x23, 0x04, +0x01, 0x05, 0x00, 0x23, 0x04, 0x00, 0x07, 0x01, 0x10, 0x01, 0x05, 0x00, 0x23, 0x30, 0x00, 0x07, 0x01, 0x10, 0x01, 0x05, +0x00, 0x23, 0x40, 0x00, 0x07, 0x01, 0x10, 0x01, 0x00, 0x00, 0x23, 0x40, 0x01, 0x00, 0x00, 0x23, 0x04, 0x01, 0x00, 0x00, +0x23, 0x04, 0x01, 0x00, 0x00, 0x23, 0x04, 0x01, 0x23, 0x04, 0x01, 0x23, 0x40, 0x01, 0x00, 0x00, 0x23, 0x04, 0x01, 0x00, +0x00, 0x23, 0x04, 0x01, 0x00, 0x00, 0x23, 0x04, 0x01, 0x00, 0x00, 0x23, 0x04, 0x00, 0x00, 0x02, 0x10, 0x04, 0x22, 0x00, +0x10, 0x00, 0x21, 0x00, 0x10, 0x32, 0x1e, 0x04, 0x00, 0x8c, 0x02, 0x00, 0x00, 0x04, 0x00, 0x00, 0x28, 0x00, 0x10, 0x00, +0x22, 0x01, 0x10, 0x00, 0x21, 0x06, 0x00, 0x02, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x02, 0x00, 0x37, 0x98, 0x01, 0x05, 0x00, +0x05, 0x00, 0x23, 0x00, 0x00, 0x07, 0x01, 0x10, 0x01, 0x00, 0x00, 0x23, 0x40, 0x00, 0x00, 0x02, 0x10, 0x04, 0x22, 0x00, +0x10, 0x00, 0x21, 0x08, 0x10, 0x46, 0x1e, 0x00, 0x00, 0xdc, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, +0x00, 0x00, 0x02, 0x00, 0x93, 0x02, 0x02, 0xa1, 0x04, 0x02, 0x02, 0xa6, 0x02, 0x0a, 0x20, 0xb7, 0x02, 0x02, 0x08, 0x03, +0x94, 0x02, 0x0c, 0xb7, 0x02, 0x08, 0x08, 0x04, 0xfe, 0x02, 0x02, 0x13, 0x13, 0x08, 0x08, 0x08, 0x09, 0x3e, 0x18, 0x07, +0x13, 0x3e, 0x02, 0x07, 0x09, 0xc1, 0x04, 0x02, 0x13, 0x20, 0x21, 0xb8, 0x02, 0x22, 0x13, 0x04, 0xb8, 0x02, 0x02, 0x09, +0x03, 0xb5, 0x02, 0x02, 0x20, 0x01, 0xb5, 0x02, 0x02, 0x20, 0x00, 0xbb, 0x04, 0x35, 0x14, 0x03, 0x00, 0x00, 0x00, 0x3e, +0x24, 0x02, 0x33, 0xbb, 0x04, 0x35, 0x0c, 0x01, 0x00, 0x00, 0x00, 0x3e, 0x02, 0x02, 0x34, 0x3e, 0x2a, 0x02, 0x08, 0xbb, +0x04, 0x36, 0x06, 0x02, 0x00, 0x00, 0x00, 0xbc, 0x02, 0x02, 0x33, 0x71, 0xb7, 0x02, 0x02, 0x08, 0x02, 0xb7, 0x02, 0x02, +0x36, 0x03, 0xbb, 0x04, 0x36, 0x02, 0x09, 0x00, 0x00, 0x00, 0xbc, 0x02, 0x02, 0x09, 0x75, 0xbb, 0x04, 0x36, 0x02, 0x04, +0x00, 0x00, 0x00, 0xbc, 0x02, 0x02, 0x13, 0x77, 0xbb, 0x04, 0x36, 0x02, 0x30, 0x00, 0x00, 0x00, 0xbc, 0x02, 0x02, 0x13, +0x79, 0xce, 0x83, 0x80, 0x02, 0x02, 0x33, 0x33, 0x33, 0x33, 0x72, 0x33, 0x33, 0x13, 0x73, 0x08, 0x08, 0x13, 0x13, 0x73, +0x73, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x13, 0x74, 0x35, 0x73, 0x08, 0x08, 0x76, +0x09, 0x08, 0x13, 0x13, 0x73, 0x35, 0x08, 0x13, 0x35, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x36, 0x09, 0x08, 0x08, 0x36, +0x08, 0x08, 0x09, 0x08, 0x08, 0x08, 0x08, 0x08, 0x34, 0x33, 0x33, 0x08, 0x08, 0x08, 0x08, 0x78, 0x35, 0x08, 0x08, 0x08, +0x7a, 0x3e, 0x02, 0x02, 0x7b, 0x39, 0x7c, 0x02, 0x02, 0x3e, 0x02, 0x02, 0x13, 0xbb, 0x04, 0x36, 0x16, 0x03, 0x00, 0x00, +0x00, 0xbb, 0x04, 0x08, 0x06, 0x00, 0x00, 0x00, 0x00, 0xbb, 0x04, 0x08, 0x08, 0x00, 0x00, 0x80, 0x3f, 0x3e, 0x0a, 0x01, +0x13, 0x39, 0x95, 0x01, 0x02, 0x01, 0x3e, 0x44, 0x07, 0x08, 0xbb, 0x04, 0x35, 0x0a, 0x37, 0x00, 0x00, 0x00, 0xbb, 0x04, +0x35, 0x12, 0x32, 0x00, 0x00, 0x00, 0x3e, 0x02, 0x02, 0x09, 0xbb, 0x04, 0x35, 0x08, 0x36, 0x00, 0x00, 0x00, 0xbb, 0x04, +0x36, 0x10, 0x01, 0x00, 0x00, 0x00, 0xbb, 0x04, 0x08, 0x0c, 0x0a, 0xd7, 0xa3, 0x3a, 0xbb, 0x04, 0x36, 0x0c, 0x00, 0x00, +0x00, 0x00, 0xbb, 0x04, 0x35, 0x1e, 0x33, 0x00, 0x00, 0x00, 0xbb, 0x04, 0x35, 0x1a, 0x34, 0x00, 0x00, 0x00, 0xbb, 0x04, +0x35, 0x0a, 0x38, 0x00, 0x00, 0x00, 0xbb, 0x04, 0x35, 0x06, 0x39, 0x00, 0x00, 0x00, 0xbb, 0x04, 0x35, 0x10, 0x3c, 0x00, +0x00, 0x00, 0xbb, 0x04, 0x35, 0x08, 0x3d, 0x00, 0x00, 0x00, 0xbb, 0x04, 0x35, 0x0c, 0x35, 0x00, 0x00, 0x00, 0x3e, 0x02, +0x02, 0x36, 0xbb, 0x04, 0x35, 0x1c, 0x06, 0x00, 0x00, 0x00, 0x89, 0x03, 0x16, 0x08, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, +0xab, 0x02, 0x02, 0xaf, 0x02, 0x3e, 0x02, 0x00, 0xb0, 0x02, 0x39, 0xb1, 0x02, 0x02, 0x00, 0xbb, 0x04, 0x35, 0x14, 0x1f, +0x00, 0x00, 0x00, 0xbb, 0x04, 0x35, 0x0e, 0x3b, 0x00, 0x00, 0x00, 0xbb, 0x04, 0x35, 0x12, 0x3a, 0x00, 0x00, 0x00, 0xbb, +0x04, 0x35, 0x16, 0x24, 0x00, 0x00, 0x00, 0xbb, 0x04, 0x35, 0x14, 0x22, 0x00, 0x00, 0x00, 0xbe, 0x02, 0x48, 0x33, 0x09, +0x3e, 0x02, 0x02, 0x85, 0x03, 0x39, 0x86, 0x03, 0x02, 0x02, 0x3e, 0x44, 0x03, 0x13, 0x39, 0xa9, 0x03, 0x02, 0x03, 0xbb, +0x04, 0x35, 0x0e, 0x3e, 0x00, 0x00, 0x00, 0xad, 0x06, 0x13, 0x9e, 0x02, 0xad, 0x06, 0x08, 0x02, 0xad, 0x06, 0x09, 0x02, +0xc6, 0x06, 0x02, 0xfb, 0x08, 0x00, 0x03, 0x18, 0x02, 0x39, 0x20, 0xe2, 0x06, 0x07, 0x39, 0x21, 0x04, 0x07, 0x01, 0x13, +0x74, 0xb8, 0x05, 0x3d, 0x09, 0x02, 0x02, 0x02, 0x18, 0x13, 0xc7, 0x01, 0x48, 0x87, 0x03, 0x58, 0x01, 0x09, 0x02, 0x02, +0xc1, 0x0a, 0x08, 0x02, 0x02, 0x00, 0xc1, 0x0a, 0x08, 0x02, 0x04, 0x01, 0xc1, 0x0a, 0x08, 0x02, 0x06, 0x02, 0xe0, 0x0a, +0x13, 0x02, 0x06, 0x04, 0x02, 0x98, 0x06, 0x80, 0x0b, 0x14, 0x52, 0x52, 0x0a, 0x08, 0x08, 0x08, 0x06, 0xc1, 0x0a, 0x13, +0x43, 0x43, 0x00, 0x22, 0xf2, 0x01, 0x00, 0x23, 0x7e, 0x16, 0x7d, 0x58, 0x40, 0x01, 0x13, 0x02, 0x02, 0x3d, 0x09, 0x02, +0x02, 0x02, 0x18, 0xc3, 0x10, 0x09, 0xff, 0x01, 0x85, 0x01, 0xff, 0x01, 0x13, 0x59, 0x04, 0x7d, 0xb1, 0x03, 0x01, 0x34, +0x02, 0x02, 0xc1, 0x12, 0x09, 0x04, 0x04, 0x0a, 0x01, 0x13, 0x04, 0x1a, 0x22, 0x02, 0x00, 0x22, 0x01, 0x04, 0xd9, 0x06, +0x13, 0x06, 0xaa, 0x06, 0x08, 0x04, 0x22, 0x20, 0x00, 0x8d, 0x1e, 0x88, 0x06, 0xc6, 0x06, 0x13, 0xa9, 0x06, 0x00, 0x22, +0xa7, 0x06, 0x20, 0x03, 0xa7, 0x06, 0x21, 0x02, 0x18, 0x04, 0x01, 0x09, 0xa8, 0x02, 0xac, 0x02, 0x5c, 0x08, 0x02, 0x01, +0x42, 0xba, 0x01, 0x13, 0x6e, 0x06, 0x7d, 0xbd, 0x01, 0x01, 0x08, 0x02, 0x02, 0xca, 0x16, 0x0f, 0x02, 0x0a, 0x02, 0xa7, +0x1e, 0x03, 0x00, 0xba, 0x1e, 0x00, 0x01, 0x03, 0x18, 0x02, 0x01, 0x13, 0x04, 0xc0, 0x02, 0x9e, 0x1e, 0xc3, 0x00, 0x00, +0x00, 0x18, 0x01, 0x13, 0xc7, 0x01, 0x0c, 0x7d, 0xc6, 0x01, 0x01, 0x09, 0x02, 0x02, 0x13, 0x6e, 0x06, 0x7d, 0xcb, 0x01, +0x01, 0x08, 0x02, 0x02, 0xc1, 0x0a, 0x08, 0x06, 0x0e, 0x02, 0x13, 0xb8, 0x01, 0x08, 0x24, 0xd3, 0x01, 0x01, 0x08, 0x02, +0x02, 0x4a, 0x08, 0x02, 0x12, 0x02, 0x5c, 0x08, 0x04, 0x01, 0x04, 0xd6, 0x01, 0xca, 0x16, 0x0f, 0x04, 0x04, 0x02, 0xa7, +0x1e, 0x03, 0x00, 0xba, 0x1e, 0x00, 0x01, 0x03, 0x18, 0x02, 0xc1, 0x0a, 0x08, 0x0c, 0x30, 0x00, 0xc1, 0x0a, 0x08, 0x04, +0x34, 0x01, 0xc3, 0x10, 0x08, 0x04, 0x04, 0x1e, 0x5c, 0x08, 0x02, 0x01, 0x1b, 0xe5, 0x01, 0x4a, 0x08, 0x02, 0x0c, 0x02, +0xc3, 0x10, 0x08, 0x02, 0x30, 0x02, 0xc8, 0x10, 0x08, 0x04, 0x04, 0x28, 0x99, 0x1e, 0x1c, 0x18, 0x1b, 0xe5, 0x1e, 0x08, +0xe0, 0x05, 0xd0, 0x00, 0x00, 0x00, 0xc2, 0x00, 0x00, 0x00, 0xea, 0x00, 0x00, 0x00, 0xdb, 0x00, 0x00, 0x00, 0x13, 0x6e, +0xb9, 0x05, 0x7d, 0xee, 0x01, 0x01, 0x08, 0x02, 0x02, 0xc3, 0x10, 0x08, 0x02, 0x6c, 0x02, 0x6c, 0x08, 0x02, 0x01, 0x28, +0xf1, 0x01, 0x8c, 0x01, 0x4a, 0x08, 0x02, 0xb1, 0x05, 0x02, 0x3f, 0x08, 0x06, 0x06, 0x5c, 0x08, 0x02, 0x01, 0x1b, 0xf6, +0x01, 0xc3, 0x10, 0x08, 0x06, 0xd4, 0x01, 0x06, 0x13, 0x6e, 0x04, 0x7d, 0xfb, 0x01, 0x01, 0x08, 0x02, 0x02, 0x6c, 0x08, +0x02, 0x01, 0x25, 0xfa, 0x01, 0xfd, 0x01, 0x13, 0xc7, 0x01, 0x06, 0x7d, 0x80, 0x02, 0x01, 0x09, 0x02, 0x02, 0x13, 0x6e, +0x04, 0x7d, 0x83, 0x02, 0x01, 0x08, 0x02, 0x02, 0xca, 0x16, 0x0f, 0x02, 0x02, 0xf4, 0x01, 0xa7, 0x1e, 0x03, 0x00, 0xba, +0x1e, 0x00, 0x01, 0x03, 0x18, 0x02, 0x13, 0x6e, 0x0a, 0x7d, 0x8b, 0x02, 0x01, 0x08, 0x02, 0x02, 0x4a, 0x08, 0x02, 0xa6, +0x01, 0x02, 0x13, 0x6e, 0x04, 0x7d, 0x8f, 0x02, 0x01, 0x08, 0x02, 0x02, 0xc3, 0x10, 0x08, 0x02, 0x08, 0x02, 0x13, 0x96, +0x02, 0x0a, 0x7d, 0x95, 0x02, 0x01, 0x36, 0x02, 0x02, 0x5c, 0x73, 0x02, 0x01, 0x3e, 0x98, 0x02, 0xc1, 0x0a, 0x08, 0x06, +0x06, 0x01, 0xc1, 0x0a, 0x08, 0x04, 0x0a, 0x00, 0x7c, 0x08, 0x04, 0x01, 0x2b, 0x92, 0x02, 0x8c, 0x01, 0x90, 0x01, 0x7c, +0x08, 0x02, 0x01, 0x2e, 0x9c, 0x02, 0x9e, 0x02, 0xa0, 0x02, 0x13, 0x52, 0x08, 0x7d, 0xa4, 0x02, 0x01, 0x33, 0x02, 0x02, +0xc1, 0x0a, 0x13, 0x02, 0x02, 0x00, 0x3d, 0x09, 0x02, 0x02, 0x02, 0x18, 0xc1, 0x0a, 0x13, 0x02, 0x06, 0x01, 0x3d, 0x09, +0x02, 0x02, 0x02, 0x18, 0xc1, 0x0a, 0x13, 0x02, 0x0a, 0x02, 0x3d, 0x09, 0x02, 0x02, 0x02, 0x18, 0xd0, 0x0a, 0x34, 0x02, +0x0a, 0x06, 0x02, 0xb4, 0x0a, 0x34, 0x02, 0x2d, 0x01, 0x00, 0x00, 0x01, 0xb0, 0x02, 0x0a, 0x02, 0x01, 0x09, 0x04, 0xa2, +0x04, 0xc1, 0x12, 0x09, 0x02, 0x10, 0x02, 0xe8, 0x0a, 0x13, 0x04, 0x0a, 0x04, 0x02, 0xa1, 0x02, 0x3d, 0x09, 0x02, 0x02, +0x02, 0x18, 0x4a, 0x09, 0x04, 0x72, 0x04, 0x99, 0x1e, 0x66, 0x18, 0x65, 0xe5, 0x1e, 0x09, 0x8a, 0x05, 0x02, 0x01, 0x00, +0x00, 0xdc, 0x00, 0x00, 0x00, 0x3b, 0x01, 0x00, 0x00, 0x07, 0x01, 0x00, 0x00, 0x13, 0x6e, 0x9f, 0x04, 0x7d, 0xbc, 0x02, +0x01, 0x08, 0x02, 0x02, 0x4a, 0x08, 0x04, 0x04, 0x84, 0x01, 0xce, 0x10, 0x09, 0x04, 0x95, 0x04, 0x04, 0x13, 0x6e, 0x04, +0x7d, 0xc3, 0x02, 0x01, 0x08, 0x02, 0x02, 0xca, 0x16, 0x0f, 0x02, 0x02, 0xf4, 0x02, 0xa7, 0x1e, 0x03, 0x00, 0xba, 0x1e, +0x00, 0x01, 0x03, 0x18, 0x02, 0x13, 0x6e, 0x0c, 0x7d, 0xcc, 0x02, 0x01, 0x08, 0x02, 0x02, 0xc3, 0x10, 0x08, 0x02, 0xa8, +0x02, 0x02, 0x6c, 0x08, 0x02, 0x01, 0x28, 0xcf, 0x02, 0x8c, 0x01, 0x4a, 0x08, 0x02, 0xf5, 0x03, 0x02, 0x3f, 0x08, 0x06, +0x06, 0x5c, 0x08, 0x02, 0x01, 0x1b, 0xd4, 0x02, 0x13, 0x7e, 0x06, 0x7d, 0xd7, 0x02, 0x01, 0x13, 0x02, 0x02, 0x3d, 0x09, +0x02, 0x02, 0x02, 0x18, 0x23, 0x6e, 0x02, 0x7d, 0xd7, 0x02, 0x89, 0x01, 0x01, 0x08, 0x02, 0x02, 0xce, 0x10, 0x09, 0x02, +0x06, 0x02, 0x01, 0x09, 0x04, 0xf6, 0x04, 0x5c, 0x09, 0x02, 0x01, 0x45, 0xdf, 0x02, 0x13, 0xc7, 0x01, 0x04, 0x7d, 0xe1, +0x02, 0x01, 0x09, 0x02, 0x02, 0xc4, 0x12, 0x08, 0x02, 0x08, 0x02, 0x6c, 0x08, 0x02, 0x01, 0x28, 0xe4, 0x02, 0x8c, 0x01, +0x6c, 0x08, 0x0a, 0x01, 0x1a, 0xe5, 0x02, 0xc5, 0x02, 0xc3, 0x10, 0x08, 0x08, 0xbc, 0x03, 0x32, 0x4a, 0x08, 0x02, 0x0a, +0x02, 0xce, 0x10, 0x09, 0x02, 0x26, 0x02, 0x4b, 0x09, 0x04, 0x60, 0x04, 0x99, 0x1e, 0x54, 0x18, 0x53, 0xe5, 0x1e, 0x09, +0x8e, 0x04, 0x42, 0x01, 0x00, 0x00, 0x08, 0x01, 0x00, 0x00, 0x72, 0x01, 0x00, 0x00, 0x47, 0x01, 0x00, 0x00, 0x01, 0x13, +0xb7, 0x03, 0xa0, 0x05, 0x3d, 0x09, 0x02, 0x02, 0x02, 0x18, 0xc3, 0x10, 0x08, 0x04, 0xcc, 0x03, 0xf0, 0x01, 0xce, 0x10, +0x09, 0x02, 0x06, 0x02, 0x4b, 0x09, 0x04, 0x04, 0xab, 0x03, 0x13, 0xb8, 0x01, 0x02, 0x23, 0xdf, 0x01, 0xc1, 0x0a, 0x08, +0x02, 0x04, 0x00, 0x22, 0x02, 0x00, 0x13, 0xb8, 0x01, 0x02, 0x23, 0xd3, 0x01, 0xc1, 0x0a, 0x08, 0x02, 0x08, 0x01, 0x22, +0x02, 0x00, 0x13, 0xb8, 0x01, 0x02, 0x23, 0x71, 0xc1, 0x0a, 0x08, 0x02, 0x0c, 0x02, 0x22, 0x02, 0x00, 0x01, 0x13, 0x02, +0xba, 0x05, 0x9e, 0x1e, 0x80, 0x01, 0x00, 0x00, 0x88, 0x06, 0x00, 0x00, 0xb5, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x4c, 0x4f, 0x4d, 0x53, 0x00, 0x03, 0x01, 0x00, 0x0a, 0x00, 0x08, 0x00, 0xb4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x48, 0x05, 0x00, 0x00, 0x91, 0x02, 0x01, 0xd1, 0x10, 0x02, 0x47, 0x4c, 0x53, 0x4c, 0x2e, 0x73, 0x74, 0x64, 0x2e, 0x34, +0x35, 0x30, 0x00, 0x00, 0x00, 0x00, 0xa0, 0x04, 0x00, 0x01, 0xef, 0x0e, 0x04, 0x04, 0xed, 0xc2, 0xa5, 0xf3, 0x06, 0x00, +0x5d, 0x64, 0xa0, 0x02, 0x04, 0x07, 0x10, 0x20, 0x06, 0x10, 0x37, 0x02, 0x0b, 0x00, 0x05, 0x00, 0x23, 0x00, 0x00, 0x07, +0x01, 0x10, 0x01, 0x05, 0x00, 0x23, 0x40, 0x00, 0x07, 0x01, 0x10, 0x01, 0x23, 0x30, 0x01, 0x23, 0x04, 0x01, 0x23, 0x04, +0x01, 0x23, 0x04, 0x01, 0x23, 0x04, 0x10, 0x04, 0x06, 0x80, 0x02, 0x47, 0x02, 0x01, 0x00, 0x23, 0x00, 0x00, 0x00, 0x02, +0x10, 0x04, 0x22, 0x00, 0x10, 0x00, 0x21, 0x01, 0x10, 0x8e, 0x01, 0x1e, 0x00, 0x10, 0x0e, 0x1e, 0x07, 0x93, 0x02, 0x02, +0xa1, 0x04, 0x02, 0x02, 0xa6, 0x02, 0x0a, 0x20, 0xb7, 0x02, 0x02, 0x08, 0x04, 0xb8, 0x02, 0x02, 0x09, 0x04, 0xb7, 0x02, +0x02, 0x08, 0x03, 0xb8, 0x02, 0x02, 0x0b, 0x03, 0xb5, 0x02, 0x02, 0x20, 0x01, 0xb5, 0x02, 0x02, 0x20, 0x00, 0xbb, 0x04, +0x0e, 0x02, 0x08, 0x00, 0x00, 0x00, 0xbc, 0x02, 0x02, 0x09, 0x0f, 0x8e, 0x03, 0x02, 0x0a, 0x0c, 0x0d, 0x0d, 0x0d, 0x08, +0x10, 0xbb, 0x04, 0x0e, 0x02, 0x40, 0x00, 0x00, 0x00, 0xbc, 0x02, 0x02, 0x11, 0x12, 0xae, 0x02, 0x02, 0x13, 0x3e, 0x02, +0x02, 0x14, 0x39, 0x15, 0x02, 0x02, 0xbb, 0x04, 0x0d, 0x02, 0x00, 0x00, 0x00, 0x00, 0xbb, 0x04, 0x0d, 0x02, 0x03, 0x00, +0x00, 0x00, 0x3e, 0x02, 0x02, 0x0d, 0xbb, 0x04, 0x0d, 0x06, 0x00, 0x08, 0x00, 0x00, 0x94, 0x02, 0x04, 0xbb, 0x04, 0x0d, +0x44, 0x04, 0x00, 0x00, 0x00, 0xbb, 0x04, 0x0e, 0x16, 0x02, 0x00, 0x00, 0x00, 0xb7, 0x02, 0x04, 0x08, 0x02, 0x3e, 0x1e, +0x03, 0x4d, 0x39, 0x5c, 0x02, 0x03, 0xbb, 0x04, 0x0e, 0x06, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x02, 0x03, 0x08, 0x3e, 0x04, +0x01, 0x09, 0x39, 0x63, 0x02, 0x01, 0x3e, 0x02, 0x01, 0x08, 0xbb, 0x04, 0x0e, 0x06, 0x03, 0x00, 0x00, 0x00, 0xbb, 0x04, +0x0e, 0x08, 0x01, 0x00, 0x00, 0x00, 0xad, 0x06, 0x0d, 0x8a, 0x01, 0xc6, 0x06, 0x02, 0xd9, 0x02, 0x00, 0x03, 0x18, 0x02, +0x33, 0x19, 0xa4, 0x02, 0x16, 0x17, 0x17, 0x18, 0x01, 0x0d, 0x02, 0x02, 0xc7, 0x18, 0x0d, 0x02, 0x02, 0xfa, 0x01, 0xcb, +0x14, 0x1e, 0x02, 0x02, 0x86, 0x02, 0xd9, 0x14, 0x0d, 0x32, 0x32, 0x04, 0xb8, 0x02, 0x33, 0x19, 0x0d, 0x16, 0x17, 0xb3, +0x01, 0x40, 0x01, 0x0d, 0x02, 0x02, 0xbc, 0x0e, 0x08, 0x9b, 0x01, 0x9b, 0x01, 0x13, 0x61, 0x06, 0x5d, 0x60, 0x22, 0x00, +0x06, 0x13, 0x65, 0x08, 0x64, 0x4b, 0x01, 0x08, 0x02, 0x02, 0x13, 0x65, 0x04, 0x64, 0x68, 0x01, 0x08, 0x02, 0x02, 0xc8, +0x10, 0x08, 0x02, 0x08, 0x02, 0x13, 0x61, 0x04, 0x5d, 0x6c, 0x22, 0x00, 0x04, 0x8d, 0x1e, 0x88, 0x06, 0x00, 0x00, 0x00, +0x83, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4c, 0x4f, 0x4d, 0x53, 0x00, 0x03, 0x01, 0x00, 0x0a, 0x00, 0x08, 0x00, +0xc4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x91, 0x02, 0x01, 0xd1, 0x10, 0x02, 0x47, 0x4c, +0x53, 0x4c, 0x2e, 0x73, 0x74, 0x64, 0x2e, 0x34, 0x35, 0x30, 0x00, 0x00, 0x00, 0x00, 0xa0, 0x04, 0x00, 0x01, 0xdf, 0x0e, +0x04, 0x04, 0xed, 0xc2, 0xa5, 0xf3, 0x06, 0x00, 0xc3, 0x01, 0xa0, 0x02, 0x04, 0x07, 0x10, 0x86, 0x03, 0x1e, 0x00, 0x93, +0x02, 0x02, 0xa1, 0x04, 0x02, 0x02, 0xa6, 0x02, 0x12, 0x20, 0xb7, 0x02, 0x02, 0x0c, 0x04, 0xbb, 0x04, 0x0c, 0xc6, 0x01, +0x00, 0x00, 0x00, 0x00, 0xec, 0x04, 0x0d, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x3e, 0x28, 0x03, 0x0d, 0x39, 0xc2, 0x01, 0x02, +0x03, 0xc6, 0x06, 0x02, 0xfd, 0x02, 0x00, 0x03, 0x18, 0x02, 0x22, 0xfb, 0x02, 0xd1, 0x02, 0x8d, 0x1e, 0x88, 0x06, 0x00, +0x00, 0x00, 0x00, 0x00, 0xe5, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4c, 0x4f, 0x4d, 0x53, 0x00, 0x03, 0x01, 0x00, +0x0a, 0x00, 0x08, 0x00, 0x56, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x98, 0x13, 0x00, 0x00, 0x91, 0x02, 0x01, 0x91, +0x02, 0x20, 0xd1, 0x10, 0x02, 0x47, 0x4c, 0x53, 0x4c, 0x2e, 0x73, 0x74, 0x64, 0x2e, 0x34, 0x35, 0x30, 0x00, 0x00, 0x00, +0x00, 0xa0, 0x04, 0x00, 0x01, 0xaf, 0x0f, 0x00, 0x04, 0xed, 0xc2, 0xa5, 0xf3, 0x06, 0x00, 0x6f, 0x9f, 0x01, 0xa1, 0x01, +0xb0, 0x01, 0xc4, 0x01, 0xdc, 0x01, 0x10, 0xb8, 0x01, 0x06, 0x40, 0x10, 0x08, 0x06, 0x10, 0x10, 0x04, 0x06, 0x10, 0x10, +0x04, 0x06, 0x10, 0x37, 0x02, 0x7c, 0x00, 0x05, 0x00, 0x23, 0x00, 0x00, 0x07, 0x01, 0x10, 0x01, 0x05, 0x00, 0x23, 0x40, +0x00, 0x07, 0x01, 0x10, 0x01, 0x05, 0x00, 0x23, 0x40, 0x00, 0x07, 0x01, 0x10, 0x01, 0x05, 0x00, 0x23, 0x40, 0x00, 0x07, +0x01, 0x10, 0x01, 0x05, 0x00, 0x23, 0x40, 0x00, 0x07, 0x01, 0x10, 0x01, 0x05, 0x00, 0x23, 0x80, 0x01, 0x00, 0x07, 0x01, +0x10, 0x01, 0x05, 0x00, 0x23, 0x40, 0x00, 0x07, 0x01, 0x10, 0x01, 0x23, 0x40, 0x01, 0x23, 0x10, 0x01, 0x23, 0x08, 0x01, +0x23, 0x04, 0x01, 0x23, 0x04, 0x01, 0x23, 0x10, 0x01, 0x23, 0x10, 0x01, 0x23, 0x08, 0x01, 0x23, 0x08, 0x01, 0x23, 0x04, +0x01, 0x23, 0x04, 0x01, 0x23, 0x04, 0x01, 0x23, 0x04, 0x01, 0x23, 0x04, 0x01, 0x23, 0x04, 0x01, 0x23, 0x04, 0x01, 0x00, +0x00, 0x23, 0x04, 0x01, 0x00, 0x00, 0x23, 0x04, 0x01, 0x00, 0x00, 0x23, 0x04, 0x01, 0x00, 0x00, 0x23, 0x04, 0x01, 0x00, +0x00, 0x23, 0x04, 0x01, 0x00, 0x00, 0x23, 0x10, 0x01, 0x00, 0x00, 0x23, 0x0c, 0x01, 0x00, 0x00, 0x23, 0x04, 0x01, 0x23, +0x08, 0x01, 0x23, 0x04, 0x01, 0x00, 0x00, 0x23, 0x04, 0x01, 0x23, 0x90, 0x01, 0x01, 0x00, 0x00, 0x23, 0x0c, 0x01, 0x23, +0x04, 0x01, 0x23, 0x10, 0x01, 0x00, 0x00, 0x23, 0x10, 0x01, 0x00, 0x00, 0x23, 0x08, 0x01, 0x00, 0x00, 0x23, 0x04, 0x01, +0x23, 0x04, 0x01, 0x00, 0x00, 0x23, 0x10, 0x01, 0x00, 0x00, 0x23, 0x04, 0x01, 0x00, 0x00, 0x23, 0x04, 0x01, 0x00, 0x00, +0x23, 0x04, 0x01, 0x00, 0x00, 0x23, 0x04, 0x01, 0x00, 0x00, 0x23, 0x04, 0x01, 0x00, 0x00, 0x23, 0x04, 0x01, 0x00, 0x00, +0x23, 0x04, 0x01, 0x23, 0x04, 0x01, 0x23, 0x0c, 0x01, 0x23, 0x04, 0x01, 0x23, 0x04, 0x01, 0x23, 0x04, 0x01, 0x23, 0x04, +0x01, 0x23, 0x04, 0x01, 0x23, 0x0c, 0x01, 0x23, 0x04, 0x01, 0x23, 0x04, 0x01, 0x23, 0x04, 0x01, 0x23, 0x04, 0x01, 0x05, +0x00, 0x23, 0x04, 0x00, 0x07, 0x01, 0x10, 0x01, 0x05, 0x00, 0x23, 0x30, 0x00, 0x07, 0x01, 0x10, 0x01, 0x05, 0x00, 0x23, +0x40, 0x00, 0x07, 0x01, 0x10, 0x01, 0x00, 0x00, 0x23, 0x40, 0x01, 0x00, 0x00, 0x23, 0x04, 0x01, 0x00, 0x00, 0x23, 0x04, +0x01, 0x00, 0x00, 0x23, 0x04, 0x01, 0x23, 0x04, 0x01, 0x23, 0x40, 0x01, 0x00, 0x00, 0x23, 0x04, 0x01, 0x00, 0x00, 0x23, +0x04, 0x01, 0x00, 0x00, 0x23, 0x04, 0x01, 0x00, 0x00, 0x23, 0x04, 0x00, 0x00, 0x02, 0x10, 0x04, 0x22, 0x00, 0x10, 0x00, +0x21, 0x00, 0x10, 0x10, 0x1e, 0x00, 0x00, 0x60, 0x0e, 0x10, 0x00, 0x1e, 0x08, 0x10, 0x04, 0x0b, 0x2b, 0x10, 0x1e, 0x1e, +0x04, 0x10, 0x28, 0x1e, 0x07, 0x47, 0x2c, 0x04, 0x00, 0x0b, 0x01, 0x00, 0x01, 0x0b, 0x01, 0x01, 0x01, 0x0b, 0x01, 0x03, +0x01, 0x0b, 0x01, 0x04, 0x00, 0x00, 0x02, 0x93, 0x02, 0x02, 0xa1, 0x04, 0x02, 0x02, 0xa6, 0x02, 0x0a, 0x20, 0xb7, 0x02, +0x02, 0x08, 0x04, 0xb8, 0x02, 0x02, 0x09, 0x04, 0xae, 0x02, 0x12, 0x09, 0xb7, 0x02, 0x12, 0x08, 0x03, 0xb8, 0x02, 0x02, +0x1c, 0x03, 0xb5, 0x02, 0x02, 0x20, 0x01, 0xb5, 0x02, 0x02, 0x20, 0x00, 0xbb, 0x04, 0x1e, 0x12, 0x00, 0x00, 0x00, 0x00, +0x94, 0x02, 0x0e, 0x3e, 0x1a, 0x02, 0x0a, 0xbb, 0x04, 0x1e, 0x0c, 0x01, 0x00, 0x00, 0x00, 0xbb, 0x04, 0x1e, 0x0c, 0x02, +0x00, 0x00, 0x00, 0xbb, 0x04, 0x1e, 0x1e, 0x05, 0x00, 0x00, 0x00, 0xbb, 0x04, 0x1f, 0x08, 0x02, 0x00, 0x00, 0x00, 0xbc, +0x02, 0x02, 0x0a, 0x5b, 0xb7, 0x02, 0x02, 0x08, 0x02, 0xb7, 0x02, 0x02, 0x1f, 0x03, 0xbb, 0x04, 0x1f, 0x02, 0x09, 0x00, +0x00, 0x00, 0xbc, 0x02, 0x02, 0x1c, 0x5f, 0xbb, 0x04, 0x1f, 0x02, 0x04, 0x00, 0x00, 0x00, 0xbc, 0x02, 0x02, 0x09, 0x61, +0xbb, 0x04, 0x1f, 0x02, 0x30, 0x00, 0x00, 0x00, 0xbc, 0x02, 0x02, 0x09, 0x63, 0xce, 0x83, 0x80, 0x02, 0x02, 0x0a, 0x0a, +0x0a, 0x0a, 0x5c, 0x0a, 0x0a, 0x09, 0x5d, 0x08, 0x08, 0x09, 0x09, 0x5d, 0x5d, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, +0x08, 0x08, 0x08, 0x08, 0x08, 0x09, 0x5e, 0x1e, 0x5d, 0x08, 0x08, 0x60, 0x1c, 0x08, 0x09, 0x09, 0x5d, 0x1e, 0x08, 0x09, +0x1e, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x1f, 0x1c, 0x08, 0x08, 0x1f, 0x08, 0x08, 0x1c, 0x08, 0x08, 0x08, 0x08, 0x08, +0x1d, 0x0a, 0x0a, 0x08, 0x08, 0x08, 0x08, 0x62, 0x1e, 0x08, 0x08, 0x08, 0x64, 0x3e, 0x02, 0x02, 0x65, 0x39, 0x66, 0x02, +0x02, 0x3e, 0x0e, 0x01, 0x09, 0x39, 0x6e, 0x02, 0x01, 0xbb, 0x04, 0x08, 0x1a, 0x00, 0x00, 0x00, 0xbf, 0xbb, 0x04, 0x08, +0x04, 0x00, 0x00, 0x00, 0x3f, 0xbb, 0x04, 0x08, 0x16, 0x00, 0x00, 0x00, 0x20, 0xbb, 0x04, 0x08, 0x0c, 0x00, 0x00, 0x00, +0x00, 0xbb, 0x04, 0x08, 0x04, 0x00, 0x00, 0x00, 0xa0, 0xbb, 0x04, 0x08, 0x08, 0x00, 0x00, 0x80, 0x3f, 0x3e, 0x12, 0x03, +0x1e, 0x39, 0x9e, 0x01, 0x02, 0x03, 0x3e, 0x02, 0x01, 0x1e, 0x39, 0xa0, 0x01, 0x02, 0x01, 0x3e, 0x1c, 0x03, 0x09, 0x39, +0xaf, 0x01, 0x02, 0x03, 0xbb, 0x04, 0x1f, 0x08, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x02, 0x03, 0x08, 0xbb, 0x04, 0x1f, 0x06, +0x01, 0x00, 0x00, 0x00, 0x39, 0xaf, 0x01, 0x18, 0x03, 0xbb, 0x04, 0x08, 0x10, 0x00, 0x00, 0x00, 0x40, 0xbc, 0x02, 0x1a, +0x08, 0xb8, 0x01, 0xde, 0x02, 0x02, 0x09, 0x08, 0xd9, 0x01, 0xd9, 0x01, 0x3e, 0x02, 0x03, 0xda, 0x01, 0x39, 0xdb, 0x01, +0x02, 0x03, 0xc6, 0x06, 0x02, 0xaf, 0x03, 0x00, 0x03, 0x18, 0x02, 0x01, 0x1e, 0xba, 0x02, 0x02, 0x22, 0x06, 0x00, 0x01, +0x1e, 0x04, 0x0a, 0xc3, 0x18, 0x1e, 0x02, 0x02, 0xc6, 0x01, 0x13, 0x3c, 0x96, 0x02, 0x67, 0x57, 0x01, 0x0a, 0x02, 0x02, +0x01, 0x09, 0x08, 0x8c, 0x03, 0xc1, 0x0a, 0x08, 0x3b, 0x3b, 0x02, 0x4a, 0x08, 0x02, 0x02, 0xb8, 0x02, 0x4b, 0x08, 0x02, +0x02, 0xb6, 0x02, 0xd2, 0x0a, 0x09, 0x56, 0x56, 0x1e, 0x02, 0xc1, 0x12, 0x09, 0x4d, 0x27, 0x4d, 0xc1, 0x0a, 0x08, 0x04, +0x04, 0x03, 0x5c, 0x08, 0x02, 0x01, 0x04, 0x9f, 0x02, 0xc8, 0x16, 0x2f, 0x02, 0x02, 0xb0, 0x02, 0xa7, 0x1e, 0x0d, 0x00, +0xba, 0x1e, 0x00, 0x01, 0x0d, 0x18, 0x02, 0xc8, 0x16, 0x2f, 0x06, 0x0c, 0xac, 0x02, 0xd9, 0x14, 0x08, 0x02, 0x02, 0xaa, +0x02, 0xba, 0x02, 0xd2, 0x0a, 0x09, 0x44, 0x44, 0x56, 0x03, 0x99, 0x1e, 0x40, 0x18, 0x3f, 0xe5, 0x1e, 0x09, 0x5a, 0x1d, +0x01, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x48, 0x01, 0x00, 0x00, 0x22, 0x01, 0x00, 0x00, 0xc1, 0x0a, 0x08, 0x53, 0x53, +0x03, 0xc8, 0x10, 0x08, 0x02, 0xae, 0x02, 0x02, 0xce, 0x10, 0x09, 0x02, 0x4f, 0x02, 0xb0, 0x0a, 0x13, 0x28, 0x28, 0xc1, +0x0a, 0x09, 0x05, 0x05, 0x00, 0x3d, 0x1c, 0x95, 0x02, 0x95, 0x02, 0x95, 0x02, 0x18, 0x13, 0xb5, 0x01, 0x06, 0xb0, 0x01, +0xb4, 0x01, 0xc1, 0x0a, 0x08, 0x02, 0x08, 0x00, 0x22, 0x02, 0x00, 0x13, 0xb5, 0x01, 0x04, 0xb0, 0x01, 0xb8, 0x01, 0xc1, +0x0a, 0x08, 0x02, 0x0e, 0x01, 0x22, 0x02, 0x00, 0x13, 0xb5, 0x01, 0x02, 0xb0, 0x01, 0x5b, 0xc1, 0x0a, 0x08, 0x02, 0x12, +0x02, 0x22, 0x02, 0x00, 0x22, 0x0f, 0x8f, 0x02, 0x01, 0x1e, 0x16, 0x50, 0xcb, 0x10, 0x1e, 0x02, 0x02, 0x80, 0x02, 0xbf, +0x0c, 0x08, 0x06, 0x06, 0x4a, 0x08, 0x04, 0x04, 0x02, 0xc3, 0x10, 0x08, 0x02, 0x02, 0x72, 0xc1, 0x0a, 0x08, 0x04, 0xe7, +0x01, 0x00, 0x4a, 0x08, 0x02, 0x02, 0xa6, 0x01, 0xc1, 0x0a, 0x08, 0x04, 0xe1, 0x01, 0x03, 0x4a, 0x08, 0x02, 0x02, 0xac, +0x01, 0x4a, 0x08, 0x04, 0x04, 0x10, 0x4b, 0x08, 0x02, 0x0c, 0x02, 0xd2, 0x0a, 0x09, 0xf2, 0x01, 0xf2, 0x01, 0x18, 0x00, +0xc1, 0x0a, 0x08, 0xe3, 0x01, 0xe3, 0x01, 0x00, 0x4a, 0x08, 0x04, 0x04, 0x24, 0x23, 0xb5, 0x01, 0x02, 0xdc, 0x01, 0x48, +0x28, 0x22, 0x00, 0x02, 0xc1, 0x0a, 0x08, 0x04, 0xd9, 0x01, 0x01, 0x3f, 0x08, 0x02, 0x02, 0xd2, 0x0a, 0x09, 0xe0, 0x01, +0xe0, 0x01, 0x08, 0x01, 0x13, 0xaf, 0x01, 0xd9, 0x01, 0xdc, 0x01, 0x28, 0x22, 0x00, 0xd9, 0x01, 0x22, 0x90, 0x01, 0x84, +0x01, 0x8d, 0x1e, 0x88, 0x06, 0x52, 0x49, 0x50, 0x53, 0x5f, 0x54, 0x41, 0x4d, 0x86, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, +0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x10, 0x01, 0x02, 0x00, 0x00, 0x00, 0x01, 0x20, 0x01, 0x03, 0x00, 0x00, 0x00, +0x01, 0x30, 0x01, 0x04, 0x00, 0x00, 0x00, 0x01, 0x44, 0x01, 0x05, 0x00, 0x00, 0x00, 0x01, 0x80, 0x00, 0x06, 0x00, 0x00, +0x00, 0x01, 0x90, 0x00, 0x06, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0x08, 0x00, +0x00, 0x00, 0x02, 0x10, 0x00, 0x07, 0x00, 0x00, 0x00, 0x02, 0x10, 0x01, 0x09, 0x00, 0x00, 0x00, 0x02, 0x20, 0x01, 0x0a, +0x00, 0x00, 0x00, 0x02, 0x30, 0x01, 0x0b, 0x00, 0x00, 0x00, 0x02, 0x44, 0x01, 0x0c, 0x00, 0x00, 0x00, 0x02, 0x80, 0x00, +0x0d, 0x00, 0x00, 0x00, 0x02, 0x90, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x4c, 0x54, 0x45, 0x4d, 0x5f, 0x54, 0x41, 0x4d, 0x64, +0x08, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x86, 0x00, 0x00, 0x00, 0x01, 0x00, +0x01, 0x88, 0x01, 0x00, 0x00, 0x01, 0x10, 0x00, 0x86, 0x00, 0x00, 0x00, 0x01, 0x10, 0x01, 0xbe, 0x01, 0x00, 0x00, 0x01, +0x20, 0x01, 0xd8, 0x01, 0x00, 0x00, 0x01, 0x30, 0x01, 0x44, 0x03, 0x00, 0x00, 0x01, 0x44, 0x01, 0xa8, 0x03, 0x00, 0x00, +0x01, 0x80, 0x00, 0xd2, 0x03, 0x00, 0x00, 0x01, 0x90, 0x00, 0xd2, 0x03, 0x00, 0x00, 0x02, 0x00, 0x00, 0xe6, 0x04, 0x00, +0x00, 0x02, 0x00, 0x01, 0x88, 0x01, 0x00, 0x00, 0x02, 0x10, 0x00, 0xe6, 0x04, 0x00, 0x00, 0x02, 0x10, 0x01, 0xbe, 0x01, +0x00, 0x00, 0x02, 0x20, 0x01, 0xe8, 0x05, 0x00, 0x00, 0x02, 0x30, 0x01, 0x44, 0x03, 0x00, 0x00, 0x02, 0x44, 0x01, 0xa8, +0x03, 0x00, 0x00, 0x02, 0x80, 0x00, 0x50, 0x07, 0x00, 0x00, 0x02, 0x90, 0x00, 0x50, 0x07, 0x00, 0x00, 0x9a, 0x0c, 0x00, +0x00, 0x7d, 0x00, 0x00, 0x00, 0x5d, 0x01, 0x5e, 0x01, 0x5f, 0x01, 0x60, 0x01, 0x5f, 0x01, 0x61, 0x01, 0x02, 0x00, 0x62, +0x01, 0x63, 0x01, 0x64, 0x01, 0x65, 0x01, 0x66, 0x01, 0x67, 0x01, 0x68, 0x01, 0x69, 0x01, 0x6a, 0x01, 0x6b, 0x01, 0x6c, +0x01, 0x6d, 0x01, 0x6e, 0x01, 0x6f, 0x01, 0x70, 0x01, 0x71, 0x01, 0x72, 0x01, 0x73, 0x01, 0x74, 0x01, 0x75, 0x01, 0x76, +0x01, 0x77, 0x01, 0x78, 0x01, 0x79, 0x01, 0x7a, 0x01, 0x7b, 0x01, 0x7c, 0x01, 0x7d, 0x01, 0x7e, 0x01, 0x7f, 0x01, 0x80, +0x01, 0x81, 0x01, 0x82, 0x01, 0x83, 0x01, 0x84, 0x01, 0x85, 0x01, 0x86, 0x01, 0x87, 0x01, 0x88, 0x01, 0x89, 0x01, 0x8a, +0x01, 0x8b, 0x01, 0x8c, 0x01, 0x8d, 0x01, 0x8e, 0x01, 0x8f, 0x01, 0x90, 0x01, 0x91, 0x01, 0x92, 0x01, 0x93, 0x01, 0x94, +0x01, 0x95, 0x01, 0x96, 0x01, 0x97, 0x01, 0x98, 0x01, 0x99, 0x01, 0x9a, 0x01, 0x9b, 0x01, 0x9c, 0x01, 0x9d, 0x01, 0x9e, +0x01, 0x9f, 0x01, 0xa0, 0x01, 0xa1, 0x01, 0xa2, 0x01, 0xa3, 0x01, 0xa4, 0x01, 0xa5, 0x01, 0xa6, 0x01, 0xa7, 0x01, 0xa8, +0x01, 0xa9, 0x01, 0xaa, 0x01, 0xab, 0x01, 0xac, 0x01, 0x04, 0x00, 0x5f, 0x01, 0xad, 0x01, 0x02, 0x00, 0xae, 0x01, 0xaf, +0x01, 0xb0, 0x01, 0xb1, 0x01, 0x04, 0x00, 0x5f, 0x01, 0xb2, 0x01, 0x02, 0x00, 0xb3, 0x01, 0x04, 0x00, 0x5f, 0x01, 0xb4, +0x01, 0x02, 0x00, 0xb5, 0x01, 0xb6, 0x01, 0xb7, 0x01, 0xb8, 0x01, 0xb9, 0x01, 0xba, 0x01, 0xbb, 0x01, 0xbc, 0x01, 0xbd, +0x01, 0xbe, 0x01, 0xbf, 0x01, 0xc0, 0x01, 0xc1, 0x01, 0xc2, 0x01, 0xbd, 0x01, 0xc3, 0x01, 0xc1, 0x01, 0xc4, 0x01, 0xc5, +0x01, 0xc6, 0x01, 0xc7, 0x01, 0xc8, 0x01, 0xc9, 0x01, 0xca, 0x01, 0x5d, 0x00, 0x5f, 0x01, 0x92, 0x01, 0x00, 0x00, 0x17, +0x00, 0x00, 0x00, 0x5d, 0x01, 0x5e, 0x01, 0x5f, 0x01, 0x60, 0x01, 0x5f, 0x01, 0xcb, 0x01, 0x02, 0x00, 0xcc, 0x01, 0xcd, +0x01, 0x04, 0x00, 0x5f, 0x01, 0xad, 0x01, 0x02, 0x00, 0xce, 0x01, 0x04, 0x00, 0x5f, 0x01, 0xcf, 0x01, 0x02, 0x00, 0xb5, +0x01, 0xd0, 0x01, 0xca, 0x01, 0x5d, 0x00, 0x5f, 0x01, 0x64, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x5d, 0x01, 0x5e, +0x01, 0x5f, 0x01, 0x60, 0x01, 0x5f, 0x01, 0xd1, 0x01, 0x02, 0x00, 0x5d, 0x00, 0x5f, 0x01, 0x7e, 0x16, 0x00, 0x00, 0xb2, +0x00, 0x00, 0x00, 0x5d, 0x01, 0x5e, 0x01, 0x5f, 0x01, 0x60, 0x01, 0x5f, 0x01, 0x61, 0x01, 0x02, 0x00, 0x62, 0x01, 0x63, +0x01, 0x64, 0x01, 0x65, 0x01, 0x66, 0x01, 0x67, 0x01, 0x68, 0x01, 0x69, 0x01, 0x6a, 0x01, 0x6b, 0x01, 0x6c, 0x01, 0x6d, +0x01, 0x6e, 0x01, 0x6f, 0x01, 0x70, 0x01, 0x71, 0x01, 0x72, 0x01, 0x73, 0x01, 0x74, 0x01, 0x75, 0x01, 0x76, 0x01, 0x77, +0x01, 0x78, 0x01, 0x79, 0x01, 0x7a, 0x01, 0x7b, 0x01, 0x7c, 0x01, 0x7d, 0x01, 0x7e, 0x01, 0x7f, 0x01, 0x80, 0x01, 0x81, +0x01, 0x82, 0x01, 0x83, 0x01, 0x84, 0x01, 0x85, 0x01, 0x86, 0x01, 0x87, 0x01, 0x88, 0x01, 0x89, 0x01, 0x8a, 0x01, 0x8b, +0x01, 0x8c, 0x01, 0x8d, 0x01, 0x8e, 0x01, 0x8f, 0x01, 0x90, 0x01, 0x91, 0x01, 0x92, 0x01, 0x93, 0x01, 0x94, 0x01, 0x95, +0x01, 0x96, 0x01, 0x97, 0x01, 0x98, 0x01, 0x99, 0x01, 0x9a, 0x01, 0x9b, 0x01, 0x9c, 0x01, 0x9d, 0x01, 0x9e, 0x01, 0x9f, +0x01, 0xa0, 0x01, 0xa1, 0x01, 0xa2, 0x01, 0xa3, 0x01, 0xa4, 0x01, 0xa5, 0x01, 0xa6, 0x01, 0xa7, 0x01, 0xa8, 0x01, 0xa9, +0x01, 0xaa, 0x01, 0xab, 0x01, 0xac, 0x01, 0x04, 0x00, 0x5f, 0x01, 0xcb, 0x01, 0x02, 0x00, 0xcc, 0x01, 0xcd, 0x01, 0x04, +0x00, 0x5f, 0x01, 0xd2, 0x01, 0xd3, 0x01, 0xd4, 0x01, 0xd5, 0x01, 0xd6, 0x01, 0xd7, 0x01, 0xd8, 0x01, 0xd9, 0x01, 0xda, +0x01, 0xdb, 0x01, 0xdc, 0x01, 0xdd, 0x01, 0xde, 0x01, 0xdf, 0x01, 0xe0, 0x01, 0x04, 0x00, 0x5f, 0x01, 0xad, 0x01, 0x02, +0x00, 0xce, 0x01, 0x04, 0x00, 0x5f, 0x01, 0xb2, 0x01, 0x02, 0x00, 0xae, 0x01, 0x04, 0x00, 0x5f, 0x01, 0xe1, 0x01, 0x02, +0x00, 0xb5, 0x01, 0xe2, 0x01, 0xe3, 0x01, 0xe4, 0x01, 0xe5, 0x01, 0xe6, 0x01, 0xbd, 0x01, 0xe7, 0x01, 0xe8, 0x01, 0xe9, +0x01, 0xea, 0x01, 0xeb, 0x01, 0xec, 0x01, 0xed, 0x01, 0xee, 0x01, 0xef, 0x01, 0xf0, 0x01, 0xe9, 0x01, 0xf1, 0x01, 0xec, +0x01, 0xf2, 0x01, 0xe9, 0x01, 0xf3, 0x01, 0xec, 0x01, 0xf4, 0x01, 0xf5, 0x01, 0xf6, 0x01, 0xf7, 0x01, 0xe9, 0x01, 0xf8, +0x01, 0xf9, 0x01, 0xec, 0x01, 0xf2, 0x01, 0xe9, 0x01, 0xfa, 0x01, 0xec, 0x01, 0xfb, 0x01, 0xfc, 0x01, 0xfd, 0x01, 0xe9, +0x01, 0xfe, 0x01, 0xec, 0x01, 0xf2, 0x01, 0xe9, 0x01, 0xff, 0x01, 0xec, 0x01, 0x00, 0x02, 0x01, 0x02, 0x02, 0x02, 0x03, +0x02, 0x04, 0x02, 0x05, 0x02, 0x06, 0x02, 0x07, 0x02, 0x08, 0x02, 0x09, 0x02, 0xca, 0x01, 0x5d, 0x00, 0x5f, 0x01, 0xf7, +0x03, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x00, 0x5d, 0x01, 0x5e, 0x01, 0x5f, 0x01, 0x60, 0x01, 0x5f, 0x01, 0xf8, 0x00, 0x02, +0x00, 0x0a, 0x02, 0x0b, 0x02, 0x0c, 0x02, 0x0d, 0x02, 0x0e, 0x02, 0x0f, 0x02, 0x10, 0x02, 0x04, 0x00, 0x5f, 0x01, 0x00, +0x01, 0x01, 0x01, 0x07, 0x00, 0x11, 0x02, 0x5f, 0x01, 0x12, 0x02, 0x02, 0x00, 0x13, 0x02, 0x04, 0x00, 0x5f, 0x01, 0x14, +0x02, 0x5f, 0x01, 0xad, 0x01, 0x02, 0x00, 0x15, 0x02, 0x04, 0x00, 0x5f, 0x01, 0xb2, 0x01, 0x02, 0x00, 0xaf, 0x01, 0x04, +0x00, 0x5f, 0x01, 0x16, 0x02, 0x02, 0x00, 0xb5, 0x01, 0x17, 0x02, 0x18, 0x02, 0xca, 0x01, 0x5d, 0x00, 0x5f, 0x01, 0xec, +0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x5d, 0x01, 0x5e, 0x01, 0x5f, 0x01, 0x60, 0x01, 0x5f, 0x01, 0xad, 0x01, 0x02, +0x00, 0xce, 0x01, 0x04, 0x00, 0x5f, 0x01, 0x19, 0x02, 0x02, 0x00, 0xb5, 0x01, 0x1a, 0x02, 0xca, 0x01, 0x5d, 0x00, 0x5f, +0x01, 0x09, 0x0e, 0x00, 0x00, 0x86, 0x00, 0x00, 0x00, 0x5d, 0x01, 0x5e, 0x01, 0x5f, 0x01, 0x60, 0x01, 0x5f, 0x01, 0x61, +0x01, 0x02, 0x00, 0x62, 0x01, 0x63, 0x01, 0x64, 0x01, 0x65, 0x01, 0x66, 0x01, 0x67, 0x01, 0x68, 0x01, 0x69, 0x01, 0x6a, +0x01, 0x6b, 0x01, 0x6c, 0x01, 0x6d, 0x01, 0x6e, 0x01, 0x6f, 0x01, 0x70, 0x01, 0x71, 0x01, 0x72, 0x01, 0x73, 0x01, 0x74, +0x01, 0x75, 0x01, 0x76, 0x01, 0x77, 0x01, 0x78, 0x01, 0x79, 0x01, 0x7a, 0x01, 0x7b, 0x01, 0x7c, 0x01, 0x7d, 0x01, 0x7e, +0x01, 0x7f, 0x01, 0x80, 0x01, 0x81, 0x01, 0x82, 0x01, 0x83, 0x01, 0x84, 0x01, 0x85, 0x01, 0x86, 0x01, 0x87, 0x01, 0x88, +0x01, 0x89, 0x01, 0x8a, 0x01, 0x8b, 0x01, 0x8c, 0x01, 0x8d, 0x01, 0x8e, 0x01, 0x8f, 0x01, 0x90, 0x01, 0x91, 0x01, 0x92, +0x01, 0x93, 0x01, 0x94, 0x01, 0x95, 0x01, 0x96, 0x01, 0x97, 0x01, 0x98, 0x01, 0x99, 0x01, 0x9a, 0x01, 0x9b, 0x01, 0x9c, +0x01, 0x9d, 0x01, 0x9e, 0x01, 0x9f, 0x01, 0xa0, 0x01, 0xa1, 0x01, 0xa2, 0x01, 0xa3, 0x01, 0xa4, 0x01, 0xa5, 0x01, 0xa6, +0x01, 0xa7, 0x01, 0xa8, 0x01, 0xa9, 0x01, 0xaa, 0x01, 0xab, 0x01, 0xac, 0x01, 0x04, 0x00, 0x5f, 0x01, 0xad, 0x01, 0x02, +0x00, 0xae, 0x01, 0xaf, 0x01, 0xb0, 0x01, 0xb1, 0x01, 0x1b, 0x02, 0x1c, 0x02, 0x04, 0x00, 0x5f, 0x01, 0xb2, 0x01, 0x02, +0x00, 0xb3, 0x01, 0x04, 0x00, 0x5f, 0x01, 0xb4, 0x01, 0x02, 0x00, 0xb5, 0x01, 0xb6, 0x01, 0x1d, 0x02, 0x1e, 0x02, 0x1f, +0x02, 0x20, 0x02, 0x21, 0x02, 0x22, 0x02, 0x23, 0x02, 0xbd, 0x01, 0x24, 0x02, 0x25, 0x02, 0x26, 0x02, 0xc1, 0x01, 0xc2, +0x01, 0xbd, 0x01, 0x27, 0x02, 0xc1, 0x01, 0x28, 0x02, 0x29, 0x02, 0x2a, 0x02, 0x2b, 0x02, 0x2c, 0x02, 0x2d, 0x02, 0x2e, +0x02, 0x2f, 0x02, 0x30, 0x02, 0x31, 0x02, 0x32, 0x02, 0x33, 0x02, 0xca, 0x01, 0x5d, 0x00, 0x5f, 0x01, 0x9a, 0x0c, 0x00, +0x00, 0x7d, 0x00, 0x00, 0x00, 0x5d, 0x01, 0x5e, 0x01, 0x5f, 0x01, 0x60, 0x01, 0x5f, 0x01, 0x61, 0x01, 0x02, 0x00, 0x62, +0x01, 0x63, 0x01, 0x64, 0x01, 0x65, 0x01, 0x66, 0x01, 0x67, 0x01, 0x68, 0x01, 0x69, 0x01, 0x6a, 0x01, 0x6b, 0x01, 0x6c, +0x01, 0x6d, 0x01, 0x6e, 0x01, 0x6f, 0x01, 0x70, 0x01, 0x71, 0x01, 0x72, 0x01, 0x73, 0x01, 0x74, 0x01, 0x75, 0x01, 0x76, +0x01, 0x77, 0x01, 0x78, 0x01, 0x79, 0x01, 0x7a, 0x01, 0x7b, 0x01, 0x7c, 0x01, 0x7d, 0x01, 0x7e, 0x01, 0x7f, 0x01, 0x80, +0x01, 0x81, 0x01, 0x82, 0x01, 0x83, 0x01, 0x84, 0x01, 0x85, 0x01, 0x86, 0x01, 0x87, 0x01, 0x88, 0x01, 0x89, 0x01, 0x8a, +0x01, 0x8b, 0x01, 0x8c, 0x01, 0x8d, 0x01, 0x8e, 0x01, 0x8f, 0x01, 0x90, 0x01, 0x91, 0x01, 0x92, 0x01, 0x93, 0x01, 0x94, +0x01, 0x95, 0x01, 0x96, 0x01, 0x97, 0x01, 0x98, 0x01, 0x99, 0x01, 0x9a, 0x01, 0x9b, 0x01, 0x9c, 0x01, 0x9d, 0x01, 0x9e, +0x01, 0x9f, 0x01, 0xa0, 0x01, 0xa1, 0x01, 0xa2, 0x01, 0xa3, 0x01, 0xa4, 0x01, 0xa5, 0x01, 0xa6, 0x01, 0xa7, 0x01, 0xa8, +0x01, 0xa9, 0x01, 0xaa, 0x01, 0xab, 0x01, 0xac, 0x01, 0x04, 0x00, 0x5f, 0x01, 0xad, 0x01, 0x02, 0x00, 0xae, 0x01, 0xaf, +0x01, 0xb0, 0x01, 0xb1, 0x01, 0x04, 0x00, 0x5f, 0x01, 0xb2, 0x01, 0x02, 0x00, 0xb3, 0x01, 0x04, 0x00, 0x5f, 0x01, 0xb4, +0x01, 0x02, 0x00, 0xb5, 0x01, 0xb6, 0x01, 0x34, 0x02, 0x35, 0x02, 0x36, 0x02, 0x37, 0x02, 0x38, 0x02, 0x39, 0x02, 0xbd, +0x01, 0x3a, 0x02, 0x3b, 0x02, 0x3c, 0x02, 0xc1, 0x01, 0xc2, 0x01, 0xbd, 0x01, 0x3d, 0x02, 0xc1, 0x01, 0x3e, 0x02, 0x3f, +0x02, 0x40, 0x02, 0x41, 0x02, 0x42, 0x02, 0x43, 0x02, 0xca, 0x01, 0x5d, 0x00, 0x5f, 0x01, 0x26, 0x16, 0x00, 0x00, 0xb0, +0x00, 0x00, 0x00, 0x5d, 0x01, 0x5e, 0x01, 0x5f, 0x01, 0x60, 0x01, 0x5f, 0x01, 0x61, 0x01, 0x02, 0x00, 0x62, 0x01, 0x63, +0x01, 0x64, 0x01, 0x65, 0x01, 0x66, 0x01, 0x67, 0x01, 0x68, 0x01, 0x69, 0x01, 0x6a, 0x01, 0x6b, 0x01, 0x6c, 0x01, 0x6d, +0x01, 0x6e, 0x01, 0x6f, 0x01, 0x70, 0x01, 0x71, 0x01, 0x72, 0x01, 0x73, 0x01, 0x74, 0x01, 0x75, 0x01, 0x76, 0x01, 0x77, +0x01, 0x78, 0x01, 0x79, 0x01, 0x7a, 0x01, 0x7b, 0x01, 0x7c, 0x01, 0x7d, 0x01, 0x7e, 0x01, 0x7f, 0x01, 0x80, 0x01, 0x81, +0x01, 0x82, 0x01, 0x83, 0x01, 0x84, 0x01, 0x85, 0x01, 0x86, 0x01, 0x87, 0x01, 0x88, 0x01, 0x89, 0x01, 0x8a, 0x01, 0x8b, +0x01, 0x8c, 0x01, 0x8d, 0x01, 0x8e, 0x01, 0x8f, 0x01, 0x90, 0x01, 0x91, 0x01, 0x92, 0x01, 0x93, 0x01, 0x94, 0x01, 0x95, +0x01, 0x96, 0x01, 0x97, 0x01, 0x98, 0x01, 0x99, 0x01, 0x9a, 0x01, 0x9b, 0x01, 0x9c, 0x01, 0x9d, 0x01, 0x9e, 0x01, 0x9f, +0x01, 0xa0, 0x01, 0xa1, 0x01, 0xa2, 0x01, 0xa3, 0x01, 0xa4, 0x01, 0xa5, 0x01, 0xa6, 0x01, 0xa7, 0x01, 0xa8, 0x01, 0xa9, +0x01, 0xaa, 0x01, 0xab, 0x01, 0xac, 0x01, 0x04, 0x00, 0x5f, 0x01, 0xcb, 0x01, 0x02, 0x00, 0xcc, 0x01, 0xcd, 0x01, 0x04, +0x00, 0x5f, 0x01, 0xd2, 0x01, 0xd3, 0x01, 0xd4, 0x01, 0xd5, 0x01, 0xd6, 0x01, 0xd7, 0x01, 0xd8, 0x01, 0xd9, 0x01, 0xda, +0x01, 0xdb, 0x01, 0xdc, 0x01, 0xdd, 0x01, 0xde, 0x01, 0xdf, 0x01, 0xe0, 0x01, 0x04, 0x00, 0x5f, 0x01, 0xad, 0x01, 0x02, +0x00, 0xce, 0x01, 0x04, 0x00, 0x5f, 0x01, 0xb2, 0x01, 0x02, 0x00, 0xae, 0x01, 0x04, 0x00, 0x5f, 0x01, 0xe1, 0x01, 0x02, +0x00, 0xb5, 0x01, 0xd0, 0x01, 0xe4, 0x01, 0x44, 0x02, 0x45, 0x02, 0xe6, 0x01, 0xbd, 0x01, 0xe7, 0x01, 0xe8, 0x01, 0xe9, +0x01, 0x46, 0x02, 0xeb, 0x01, 0xec, 0x01, 0xed, 0x01, 0xee, 0x01, 0xef, 0x01, 0xf0, 0x01, 0xe9, 0x01, 0xf1, 0x01, 0xec, +0x01, 0xf2, 0x01, 0xe9, 0x01, 0xf3, 0x01, 0xec, 0x01, 0x47, 0x02, 0x48, 0x02, 0xf7, 0x01, 0xe9, 0x01, 0x49, 0x02, 0x4a, +0x02, 0xec, 0x01, 0xf2, 0x01, 0xe9, 0x01, 0x4b, 0x02, 0xec, 0x01, 0x4c, 0x02, 0x4d, 0x02, 0xfd, 0x01, 0xe9, 0x01, 0x4e, +0x02, 0xec, 0x01, 0xf2, 0x01, 0xe9, 0x01, 0xff, 0x01, 0xec, 0x01, 0x4f, 0x02, 0x50, 0x02, 0x03, 0x02, 0x04, 0x02, 0x05, +0x02, 0x06, 0x02, 0x07, 0x02, 0x08, 0x02, 0x51, 0x02, 0xca, 0x01, 0x5d, 0x00, 0x5f, 0x01, 0x2d, 0x0e, 0x00, 0x00, 0x86, +0x00, 0x00, 0x00, 0x5d, 0x01, 0x5e, 0x01, 0x5f, 0x01, 0x60, 0x01, 0x5f, 0x01, 0x61, 0x01, 0x02, 0x00, 0x62, 0x01, 0x63, +0x01, 0x64, 0x01, 0x65, 0x01, 0x66, 0x01, 0x67, 0x01, 0x68, 0x01, 0x69, 0x01, 0x6a, 0x01, 0x6b, 0x01, 0x6c, 0x01, 0x6d, +0x01, 0x6e, 0x01, 0x6f, 0x01, 0x70, 0x01, 0x71, 0x01, 0x72, 0x01, 0x73, 0x01, 0x74, 0x01, 0x75, 0x01, 0x76, 0x01, 0x77, +0x01, 0x78, 0x01, 0x79, 0x01, 0x7a, 0x01, 0x7b, 0x01, 0x7c, 0x01, 0x7d, 0x01, 0x7e, 0x01, 0x7f, 0x01, 0x80, 0x01, 0x81, +0x01, 0x82, 0x01, 0x83, 0x01, 0x84, 0x01, 0x85, 0x01, 0x86, 0x01, 0x87, 0x01, 0x88, 0x01, 0x89, 0x01, 0x8a, 0x01, 0x8b, +0x01, 0x8c, 0x01, 0x8d, 0x01, 0x8e, 0x01, 0x8f, 0x01, 0x90, 0x01, 0x91, 0x01, 0x92, 0x01, 0x93, 0x01, 0x94, 0x01, 0x95, +0x01, 0x96, 0x01, 0x97, 0x01, 0x98, 0x01, 0x99, 0x01, 0x9a, 0x01, 0x9b, 0x01, 0x9c, 0x01, 0x9d, 0x01, 0x9e, 0x01, 0x9f, +0x01, 0xa0, 0x01, 0xa1, 0x01, 0xa2, 0x01, 0xa3, 0x01, 0xa4, 0x01, 0xa5, 0x01, 0xa6, 0x01, 0xa7, 0x01, 0xa8, 0x01, 0xa9, +0x01, 0xaa, 0x01, 0xab, 0x01, 0xac, 0x01, 0x04, 0x00, 0x5f, 0x01, 0xad, 0x01, 0x02, 0x00, 0xae, 0x01, 0xaf, 0x01, 0xb0, +0x01, 0xb1, 0x01, 0x1b, 0x02, 0x1c, 0x02, 0x04, 0x00, 0x5f, 0x01, 0xb2, 0x01, 0x02, 0x00, 0xb3, 0x01, 0x04, 0x00, 0x5f, +0x01, 0xb4, 0x01, 0x02, 0x00, 0xb5, 0x01, 0xb6, 0x01, 0x52, 0x02, 0x53, 0x02, 0x54, 0x02, 0x55, 0x02, 0x56, 0x02, 0x57, +0x02, 0x58, 0x02, 0xbd, 0x01, 0x59, 0x02, 0x5a, 0x02, 0x5b, 0x02, 0xc1, 0x01, 0xc2, 0x01, 0xbd, 0x01, 0x5c, 0x02, 0xc1, +0x01, 0x5d, 0x02, 0x5e, 0x02, 0x5f, 0x02, 0x60, 0x02, 0x61, 0x02, 0x62, 0x02, 0x63, 0x02, 0x64, 0x02, 0x65, 0x02, 0x66, +0x02, 0x32, 0x02, 0x33, 0x02, 0xca, 0x01, 0x5d, 0x00, 0x5f, 0x01, + +}; + +int GIZMO_GIZMO_OFFSET = 0; +int GIZMO_GIZMO_SIZE = 36751; diff --git a/ios/include/material/gizmo.h b/ios/include/material/gizmo.h new file mode 100644 index 00000000..86a4282a --- /dev/null +++ b/ios/include/material/gizmo.h @@ -0,0 +1,13 @@ +#ifndef GIZMO_H_ +#define GIZMO_H_ + +#include + +extern "C" { + extern const uint8_t GIZMO_PACKAGE[]; + extern int GIZMO_GIZMO_OFFSET; + extern int GIZMO_GIZMO_SIZE; +} +#define GIZMO_GIZMO_DATA (GIZMO_PACKAGE + GIZMO_GIZMO_OFFSET) + +#endif diff --git a/ios/src/FilamentViewer.cpp b/ios/src/FilamentViewer.cpp index 621a682d..81e5484e 100644 --- a/ios/src/FilamentViewer.cpp +++ b/ios/src/FilamentViewer.cpp @@ -248,6 +248,7 @@ namespace flutter_filament .build(*_engine, imageEntity); _imageEntity = &imageEntity; _scene->addEntity(imageEntity); + Log("Added imageEntity %d", imageEntity); } void FilamentViewer::setAntiAliasing(bool msaa, bool fxaa, bool taa) { @@ -320,6 +321,9 @@ namespace flutter_filament int32_t FilamentViewer::addLight(LightManager::Type t, float colour, float intensity, float posX, float posY, float posZ, float dirX, float dirY, float dirZ, bool shadows) { auto light = EntityManager::get().create(); + auto& transformManager = _engine->getTransformManager(); + transformManager.create(light); + auto parent = transformManager.getInstance(light); auto builder = LightManager::Builder(t) .color(Color::cct(colour)) .intensity(intensity) @@ -329,6 +333,7 @@ namespace flutter_filament .build(*_engine, light); _scene->addEntity(light); _lights.push_back(light); + auto entityId = Entity::smuggle(light); Log("Added light under entity ID %d of type %d with colour %f intensity %f at (%f, %f, %f) with direction (%f, %f, %f) with shadows %d", entityId, t, colour, intensity, posX, posY, posZ, dirX, dirY, dirZ, shadows); return entityId; @@ -1450,7 +1455,7 @@ namespace flutter_filament }); } - EntityId FilamentViewer::createGeometry(float *vertices, uint32_t numVertices, uint16_t *indices, uint32_t numIndices, const char* materialPath) + EntityId FilamentViewer::createGeometry(float *vertices, uint32_t numVertices, uint16_t *indices, uint32_t numIndices, RenderableManager::PrimitiveType primitiveType, const char* materialPath) { float *verticesCopy = (float*)malloc(numVertices * sizeof(float)); @@ -1499,7 +1504,7 @@ namespace flutter_filament RenderableManager::Builder builder = RenderableManager::Builder(1); builder .boundingBox({{minX, minY, minZ}, {maxX, maxY, maxZ}}) - .geometry(0, RenderableManager::PrimitiveType::TRIANGLES, + .geometry(0, primitiveType, vb, ib, 0, numIndices) .culling(false) .receiveShadows(false) @@ -1510,7 +1515,8 @@ namespace flutter_filament auto result = builder.build(*_engine, renderable); _scene->addEntity(renderable); - Log("Created geometry with result %d", result); + + Log("Created geometry with primitive type %d (result %d)", primitiveType, result); return Entity::smuggle(renderable); } diff --git a/ios/src/FlutterFilamentApi.cpp b/ios/src/FlutterFilamentApi.cpp index cd23252b..9b310060 100644 --- a/ios/src/FlutterFilamentApi.cpp +++ b/ios/src/FlutterFilamentApi.cpp @@ -597,8 +597,15 @@ extern "C" ((SceneManager*)sceneManager)->addAnimationComponent(entityId); } - FLUTTER_PLUGIN_EXPORT EntityId create_geometry(void *const viewer, float* vertices, int numVertices, uint16_t* indices, int numIndices, const char* materialPath) { - return ((FilamentViewer*)viewer)->createGeometry(vertices, (uint32_t)numVertices, indices, numIndices, materialPath); + FLUTTER_PLUGIN_EXPORT EntityId create_geometry(void *const viewer, float* vertices, int numVertices, uint16_t* indices, int numIndices, int primitiveType, const char* materialPath) { + return ((FilamentViewer*)viewer)->createGeometry( + vertices, + (uint32_t)numVertices, + indices, + numIndices, + (filament::RenderableManager::PrimitiveType)primitiveType, + materialPath + ); } FLUTTER_PLUGIN_EXPORT EntityId find_child_entity_by_name(void *const sceneManager, const EntityId parent, const char* name) { @@ -614,4 +621,12 @@ extern "C" ((SceneManager*)sceneManager)->testCollisions(entity); } + FLUTTER_PLUGIN_EXPORT void set_priority(void *const sceneManager, EntityId entity, int priority) { + ((SceneManager*)sceneManager)->setPriority(entity, priority); + } + + FLUTTER_PLUGIN_EXPORT void get_gizmo(void *const sceneManager, EntityId* out) { + return ((SceneManager*)sceneManager)->getGizmo(out); + } + } diff --git a/ios/src/FlutterFilamentFFIApi.cpp b/ios/src/FlutterFilamentFFIApi.cpp index cb21a94d..a3b93a01 100644 --- a/ios/src/FlutterFilamentFFIApi.cpp +++ b/ios/src/FlutterFilamentFFIApi.cpp @@ -40,7 +40,6 @@ public: _t = new std::thread([this]() { auto last = std::chrono::high_resolution_clock::now(); while (!_stop) { - last = std::chrono::high_resolution_clock::now(); if (_rendering) { // auto frameStart = std::chrono::high_resolution_clock::now(); @@ -48,6 +47,8 @@ public: // auto frameEnd = std::chrono::high_resolution_clock::now(); } + last = std::chrono::high_resolution_clock::now(); + auto now = std::chrono::high_resolution_clock::now(); float elapsed = float(std::chrono::duration_cast(now - last).count()); @@ -58,7 +59,6 @@ public: if(_tasks.empty()) { _cond.wait_for(lock, std::chrono::duration(1)); - continue; } while(!_tasks.empty()) { task = std::move(_tasks.front()); @@ -571,10 +571,10 @@ FLUTTER_PLUGIN_EXPORT void add_bone_animation_ffi( FLUTTER_PLUGIN_EXPORT void ios_dummy_ffi() { Log("Dummy called"); } -FLUTTER_PLUGIN_EXPORT void create_geometry_ffi(void* const viewer, float* vertices, int numVertices, uint16_t* indices, int numIndices, const char* materialPath, void (*callback)(EntityId) ) { +FLUTTER_PLUGIN_EXPORT void create_geometry_ffi(void* const viewer, float* vertices, int numVertices, uint16_t* indices, int numIndices, int primitiveType, const char* materialPath, void (*callback)(EntityId) ) { std::packaged_task lambda( [=] { - auto entity = create_geometry(viewer, vertices, numVertices, indices, numIndices, materialPath); + auto entity = create_geometry(viewer, vertices, numVertices, indices, numIndices, primitiveType, materialPath); callback(entity); return entity; }); diff --git a/ios/src/SceneManager.cpp b/ios/src/SceneManager.cpp index cdd44d98..3cddfcef 100644 --- a/ios/src/SceneManager.cpp +++ b/ios/src/SceneManager.cpp @@ -23,7 +23,6 @@ #include "Log.hpp" #include "SceneManager.hpp" -#include "material/FileMaterialProvider.hpp" #include "gltfio/materials/uberarchive.h" extern "C" @@ -88,6 +87,8 @@ namespace flutter_filament _collisionComponentManager = new CollisionComponentManager(tm); _animationComponentManager = new AnimationComponentManager(tm, _engine->getRenderableManager()); + addGizmo(); + } SceneManager::~SceneManager() @@ -307,8 +308,10 @@ namespace flutter_filament if(asset) { instance = asset->getInstance(); } else { - return false; - } + // Log("Failed to find glTF instance under entityID %d, hiding as regular entity", entityId); + _scene->remove(Entity::import(entityId)); + return true; + } } utils::Entity entity; @@ -342,7 +345,9 @@ namespace flutter_filament if(asset) { instance = asset->getInstance(); } else { - return false; + // Log("Failed to find glTF instance under entityID %d, revealing as regular entity", entityId); + _scene->addEntity(Entity::import(entityId)); + return true; } } @@ -1563,4 +1568,161 @@ namespace flutter_filament return _ncm->getName(inst); } + void SceneManager::setPriority(EntityId entityId, int priority) { + auto& rm = _engine->getRenderableManager(); + auto renderableInstance = rm.getInstance(Entity::import(entityId)); + if(!renderableInstance.isValid()) { + Log("Error: invalid renderable, did you pass the correct entity?", priority); + return; + } + rm.setPriority(renderableInstance, priority); + Log("Set instance renderable priority to %d", priority); + } + + EntityId SceneManager::addGizmo() { + auto mat = _resourceLoaderWrapper->load("file:///Users/nickfisher/Documents/polyvox/flutter/flutter_filament/materials/gizmo.filamat"); + _gizmoMaterial = + Material::Builder() + .package(mat.data, mat.size) + // .package(GIZMO_GIZMO_DATA, GIZMO_GIZMO_SIZE) + .build(*_engine); + + auto vertexCount = 9; + + float* vertices = new float[vertexCount * 3] { + -0.05, 0.0f, 0.05f, + 0.05f, 0.0f, 0.05f, + 0.05f, 0.0f, -0.05f, + -0.05f, 0.0f, -0.05f, + -0.05f, 1.0f, 0.05f, + 0.05f, 1.0f, 0.05f, + 0.05f, 1.0f, -0.05f, + -0.05f, 1.0f, -0.05f, + 0.00f, 1.1f, 0.0f + }; + + VertexBuffer::BufferDescriptor::Callback vertexCallback = [](void *buf, size_t, + void *data) + { + free((void*)buf); + }; + + auto indexCount = 42; + uint16_t* indices = new uint16_t[indexCount] { + //bottom quad + 0,1,2, + 0,2,3, + // top "cone" + 4,5,8, + 5,6,8, + 4,7,8, + 6,7,8, + // front + 0,1,4, + 1,5,4, + // right + 1,2,5, + 2,6,5, + // back + 2,6,7, + 7,3,2, + // left + 0,4,7, + 7,3,0 + + }; + + IndexBuffer::BufferDescriptor::Callback indexCallback = [](void *buf, size_t, + void *data) + { + free((void*)buf); + }; + + auto vb = VertexBuffer::Builder() + .vertexCount(vertexCount) + .bufferCount(1) + .attribute( + VertexAttribute::POSITION, 0, VertexBuffer::AttributeType::FLOAT3) + .build(*_engine); + + vb->setBufferAt( + *_engine, + 0, + VertexBuffer::BufferDescriptor(vertices, vb->getVertexCount() * sizeof(filament::math::float3), 0, vertexCallback) + ); + + auto ib = IndexBuffer::Builder().indexCount(indexCount).bufferType(IndexBuffer::IndexType::USHORT).build(*_engine); + ib->setBuffer(*_engine, IndexBuffer::BufferDescriptor(indices, ib->getIndexCount() * sizeof(uint16_t), 0, indexCallback)); + + auto &entityManager = EntityManager::get(); + + _gizmoY = entityManager.create(); + auto materialY = _gizmoMaterial->createInstance(); + materialY->setParameter("color", math::float3 { 1.0f, 0.0f, 0.0f }); + RenderableManager::Builder(1) + .boundingBox({{}, {1.0f, 1.0f, 1.0f}}) + .material(0, materialY) + .geometry(0, RenderableManager::PrimitiveType::TRIANGLES, vb, + ib, 0, indexCount) + .culling(false) + .build(*_engine, _gizmoY); + + _gizmoX = entityManager.create(); + auto materialX = _gizmoMaterial->createInstance(); + materialX->setParameter("color", math::float3 { 0.0f, 1.0f, 0.0f }); + auto xTransform = math::mat4f::translation(math::float3 { 0.0f, 0.05f, -0.05f}) * math::mat4f::rotation(-math::F_PI_2, math::float3 { 0, 0, 1 }); + auto* instanceBufferX = InstanceBuffer::Builder(1).localTransforms(&xTransform).build(*_engine); + RenderableManager::Builder(1) + .boundingBox({{}, {1.0f, 1.0f, 1.0f}}) + .instances(1, instanceBufferX) + .material(0, materialX) + .geometry(0, RenderableManager::PrimitiveType::TRIANGLES, vb, + ib, 0, indexCount) + .culling(false) + .build(*_engine, _gizmoX); + + _gizmoZ = entityManager.create(); + auto materialZ = _gizmoMaterial->createInstance(); + materialZ->setParameter("color", math::float3 { 0.0f, 0.0f, 1.0f }); + auto zTransform = math::mat4f::translation(math::float3 { 0.0f, 0.05f, -0.05f}) * math::mat4f::rotation(3 * math::F_PI_2, math::float3 { 1, 0, 0 }); + auto* instanceBufferZ = InstanceBuffer::Builder(1).localTransforms(&zTransform).build(*_engine); + RenderableManager::Builder(1) + .boundingBox({{}, {1.0f, 1.0f, 1.0f}}) + .instances(1, instanceBufferZ) + .material(0, materialZ) + .geometry(0, RenderableManager::PrimitiveType::TRIANGLES, vb, + ib, 0, indexCount) + .culling(false) + .build(*_engine, _gizmoZ); + + + // auto localTransforms = math::mat4f[3] { + // math::mat4f(), + // math::mat4f::translation(math::float3 { 0.0f, 0.05f, -0.05f}) * math::mat4f::rotation(3 * math::F_PI_2, math::float3 { 1, 0, 0 }) , + // math::mat4f::translation(math::float3 { 0.0f, 0.05f, -0.05f}) * math::mat4f::rotation(math::F_PI_2, math::float3 { 0, 0, 1 }) + // }; + + + // RenderableManager::Builder(1) + // .boundingBox({{}, {1.0f, 1.0f, 1.0f}}) + // .instances(3, instanceBuffer) + // .material(0, _gizmoMaterial->getDefaultInstance()) + // .geometry(0, RenderableManager::PrimitiveType::TRIANGLES, vb, + // ib, 0, indexCount) + // .culling(false) + // .build(*_engine, _gizmo); + + auto& rm = _engine->getRenderableManager(); + rm.setPriority(rm.getInstance(_gizmoX), 7); + rm.setPriority(rm.getInstance(_gizmoY), 7); + rm.setPriority(rm.getInstance(_gizmoZ), 7); + return Entity::smuggle(_gizmoX); + } + + void SceneManager::getGizmo(EntityId* out) { + out[0] = Entity::smuggle(_gizmoX); + out[1] = Entity::smuggle(_gizmoY); + out[2] = Entity::smuggle(_gizmoZ); + } + } // namespace flutter_filament diff --git a/lib/FlutterFilamentPluginWeb.dart b/lib/FlutterFilamentPluginWeb.dart index e68babd4..9b239bc9 100644 --- a/lib/FlutterFilamentPluginWeb.dart +++ b/lib/FlutterFilamentPluginWeb.dart @@ -6,7 +6,7 @@ import 'dart:html'; import 'dart:ui'; import 'dart:web_gl'; import 'package:wasm_ffi/wasm_ffi.dart'; -import 'generated_bindings_web.dart'; +import 'filament/generated_bindings_web.dart'; import 'package:flutter/services.dart'; import 'package:flutter_web_plugins/flutter_web_plugins.dart'; diff --git a/lib/animations/animation_builder.dart b/lib/filament/animations/animation_builder.dart similarity index 98% rename from lib/animations/animation_builder.dart rename to lib/filament/animations/animation_builder.dart index 83aca99c..c7994e6e 100644 --- a/lib/animations/animation_builder.dart +++ b/lib/filament/animations/animation_builder.dart @@ -1,4 +1,4 @@ -import 'package:flutter_filament/animations/animation_data.dart'; +import 'package:flutter_filament/filament/animations/animation_data.dart'; import 'package:vector_math/vector_math.dart'; class AnimationBuilder { diff --git a/lib/animations/animation_data.dart b/lib/filament/animations/animation_data.dart similarity index 100% rename from lib/animations/animation_data.dart rename to lib/filament/animations/animation_data.dart diff --git a/lib/animations/bvh.dart b/lib/filament/animations/bvh.dart similarity index 97% rename from lib/animations/bvh.dart rename to lib/filament/animations/bvh.dart index b2d6583f..800d01ac 100644 --- a/lib/animations/bvh.dart +++ b/lib/filament/animations/bvh.dart @@ -1,7 +1,6 @@ -import 'dart:io'; import 'dart:math'; -import 'dart:ui'; -import 'package:flutter_filament/animations/animation_data.dart'; + +import 'package:flutter_filament/filament/animations/animation_data.dart'; import 'package:vector_math/vector_math_64.dart'; enum RotationMode { ZYX, XYZ } diff --git a/lib/entities/entity_transform_controller.dart b/lib/filament/entities/entity_transform_controller.dart similarity index 81% rename from lib/entities/entity_transform_controller.dart rename to lib/filament/entities/entity_transform_controller.dart index 071bb2be..88781aa2 100644 --- a/lib/entities/entity_transform_controller.dart +++ b/lib/filament/entities/entity_transform_controller.dart @@ -1,8 +1,8 @@ import 'dart:async'; import 'dart:math'; -import 'package:flutter/services.dart'; -import 'package:flutter_filament/filament_controller.dart'; +import 'package:flutter_filament/filament/filament_controller.dart'; +import 'package:flutter_filament/filament/utils/hardware_keyboard_listener.dart'; import 'package:vector_math/vector_math_64.dart' as v; class EntityTransformController { @@ -178,4 +178,26 @@ class EntityTransformController { void mouse2Up() async {} void mouse2Down() async {} + static HardwareKeyboardListener? _keyboardListener; + + static Future create( + FilamentController controller, FilamentEntity entity, + {double? translationSpeed, String? forwardAnimation}) async { + int? forwardAnimationIndex; + if (forwardAnimation != null) { + final animationNames = await controller.getAnimationNames(entity); + forwardAnimationIndex = animationNames.indexOf(forwardAnimation); + } + + if (forwardAnimationIndex == -1) { + throw Exception("Invalid animation : $forwardAnimation"); + } + + _keyboardListener?.dispose(); + var transformController = EntityTransformController(controller, entity, + translationSpeed: translationSpeed ?? 1.0, + forwardAnimationIndex: forwardAnimationIndex); + _keyboardListener = HardwareKeyboardListener(transformController); + return transformController; + } } diff --git a/lib/filament/entities/gizmo.dart b/lib/filament/entities/gizmo.dart new file mode 100644 index 00000000..5cf348ed --- /dev/null +++ b/lib/filament/entities/gizmo.dart @@ -0,0 +1,72 @@ +import 'dart:ui'; + +import 'package:vector_math/vector_math_64.dart'; + +import '../filament_controller.dart'; + +class Gizmo { + final FilamentEntity x; + Vector3 _x = Vector3(0.1, 0, 0); + final FilamentEntity y; + Vector3 _y = Vector3(0.0, 0.1, 0); + final FilamentEntity z; + Vector3 _z = Vector3(0.0, 0.0, 0.1); + + final FilamentController controller; + + FilamentEntity? _activeAxis; + FilamentEntity? _activeEntity; + bool get isActive => _activeAxis != null; + + Gizmo(this.x, this.y, this.z, this.controller) { + controller.pickResult.listen(_onPickResult); + } + + Future _reveal() async { + await controller.reveal(x, null); + await controller.reveal(y, null); + await controller.reveal(z, null); + } + + void translate(Offset offset) async { + late Vector3 vec; + if (_activeAxis == x) { + vec = _x; + } else if (_activeAxis == y) { + vec = _y; + } else if (_activeAxis == z) { + vec = _z; + } + await controller.queuePositionUpdate(_activeEntity!, offset.dx * vec.x, + -offset.dy * vec.y, -offset.dx * vec.z, + relative: true); + } + + void reset() { + _activeAxis = null; + } + + void _onPickResult(FilamentPickResult result) async { + if (result.entity == x || result.entity == y || result.entity == z) { + _activeAxis = result.entity; + } else { + attach(result.entity); + } + } + + void attach(FilamentEntity entity) async { + print("Attaching to $entity"); + _activeAxis = null; + _activeEntity = entity; + await _reveal(); + await controller.setParent(x, entity); + await controller.setParent(y, entity); + await controller.setParent(z, entity); + } + + void detach() async { + await controller.hide(x, null); + await controller.hide(y, null); + await controller.hide(z, null); + } +} diff --git a/lib/filament_controller.dart b/lib/filament/filament_controller.dart similarity index 92% rename from lib/filament_controller.dart rename to lib/filament/filament_controller.dart index 0110ab56..60da124d 100644 --- a/lib/filament_controller.dart +++ b/lib/filament/filament_controller.dart @@ -5,17 +5,28 @@ import 'dart:typed_data'; import 'dart:ui' as ui; import 'package:flutter/widgets.dart'; -import 'package:flutter_filament/animations/animation_data.dart'; -import 'package:flutter_filament/entities/entity_transform_controller.dart'; -import 'package:flutter_filament/generated_bindings.dart'; +import 'package:flutter_filament/filament/entities/gizmo.dart'; import 'package:vector_math/vector_math_64.dart'; +import 'animations/animation_data.dart'; + // a handle that can be safely passed back to the rendering layer to manipulate an Entity typedef FilamentEntity = int; // "picking" means clicking/tapping on the viewport, and unprojecting the X/Y coordinate to determine whether any renderable entities were present at those coordinates. typedef FilamentPickResult = ({FilamentEntity entity, double x, double y}); +// copied from filament/backened/DriverEnums.h +enum PrimitiveType { + // don't change the enums values (made to match GL) + POINTS, //!< points + LINES, //!< lines + UNUSED1, + LINE_STRIP, //!< line strip + TRIANGLES, //!< triangles + TRIANGLE_STRIP, //!< triangle strip +} + enum ToneMapper { ACES, FILMIC, LINEAR } // see filament Manipulator.h for more details @@ -33,17 +44,6 @@ class TextureDetails { } abstract class FilamentController { - /// - /// A Stream containing every FilamentEntity added to the scene (i.e. via [loadGlb], [loadGltf] or [addLight]). - /// This is provided for convenience so you can set listeners in front-end widgets that can respond to entity loads without manually passing around the FilamentEntity returned from those methods. - /// - Stream get onLoad; - - /// - /// A Stream containing every FilamentEntity removed from the scene (i.e. via [removeEntity], [clearEntities], [removeLight] or [clearLights]). - - Stream get onUnload; - /// /// A [ValueNotifier] to indicate whether a FilamentViewer is currently available. /// (FilamentViewer is a C++ type, hence why it is not referenced) here. @@ -212,14 +212,6 @@ abstract class FilamentController { /// Future loadGlb(String path, {int numInstances = 1}); - /// - /// Load the .glb asset from the specified path (either Flutter asset URI or filepath) and insert into the scene. - /// If [cache] is true, the contents of the path will be cached locally and re-used for any future calls to load that asset. - /// See also [evictCache]. - /// - Future loadGlbFromBuffer(String path, - {bool cache = false, int numInstances = 1}); - /// /// Create a new instance of [entity]. /// @@ -235,11 +227,6 @@ abstract class FilamentController { /// Future> getInstances(FilamentEntity entity); - /// - /// Frees all cached resources loaded via [loadGlbFromBuffer]. - /// - Future evictCache(); - /// /// Load the .gltf asset at the given path and insert into the scene. /// [relativeResourcePath] is the folder path where the glTF resources are stored; @@ -558,7 +545,7 @@ abstract class FilamentController { /// /// Reveal the node [meshName] under [entity]. Only applicable if [hide] had previously been called; this is a no-op otherwise. /// - Future reveal(FilamentEntity entity, String meshName); + Future reveal(FilamentEntity entity, String? meshName); /// /// If [meshName] is provided, hide the node [meshName] under [entity], otherwise hide the root node for [entity]. @@ -613,12 +600,6 @@ abstract class FilamentController { /// Future setRecordingOutputDirectory(String outputDirectory); - /// - /// Attach the keyboard/mouse to [entity]. - /// - Future control(FilamentEntity entity, - {double? translationSpeed, String? forwardAnimation}); - /// /// An [entity] will only be animatable after an animation component is attached. /// Any calls to [playAnimation]/[setBoneAnimation]/[setMorphAnimation] will have no visual effect until [addAnimationComponent] has been called on the instance. @@ -642,8 +623,9 @@ abstract class FilamentController { /// /// Creates a (renderable) entity with the specified geometry and adds to the scene. /// - Future createGeometry( - List vertices, List indices, String? materialPath); + Future createGeometry(List vertices, List indices, + {String? materialPath, + PrimitiveType primitiveType = PrimitiveType.TRIANGLES}); /// /// Sets the parent transform of [child] to the transform of [parent]. @@ -655,4 +637,59 @@ abstract class FilamentController { /// This method returns void; the relevant callback passed to [addCollisionComponent] will be fired if a collision is detected. /// Future testCollisions(FilamentEntity entity); + + /// + /// Sets the draw priority for the given entity. See RenderableManager.h for more details. + /// + Future setPriority(FilamentEntity entityId, int priority); + + /// + /// The Scene holds the transform gizmo and all loaded entities/lights. + /// + Scene get scene; +} + +/// +/// For now, this class just holds the entities that have been loaded (though not necessarily visible in the Filament Scene). +/// +abstract class Scene { + /// + /// The last entity clicked/tapped in the viewport (internally, the result of calling pick); + FilamentEntity? selected; + + /// + /// A Stream updated whenever an entity is added/removed from the scene. + /// + Stream get onUpdated; + + /// + /// A Stream containing every FilamentEntity added to the scene (i.e. via [loadGlb], [loadGltf] or [addLight]). + /// This is provided for convenience so you can set listeners in front-end widgets that can respond to entity loads without manually passing around the FilamentEntity returned from those methods. + /// + Stream get onLoad; + + /// + /// A Stream containing every FilamentEntity removed from the scene (i.e. via [removeEntity], [clearEntities], [removeLight] or [clearLights]). + + Stream get onUnload; + + /// + /// Lists all light entities currently loaded (not necessarily active in the scene). Does not account for instances. + /// + Iterable listLights(); + + /// + /// Lists all entities currently loaded (not necessarily active in the scene). Does not account for instances. + /// + Iterable listEntities(); + + /// + /// Attach the gizmo to the specified entity. + /// + void select(FilamentEntity entity); + + /// + /// The transform gizmo. + /// + Gizmo get gizmo; } diff --git a/lib/filament_controller_ffi.dart b/lib/filament/filament_controller_ffi.dart similarity index 92% rename from lib/filament_controller_ffi.dart rename to lib/filament/filament_controller_ffi.dart index 6e56212d..6f330e8e 100644 --- a/lib/filament_controller_ffi.dart +++ b/lib/filament/filament_controller_ffi.dart @@ -1,24 +1,20 @@ import 'dart:async'; import 'dart:ffi'; import 'dart:io'; -import 'dart:typed_data'; import 'dart:ui' as ui; import 'dart:developer' as dev; import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; import 'package:ffi/ffi.dart'; import 'package:flutter/widgets.dart'; -import 'package:flutter_filament/entities/entity_transform_controller.dart'; - -import 'package:flutter_filament/filament_controller.dart'; - -import 'package:flutter_filament/animations/animation_data.dart'; -import 'package:flutter_filament/generated_bindings.dart'; -import 'package:flutter_filament/generated_bindings.dart' as gb; -import 'package:flutter_filament/hardware/hardware_keyboard_listener.dart'; - -import 'package:flutter_filament/rendering_surface.dart'; +import 'package:flutter_filament/filament/animations/animation_data.dart'; +import 'package:flutter_filament/filament/entities/gizmo.dart'; +import 'package:flutter_filament/filament/filament_controller.dart'; +import 'package:flutter_filament/filament/generated_bindings.dart'; +import 'package:flutter_filament/filament/generated_bindings.dart' as gb; +import 'package:flutter_filament/filament/rendering_surface.dart'; import 'package:vector_math/vector_math_64.dart'; +import 'scene.dart'; // ignore: constant_identifier_names const FilamentEntity _FILAMENT_ASSET_ERROR = 0; @@ -26,6 +22,9 @@ const FilamentEntity _FILAMENT_ASSET_ERROR = 0; class FilamentControllerFFI extends FilamentController { final _channel = const MethodChannel("app.polyvox.filament/event"); + late SceneImpl _scene; + Scene get scene => _scene; + /// /// This will be set on constructor invocation. /// On Windows, this will be set to the value returned by the [usesBackingWindow] method call. @@ -64,22 +63,8 @@ class FilamentControllerFFI extends FilamentController { Timer? _resizeTimer; - final _lights = {}; - final _entities = {}; - - final _onLoadController = StreamController.broadcast(); - Stream get onLoad => _onLoadController.stream; - - final _onUnloadController = StreamController.broadcast(); - Stream get onUnload => _onUnloadController.stream; - final allocator = calloc; - void _using(Pointer ptr, Future Function(Pointer ptr) function) async { - await function.call(ptr); - allocator.free(ptr); - } - /// /// This controller uses platform channels to bridge Dart with the C/C++ code for the Filament API. /// Setting up the context/texture (since this is platform-specific) and the render ticker are platform-specific; all other methods are passed through by the platform channel to the methods specified in FlutterFilamentApi.h. @@ -145,7 +130,6 @@ class FilamentControllerFFI extends FilamentController { Future setFrameRate(int framerate) async { final interval = 1000.0 / framerate; set_frame_interval_ffi(interval); - print("Set frame interval to $interval"); } @override @@ -360,7 +344,7 @@ class FilamentControllerFFI extends FilamentController { textureId: renderingSurface.flutterTextureId, width: _rect.value!.width.toInt(), height: _rect.value!.height.toInt()); - print("texture details ${textureDetails.value}"); + await _withVoidCallback((callback) { update_viewport_and_camera_projection_ffi( _viewer!, @@ -369,6 +353,13 @@ class FilamentControllerFFI extends FilamentController { 1.0, callback); }); + + final out = allocator(3); + get_gizmo(_sceneManager!, out); + var gizmo = Gizmo(out[0], out[1], out[2], this); + allocator.free(out); + _scene = SceneImpl(gizmo); + hasViewer.value = true; _creating = false; } @@ -642,8 +633,8 @@ class FilamentControllerFFI extends FilamentController { dirZ, castShadows, callback)); - _onLoadController.sink.add(entity); - _lights.add(entity); + + _scene.registerLight(entity); return entity; } @@ -652,9 +643,8 @@ class FilamentControllerFFI extends FilamentController { if (_viewer == null) { throw Exception("No viewer available, ignoring"); } - _lights.remove(entity); + _scene.unregisterLight(entity); remove_light_ffi(_viewer!, entity); - _onUnloadController.add(entity); } @override @@ -663,48 +653,8 @@ class FilamentControllerFFI extends FilamentController { throw Exception("No viewer available, ignoring"); } clear_lights_ffi(_viewer!); - for (final entity in _lights) { - _onUnloadController.add(entity); - } - _lights.clear(); - } - final _assetCache = , int)>{}; - - @override - Future loadGlbFromBuffer(String path, - {bool cache = false, int numInstances = 1}) async { - late (Pointer, int) data; - - if (cache && _assetCache.containsKey(path)) { - data = _assetCache[path]!; - } else { - late ByteData asset; - if (path.startsWith("file://")) { - var raw = File(path.replaceAll("file://", "")).readAsBytesSync(); - asset = raw.buffer.asByteData(raw.offsetInBytes); - } else { - asset = await rootBundle.load(path.replaceAll("asset://", "")); - } - - var ptr = allocator(asset.lengthInBytes); - for (int i = 0; i < asset.lengthInBytes; i++) { - ptr[i] = asset.getUint8(i); - } - - data = (ptr.cast(), asset.lengthInBytes); - } - var entity = await _withIntCallback((callback) => load_glb_from_buffer_ffi( - _sceneManager!, data.$1, data.$2, numInstances, callback)); - if (!cache) { - allocator.free(data.$1); - } else { - _assetCache[path] = data; - } - if (entity == _FILAMENT_ASSET_ERROR) { - throw Exception("Failed to load GLB from path $path"); - } - return entity; + _scene.clearLights(); } @override @@ -735,14 +685,6 @@ class FilamentControllerFFI extends FilamentController { return instances; } - @override - Future evictCache() async { - for (final value in _assetCache.values) { - allocator.free(value.$1); - } - _assetCache.clear(); - } - @override Future loadGlb(String path, {bool unlit = false, int numInstances = 1}) async { @@ -759,8 +701,8 @@ class FilamentControllerFFI extends FilamentController { if (entity == _FILAMENT_ASSET_ERROR) { throw Exception("An error occurred loading the asset at $path"); } - _entities.add(entity); - _onLoadController.sink.add(entity); + _scene.registerEntity(entity); + return entity; } @@ -784,8 +726,8 @@ class FilamentControllerFFI extends FilamentController { if (entity == _FILAMENT_ASSET_ERROR) { throw Exception("An error occurred loading the asset at $path"); } - _entities.add(entity); - _onLoadController.sink.add(entity); + _scene.registerEntity(entity); + return entity; } @@ -1021,10 +963,10 @@ class FilamentControllerFFI extends FilamentController { if (_viewer == null) { throw Exception("No viewer available, ignoring"); } - _entities.remove(entity); + _scene.unregisterEntity(entity); + await _withVoidCallback( (callback) => remove_entity_ffi(_viewer!, entity, callback)); - _onUnloadController.add(entity); } @override @@ -1034,11 +976,7 @@ class FilamentControllerFFI extends FilamentController { } await _withVoidCallback( (callback) => clear_entities_ffi(_viewer!, callback)); - - for (final entity in _entities) { - _onUnloadController.add(entity); - } - _entities.clear(); + _scene.clearEntities(); } @override @@ -1078,6 +1016,14 @@ class FilamentControllerFFI extends FilamentController { _sceneManager!, entity, index, loop, reverse, replaceActive, crossfade); } + @override + Future stopAnimation(FilamentEntity entity, int animationIndex) async { + if (_viewer == null) { + throw Exception("No viewer available, ignoring"); + } + stop_animation(_sceneManager!, entity, animationIndex); + } + @override Future stopAnimationByName(FilamentEntity entity, String name) async { var animations = await getAnimationNames(entity); @@ -1107,14 +1053,6 @@ class FilamentControllerFFI extends FilamentController { set_animation_frame(_sceneManager!, entity, index, animationFrame); } - @override - Future stopAnimation(FilamentEntity entity, int animationIndex) async { - if (_viewer == null) { - throw Exception("No viewer available, ignoring"); - } - stop_animation(_sceneManager!, entity, animationIndex); - } - @override Future setMainCamera() async { set_main_camera(_viewer!); @@ -1303,16 +1241,6 @@ class FilamentControllerFFI extends FilamentController { set_position(_sceneManager!, entity, x, y, z); } - @override - Future setRotation( - FilamentEntity entity, double rads, double x, double y, double z) async { - if (_viewer == null) { - throw Exception("No viewer available, ignoring"); - } - var quat = Quaternion.axisAngle(Vector3(x, y, z), rads); - await setRotationQuat(entity, quat); - } - @override Future setRotationQuat(FilamentEntity entity, Quaternion rotation, {bool relative = false}) async { @@ -1323,6 +1251,16 @@ class FilamentControllerFFI extends FilamentController { rotation.y, rotation.z, rotation.w); } + @override + Future setRotation( + FilamentEntity entity, double rads, double x, double y, double z) async { + if (_viewer == null) { + throw Exception("No viewer available, ignoring"); + } + var quat = Quaternion.axisAngle(Vector3(x, y, z), rads); + await setRotationQuat(entity, quat); + } + @override Future setScale(FilamentEntity entity, double scale) async { if (_viewer == null) { @@ -1331,15 +1269,13 @@ class FilamentControllerFFI extends FilamentController { set_scale(_sceneManager!, entity, scale); } - @override - Future queuePositionUpdate( - FilamentEntity entity, double x, double y, double z, + Future queueRotationUpdateQuat(FilamentEntity entity, Quaternion rotation, {bool relative = false}) async { if (_viewer == null) { throw Exception("No viewer available, ignoring"); } - - queue_position_update(_sceneManager!, entity, x, y, z, relative); + queue_rotation_update(_sceneManager!, entity, rotation.radians, rotation.x, + rotation.y, rotation.z, rotation.w, relative); } @override @@ -1353,13 +1289,15 @@ class FilamentControllerFFI extends FilamentController { await queueRotationUpdateQuat(entity, quat, relative: relative); } - Future queueRotationUpdateQuat(FilamentEntity entity, Quaternion rotation, + @override + Future queuePositionUpdate( + FilamentEntity entity, double x, double y, double z, {bool relative = false}) async { if (_viewer == null) { throw Exception("No viewer available, ignoring"); } - queue_rotation_update(_sceneManager!, entity, rotation.radians, rotation.x, - rotation.y, rotation.z, rotation.w, relative); + + queue_position_update(_sceneManager!, entity, x, y, z, relative); } @override @@ -1395,14 +1333,13 @@ class FilamentControllerFFI extends FilamentController { return result.cast().toDartString(); } - final _pick = >{}; - void _onPickResult(FilamentEntity entityId, int x, int y) { _pickResultController.add(( entity: entityId, x: (x / _pixelRatio).toDouble(), y: (textureDetails.value!.height - y) / _pixelRatio )); + _scene.registerSelected(entityId); } late NativeCallable @@ -1414,6 +1351,8 @@ class FilamentControllerFFI extends FilamentController { throw Exception("No viewer available, ignoring"); } + _scene.unregisterSelected(); + gb.pick( _viewer!, (x * _pixelRatio).toInt(), @@ -1592,28 +1531,6 @@ class FilamentControllerFFI extends FilamentController { allocator.free(pathPtr); } - HardwareKeyboardListener? _keyboardListener; - @override - Future control(FilamentEntity entity, - {double? translationSpeed, String? forwardAnimation}) async { - int? forwardAnimationIndex; - if (forwardAnimation != null) { - final animationNames = await getAnimationNames(entity); - forwardAnimationIndex = animationNames.indexOf(forwardAnimation); - } - - if (forwardAnimationIndex == -1) { - throw Exception("Invalid animation : $forwardAnimation"); - } - - _keyboardListener?.dispose(); - var transformController = EntityTransformController(this, entity, - translationSpeed: translationSpeed ?? 1.0, - forwardAnimationIndex: forwardAnimationIndex); - _keyboardListener = HardwareKeyboardListener(transformController); - return transformController; - } - final _collisions = {}; @override @@ -1649,7 +1566,9 @@ class FilamentControllerFFI extends FilamentController { @override Future createGeometry( - List vertices, List indices, String? materialPath) async { + List vertices, List indices, + {String? materialPath, + PrimitiveType primitiveType = PrimitiveType.TRIANGLES}) async { if (_viewer == null) { throw Exception("Viewer must not be null"); } @@ -1672,14 +1591,14 @@ class FilamentControllerFFI extends FilamentController { vertices.length, indicesPtr, indices.length, + primitiveType.index, materialPathPtr.cast(), callback)); if (entity == _FILAMENT_ASSET_ERROR) { throw Exception("Failed to create geometry"); } - _entities.add(entity); - _onLoadController.sink.add(entity); + _scene.registerEntity(entity); allocator.free(materialPathPtr); allocator.free(vertexPtr); @@ -1700,4 +1619,9 @@ class FilamentControllerFFI extends FilamentController { Future testCollisions(FilamentEntity entity) async { test_collisions(_sceneManager!, entity); } + + @override + Future setPriority(FilamentEntity entityId, int priority) async { + set_priority(_sceneManager!, entityId, priority); + } } diff --git a/lib/generated_bindings.dart b/lib/filament/generated_bindings.dart similarity index 98% rename from lib/generated_bindings.dart rename to lib/filament/generated_bindings.dart index 9c996011..8ed8e408 100644 --- a/lib/generated_bindings.dart +++ b/lib/filament/generated_bindings.dart @@ -955,8 +955,14 @@ external void add_animation_component( ); @ffi.Native< - EntityId Function(ffi.Pointer, ffi.Pointer, - ffi.Int, ffi.Pointer, ffi.Int, ffi.Pointer)>( + EntityId Function( + ffi.Pointer, + ffi.Pointer, + ffi.Int, + ffi.Pointer, + ffi.Int, + ffi.Int, + ffi.Pointer)>( symbol: 'create_geometry', assetId: 'flutter_filament_plugin') external int create_geometry( ffi.Pointer viewer, @@ -964,6 +970,7 @@ external int create_geometry( int numVertices, ffi.Pointer indices, int numIndices, + int primitiveType, ffi.Pointer materialPath, ); @@ -982,6 +989,21 @@ external void test_collisions( int entity, ); +@ffi.Native, EntityId, ffi.Int)>( + symbol: 'set_priority', assetId: 'flutter_filament_plugin') +external void set_priority( + ffi.Pointer sceneManager, + int entityId, + int priority, +); + +@ffi.Native, ffi.Pointer)>( + symbol: 'get_gizmo', assetId: 'flutter_filament_plugin') +external void get_gizmo( + ffi.Pointer sceneManager, + ffi.Pointer out, +); + @ffi.Native< ffi.Void Function( ffi.Pointer, @@ -1510,6 +1532,7 @@ external void ios_dummy_ffi(); ffi.Int, ffi.Pointer, ffi.Int, + ffi.Int, ffi.Pointer, ffi.Pointer>)>( symbol: 'create_geometry_ffi', assetId: 'flutter_filament_plugin') @@ -1519,6 +1542,7 @@ external void create_geometry_ffi( int numVertices, ffi.Pointer indices, int numIndices, + int primitiveType, ffi.Pointer materialPath, ffi.Pointer> callback, ); diff --git a/lib/generated_bindings_web.dart b/lib/filament/generated_bindings_web.dart similarity index 100% rename from lib/generated_bindings_web.dart rename to lib/filament/generated_bindings_web.dart diff --git a/lib/rendering_surface.dart b/lib/filament/rendering_surface.dart similarity index 100% rename from lib/rendering_surface.dart rename to lib/filament/rendering_surface.dart diff --git a/lib/filament/scene.dart b/lib/filament/scene.dart new file mode 100644 index 00000000..28469e85 --- /dev/null +++ b/lib/filament/scene.dart @@ -0,0 +1,118 @@ +import 'dart:async'; +import 'package:flutter_filament/filament/entities/gizmo.dart'; +import 'package:flutter_filament/filament/filament_controller.dart'; + +/// +/// For now, this class just holds the entities that have been loaded (though not necessarily visible in the Filament Scene). +/// +class SceneImpl extends Scene { + final Gizmo _gizmo; + Gizmo get gizmo => _gizmo; + + SceneImpl(this._gizmo); + + @override + FilamentEntity? selected; + + final _onUpdatedController = StreamController.broadcast(); + @override + Stream get onUpdated => _onUpdatedController.stream; + + final _onLoadController = StreamController.broadcast(); + @override + Stream get onLoad => _onLoadController.stream; + + final _onUnloadController = StreamController.broadcast(); + @override + Stream get onUnload => _onUnloadController.stream; + + final _lights = {}; + final _entities = {}; + + void registerLight(FilamentEntity entity) { + _lights.add(entity); + _onLoadController.sink.add(entity); + _onUpdatedController.add(true); + } + + void unregisterLight(FilamentEntity entity) { + if (selected == entity) { + selected = null; + _gizmo.detach(); + } + _lights.remove(entity); + _onUnloadController.add(entity); + _onUpdatedController.add(true); + } + + void unregisterEntity(FilamentEntity entity) { + if (selected == entity) { + selected = null; + _gizmo.detach(); + } + _entities.remove(entity); + _onUnloadController.add(entity); + _onUpdatedController.add(true); + } + + void registerEntity(FilamentEntity entity) { + _entities.add(entity); + _entities.add(entity); + _onLoadController.sink.add(entity); + _onUpdatedController.add(true); + } + + void clearLights() { + for (final light in _lights) { + if (selected == light) { + selected = null; + _gizmo.detach(); + } + _onUnloadController.add(light); + } + + _lights.clear(); + _onUpdatedController.add(true); + } + + void clearEntities() { + for (final entity in _entities) { + if (selected == entity) { + selected = null; + _gizmo.detach(); + } + _onUnloadController.add(entity); + } + _entities.clear(); + _onUpdatedController.add(true); + } + + /// + /// Lists all entities currently loaded (not necessarily active in the scene). + /// + Iterable listLights() { + return _lights; + } + + @override + Iterable listEntities() { + return _entities; + } + + void registerSelected(FilamentEntity entity) { + selected = entity; + _onUpdatedController.add(true); + } + + void unregisterSelected() { + selected = null; + _onUpdatedController.add(true); + } + + @override + void select(FilamentEntity entity) { + selected = entity; + _gizmo.attach(entity); + _onUpdatedController.add(true); + } +} diff --git a/lib/camera/camera_orientation.dart b/lib/filament/utils/camera_orientation.dart similarity index 100% rename from lib/camera/camera_orientation.dart rename to lib/filament/utils/camera_orientation.dart diff --git a/lib/hardware/hardware_keyboard_listener.dart b/lib/filament/utils/hardware_keyboard_listener.dart similarity index 93% rename from lib/hardware/hardware_keyboard_listener.dart rename to lib/filament/utils/hardware_keyboard_listener.dart index 5fca3e8a..af1412ef 100644 --- a/lib/hardware/hardware_keyboard_listener.dart +++ b/lib/filament/utils/hardware_keyboard_listener.dart @@ -1,6 +1,5 @@ import 'package:flutter/services.dart'; -import 'package:flutter_filament/entities/entity_transform_controller.dart'; -import 'package:flutter_filament/filament_controller.dart'; +import 'package:flutter_filament/filament/entities/entity_transform_controller.dart'; class HardwareKeyboardListener { final EntityTransformController _controller; diff --git a/lib/hardware/hardware_keyboard_poll.dart b/lib/filament/utils/hardware_keyboard_poll.dart similarity index 85% rename from lib/hardware/hardware_keyboard_poll.dart rename to lib/filament/utils/hardware_keyboard_poll.dart index c5473627..38066953 100644 --- a/lib/hardware/hardware_keyboard_poll.dart +++ b/lib/filament/utils/hardware_keyboard_poll.dart @@ -1,15 +1,13 @@ import 'dart:async'; import 'package:flutter/services.dart'; -import 'package:flutter_filament/entities/entity_transform_controller.dart'; -import 'package:flutter_filament/filament_controller.dart'; +import 'package:flutter_filament/filament/entities/entity_transform_controller.dart'; class HardwareKeyboardPoll { final EntityTransformController _controller; late Timer _timer; HardwareKeyboardPoll(this._controller) { _timer = Timer.periodic(const Duration(milliseconds: 16), (_) { - print(RawKeyboard.instance.keysPressed); if (RawKeyboard.instance.keysPressed.contains(LogicalKeyboardKey.keyW)) { _controller.forwardPressed(); } else { diff --git a/lib/lights/light_options.dart b/lib/filament/utils/light_options.dart similarity index 100% rename from lib/lights/light_options.dart rename to lib/filament/utils/light_options.dart diff --git a/lib/filament/utils/using_pointer.dart b/lib/filament/utils/using_pointer.dart new file mode 100644 index 00000000..d192ec03 --- /dev/null +++ b/lib/filament/utils/using_pointer.dart @@ -0,0 +1,10 @@ +import 'dart:ffi'; + +import 'package:ffi/ffi.dart'; + +final allocator = calloc; + +void using(Pointer ptr, Future Function(Pointer ptr) function) async { + await function.call(ptr); + allocator.free(ptr); +} diff --git a/lib/widgets/camera_options_widget.dart b/lib/filament/widgets/camera_options_widget.dart similarity index 98% rename from lib/widgets/camera_options_widget.dart rename to lib/filament/widgets/camera_options_widget.dart index c2273fa4..101ac6b4 100644 --- a/lib/widgets/camera_options_widget.dart +++ b/lib/filament/widgets/camera_options_widget.dart @@ -1,7 +1,8 @@ import 'package:flutter/material.dart'; import 'package:flutter/widgets.dart'; -import 'package:flutter_filament/camera/camera_orientation.dart'; -import 'package:flutter_filament/filament_controller.dart'; + +import 'package:flutter_filament/filament/filament_controller.dart'; +import 'package:flutter_filament/filament/utils/camera_orientation.dart'; import 'dart:math'; import 'package:vector_math/vector_math_64.dart' as v64; diff --git a/lib/filament/widgets/debug/entity_list_widget.dart b/lib/filament/widgets/debug/entity_list_widget.dart new file mode 100644 index 00000000..cb2fde0f --- /dev/null +++ b/lib/filament/widgets/debug/entity_list_widget.dart @@ -0,0 +1,147 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_filament/filament/entities/gizmo.dart'; +import 'package:flutter_filament/filament/filament_controller.dart'; + +class EntityListWidget extends StatefulWidget { + final FilamentController? controller; + + const EntityListWidget({super.key, required this.controller}); + + @override + State createState() => _EntityListWidget(); +} + +class _EntityListWidget extends State { + @override + void didUpdateWidget(EntityListWidget oldWidget) { + super.didUpdateWidget(oldWidget); + } + + Widget _entity(FilamentEntity entity) { + return FutureBuilder( + future: widget.controller!.getAnimationNames(entity), + builder: (_, animations) { + if (animations.data == null) { + return Container(); + } + final menuController = MenuController(); + return Row(children: [ + Expanded( + child: GestureDetector( + onTap: () { + widget.controller!.scene.select(entity); + }, + child: Text(entity.toString(), + style: TextStyle( + fontWeight: + entity == widget.controller!.scene.selected + ? FontWeight.bold + : FontWeight.normal)))), + MenuAnchor( + controller: menuController, + child: Container( + color: Colors.transparent, + child: IconButton( + icon: const Icon( + Icons.arrow_drop_down, + color: Colors.black, + ), + onPressed: () { + menuController.open(); + }, + )), + menuChildren: [ + MenuItemButton( + child: const Text("Remove"), + onPressed: () async { + await widget.controller!.removeEntity(entity); + }), + MenuItemButton( + child: const Text("Transform to unit cube"), + onPressed: () async { + await widget.controller!.transformToUnitCube(entity); + }), + SubmenuButton( + child: const Text("Animations"), + menuChildren: animations.data! + .map((a) => MenuItemButton( + child: Text(a), + onPressed: () { + widget.controller!.playAnimation( + entity, animations.data!.indexOf(a)); + }, + )) + .toList()) + ]) + ]); + }); + } + + Widget _light(FilamentEntity entity) { + final controller = MenuController(); + return Row(children: [ + GestureDetector( + onTap: () { + widget.controller!.scene.select(entity); + }, + child: Container( + color: Colors.transparent, + child: Text("Light $entity", + style: TextStyle( + fontWeight: entity == widget.controller!.scene.selected + ? FontWeight.bold + : FontWeight.normal)))), + MenuAnchor( + controller: controller, + child: Container( + color: Colors.transparent, + child: IconButton( + icon: const Icon( + Icons.arrow_drop_down, + color: Colors.black, + ), + onPressed: () { + controller.open(); + }, + )), + menuChildren: [ + MenuItemButton( + child: const Text("Remove"), + onPressed: () async { + await widget.controller!.removeLight(entity); + }) + ]) + ]); + } + + @override + Widget build(BuildContext context) { + if (widget.controller == null) { + return Container(); + } + return ValueListenableBuilder( + valueListenable: widget.controller!.hasViewer, + builder: (_, bool hasViewer, __) => !hasViewer + ? Container() + : StreamBuilder( + stream: widget.controller!.scene.onUpdated, + builder: (_, __) => Container( + padding: const EdgeInsets.symmetric( + horizontal: 30, vertical: 10), + height: 100, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(30), + color: Colors.white.withOpacity(0.25), + ), + child: ListView( + reverse: true, + children: widget.controller!.scene + .listLights() + .map(_light) + .followedBy(widget.controller!.scene + .listEntities() + .map(_entity)) + .cast() + .toList())))); + } +} diff --git a/lib/widgets/entity_controller_mouse_widget.dart b/lib/filament/widgets/entity_controller_mouse_widget.dart similarity index 94% rename from lib/widgets/entity_controller_mouse_widget.dart rename to lib/filament/widgets/entity_controller_mouse_widget.dart index 5aeb897e..f8f8cd51 100644 --- a/lib/widgets/entity_controller_mouse_widget.dart +++ b/lib/filament/widgets/entity_controller_mouse_widget.dart @@ -1,8 +1,10 @@ import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_filament/entities/entity_transform_controller.dart'; + import 'dart:async'; +import 'package:flutter_filament/filament/entities/entity_transform_controller.dart'; + /// /// A widget that translates mouse gestures to zoom/pan/rotate actions. /// diff --git a/lib/widgets/filament_gesture_detector.dart b/lib/filament/widgets/filament_gesture_detector.dart similarity index 59% rename from lib/widgets/filament_gesture_detector.dart rename to lib/filament/widgets/filament_gesture_detector.dart index fa697e67..fecc1f65 100644 --- a/lib/widgets/filament_gesture_detector.dart +++ b/lib/filament/widgets/filament_gesture_detector.dart @@ -2,8 +2,8 @@ import 'dart:io'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_filament/widgets/filament_gesture_detector_desktop.dart'; -import 'package:flutter_filament/widgets/filament_gesture_detector_mobile.dart'; +import 'package:flutter_filament/filament/widgets/filament_gesture_detector_desktop.dart'; +import 'package:flutter_filament/filament/widgets/filament_gesture_detector_mobile.dart'; import '../filament_controller.dart'; enum GestureType { rotateCamera, panCamera, panBackground } @@ -59,26 +59,35 @@ class FilamentGestureDetector extends StatelessWidget { @override Widget build(BuildContext context) { - if (kIsWeb) { - throw Exception("TODO"); - } else if (Platform.isLinux || Platform.isWindows || Platform.isMacOS) { - return FilamentGestureDetectorDesktop( - controller: controller, - child: child, - showControlOverlay: showControlOverlay, - enableCamera: enableCamera, - enablePicking: enablePicking, - ); - } else { - return FilamentGestureDetectorMobile( - controller: controller, - child: child, - showControlOverlay: showControlOverlay, - enableCamera: enableCamera, - enablePicking: enablePicking, - onScaleStart: onScaleStart, - onScaleUpdate: onScaleUpdate, - onScaleEnd: onScaleEnd); - } + return ValueListenableBuilder( + valueListenable: controller.hasViewer, + builder: (_, bool hasViewer, __) { + if (!hasViewer) { + return Container(child: child); + } + if (kIsWeb) { + throw Exception("TODO"); + } else if (Platform.isLinux || + Platform.isWindows || + Platform.isMacOS) { + return FilamentGestureDetectorDesktop( + controller: controller, + child: child, + showControlOverlay: showControlOverlay, + enableCamera: enableCamera, + enablePicking: enablePicking, + ); + } else { + return FilamentGestureDetectorMobile( + controller: controller, + child: child, + showControlOverlay: showControlOverlay, + enableCamera: enableCamera, + enablePicking: enablePicking, + onScaleStart: onScaleStart, + onScaleUpdate: onScaleUpdate, + onScaleEnd: onScaleEnd); + } + }); } } diff --git a/lib/widgets/filament_gesture_detector_desktop.dart b/lib/filament/widgets/filament_gesture_detector_desktop.dart similarity index 86% rename from lib/widgets/filament_gesture_detector_desktop.dart rename to lib/filament/widgets/filament_gesture_detector_desktop.dart index ceef215f..f05e149a 100644 --- a/lib/widgets/filament_gesture_detector_desktop.dart +++ b/lib/filament/widgets/filament_gesture_detector_desktop.dart @@ -2,6 +2,7 @@ import 'dart:async'; import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_filament/filament/entities/gizmo.dart'; import '../filament_controller.dart'; /// @@ -60,6 +61,13 @@ class _FilamentGestureDetectorDesktopState bool _pointerMoving = false; + Gizmo get _gizmo => widget.controller.scene.gizmo; + + @override + void initState() { + super.initState(); + } + @override void didUpdateWidget(FilamentGestureDetectorDesktop oldWidget) { if (widget.showControlOverlay != oldWidget.showControlOverlay || @@ -91,9 +99,21 @@ class _FilamentGestureDetectorDesktopState }); } + Timer? _pickTimer; + @override Widget build(BuildContext context) { return Listener( + // onPointerHover: (event) async { + // if (_gizmo.isActive) { + // return; + // } + // _pickTimer?.cancel(); + // _pickTimer = Timer(const Duration(milliseconds: 100), () async { + // widget.controller + // .pick(event.position.dx.toInt(), event.position.dy.toInt()); + // }); + // }, onPointerSignal: (PointerSignalEvent pointerSignal) async { if (pointerSignal is PointerScrollEvent) { if (widget.enableCamera) { @@ -106,10 +126,10 @@ class _FilamentGestureDetectorDesktopState onPointerPanZoomStart: (pzs) { throw Exception("TODO - is this a pinch zoom on laptop trackpad?"); }, - // ignore all pointer down events - // so we can wait to see if the pointer will be held/moved (interpreted as rotate/pan), - // or if this is a single mousedown event (interpreted as viewport pick) onPointerDown: (d) async { + if (_gizmo.isActive) { + return; + } if (d.buttons != kTertiaryButton && widget.enablePicking) { widget.controller .pick(d.localPosition.dx.toInt(), d.localPosition.dy.toInt()); @@ -118,6 +138,10 @@ class _FilamentGestureDetectorDesktopState }, // holding/moving the left mouse button is interpreted as a pan, middle mouse button as a rotate onPointerMove: (PointerMoveEvent d) async { + if (_gizmo.isActive) { + _gizmo.translate(d.delta); + return; + } // if this is the first move event, we need to call rotateStart/panStart to set the first coordinates if (!_pointerMoving) { if (d.buttons == kTertiaryButton && widget.enableCamera) { @@ -142,6 +166,11 @@ class _FilamentGestureDetectorDesktopState // 2) if _pointerMoving is false, this is interpreted as a pick // same applies to middle mouse button, but this is ignored as a pick onPointerUp: (PointerUpEvent d) async { + if (_gizmo.isActive) { + _gizmo.reset(); + return; + } + if (d.buttons == kTertiaryButton && widget.enableCamera) { widget.controller.rotateEnd(); } else { diff --git a/lib/widgets/filament_gesture_detector_mobile.dart b/lib/filament/widgets/filament_gesture_detector_mobile.dart similarity index 100% rename from lib/widgets/filament_gesture_detector_mobile.dart rename to lib/filament/widgets/filament_gesture_detector_mobile.dart diff --git a/lib/widgets/filament_widget.dart b/lib/filament/widgets/filament_widget.dart similarity index 99% rename from lib/widgets/filament_widget.dart rename to lib/filament/widgets/filament_widget.dart index e40f498c..500645fb 100644 --- a/lib/widgets/filament_widget.dart +++ b/lib/filament/widgets/filament_widget.dart @@ -6,7 +6,7 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; -import 'package:flutter_filament/filament_controller.dart'; +import 'package:flutter_filament/filament/filament_controller.dart'; import 'dart:async'; diff --git a/lib/widgets/ibl_rotation_slider.dart b/lib/filament/widgets/ibl_rotation_slider.dart similarity index 92% rename from lib/widgets/ibl_rotation_slider.dart rename to lib/filament/widgets/ibl_rotation_slider.dart index 2efde291..2d114f56 100644 --- a/lib/widgets/ibl_rotation_slider.dart +++ b/lib/filament/widgets/ibl_rotation_slider.dart @@ -2,7 +2,7 @@ import 'dart:math'; import 'package:flutter/material.dart'; import 'package:flutter/widgets.dart'; -import 'package:flutter_filament/filament_controller.dart'; +import 'package:flutter_filament/filament/filament_controller.dart'; import 'package:vector_math/vector_math_64.dart' as v; class IblRotationSliderWidget extends StatefulWidget { diff --git a/lib/widgets/light_slider.dart b/lib/filament/widgets/light_slider.dart similarity index 98% rename from lib/widgets/light_slider.dart rename to lib/filament/widgets/light_slider.dart index 56e25b24..d158e453 100644 --- a/lib/widgets/light_slider.dart +++ b/lib/filament/widgets/light_slider.dart @@ -2,8 +2,8 @@ import 'dart:math'; import 'package:flutter/material.dart'; import 'package:flutter/widgets.dart'; -import 'package:flutter_filament/filament_controller.dart'; -import 'package:flutter_filament/lights/light_options.dart'; +import 'package:flutter_filament/filament/filament_controller.dart'; +import 'package:flutter_filament/filament/utils/light_options.dart'; import 'package:vector_math/vector_math_64.dart' as v; class LightSliderWidget extends StatefulWidget { diff --git a/lib/flutter_filament.dart b/lib/flutter_filament.dart new file mode 100644 index 00000000..811a0d62 --- /dev/null +++ b/lib/flutter_filament.dart @@ -0,0 +1,12 @@ +library flutter_filament; + +export 'filament/filament_controller.dart'; +export 'filament/filament_controller_ffi.dart'; +export 'filament/animations/animation_builder.dart'; +export 'filament/animations/animation_data.dart'; +export 'filament/widgets/camera_options_widget.dart'; +export 'filament/widgets/filament_gesture_detector.dart'; +export 'filament/widgets/filament_widget.dart'; +export 'filament/widgets/debug/entity_list_widget.dart'; +export 'filament/entities/entity_transform_controller.dart'; +export 'filament/widgets/entity_controller_mouse_widget.dart'; diff --git a/materials/gizmo.mat b/materials/gizmo.mat new file mode 100644 index 00000000..a655f786 --- /dev/null +++ b/materials/gizmo.mat @@ -0,0 +1,40 @@ +material { + name : Gizmo, + parameters : [ + { + type : mat4, + name : transform, + precision : high + }, + { + type : float3, + name : color, + precision : low + } + ], + depthWrite : true, + depthCulling : false, + shadingModel : unlit, + variantFilter : [ skinning, shadowReceiver, vsm ], + culling: none, + instanced: false, + vertexDomain: object +} + +vertex { + void materialVertex(inout MaterialVertexInputs material) { + vec4 modelSpace = getPosition(); + vec4 worldSpace = getWorldFromModelMatrix() * modelSpace; + vec4 clipSpace = getClipFromWorldMatrix() * worldSpace; + clipSpace.z = 0.99f; + material.worldPosition = getWorldFromClipMatrix() * clipSpace; + } +} + +fragment { + void material(inout MaterialInputs material) { + prepareMaterial(material); + material.baseColor = float4(materialParams.color, 1.0f); + } +} + From cfab7eac3bef06f90746d212bdb68edfefc292ad Mon Sep 17 00:00:00 2001 From: Nick Fisher Date: Mon, 25 Mar 2024 22:22:38 +0800 Subject: [PATCH 52/52] update path for generated bindings --- pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pubspec.yaml b/pubspec.yaml index ca8c4796..cd924faa 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -24,7 +24,7 @@ dev_dependencies: ffigen: ^9.0.1 ffigen: - output: 'lib/generated_bindings.dart' + output: 'lib/filament/generated_bindings.dart' headers: entry-points: - 'ios/include/FlutterFilamentFFIApi.h'